/ Check-in [c1be211c]
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:Use the IsReuseSchema() macro more consistently. Also, rename it to IsSharedSchema().
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | reuse-schema
Files: files | file ages | folders
SHA3-256: c1be211c0286a6ff3ef8ab6839136175efb000118ff8704d3cc01b2a23b841ab
User & Date: dan 2019-03-20 20:02:03
Wiki:reuse-schema
Context
2019-03-21
17:13
Disable the feature on this branch in non-SQLITE_ENABLE_SHARED_SCHEMA builds. check-in: b8e53608 user: dan tags: reuse-schema
2019-03-20
20:02
Use the IsReuseSchema() macro more consistently. Also, rename it to IsSharedSchema(). check-in: c1be211c user: dan tags: reuse-schema
19:17
Fix an error message. check-in: a70fdaa3 user: dan tags: reuse-schema
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/attach.c.

229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  ** If this fails, or if opening the file failed, then close the file and 
  ** remove the entry from the db->aDb[] array. i.e. put everything back the
  ** way we found it.
  */
  if( rc==SQLITE_OK ){
    db->init.iDb = 0;
    db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
    if( !IsReuseSchema(db) && !REOPEN_AS_MEMDB(db) ){
      sqlite3BtreeEnterAll(db);
      rc = sqlite3Init(db, &zErrDyn);
      sqlite3BtreeLeaveAll(db);
      assert( zErrDyn==0 || rc!=SQLITE_OK );
    }
  }
#ifdef SQLITE_USER_AUTHENTICATION







|







229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  ** If this fails, or if opening the file failed, then close the file and 
  ** remove the entry from the db->aDb[] array. i.e. put everything back the
  ** way we found it.
  */
  if( rc==SQLITE_OK ){
    db->init.iDb = 0;
    db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
    if( !IsSharedSchema(db) && !REOPEN_AS_MEMDB(db) ){
      sqlite3BtreeEnterAll(db);
      rc = sqlite3Init(db, &zErrDyn);
      sqlite3BtreeLeaveAll(db);
      assert( zErrDyn==0 || rc!=SQLITE_OK );
    }
  }
#ifdef SQLITE_USER_AUTHENTICATION

Changes to src/build.c.

