/ Check-in [ba0ab042]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Further test cases and fixes for SQLITE_OPEN_SHARED_SCHEMA.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | reuse-schema
Files: files | file ages | folders
SHA3-256: ba0ab042f401a9d3b81dc546573214bbe2ad712c932aafcf0c045f189bbf09ca
User & Date: dan 2019-02-20 18:44:28
Wiki:reuse-schema
Context
2019-02-22
17:44
Merge latest trunk changes into this branch. check-in: 001771af user: dan tags: reuse-schema
2019-02-20
18:44
Further test cases and fixes for SQLITE_OPEN_SHARED_SCHEMA. check-in: ba0ab042 user: dan tags: reuse-schema
17:36
Add test and fixes for SQLITE_OPEN_SHARED_SCHEMA mode. check-in: 9a78d89c user: dan tags: reuse-schema
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/build.c.

   300    300         && DbHasProperty(db, iDb, DB_SchemaLoaded)==0 
   301    301         && (db->init.busy==0 || (iDb!=1 && db->init.iDb==1))
   302    302     ){
   303    303       struct sqlite3InitInfo sv = db->init;
   304    304       memset(&db->init, 0, sizeof(struct sqlite3InitInfo));
   305    305       rc = sqlite3InitOne(db, iDb, pzErr, 0);
   306    306       db->init = sv;
   307         -    *pbUnload = (iDb!=1);
          307  +    *pbUnload = (rc==SQLITE_OK && (iDb!=1));
   308    308     }
   309    309     return rc;
   310    310   }
   311    311   
   312    312   /*
   313    313   ** Locate the in-memory structure that describes a particular database
   314    314   ** table given the name of that table and (optionally) the name of the

Changes to src/callback.c.

   674    674       }
   675    675     }
   676    676   
   677    677     if( p ) p->nRef++;
   678    678   
   679    679     /* If the SchemaPool contains one or more free schemas at the moment, 
   680    680     ** delete one of them. */
   681         -  if( p->pSchema ){
          681  +  if( p && p->pSchema ){
   682    682       Schema *pDel = p->pSchema;
   683    683       p->pSchema = pDel->pNext;
   684    684       schemaDelete(pDel);
   685    685     }
   686    686   
   687    687     sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
   688    688   
................................................................................
   703    703   ** If the bNew parameter is true, then this function may allocate memory. 
   704    704   ** If the allocation attempt fails, then SQLITE_NOMEM is returned and the
   705    705   ** schema-pool is not disconnected from. Or, if no OOM error occurs, 
   706    706   ** SQLITE_OK is returned.
   707    707   */
   708    708   int sqlite3SchemaDisconnect(sqlite3 *db, int iDb, int bNew){
   709    709     int rc = SQLITE_OK;
   710         -  if( IsReuseSchema(db) && iDb!=1 ){
          710  +  if( IsReuseSchema(db) ){
   711    711       Db *pDb = &db->aDb[iDb];
   712    712       SchemaPool *pSPool = pDb->pSPool;
   713    713       assert_schema_state_ok(db);
   714    714       assert( pDb->pSchema );
   715    715   
   716    716       if( pSPool==0 ){
   717    717         assert( pDb->pVTable==0 );
   718         -      if( bNew==0 ){
   719         -        schemaDelete(pDb->pSchema);
   720         -        pDb->pSchema = 0;
   721         -      }
          718  +      assert( bNew==0 );
          719  +      schemaDelete(pDb->pSchema);
          720  +      pDb->pSchema = 0;
   722    721       }else{
   723    722         VTable *p;
   724    723         VTable *pNext;
   725    724         for(p=pDb->pVTable; p; p=pNext){
   726    725           pNext = p->pNext;
   727    726           sqlite3VtabUnlock(p);
   728    727         }
................................................................................
   764    763   /*
   765    764   ** Extract and return a pointer to a schema object from the SchemaPool passed
   766    765   ** as the only argument, if one is available. If one is not available, return
   767    766   ** NULL.
   768    767   */
   769    768   Schema *sqlite3SchemaExtract(SchemaPool *pSPool){
   770    769     Schema *pRet = 0;
   771         -  if( pSPool ){
   772         -    sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
   773         -    if( pSPool->pSchema ){
   774         -      pRet = pSPool->pSchema;
   775         -      pSPool->pSchema = pRet->pNext;
   776         -      pRet->pNext = 0;
   777         -    }
   778         -    sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
          770  +  sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
          771  +  if( pSPool->pSchema ){
          772  +    pRet = pSPool->pSchema;
          773  +    pSPool->pSchema = pRet->pNext;
          774  +    pRet->pNext = 0;
   779    775     }
          776  +  sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
   780    777     return pRet;
   781    778   }
   782    779   
   783    780   /*
   784    781   ** Return all sharable schemas held by database handle db back to their
   785    782   ** respective schema-pools. Db.pSchema variables are left pointing to
   786    783   ** the static, empty, Schema object owned by each schema-pool.
................................................................................
   807    804   ** owned by the schema-pool.
   808    805   */
   809    806   void sqlite3SchemaRelease(sqlite3 *db, int iDb){
   810    807     Db *pDb = &db->aDb[iDb];
   811    808     assert( iDb!=1 );
   812    809     assert_schema_state_ok(db);
   813    810     sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
   814         -  if( pDb->pSPool && DbHasProperty(db, iDb, DB_SchemaLoaded) ){
   815         -    schemaRelease(db, pDb);
   816         -  }
          811  +  schemaRelease(db, pDb);
   817    812     sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
   818    813   }
   819    814   
   820    815   /*
   821    816   ** In most cases, this function finds and returns the schema associated 
   822    817   ** with BTree handle pBt, creating a new one if necessary. However, if
   823    818   ** the database handle was opened with the SQLITE_OPEN_SHARED_SCHEMA flag
   824    819   ** specified, a new, empty, Schema object in memory obtained by 
   825    820   ** sqlite3_malloc() is always returned.
   826    821   */
   827    822   Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
   828    823     Schema *p;
   829    824     if( pBt && (db->openFlags & SQLITE_OPEN_SHARED_SCHEMA)==0 ){
   830         -    p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear);
          825  +    p = (Schema*)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear);
   831    826     }else{
   832         -    p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema));
          827  +    db->lookaside.bDisable++;
          828  +    p = (Schema*)sqlite3DbMallocZero(db, sizeof(Schema));
          829  +    db->lookaside.bDisable--;
   833    830     }
   834    831     if( !p ){
   835    832       sqlite3OomFault(db);
   836    833     }else if ( 0==p->file_format ){
   837    834       sqlite3HashInit(&p->tblHash);
   838    835       sqlite3HashInit(&p->idxHash);
   839    836       sqlite3HashInit(&p->trigHash);
   840    837       sqlite3HashInit(&p->fkeyHash);
   841    838       p->enc = SQLITE_UTF8;
   842    839     }
   843    840     return p;
   844    841   }

Changes to src/prepare.c.

   136    136       if( SQLITE_OK!=rc ){
   137    137         if( db->init.orphanTrigger ){
   138    138           assert( iDb==1 );
   139    139         }else{
   140    140           pData->rc = rc;
   141    141           if( rc==SQLITE_NOMEM ){
   142    142             sqlite3OomFault(db);
   143         -        }else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
          143  +        }else if( rc!=SQLITE_INTERRUPT 
          144  +               && (rc&0xFF)!=SQLITE_LOCKED 
          145  +               && (rc&0xFF)!=SQLITE_IOERR
          146  +        ){
   144    147             corruptSchema(pData, argv[0], sqlite3_errmsg(db));
   145    148           }
   146    149         }
   147    150       }
   148    151       sqlite3_finalize(pStmt);
   149    152     }else if( argv[0]==0 || (argv[2]!=0 && argv[2][0]!=0) ){
   150    153       corruptSchema(pData, argv[0], 0);

Changes to src/vdbe.c.

  3288   3288       }
  3289   3289     }
  3290   3290     assert( pOp->p5==0 || pOp->p4type==P4_INT32 );
  3291   3291     if( pOp->p5
  3292   3292      && (iMeta!=pOp->p3
  3293   3293         || db->aDb[pOp->p1].pSchema->iGeneration!=pOp->p4.i)
  3294   3294     ){
  3295         -    /*
  3296         -    ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema
  3297         -    ** version is checked to ensure that the schema has not changed since the
  3298         -    ** SQL statement was prepared.
  3299         -    */
  3300         -    sqlite3DbFree(db, p->zErrMsg);
  3301         -    p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
  3302   3295       /* If the schema-cookie from the database file matches the cookie 
  3303   3296       ** stored with the in-memory representation of the schema, do
  3304   3297       ** not reload the schema from the database file.
  3305   3298       **
  3306   3299       ** If virtual-tables are in use, this is not just an optimization.
  3307   3300       ** Often, v-tables store their data in other SQLite tables, which
  3308   3301       ** are queried from within xNext() and other v-table methods using
................................................................................
  3311   3304       ** v-table would have to be ready for the sqlite3_vtab structure itself
  3312   3305       ** to be invalidated whenever sqlite3_step() is called from within 
  3313   3306       ** a v-table method.
  3314   3307       */
  3315   3308       if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){
  3316   3309         sqlite3ResetOneSchema(db, pOp->p1);
  3317   3310       }
         3311  +    /*
         3312  +    ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema
         3313  +    ** version is checked to ensure that the schema has not changed since the
         3314  +    ** SQL statement was prepared.
         3315  +    */
         3316  +    sqlite3DbFree(db, p->zErrMsg);
         3317  +    p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
  3318   3318       p->expired = 1;
  3319   3319       rc = SQLITE_SCHEMA;
  3320   3320     }
  3321   3321     if( rc ) goto abort_due_to_error;
  3322   3322     break;
  3323   3323   }
  3324   3324   

