/ Check-in [ba0ab042]
Login

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 Unified Diffs Show Whitespace Changes Patch

Changes to src/build.c.

300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
      && DbHasProperty(db, iDb, DB_SchemaLoaded)==0 
      && (db->init.busy==0 || (iDb!=1 && db->init.iDb==1))
  ){
    struct sqlite3InitInfo sv = db->init;
    memset(&db->init, 0, sizeof(struct sqlite3InitInfo));
    rc = sqlite3InitOne(db, iDb, pzErr, 0);
    db->init = sv;
    *pbUnload = (iDb!=1);
  }
  return rc;
}

/*
** Locate the in-memory structure that describes a particular database
** table given the name of that table and (optionally) the name of the







|







300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
      && DbHasProperty(db, iDb, DB_SchemaLoaded)==0 
      && (db->init.busy==0 || (iDb!=1 && db->init.iDb==1))
  ){
    struct sqlite3InitInfo sv = db->init;
    memset(&db->init, 0, sizeof(struct sqlite3InitInfo));
    rc = sqlite3InitOne(db, iDb, pzErr, 0);
    db->init = sv;
    *pbUnload = (rc==SQLITE_OK && (iDb!=1));
  }
  return rc;
}

/*
** Locate the in-memory structure that describes a particular database
** table given the name of that table and (optionally) the name of the

Changes to src/callback.c.

674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
...
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
...
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
...
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
...
825
826
827
828
829
830
831

832

833
834
835
836
837
838
839
840
841
842
843
844
    }
  }

  if( p ) p->nRef++;

  /* If the SchemaPool contains one or more free schemas at the moment, 
  ** delete one of them. */
  if( p->pSchema ){
    Schema *pDel = p->pSchema;
    p->pSchema = pDel->pNext;
    schemaDelete(pDel);
  }

  sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );

................................................................................
** If the bNew parameter is true, then this function may allocate memory. 
** If the allocation attempt fails, then SQLITE_NOMEM is returned and the
** schema-pool is not disconnected from. Or, if no OOM error occurs, 
** SQLITE_OK is returned.
*/
int sqlite3SchemaDisconnect(sqlite3 *db, int iDb, int bNew){
  int rc = SQLITE_OK;
  if( IsReuseSchema(db) && iDb!=1 ){
    Db *pDb = &db->aDb[iDb];
    SchemaPool *pSPool = pDb->pSPool;
    assert_schema_state_ok(db);
    assert( pDb->pSchema );

    if( pSPool==0 ){
      assert( pDb->pVTable==0 );
      if( bNew==0 ){
        schemaDelete(pDb->pSchema);
        pDb->pSchema = 0;
      }
    }else{
      VTable *p;
      VTable *pNext;
      for(p=pDb->pVTable; p; p=pNext){
        pNext = p->pNext;
        sqlite3VtabUnlock(p);
      }
................................................................................
/*
** Extract and return a pointer to a schema object from the SchemaPool passed
** as the only argument, if one is available. If one is not available, return
** NULL.
*/
Schema *sqlite3SchemaExtract(SchemaPool *pSPool){
  Schema *pRet = 0;
  if( pSPool ){
    sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
    if( pSPool->pSchema ){
      pRet = pSPool->pSchema;
      pSPool->pSchema = pRet->pNext;
      pRet->pNext = 0;
    }
    sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
  }
  return pRet;
}

/*
** Return all sharable schemas held by database handle db back to their
** respective schema-pools. Db.pSchema variables are left pointing to
** the static, empty, Schema object owned by each schema-pool.
................................................................................
** owned by the schema-pool.
*/
void sqlite3SchemaRelease(sqlite3 *db, int iDb){
  Db *pDb = &db->aDb[iDb];
  assert( iDb!=1 );
  assert_schema_state_ok(db);
  sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
  if( pDb->pSPool && DbHasProperty(db, iDb, DB_SchemaLoaded) ){
    schemaRelease(db, pDb);
  }
  sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
}

/*
** In most cases, this function finds and returns the schema associated 
** with BTree handle pBt, creating a new one if necessary. However, if
** the database handle was opened with the SQLITE_OPEN_SHARED_SCHEMA flag
................................................................................
** sqlite3_malloc() is always returned.
*/
Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
  Schema *p;
  if( pBt && (db->openFlags & SQLITE_OPEN_SHARED_SCHEMA)==0 ){
    p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear);
  }else{

    p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema));

  }
  if( !p ){
    sqlite3OomFault(db);
  }else if ( 0==p->file_format ){
    sqlite3HashInit(&p->tblHash);
    sqlite3HashInit(&p->idxHash);
    sqlite3HashInit(&p->trigHash);
    sqlite3HashInit(&p->fkeyHash);
    p->enc = SQLITE_UTF8;
  }
  return p;
}







|







 







|







|
|
|
<







 







<







<







 







<
|
<







 







>
|
>












674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
...
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720

