/ Check-in [d43b3c05]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix SQLITE_DBSTATUS_SCHEMA_USED so that it works with SQLITE_OPEN_SHARED_SCHEMA connections.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | reuse-schema
Files: files | file ages | folders
SHA3-256:d43b3c056cb13930865c504c9498b2c83e4bebce9bff01ee21293e7dc7a6711e
User & Date: dan 2019-02-14 21:04:27
Wiki:reuse-schema
Context
2019-02-15
11:54
Revert the rearrangement of VDBE code in [219b39e14] so that vdbe.c matches trunk. Since the new call to sqlite3Init() in OP_ParseSchema was removed, the rearrangement no longer provides any performance advantage. check-in: 03c4f003 user: dan tags: reuse-schema
2019-02-14
21:04
Fix SQLITE_DBSTATUS_SCHEMA_USED so that it works with SQLITE_OPEN_SHARED_SCHEMA connections. check-in: d43b3c05 user: dan tags: reuse-schema
18:38
Change the name of the SQLITE_OPEN_REUSE_SCHEMA flag to SQLITE_OPEN_SHARED_SCHEMA. check-in: 7257fcc8 user: dan tags: reuse-schema
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to doc/shared_schema.md.

    29     29   If the schema of a database attached to an
    30     30   SQLITE_OPEN_SHARED_SCHEMA database handle is corrupt, or if
    31     31   corruption is encountered while parsing the database schema, then the
    32     32   database is treated as empty. This usually means that corruption results in
    33     33   a "no such table: xxx" error instead of a more specific error message.
    34     34   
    35     35   For SQLITE_OPEN_SHARED_SCHEMA connections, the
    36         -SQLITE_DBSTATUS_SCHEMA_USED sqlite3_db_used() distributes
    37         -the memory used for a shared schema object evenly between all database
    38         -connections that share it.
           36  +SQLITE_DBSTATUS_SCHEMA_USED sqlite3_db_status() verb
           37  +distributes the memory used for a shared schema object evenly between all
           38  +database connections that share it.
    39     39   
    40     40   ## Implementation Notes
    41     41   
    42     42   A single Schema object is never used by more than one database simultaneously,
    43     43   regardless of whether or not those databases are attached to the same or
    44     44   different database handles. Instead, a pool of schema objects is maintained 
    45     45   for each unique sqlite_master-contents/schema-cookie combination