Changes to test/reuse3.test.

   269    269     UPDATE sqlite_master SET sql='CREATE TABLE t3(a,b)' WHERE name = 't3';
   270    270   }
   271    271   
   272    272   do_test 5.5 { 
   273    273     catchsql { SELECT nref,nschema FROM schemapool } db2
   274    274   } {0 {1 1}}
   275    275   
          276  +db2 close
          277  +db close
          278  +do_test 5.6.1 {
          279  +  forcedelete test.db2 test.db2-wal test.db2-journal
          280  +  forcecopy test.db test.db2
          281  +  sqlite3 db test.db
          282  +  sqlite3 db2 test.db  -shared-schema 1
          283  +  sqlite3 db3 test.db2 -shared-schema 1
          284  +  register_schemapool_module db
          285  +} {}
          286  +
          287  +do_execsql_test -db db2 5.6.2 { SELECT * FROM t1 }
          288  +do_execsql_test -db db3 5.6.3 { SELECT * FROM t1 }
          289  +do_execsql_test 5.6.4 {
          290  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool; 
          291  +  CREATE TABLE t4(x);
          292  +  DROP TABLE t4;
          293  +} {nref=2 nschema=1}
          294  +do_execsql_test -db db2 5.6.5 { SELECT * FROM t1 }
          295  +do_execsql_test -db db3 5.6.6 { SELECT * FROM t1 }
          296  +do_execsql_test 5.6.7 {
          297  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool; 
          298  +  ATTACH 'test.db2' AS db2;
          299  +  CREATE TABLE db2.t4(x);
          300  +  DROP TABLE db2.t4;
          301  +} {nref=1 nschema=1 nref=1 nschema=1}
          302  +do_execsql_test -db db2 5.6.8 { SELECT * FROM t1 }
          303  +do_execsql_test -db db3 5.6.9 { SELECT * FROM t1 }
          304  +do_execsql_test 5.6.10 {
          305  +  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool; 
          306  +} {nref=2 nschema=1}
          307  +
   276    308   finish_test
   277    309   