300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
...
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
...
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
...
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
....
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
....
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
** SQLITE_OK is returned if successful, or an SQLite error code otherwise. If
** an error code is returned, (*pzErr) may be set to point to a buffer
** containing an error message. It is the responsibility of the caller to
** eventually free this buffer using sqlite3_free().
*/
int sqlite3SchemaLoad(sqlite3 *db, int iDb, int *pbUnload, char **pzErr){
  int rc = SQLITE_OK;
  if( IsReuseSchema(db) 
      && 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;
................................................................................
#endif
  while(1){
    for(i=OMIT_TEMPDB; i<db->nDb; i++){
      int j = (i<2) ? i^1 : i;   /* Search TEMP before MAIN */
      if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
        int bUnload = 0;
        assert( sqlite3SchemaMutexHeld(db, j, 0) );
        if( IsReuseSchema(db) ){
          Parse *pParse = db->pParse;
          if( pParse && pParse->nErr==0 ){
            pParse->rc = sqlite3SchemaLoad(db, j, &bUnload, &pParse->zErrMsg);
            if( pParse->rc ) pParse->nErr++;
          }
        }
        p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
................................................................................
){
  Table *p;
  sqlite3 *db = pParse->db;

  /* Read the database schema. If an error occurs, leave an error message
  ** and code in pParse and return NULL. */
  if( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 
   && !IsReuseSchema(db)
   && SQLITE_OK!=sqlite3ReadSchema(pParse)
  ){
    return 0;
  }

  p = sqlite3FindTable(db, zName, zDbase);
  if( p==0 ){
................................................................................
    ** can be an eponymous virtual table. */
    if( pParse->disableVtab==0 ){
      Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
      if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
        pMod = sqlite3PragmaVtabRegister(db, zName);
      }
      if( pMod ){
        if( IsReuseSchema(db) && pParse->nErr==0 ){
          int bDummy = 0;
          pParse->rc = sqlite3SchemaLoad(db, 0, &bDummy, &pParse->zErrMsg);
          if( pParse->rc ) pParse->nErr++;
        }
        if( sqlite3VtabEponymousTableInit(pParse, pMod) ){
          Table *pEpoTab = pMod->pEpoTab;
          assert( IsReuseSchema(db) || pEpoTab->pSchema==db->aDb[0].pSchema );
          pEpoTab->pSchema = db->aDb[0].pSchema;  /* For SHARED_SCHEMA mode */
          return pEpoTab;
        }
      }
    }
#endif
    if( flags & LOCATE_NOERR ) return 0;
    pParse->checkSchema = 1;
  }else if( IsVirtual(p) && pParse->disableVtab ){
    p = 0;
  }

  if( p==0 && (!IsReuseSchema(db) || pParse->nErr==0) ){
    const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table";
    if( zDbase ){
      sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
    }else{
      sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
    }
  }
................................................................................
  ** it does. The exception is if the statement being parsed was passed
  ** to an sqlite3_declare_vtab() call. In that case only the column names
  ** and types will be used, so there is no need to test for namespace
  ** collisions.
  */
  if( !IN_SPECIAL_PARSE ){
    char *zDb = db->aDb[iDb].zDbSName;
    if( !IsReuseSchema(db) && SQLITE_OK!=sqlite3ReadSchema(pParse) ){
      goto begin_table_error;
    }
    pTable = sqlite3FindTable(db, zName, zDb);
    if( pTable ){
      if( !noErr ){
        sqlite3ErrorMsg(pParse, "table %T already exists", pName);
      }else{
................................................................................
  int iDb;

  if( db->mallocFailed ){
    goto exit_drop_table;
  }
  assert( pParse->nErr==0 );
  assert( pName->nSrc==1 );
  if( !IsReuseSchema(db) && sqlite3ReadSchema(pParse) ) goto exit_drop_table;
  if( noErr ) db->suppressErr++;
  assert( isView==0 || isView==LOCATE_VIEW );
  pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]);
  if( noErr ) db->suppressErr--;

  if( pTab==0 ){
    if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
................................................................................

  if( db->mallocFailed || pParse->nErr>0 ){
    goto exit_create_index;
  }
  if( IN_DECLARE_VTAB && idxType!=SQLITE_IDXTYPE_PRIMARYKEY ){
    goto exit_create_index;
  }
  if( !IsReuseSchema(db) && SQLITE_OK!=sqlite3ReadSchema(pParse) ){
    goto exit_create_index;
  }

  /*
  ** Find the table that is to be indexed.  Return early if not found.
  */
  if( pTblName!=0 ){







|







 







|







 







|







 







|






|












|







 







|







 







|







 







|







300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
...
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
...
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
...
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
....
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
....
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
** SQLITE_OK is returned if successful, or an SQLite error code otherwise. If
** an error code is returned, (*pzErr) may be set to point to a buffer
** containing an error message. It is the responsibility of the caller to
** eventually free this buffer using sqlite3_free().
*/
int sqlite3SchemaLoad(sqlite3 *db, int iDb, int *pbUnload, char **pzErr){
  int rc = SQLITE_OK;
  if( IsSharedSchema(db) 
      && 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;
................................................................................
#endif
  while(1){
    for(i=OMIT_TEMPDB; i<db->nDb; i++){
      int j = (i<2) ? i^1 : i;   /* Search TEMP before MAIN */
      if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
        int bUnload = 0;
        assert( sqlite3SchemaMutexHeld(db, j, 0) );
        if( IsSharedSchema(db) ){
          Parse *pParse = db->pParse;
          if( pParse && pParse->nErr==0 ){
            pParse->rc = sqlite3SchemaLoad(db, j, &bUnload, &pParse->zErrMsg);
            if( pParse->rc ) pParse->nErr++;
          }
        }
        p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
................................................................................
){
  Table *p;
  sqlite3 *db = pParse->db;

  /* Read the database schema. If an error occurs, leave an error message
  ** and code in pParse and return NULL. */
  if( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 
   && !IsSharedSchema(db)
   && SQLITE_OK!=sqlite3ReadSchema(pParse)
  ){
    return 0;
  }

  p = sqlite3FindTable(db, zName, zDbase);
  if( p==0 ){
................................................................................
    ** can be an eponymous virtual table. */
    if( pParse->disableVtab==0 ){
      Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
      if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
        pMod = sqlite3PragmaVtabRegister(db, zName);
      }
      if( pMod ){
        if( IsSharedSchema(db) && pParse->nErr==0 ){
          int bDummy = 0;
          pParse->rc = sqlite3SchemaLoad(db, 0, &bDummy, &pParse->zErrMsg);
          if( pParse->rc ) pParse->nErr++;
        }
        if( sqlite3VtabEponymousTableInit(pParse, pMod) ){
          Table *pEpoTab = pMod->pEpoTab;
          assert( IsSharedSchema(db) || pEpoTab->pSchema==db->aDb[0].pSchema );
          pEpoTab->pSchema = db->aDb[0].pSchema;  /* For SHARED_SCHEMA mode */
          return pEpoTab;
        }
      }
    }
#endif
    if( flags & LOCATE_NOERR ) return 0;
    pParse->checkSchema = 1;
  }else if( IsVirtual(p) && pParse->disableVtab ){
    p = 0;
  }

  if( p==0 && (!IsSharedSchema(db) || pParse->nErr==0) ){
    const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table";
    if( zDbase ){
      sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
    }else{
      sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
    }
  }
................................................................................
  ** it does. The exception is if the statement being parsed was passed
  ** to an sqlite3_declare_vtab() call. In that case only the column names
  ** and types will be used, so there is no need to test for namespace
  ** collisions.
  */
  if( !IN_SPECIAL_PARSE ){
    char *zDb = db->aDb[iDb].zDbSName;
    if( !IsSharedSchema(db) && SQLITE_OK!=sqlite3ReadSchema(pParse) ){
      goto begin_table_error;
    }
    pTable = sqlite3FindTable(db, zName, zDb);
    if( pTable ){
      if( !noErr ){
        sqlite3ErrorMsg(pParse, "table %T already exists", pName);
      }else{
................................................................................
  int iDb;

  if( db->mallocFailed ){
    goto exit_drop_table;
  }
  assert( pParse->nErr==0 );
  assert( pName->nSrc==1 );
  if( !IsSharedSchema(db) && sqlite3ReadSchema(pParse) ) goto exit_drop_table;
  if( noErr ) db->suppressErr++;
  assert( isView==0 || isView==LOCATE_VIEW );
  pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]);
  if( noErr ) db->suppressErr--;

  if( pTab==0 ){
    if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
................................................................................

  if( db->mallocFailed || pParse->nErr>0 ){
    goto exit_create_index;
  }
  if( IN_DECLARE_VTAB && idxType!=SQLITE_IDXTYPE_PRIMARYKEY ){
    goto exit_create_index;
  }
  if( !IsSharedSchema(db) && SQLITE_OK!=sqlite3ReadSchema(pParse) ){
    goto exit_create_index;
  }

  /*
  ** Find the table that is to be indexed.  Return early if not found.
  */
  if( pTblName!=0 ){

Changes to src/callback.c.

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
...
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
...
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
...
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
...
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
  Schema *pSchema;                /* Linked list of Schema objects */
  Schema sSchema;                 /* The single dummy schema object */
  SchemaPool *pNext;              /* Next element in schemaPoolList */
};

#ifdef SQLITE_DEBUG
static void assert_schema_state_ok(sqlite3 *db){
  if( IsReuseSchema(db) && db->magic!=SQLITE_MAGIC_ZOMBIE ){
    int i;
    for(i=0; i<db->nDb; i++){
      if( i!=1 ){
        Db *pDb = &db->aDb[i];
        Btree *pBt = pDb->pBt;
        assert( pBt==0 || sqlite3BtreeSchema(pBt, 0, 0)==0 );
        assert( pDb->pSchema );
................................................................................
** database iDb. 
**
** If an OOM error occurs while disconnecting from a schema-pool, 
** the db->mallocFailed flag is set.
*/
void sqlite3SchemaClearOrDisconnect(sqlite3 *db, int iDb){
  Db *pDb = &db->aDb[iDb];
  if( IsReuseSchema(db) && iDb!=1 && pDb->pSPool ){
    sqlite3SchemaDisconnect(db, iDb, 1);
  }else{
    sqlite3SchemaClear(pDb->pSchema);
  }
}

/*
................................................................................
/*
** Check that the schema of db iDb is writable (either because it is the 
** temp db schema or because the db handle was opened without
** SQLITE_OPEN_SHARED_SCHEMA). If so, do nothing. Otherwise, leave an 
** error in the Parse object.
*/
void sqlite3SchemaWritable(Parse *pParse, int iDb){
  if( iDb!=1 && (pParse->db->openFlags & SQLITE_OPEN_SHARED_SCHEMA) 
   && IN_DECLARE_VTAB==0
  ){
    sqlite3ErrorMsg(pParse, "attempt to modify read-only schema");
  }
}

/*
** The schema object passed as the only argument was allocated using
** sqlite3_malloc() and then populated using the usual mechanism. This
................................................................................
** 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 );
................................................................................
** with BTree handle pBt, creating a new one if necessary. However, if
** the database handle was opened with the SQLITE_OPEN_SHARED_SCHEMA flag
** specified, a new, empty, Schema object in memory obtained by 
** 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 ){







|







 







|







 







<
|
<







 







|







 







|







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
...
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
...
575
576
577
578
579
580
581

582

583
584
585
586
587
588
589
...
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
...
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
  Schema *pSchema;                /* Linked list of Schema objects */
  Schema sSchema;                 /* The single dummy schema object */
  SchemaPool *pNext;              /* Next element in schemaPoolList */
};

#ifdef SQLITE_DEBUG
static void assert_schema_state_ok(sqlite3 *db){
  if( IsSharedSchema(db) && db->magic!=SQLITE_MAGIC_ZOMBIE ){
    int i;
    for(i=0; i<db->nDb; i++){
      if( i!=1 ){
        Db *pDb = &db->aDb[i];
        Btree *pBt = pDb->pBt;
        assert( pBt==0 || sqlite3BtreeSchema(pBt, 0, 0)==0 );
        assert( pDb->pSchema );
................................................................................
** database iDb. 
**
** If an OOM error occurs while disconnecting from a schema-pool, 
** the db->mallocFailed flag is set.
*/
void sqlite3SchemaClearOrDisconnect(sqlite3 *db, int iDb){
  Db *pDb = &db->aDb[iDb];
  if( IsSharedSchema(db) && iDb!=1 && pDb->pSPool ){
    sqlite3SchemaDisconnect(db, iDb, 1);
  }else{
    sqlite3SchemaClear(pDb->pSchema);
  }
}

/*
................................................................................
/*
** Check that the schema of db iDb is writable (either because it is the 
** temp db schema or because the db handle was opened without
** SQLITE_OPEN_SHARED_SCHEMA). If so, do nothing. Otherwise, leave an 
** error in the Parse object.
*/
void sqlite3SchemaWritable(Parse *pParse, int iDb){

  if( iDb!=1 && IsSharedSchema(pParse->db) && IN_DECLARE_VTAB==0 ){

    sqlite3ErrorMsg(pParse, "attempt to modify read-only schema");
  }
}

/*
** The schema object passed as the only argument was allocated using
** sqlite3_malloc() and then populated using the usual mechanism. This
................................................................................
** 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( IsSharedSchema(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 );
................................................................................
** with BTree handle pBt, creating a new one if necessary. However, if
** the database handle was opened with the SQLITE_OPEN_SHARED_SCHEMA flag
** specified, a new, empty, Schema object in memory obtained by 
** sqlite3_malloc() is always returned.
*/
Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
  Schema *p;
  if( pBt && IsSharedSchema(db)==0 ){
    p = (Schema*)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear);
  }else{
    db->lookaside.bDisable++;
    p = (Schema*)sqlite3DbMallocZero(db, sizeof(Schema));
    db->lookaside.bDisable--;
  }
  if( !p ){

Changes to src/main.c.

1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
....
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
    Schema *pSchema = db->aDb[i].pSchema;
    if( pSchema ){
      for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
        Table *pTab = (Table *)sqliteHashData(p);
        if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
      }
    }
    if( i!=1 && IsReuseSchema(db) ){
      VTable *pVTable;
      VTable *pNext;
      for(pVTable=db->aDb[i].pVTable; pVTable; pVTable=pNext){
        pNext = pVTable->pNext;
        sqlite3VtabUnlock(pVTable);
      }
      db->aDb[i].pVTable = 0;
................................................................................
  }
#endif

  /* Ensure the database schema has been loaded */
  sqlite3_mutex_enter(db->mutex);
  bUnlock = sqlite3LockReusableSchema(db);
  sqlite3BtreeEnterAll(db);
  if( IsReuseSchema(db)==0 ){
    rc = sqlite3Init(db, &zErrMsg);
  }

  /* Locate the table in question */
  if( rc==SQLITE_OK ){
    Parse sParse;                   /* Fake Parse object for FindTable */
    Parse *pSaved = db->pParse;







|







 







|







1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
....
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
    Schema *pSchema = db->aDb[i].pSchema;
    if( pSchema ){
      for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
        Table *pTab = (Table *)sqliteHashData(p);
        if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
      }
    }
    if( i!=1 && IsSharedSchema(db) ){
      VTable *pVTable;
      VTable *pNext;
      for(pVTable=db->aDb[i].pVTable; pVTable; pVTable=pNext){
        pNext = pVTable->pNext;
        sqlite3VtabUnlock(pVTable);
      }
      db->aDb[i].pVTable = 0;
................................................................................
  }
#endif

  /* Ensure the database schema has been loaded */
  sqlite3_mutex_enter(db->mutex);
  bUnlock = sqlite3LockReusableSchema(db);
  sqlite3BtreeEnterAll(db);
  if( IsSharedSchema(db)==0 ){
    rc = sqlite3Init(db, &zErrMsg);
  }

  /* Locate the table in question */
  if( rc==SQLITE_OK ){
    Parse sParse;                   /* Fake Parse object for FindTable */
    Parse *pSaved = db->pParse;

Changes to src/pragma.c.

416
417
418
419
420
421
422
423
424
425
426
427
428
429
430

  /* Locate the pragma in the lookup table */
  pPragma = pragmaLocate(zLeft);
  if( pPragma==0 ) goto pragma_out;

  /* Make sure the database schema is loaded if the pragma requires that */
  if( (pPragma->mPragFlg & PragFlg_NeedSchema)!=0 ){
    if( IsReuseSchema(db) && (zDb || (pPragma->mPragFlg & PragFlg_OneSchema)) ){
      assert( iDb>=0 && iDb<db->nDb );
      pParse->rc = sqlite3SchemaLoad(db, iDb, 0, &pParse->zErrMsg);
      if( pParse->rc ) goto pragma_out;
    }else{
      if( sqlite3ReadSchema(pParse) ) goto pragma_out;
    }
  }







|







416
417
418
419
420
421
422
423
424
425
426
427
428
429
430

  /* Locate the pragma in the lookup table */
  pPragma = pragmaLocate(zLeft);
  if( pPragma==0 ) goto pragma_out;

  /* Make sure the database schema is loaded if the pragma requires that */
  if( (pPragma->mPragFlg & PragFlg_NeedSchema)!=0 ){
    if( IsSharedSchema(db) && (zDb || (pPragma->mPragFlg & PragFlg_OneSchema)) ){
      assert( iDb>=0 && iDb<db->nDb );
      pParse->rc = sqlite3SchemaLoad(db, iDb, 0, &pParse->zErrMsg);
      if( pParse->rc ) goto pragma_out;
    }else{
      if( sqlite3ReadSchema(pParse) ) goto pragma_out;
    }
  }

Changes to src/prepare.c.

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
...
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
...
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
...
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
...
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
  }else if( pData->pzErrMsg[0]!=0 ){
    /* A error message has already been generated.  Do not overwrite it */
  }else if( pData->mInitFlags & INITFLAG_AlterTable ){
    *pData->pzErrMsg = sqlite3DbStrDup(db, zExtra);
    pData->rc = SQLITE_ERROR;
  }else if( db->flags & SQLITE_WriteSchema ){
    pData->rc = SQLITE_CORRUPT_BKPT;
  }else if( IsReuseSchema(db) 
         && 0==sqlite3StrNICmp(zExtra, "malformed database schema", 17)
  ){
    pData->rc = SQLITE_CORRUPT_BKPT;
    *pData->pzErrMsg = sqlite3DbStrDup(db, zExtra);
  }else{
    char *z;
    if( zObj==0 ) zObj = "?";
................................................................................
     || pIndex->tnum<2
     || sqlite3IndexHasDuplicateRootPage(pIndex)
    ){
      corruptSchema(pData, argv[0], pIndex?"invalid rootpage":"orphan index");
    }
  }

  if( iDb!=1 && (db->openFlags & SQLITE_OPEN_SHARED_SCHEMA) ){
    schemaUpdateChecksum(pData, argv[0], argv[1], argv[2]);
  }
  return 0;
}

/*
** Attempt to read the database schema and initialize internal
................................................................................
  int meta[5];
  InitData initData;
  const char *zMasterName;
  int openedTransaction = 0;

  assert( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 );
  assert( iDb>=0 && iDb<db->nDb );
  assert( db->aDb[iDb].pSchema || (IsReuseSchema(db) && iDb!=1) );
  assert( sqlite3_mutex_held(db->mutex) );
  assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );

  pDb = &db->aDb[iDb];
  assert( pDb->pSPool==0 || IsReuseSchema(db) );
  if( pDb->pSPool ){
    /* See if there is a free schema object in the schema-pool. If not,
    ** disconnect from said schema pool and continue. This function will
    ** connect to a (possibly different) schema-pool before returning. */
    Schema *pNew = sqlite3SchemaExtract(pDb->pSPool);
    if( pNew ){
      pDb->pSchema = pNew;
................................................................................
    ** purpose of this is to allow access to the sqlite_master table
    ** even when its contents have been corrupted.
    */
    DbSetProperty(db, iDb, DB_SchemaLoaded);
    rc = SQLITE_OK;
  }

  if( rc==SQLITE_OK && iDb!=1 && (db->openFlags & SQLITE_OPEN_SHARED_SCHEMA) ){
    rc = sqlite3SchemaConnect(db, iDb, initData.cksum);
  }

  /* Jump here for an error that occurs after successfully allocating
  ** curMain and calling sqlite3BtreeEnter(). For an error that occurs
  ** before that point, jump to error_out.
  */
................................................................................
    sqlite3ResetOneSchema(db, iDb);
  }
  db->init.busy = 0;
  return rc;
}

int sqlite3LockReusableSchema(sqlite3 *db){
  if( IsReuseSchema(db) && (db->mDbFlags & DBFLAG_SchemaInuse)==0 ){
    db->mDbFlags |= DBFLAG_SchemaInuse;
    return 1;
  }
  return 0;
}
void sqlite3UnlockReusableSchema(sqlite3 *db, int bRelease){
  if( bRelease ){
................................................................................
  assert( sqlite3_mutex_held(db->mutex) );
  if( !db->init.busy ){
    db->mDbFlags |= DBFLAG_FreeSchema;      /* For sharable-schema mode */
    rc = sqlite3Init(db, &pParse->zErrMsg);
    if( rc!=SQLITE_OK ){
      pParse->rc = rc;
      pParse->nErr++;
    }else if( db->noSharedCache && !IsReuseSchema(db) ){
      db->mDbFlags |= DBFLAG_SchemaKnownOk;
    }
  }
  return rc;
}









|







 







|







 







|




|







 







|







 







|







 







|







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
...
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
...
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
...
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
...
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
  }else if( pData->pzErrMsg[0]!=0 ){
    /* A error message has already been generated.  Do not overwrite it */
  }else if( pData->mInitFlags & INITFLAG_AlterTable ){
    *pData->pzErrMsg = sqlite3DbStrDup(db, zExtra);
    pData->rc = SQLITE_ERROR;
  }else if( db->flags & SQLITE_WriteSchema ){
    pData->rc = SQLITE_CORRUPT_BKPT;
  }else if( IsSharedSchema(db) 
         && 0==sqlite3StrNICmp(zExtra, "malformed database schema", 17)
  ){
    pData->rc = SQLITE_CORRUPT_BKPT;
    *pData->pzErrMsg = sqlite3DbStrDup(db, zExtra);
  }else{
    char *z;
    if( zObj==0 ) zObj = "?";
................................................................................
     || pIndex->tnum<2
     || sqlite3IndexHasDuplicateRootPage(pIndex)
    ){
      corruptSchema(pData, argv[0], pIndex?"invalid rootpage":"orphan index");
    }
  }

  if( iDb!=1 && IsSharedSchema(db) ){
    schemaUpdateChecksum(pData, argv[0], argv[1], argv[2]);
  }
  return 0;
}

/*
** Attempt to read the database schema and initialize internal
................................................................................
  int meta[5];
  InitData initData;
  const char *zMasterName;
  int openedTransaction = 0;

  assert( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 );
  assert( iDb>=0 && iDb<db->nDb );
  assert( db->aDb[iDb].pSchema || (IsSharedSchema(db) && iDb!=1) );
  assert( sqlite3_mutex_held(db->mutex) );
  assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );

  pDb = &db->aDb[iDb];
  assert( pDb->pSPool==0 || IsSharedSchema(db) );
  if( pDb->pSPool ){
    /* See if there is a free schema object in the schema-pool. If not,
    ** disconnect from said schema pool and continue. This function will
    ** connect to a (possibly different) schema-pool before returning. */
    Schema *pNew = sqlite3SchemaExtract(pDb->pSPool);
    if( pNew ){
      pDb->pSchema = pNew;
................................................................................
    ** purpose of this is to allow access to the sqlite_master table
    ** even when its contents have been corrupted.
    */
    DbSetProperty(db, iDb, DB_SchemaLoaded);
    rc = SQLITE_OK;
  }

  if( rc==SQLITE_OK && iDb!=1 && IsSharedSchema(db) ){
    rc = sqlite3SchemaConnect(db, iDb, initData.cksum);
  }

  /* Jump here for an error that occurs after successfully allocating
  ** curMain and calling sqlite3BtreeEnter(). For an error that occurs
  ** before that point, jump to error_out.
  */
................................................................................
    sqlite3ResetOneSchema(db, iDb);
  }
  db->init.busy = 0;
  return rc;
}

int sqlite3LockReusableSchema(sqlite3 *db){
  if( IsSharedSchema(db) && (db->mDbFlags & DBFLAG_SchemaInuse)==0 ){
    db->mDbFlags |= DBFLAG_SchemaInuse;
    return 1;
  }
  return 0;
}
void sqlite3UnlockReusableSchema(sqlite3 *db, int bRelease){
  if( bRelease ){
................................................................................
  assert( sqlite3_mutex_held(db->mutex) );
  if( !db->init.busy ){
    db->mDbFlags |= DBFLAG_FreeSchema;      /* For sharable-schema mode */
    rc = sqlite3Init(db, &pParse->zErrMsg);
    if( rc!=SQLITE_OK ){
      pParse->rc = rc;
      pParse->nErr++;
    }else if( db->noSharedCache && !IsSharedSchema(db) ){
      db->mDbFlags |= DBFLAG_SchemaKnownOk;
    }
  }
  return rc;
}


Changes to src/sqliteInt.h.

1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
....
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
  sqlite3 *pNextBlocked;        /* Next in list of all blocked connections */
#endif
#ifdef SQLITE_USER_AUTHENTICATION
  sqlite3_userauth auth;        /* User authentication information */
#endif
};

#define IsReuseSchema(db) (((db)->openFlags & SQLITE_OPEN_SHARED_SCHEMA)!=0)

/*
** A macro to discover the encoding of a database.
*/
#define SCHEMA_ENC(db) ((db)->aDb[0].pSchema->enc)
#define ENC(db)        ((db)->enc)

................................................................................
  u8 op;                  /* One of TK_DELETE, TK_UPDATE, TK_INSERT         */
  u8 tr_tm;               /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
  Expr *pWhen;            /* The WHEN clause of the expression (may be NULL) */
  IdList *pColumns;       /* If this is an UPDATE OF <column-list> trigger,
                             the <column-list> is stored here */
  Schema *pSchema;        /* Schema containing the trigger */
  Schema *pTabSchema;     /* Schema containing the table */
  char *zTabSchema;       /* Temp triggers in IsReuseSchema() dbs only */
  TriggerStep *step_list; /* Link list of trigger program steps             */
  Trigger *pNext;         /* Next trigger associated with the table */
};

/*
** A trigger is either a BEFORE or an AFTER trigger.  The following constants
** determine which.







|







 







|







1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
....
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
  sqlite3 *pNextBlocked;        /* Next in list of all blocked connections */
#endif
#ifdef SQLITE_USER_AUTHENTICATION
  sqlite3_userauth auth;        /* User authentication information */
#endif
};

#define IsSharedSchema(db) (((db)->openFlags & SQLITE_OPEN_SHARED_SCHEMA)!=0)

/*
** A macro to discover the encoding of a database.
*/
#define SCHEMA_ENC(db) ((db)->aDb[0].pSchema->enc)
#define ENC(db)        ((db)->enc)

................................................................................
  u8 op;                  /* One of TK_DELETE, TK_UPDATE, TK_INSERT         */
  u8 tr_tm;               /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
  Expr *pWhen;            /* The WHEN clause of the expression (may be NULL) */
  IdList *pColumns;       /* If this is an UPDATE OF <column-list> trigger,
                             the <column-list> is stored here */
  Schema *pSchema;        /* Schema containing the trigger */
  Schema *pTabSchema;     /* Schema containing the table */
  char *zTabSchema;       /* Temp triggers in IsSharedSchema() dbs only */
  TriggerStep *step_list; /* Link list of trigger program steps             */
  Trigger *pNext;         /* Next trigger associated with the table */
};

/*
** A trigger is either a BEFORE or an AFTER trigger.  The following constants
** determine which.

Changes to src/trigger.c.

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
...
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
    return 0;
  }

  if( pTmpSchema!=pTab->pSchema ){
    sqlite3 *db = pParse->db;
    HashElem *p;
    char *zSchema = 0;
    if( IsReuseSchema(db) ){
      zSchema = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
    }
    assert( sqlite3SchemaMutexHeld(db, 0, pTmpSchema) );
    for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){
      Trigger *pTrig = (Trigger *)sqliteHashData(p);
      if( (zSchema==0 && pTrig->pTabSchema==pTab->pSchema)
       || (zSchema!=0 && 0==sqlite3StrICmp(pTrig->zTabSchema, zSchema))
................................................................................

  /* Build the Trigger object */
  pTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger));
  if( pTrigger==0 ) goto trigger_cleanup;
  pTrigger->zName = zName;
  zName = 0;
  pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName);
  if( IsReuseSchema(db) && iDb==1 ){
    int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
    pTrigger->zTabSchema = sqlite3DbStrDup(db, db->aDb[iTabDb].zDbSName);
  }
  pTrigger->pSchema = db->aDb[iDb].pSchema;
  pTrigger->pTabSchema = pTab->pSchema;
  pTrigger->op = (u8)op;
  pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;







|







 







|







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
...
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
    return 0;
  }

  if( pTmpSchema!=pTab->pSchema ){
    sqlite3 *db = pParse->db;
    HashElem *p;
    char *zSchema = 0;
    if( IsSharedSchema(db) ){
      zSchema = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
    }
    assert( sqlite3SchemaMutexHeld(db, 0, pTmpSchema) );
    for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){
      Trigger *pTrig = (Trigger *)sqliteHashData(p);
      if( (zSchema==0 && pTrig->pTabSchema==pTab->pSchema)
       || (zSchema!=0 && 0==sqlite3StrICmp(pTrig->zTabSchema, zSchema))
................................................................................

  /* Build the Trigger object */
  pTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger));
  if( pTrigger==0 ) goto trigger_cleanup;
  pTrigger->zName = zName;
  zName = 0;
  pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName);
  if( IsSharedSchema(db) && iDb==1 ){
    int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
    pTrigger->zTabSchema = sqlite3DbStrDup(db, db->aDb[iTabDb].zDbSName);
  }
  pTrigger->pSchema = db->aDb[iDb].pSchema;
  pTrigger->pTabSchema = pTab->pSchema;
  pTrigger->op = (u8)op;
  pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;

Changes to src/vtab.c.

140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
...
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
** pTab is a pointer to a Table structure representing a virtual-table.
** Return a pointer to the VTable object used by connection db to access 
** this virtual-table, if one has been created, or NULL otherwise.
*/
VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){
  VTable *pVtab;
  assert( IsVirtual(pTab) );
  if( IsReuseSchema(db) ){
    int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
    if( iDb!=1 ){
      for(pVtab=db->aDb[iDb].pVTable; pVtab; pVtab=pVtab->pNext){
        if( sqlite3StrICmp(pTab->zName, pVtab->zName)==0 ) break;
      }
      return pVtab;
    }
................................................................................
      /* If everything went according to plan, link the new VTable structure
      ** into the linked list headed by pTab->pVTable. Or, if this is a
      ** reusable schema, into the linked list headed by Db.pVTable.
      **
      ** Then loop through the columns of the table to see if any of them
      ** contain the token "hidden". If so, set the Column COLFLAG_HIDDEN flag
      ** and remove the token from the type string.  */
      if( IsReuseSchema(db) && iDb!=1 ){
        pVTable->pNext = db->aDb[iDb].pVTable;
        db->aDb[iDb].pVTable = pVTable;
      }else{
        pVTable->pNext = pTab->pVTable;
        pTab->pVTable = pVTable;
      }








|







 







|







140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
...
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
** pTab is a pointer to a Table structure representing a virtual-table.
** Return a pointer to the VTable object used by connection db to access 
** this virtual-table, if one has been created, or NULL otherwise.
*/
VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){
  VTable *pVtab;
  assert( IsVirtual(pTab) );
  if( IsSharedSchema(db) ){
    int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
    if( iDb!=1 ){
      for(pVtab=db->aDb[iDb].pVTable; pVtab; pVtab=pVtab->pNext){
        if( sqlite3StrICmp(pTab->zName, pVtab->zName)==0 ) break;
      }
      return pVtab;
    }
................................................................................
      /* If everything went according to plan, link the new VTable structure
      ** into the linked list headed by pTab->pVTable. Or, if this is a
      ** reusable schema, into the linked list headed by Db.pVTable.
      **
      ** Then loop through the columns of the table to see if any of them
      ** contain the token "hidden". If so, set the Column COLFLAG_HIDDEN flag
      ** and remove the token from the type string.  */
      if( IsSharedSchema(db) && iDb!=1 ){
        pVTable->pNext = db->aDb[iDb].pVTable;
        db->aDb[iDb].pVTable = pVTable;
      }else{
        pVTable->pNext = pTab->pVTable;
        pTab->pVTable = pVTable;
      }