Changes to src/build.c.

   290    290   **
   291    291   ** If the database handle was not opened with SQLITE_OPEN_SHARED_SCHEMA, or
   292    292   ** if the schema for database iDb is already loaded, this function is a no-op.
   293    293   **
   294    294   ** Non-zero is returned if a schema is loaded, or zero if it was already 
   295    295   ** loaded when this function was called..
   296    296   */
   297         -static int loadSharableSchema(sqlite3 *db, int iDb){
          297  +int sqlite3SchemaLoad(sqlite3 *db, int iDb){
   298    298     if( IsReuseSchema(db) 
   299    299      && DbHasProperty(db, iDb, DB_SchemaLoaded)==0 
   300    300      && (db->init.busy==0 || (iDb!=1 && db->init.iDb==1))
   301    301     ){
   302    302       char *zDummy = 0;
   303    303       struct sqlite3InitInfo sv = db->init;
   304    304       memset(&db->init, 0, sizeof(struct sqlite3InitInfo));
................................................................................
   337    337   #endif
   338    338     while(1){
   339    339       for(i=OMIT_TEMPDB; i<db->nDb; i++){
   340    340         int j = (i<2) ? i^1 : i;   /* Search TEMP before MAIN */
   341    341         if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
   342    342           int bUnload;
   343    343           assert( sqlite3SchemaMutexHeld(db, j, 0) );
   344         -        bUnload = loadSharableSchema(db, j);
          344  +        bUnload = sqlite3SchemaLoad(db, j);
   345    345           p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
   346    346           if( p ) return p;
   347    347           if( bUnload ){
   348    348             sqlite3SchemaRelease(db, j);
   349    349           }
   350    350         }
   351    351       }
................................................................................
   394    394       ** can be an eponymous virtual table. */
   395    395       if( pParse->disableVtab==0 ){
   396    396         Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
   397    397         if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
   398    398           pMod = sqlite3PragmaVtabRegister(db, zName);
   399    399         }
   400    400         if( pMod ){
   401         -        loadSharableSchema(db, 0);
          401  +        sqlite3SchemaLoad(db, 0);
   402    402           if( sqlite3VtabEponymousTableInit(pParse, pMod) ){
   403    403             return pMod->pEpoTab;
   404    404           }
   405    405         }
   406    406       }
   407    407   #endif
   408    408       if( flags & LOCATE_NOERR ) return 0;

Changes to src/callback.c.

   544    544   #ifdef SQLITE_TEST
   545    545   /*
   546    546   ** Return a pointer to the head of the linked list of SchemaPool objects.
   547    547   ** This is used by the virtual table in file test_schemapool.c.
   548    548   */
   549    549   SchemaPool *sqlite3SchemaPoolList(void){ return schemaPoolList; }
   550    550   #endif
          551  +
          552  +/*
          553  +** Database handle db was opened with the SHARED_SCHEMA flag, and database
          554  +** iDb is currently connected to a schema-pool. When this function is called,
          555  +** (*pnByte) is set to nInit plus the amount of memory used to store a 
          556  +** single instance of the Schema objects managed by the schema-pool.
          557  +** This function adjusts (*pnByte) sot hat it is set to nInit plus
          558  +** (nSchema/nRef) of the amount of memory used by a single Schema object,
          559  +** where nSchema is the number of Schema objects allocated by this pool,
          560  +** and nRef is the number of connections to the schema-pool.
          561  +*/
          562  +void sqlite3SchemaAdjustUsed(sqlite3 *db, int iDb, int nInit, int *pnByte){
          563  +  SchemaPool *pSPool = db->aDb[iDb].pSPool;
          564  +  int nSchema = 0;
          565  +  Schema *p;
          566  +  sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
          567  +  for(p=pSPool->pSchema; p; p=p->pNext){
          568  +    nSchema++;
          569  +  }
          570  +  *pnByte = nInit + ((*pnByte - nInit) * nSchema) / pSPool->nRef;
          571  +  sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
          572  +}
   551    573   
   552    574   /*
   553    575   ** Check that the schema of db iDb is writable (either because it is the 
   554    576   ** temp db schema or because the db handle was opened without
   555    577   ** SQLITE_OPEN_SHARED_SCHEMA). If so, do nothing. Otherwise, leave an 
   556    578   ** error in the Parse object.
   557    579   */

Changes to src/sqliteInt.h.

  4321   4321   void sqlite3RegisterLikeFunctions(sqlite3*, int);
  4322   4322   int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
  4323   4323   void sqlite3SchemaClear(void *);
  4324   4324   int sqlite3SchemaConnect(sqlite3*, int, u64);
  4325   4325   int sqlite3SchemaDisconnect(sqlite3 *, int, int);
  4326   4326   void sqlite3SchemaClearOrDisconnect(sqlite3*, int);
  4327   4327   Schema *sqlite3SchemaExtract(SchemaPool*);
         4328  +int sqlite3SchemaLoad(sqlite3*, int);
  4328   4329   void sqlite3SchemaReleaseAll(sqlite3*);
  4329   4330   void sqlite3SchemaRelease(sqlite3*, int);
         4331  +void sqlite3SchemaAdjustUsed(sqlite3*, int, int, int*);
  4330   4332   void sqlite3SchemaWritable(Parse*, int);
  4331   4333   Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
  4332   4334   int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
  4333   4335   KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
  4334   4336   void sqlite3KeyInfoUnref(KeyInfo*);
  4335   4337   KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
  4336   4338   KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);

Changes to src/status.c.

   271    271       ** *pCurrent gets an accurate estimate of the amount of memory used
   272    272       ** to store the schema for all databases (main, temp, and any ATTACHed
   273    273       ** databases.  *pHighwater is set to zero.
   274    274       */
   275    275       case SQLITE_DBSTATUS_SCHEMA_USED: {
   276    276         int i;                      /* Used to iterate through schemas */
   277    277         int nByte = 0;              /* Used to accumulate return value */
          278  +      int bReleaseSchema;
   278    279   
   279    280         sqlite3BtreeEnterAll(db);
          281  +      bReleaseSchema = sqlite3LockReusableSchema(db);
   280    282         db->pnBytesFreed = &nByte;
   281    283         for(i=0; i<db->nDb; i++){
   282         -        Schema *pSchema = db->aDb[i].pSchema;
          284  +        int bUnload = 0;
          285  +        int nUsed = nByte;
          286  +        Schema *pSchema;
          287  +        if( db->aDb[i].pSPool ){
          288  +          bUnload = sqlite3SchemaLoad(db, i);
          289  +        }
          290  +        pSchema = db->aDb[i].pSchema;
   283    291           if( ALWAYS(pSchema!=0) ){
   284    292             HashElem *p;
   285    293   
   286    294             nByte += sqlite3GlobalConfig.m.xRoundup(sizeof(HashElem)) * (
   287    295                 pSchema->tblHash.count 
   288    296               + pSchema->trigHash.count
   289    297               + pSchema->idxHash.count
................................................................................
   297    305             for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){
   298    306               sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
   299    307             }
   300    308             for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
   301    309               sqlite3DeleteTable(db, (Table *)sqliteHashData(p));
   302    310             }
   303    311           }
          312  +        if( db->aDb[i].pSPool ){
          313  +          if( bUnload ) sqlite3SchemaRelease(db, i);
          314  +          sqlite3SchemaAdjustUsed(db, i, nUsed, &nByte);
          315  +        }
   304    316         }
          317  +      sqlite3UnlockReusableSchema(db, bReleaseSchema);
   305    318         db->pnBytesFreed = 0;
   306    319         sqlite3BtreeLeaveAll(db);
   307    320   
   308    321         *pHighwater = 0;
   309    322         *pCurrent = nByte;
   310    323         break;
   311    324       }

Changes to test/reuse3.test.

    83     83   do_catchsql_test 2.1 {
    84     84     SELECT * FROM x1;
    85     85   } {1 {no such table: x1}}
    86     86   
    87     87   do_catchsql_test 2.2 {
    88     88     SELECT * FROM x1;
    89     89   } {1 {no such table: x1}}
           90  +
           91  +#-------------------------------------------------------------------------
           92  +reset_db
           93  +do_execsql_test 3.0 {
           94  +  CREATE TABLE x1(a, b, c);
           95  +  CREATE INDEX i1 ON x1(a, b, c);
           96  +  CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN
           97  +    SELECT 1, 2, 3, 4, 5;
           98  +  END;
           99  +  INSERT INTO x1 VALUES(1, 2, 3);
          100  +}
          101  +sqlite3 db1 test.db -shared-schema 1
          102  +
          103  +do_test 3.1 {
          104  +  execsql { SELECT * FROM x1 } db1
          105  +  set N [lindex [sqlite3_db_status db1 SCHEMA_USED 0] 1]
          106  +  expr $N==$N
          107  +} 1
          108  +
          109  +sqlite3 db2 test.db -shared-schema 1
          110  +do_test 3.2 {
          111  +  execsql { SELECT * FROM x1 } db2
          112  +  breakpoint
          113  +  set N2 [lindex [sqlite3_db_status db2 SCHEMA_USED 0] 1]
          114  +  expr $N2>($N/2) && $N2<($N/2)+400
          115  +} 1
          116  +
          117  +sqlite3 db3 test.db -shared-schema 1
          118  +sqlite3 db4 test.db -shared-schema 1
          119  +do_test 3.3 {
          120  +  execsql { SELECT * FROM x1 } db3
          121  +  execsql { SELECT * FROM x1 } db4
          122  +  set N4 [lindex [sqlite3_db_status db2 SCHEMA_USED 0] 1]
          123  +  set M [expr 2*($N-$N2)]
          124  +  expr {$N4 == (($M / 4) + $N-$M)}
          125  +} 1
    90    126   
    91    127   finish_test
    92    128   

Changes to test/tclsqlite.test.

    20     20   catch {sqlite3}
    21     21   
    22     22   set testdir [file dirname $argv0]
    23     23   source $testdir/tester.tcl
    24     24   
    25     25   # Check the error messages generated by tclsqlite
    26     26   #
    27         -set r "sqlite_orig HANDLE ?FILENAME? ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN? ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN? ?-reuse-schema BOOLEAN?"
           27  +set r "sqlite_orig HANDLE ?FILENAME? ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN? ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN? ?-shared-schema BOOLEAN?"
    28     28   if {[sqlite3 -has-codec]} {
    29     29     append r " ?-key CODECKEY?"
    30     30   }
    31     31   do_test tcl-1.1 {
    32     32     set v [catch {sqlite3 -bogus} msg]
    33     33     regsub {really_sqlite3} $msg {sqlite3} msg
    34     34     lappend v $msg