Added test/reusefault.test.

            1  +# 2019 February 12
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +#
           13  +
           14  +set testdir [file dirname $argv0]
           15  +source $testdir/tester.tcl
           16  +set testprefix reusefault
           17  +
           18  +do_execsql_test 1.0 {
           19  +  PRAGMA cache_size = 10;
           20  +  CREATE TABLE t1(a UNIQUE, b UNIQUE);
           21  +  INSERT INTO t1 VALUES(1, 2), (3, 4);
           22  +}
           23  +faultsim_save_and_close
           24  +
           25  +do_faultsim_test 1.1 -prep {
           26  +  faultsim_restore
           27  +  sqlite3 db test.db -shared-schema 1
           28  +} -body {
           29  +  execsql { SELECT * FROM t1 }
           30  +} -test {
           31  +  faultsim_test_result {0 {1 2 3 4}}
           32  +}
           33  +
           34  +do_faultsim_test 1.2 -prep {
           35  +  faultsim_restore
           36  +  sqlite3 db test.db -shared-schema 1
           37  +  execsql { SELECT * FROM t1 }
           38  +  sqlite3 db2 test.db
           39  +  db2 eval {CREATE TABLE a(a)}
           40  +  db2 close
           41  +} -body {
           42  +  execsql { SELECT  * FROM t1 }
           43  +} -test {
           44  +  faultsim_test_result {0 {1 2 3 4}}
           45  +}
           46  +
           47  +
           48  +finish_test
           49  +