721
722
723
724
725
726
727
...
763
764
765
766
767
768
769

770
771
772
773
774
775
776

777
778
779
780
781
782
783
...
804
805
806
807
808
809
810

811

812
813
814
815
816
817
818
...
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
    }
  }

  if( p ) p->nRef++;

  /* If the SchemaPool contains one or more free schemas at the moment, 
  ** delete one of them. */
  if( p && p->pSchema ){
    Schema *pDel = p->pSchema;
    p->pSchema = pDel->pNext;
    schemaDelete(pDel);
  }

  sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );

................................................................................
** If the bNew parameter is true, then this function may allocate memory. 
** If the allocation attempt fails, then SQLITE_NOMEM is returned and the
** schema-pool is not disconnected from. Or, if no OOM error occurs, 
** SQLITE_OK is returned.
*/
int sqlite3SchemaDisconnect(sqlite3 *db, int iDb, int bNew){
  int rc = SQLITE_OK;
  if( IsReuseSchema(db) ){
    Db *pDb = &db->aDb[iDb];
    SchemaPool *pSPool = pDb->pSPool;
    assert_schema_state_ok(db);
    assert( pDb->pSchema );

    if( pSPool==0 ){
      assert( pDb->pVTable==0 );
      assert( bNew==0 );
      schemaDelete(pDb->pSchema);
      pDb->pSchema = 0;

    }else{
      VTable *p;
      VTable *pNext;
      for(p=pDb->pVTable; p; p=pNext){
        pNext = p->pNext;
        sqlite3VtabUnlock(p);
      }
................................................................................
/*
** Extract and return a pointer to a schema object from the SchemaPool passed
** as the only argument, if one is available. If one is not available, return
** NULL.
*/
Schema *sqlite3SchemaExtract(SchemaPool *pSPool){
  Schema *pRet = 0;

  sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
  if( pSPool->pSchema ){
    pRet = pSPool->pSchema;
    pSPool->pSchema = pRet->pNext;
    pRet->pNext = 0;
  }
  sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );

  return pRet;
}

/*
** Return all sharable schemas held by database handle db back to their
** respective schema-pools. Db.pSchema variables are left pointing to
** the static, empty, Schema object owned by each schema-pool.
................................................................................
** owned by the schema-pool.
*/
void sqlite3SchemaRelease(sqlite3 *db, int iDb){
  Db *pDb = &db->aDb[iDb];
  assert( iDb!=1 );
  assert_schema_state_ok(db);
  sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );

  schemaRelease(db, pDb);

  sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
}

/*
** In most cases, this function finds and returns the schema associated 
** with BTree handle pBt, creating a new one if necessary. However, if
** the database handle was opened with the SQLITE_OPEN_SHARED_SCHEMA flag
................................................................................
** sqlite3_malloc() is always returned.
*/
Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
  Schema *p;
  if( pBt && (db->openFlags & SQLITE_OPEN_SHARED_SCHEMA)==0 ){
    p = (Schema*)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear);
  }else{
    db->lookaside.bDisable++;
    p = (Schema*)sqlite3DbMallocZero(db, sizeof(Schema));
    db->lookaside.bDisable--;
  }
  if( !p ){
    sqlite3OomFault(db);
  }else if ( 0==p->file_format ){
    sqlite3HashInit(&p->tblHash);
    sqlite3HashInit(&p->idxHash);
    sqlite3HashInit(&p->trigHash);
    sqlite3HashInit(&p->fkeyHash);
    p->enc = SQLITE_UTF8;
  }
  return p;
}

Changes to src/prepare.c.

136
137
138
139
140
141
142
143



144
145
146
147
148
149
150
    if( SQLITE_OK!=rc ){
      if( db->init.orphanTrigger ){
        assert( iDb==1 );
      }else{
        pData->rc = rc;
        if( rc==SQLITE_NOMEM ){
          sqlite3OomFault(db);
        }else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){



          corruptSchema(pData, argv[0], sqlite3_errmsg(db));
        }
      }
    }
    sqlite3_finalize(pStmt);
  }else if( argv[0]==0 || (argv[2]!=0 && argv[2][0]!=0) ){
    corruptSchema(pData, argv[0], 0);







|
>
>
>







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

Changes to src/vdbe.c.

3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
....
3311
3312
3313
3314
3315
3316
3317







3318
3319
3320
3321
3322
3323
3324
    }
  }
  assert( pOp->p5==0 || pOp->p4type==P4_INT32 );
  if( pOp->p5
   && (iMeta!=pOp->p3
      || db->aDb[pOp->p1].pSchema->iGeneration!=pOp->p4.i)
  ){
    /*
    ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema
    ** version is checked to ensure that the schema has not changed since the
    ** SQL statement was prepared.
    */
    sqlite3DbFree(db, p->zErrMsg);
    p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
    /* If the schema-cookie from the database file matches the cookie 
    ** stored with the in-memory representation of the schema, do
    ** not reload the schema from the database file.
    **
    ** If virtual-tables are in use, this is not just an optimization.
    ** Often, v-tables store their data in other SQLite tables, which
    ** are queried from within xNext() and other v-table methods using
................................................................................
    ** v-table would have to be ready for the sqlite3_vtab structure itself
    ** to be invalidated whenever sqlite3_step() is called from within 
    ** a v-table method.
    */
    if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){
      sqlite3ResetOneSchema(db, pOp->p1);
    }







    p->expired = 1;
    rc = SQLITE_SCHEMA;
  }
  if( rc ) goto abort_due_to_error;
  break;
}








<
<
<
<
<
<
<







 







>
>
>
>
>
>
>







3288
3289
3290
3291
3292
3293
3294







3295
3296
3297
3298
3299
3300
3301
....
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
    }
  }
  assert( pOp->p5==0 || pOp->p4type==P4_INT32 );
  if( pOp->p5
   && (iMeta!=pOp->p3
      || db->aDb[pOp->p1].pSchema->iGeneration!=pOp->p4.i)
  ){







    /* If the schema-cookie from the database file matches the cookie 
    ** stored with the in-memory representation of the schema, do
    ** not reload the schema from the database file.
    **
    ** If virtual-tables are in use, this is not just an optimization.
    ** Often, v-tables store their data in other SQLite tables, which
    ** are queried from within xNext() and other v-table methods using
................................................................................
    ** v-table would have to be ready for the sqlite3_vtab structure itself
    ** to be invalidated whenever sqlite3_step() is called from within 
    ** a v-table method.
    */
    if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){
      sqlite3ResetOneSchema(db, pOp->p1);
    }
    /*
    ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema
    ** version is checked to ensure that the schema has not changed since the
    ** SQL statement was prepared.
    */
    sqlite3DbFree(db, p->zErrMsg);
    p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
    p->expired = 1;
    rc = SQLITE_SCHEMA;
  }
  if( rc ) goto abort_due_to_error;
  break;
}

Changes to test/reuse3.test.

269
270
271
272
273
274
275
































276
277
  UPDATE sqlite_master SET sql='CREATE TABLE t3(a,b)' WHERE name = 't3';
}

do_test 5.5 { 
  catchsql { SELECT nref,nschema FROM schemapool } db2
} {0 {1 1}}

































finish_test








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  UPDATE sqlite_master SET sql='CREATE TABLE t3(a,b)' WHERE name = 't3';
}

do_test 5.5 { 
  catchsql { SELECT nref,nschema FROM schemapool } db2
} {0 {1 1}}

db2 close
db close
do_test 5.6.1 {
  forcedelete test.db2 test.db2-wal test.db2-journal
  forcecopy test.db test.db2
  sqlite3 db test.db
  sqlite3 db2 test.db  -shared-schema 1
  sqlite3 db3 test.db2 -shared-schema 1
  register_schemapool_module db
} {}

do_execsql_test -db db2 5.6.2 { SELECT * FROM t1 }
do_execsql_test -db db3 5.6.3 { SELECT * FROM t1 }
do_execsql_test 5.6.4 {
  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool; 
  CREATE TABLE t4(x);
  DROP TABLE t4;
} {nref=2 nschema=1}
do_execsql_test -db db2 5.6.5 { SELECT * FROM t1 }
do_execsql_test -db db3 5.6.6 { SELECT * FROM t1 }
do_execsql_test 5.6.7 {
  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool; 
  ATTACH 'test.db2' AS db2;
  CREATE TABLE db2.t4(x);
  DROP TABLE db2.t4;
} {nref=1 nschema=1 nref=1 nschema=1}
do_execsql_test -db db2 5.6.8 { SELECT * FROM t1 }
do_execsql_test -db db3 5.6.9 { SELECT * FROM t1 }
do_execsql_test 5.6.10 {
  SELECT 'nref=' || nRef, 'nschema=' || nSchema FROM schemapool; 
} {nref=2 nschema=1}

finish_test

Added test/reusefault.test.



































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 2019 February 12
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix reusefault

do_execsql_test 1.0 {
  PRAGMA cache_size = 10;
  CREATE TABLE t1(a UNIQUE, b UNIQUE);
  INSERT INTO t1 VALUES(1, 2), (3, 4);
}
faultsim_save_and_close

do_faultsim_test 1.1 -prep {
  faultsim_restore
  sqlite3 db test.db -shared-schema 1
} -body {
  execsql { SELECT * FROM t1 }
} -test {
  faultsim_test_result {0 {1 2 3 4}}
}

do_faultsim_test 1.2 -prep {
  faultsim_restore
  sqlite3 db test.db -shared-schema 1
  execsql { SELECT * FROM t1 }
  sqlite3 db2 test.db
  db2 eval {CREATE TABLE a(a)}
  db2 close
} -body {
  execsql { SELECT  * FROM t1 }
} -test {
  faultsim_test_result {0 {1 2 3 4}}
}


finish_test