SQLite

Check-in [2b2e9f81cd]
Login

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

Overview
Comment:Add test cases and fix problems on this branch.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | reuse-schema
Files: files | file ages | folders
SHA3-256: 2b2e9f81cdc7a3ac1eacc087fbd0414ead732878aae296bab6b54b4a7cd0a06e
User & Date: dan 2019-02-08 19:30:09.365
Context
2019-02-09
17:47
Fix virtual table support for SQLITE_OPEN_REUSABLE_SCHEMA connections. (check-in: 3ca8856a7b user: dan tags: reuse-schema)
2019-02-08
19:30
Add test cases and fix problems on this branch. (check-in: 2b2e9f81cd user: dan tags: reuse-schema)
2019-02-05
19:51
Merge latest trunk into this branch. (check-in: c089cc4fbe user: dan tags: reuse-schema)
Changes
Unified Diff Show Whitespace Changes Patch
Changes to src/analyze.c.
212
213
214
215
216
217
218

219
220
221
222
223
224
225
    Table *pStat;
    if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
      if( aTable[i].zCols ){
        /* The sqlite_statN table does not exist. Create it. Note that a 
        ** side-effect of the CREATE TABLE statement is to leave the rootpage 
        ** of the new table in register pParse->regRoot. This is important 
        ** because the OpenWrite opcode below will be needing it. */

        sqlite3NestedParse(pParse,
            "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
        );
        aRoot[i] = pParse->regRoot;
        aCreateTbl[i] = OPFLAG_P2ISREG;
      }
    }else{







>







212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
    Table *pStat;
    if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
      if( aTable[i].zCols ){
        /* The sqlite_statN table does not exist. Create it. Note that a 
        ** side-effect of the CREATE TABLE statement is to leave the rootpage 
        ** of the new table in register pParse->regRoot. This is important 
        ** because the OpenWrite opcode below will be needing it. */
        sqlite3SchemaWritable(pParse, iDb);
        sqlite3NestedParse(pParse,
            "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
        );
        aRoot[i] = pParse->regRoot;
        aCreateTbl[i] = OPFLAG_P2ISREG;
      }
    }else{
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
  sqlite3 *db = pParse->db;
  Schema *pSchema = db->aDb[iDb].pSchema;    /* Schema of database iDb */
  HashElem *k;
  int iStatCur;
  int iMem;
  int iTab;

  sqlite3SchemaWritable(pParse, iDb);
  sqlite3BeginWriteOperation(pParse, 0, iDb);
  iStatCur = pParse->nTab;
  pParse->nTab += 3;
  openStatTable(pParse, iDb, iStatCur, 0, 0);
  iMem = pParse->nMem+1;
  iTab = pParse->nTab;
  assert( sqlite3SchemaMutexHeld(db, iDb, 0) );







<







1336
1337
1338
1339
1340
1341
1342

1343
1344
1345
1346
1347
1348
1349
  sqlite3 *db = pParse->db;
  Schema *pSchema = db->aDb[iDb].pSchema;    /* Schema of database iDb */
  HashElem *k;
  int iStatCur;
  int iMem;
  int iTab;


  sqlite3BeginWriteOperation(pParse, 0, iDb);
  iStatCur = pParse->nTab;
  pParse->nTab += 3;
  openStatTable(pParse, iDb, iStatCur, 0, 0);
  iMem = pParse->nMem+1;
  iTab = pParse->nTab;
  assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
Changes to src/callback.c.
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  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) ){
    int i;
    for(i=0; i<db->nDb; i++){
      if( i!=1 ){
        Db *pDb = &db->aDb[i];
        Btree *pBt = pDb->pBt;
        if( pDb->pSPool ){
          if( DbHasProperty(db, i, DB_SchemaLoaded)==0 ){







|







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  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;
        if( pDb->pSPool ){
          if( DbHasProperty(db, i, DB_SchemaLoaded)==0 ){
Changes to src/prepare.c.
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
442
443
444
445
446
447
448
449
450
451

452
453
454
455
456
457
458
459
      sqlite3OomFault(db);
    }
    sqlite3ResetOneSchema(db, iDb);
  }
  db->init.busy = 0;
  return rc;
}















/*
** Initialize all database files - the main database file, the file
** used to store temporary tables, and any additional database files
** created using ATTACH statements.  Return a success code.  If an
** error occurs, write an error message into *pzErrMsg.
**
** After a database is initialized, the DB_SchemaLoaded bit is set
** bit is set in the flags field of the Db structure. If the database
** file was of zero-length, then the DB_Empty flag is also set.
*/
int sqlite3Init(sqlite3 *db, char **pzErrMsg){


  int i, rc;
  int commit_internal = !(db->mDbFlags&DBFLAG_SchemaChange);


  
  assert( sqlite3_mutex_held(db->mutex) );
  assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) );
  assert( db->init.busy==0 );
  ENC(db) = SCHEMA_ENC(db);
  assert( db->nDb>0 );
  /* Do the main schema first */
  if( !DbHasProperty(db, 0, DB_SchemaLoaded) ){
    rc = sqlite3InitOne(db, 0, pzErrMsg, 0);
    if( rc ) return rc;
  }
  /* All other schemas after the main schema. The "temp" schema must be last */
  for(i=db->nDb-1; i>0; i--){
    assert( i==1 || sqlite3BtreeHoldsMutex(db->aDb[i].pBt) );
    if( !DbHasProperty(db, i, DB_SchemaLoaded) ){
      rc = sqlite3InitOne(db, i, pzErrMsg, 0);
      if( rc ) return rc;
    }
  }
  if( commit_internal ){
    sqlite3CommitInternalChanges(db);
  }

  return SQLITE_OK;
}

/*
** This routine is a no-op if the database schema is already initialized.
** Otherwise, the schema is loaded. An error code is returned.
*/
int sqlite3ReadSchema(Parse *pParse){







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












>
>
|

>
>









<


|



<


|


>
|







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
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456

457
458
459
460
461
462

463
464
465
466
467
468
469
470
471
472
473
474
475
476
      sqlite3OomFault(db);
    }
    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 ){
    db->mDbFlags &= ~DBFLAG_SchemaInuse;
    sqlite3SchemaReleaseAll(db);
  }
}

/*
** Initialize all database files - the main database file, the file
** used to store temporary tables, and any additional database files
** created using ATTACH statements.  Return a success code.  If an
** error occurs, write an error message into *pzErrMsg.
**
** After a database is initialized, the DB_SchemaLoaded bit is set
** bit is set in the flags field of the Db structure. If the database
** file was of zero-length, then the DB_Empty flag is also set.
*/
int sqlite3Init(sqlite3 *db, char **pzErrMsg){
  int rc = SQLITE_OK;
  int bReleaseSchema;
  int i;
  int commit_internal = !(db->mDbFlags&DBFLAG_SchemaChange);

  bReleaseSchema = sqlite3LockReusableSchema(db);
  
  assert( sqlite3_mutex_held(db->mutex) );
  assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) );
  assert( db->init.busy==0 );
  ENC(db) = SCHEMA_ENC(db);
  assert( db->nDb>0 );
  /* Do the main schema first */
  if( !DbHasProperty(db, 0, DB_SchemaLoaded) ){
    rc = sqlite3InitOne(db, 0, pzErrMsg, 0);

  }
  /* All other schemas after the main schema. The "temp" schema must be last */
  for(i=db->nDb-1; rc==SQLITE_OK && i>0; i--){
    assert( i==1 || sqlite3BtreeHoldsMutex(db->aDb[i].pBt) );
    if( !DbHasProperty(db, i, DB_SchemaLoaded) ){
      rc = sqlite3InitOne(db, i, pzErrMsg, 0);

    }
  }
  if( rc==SQLITE_OK && commit_internal ){
    sqlite3CommitInternalChanges(db);
  }
  sqlite3UnlockReusableSchema(db, bReleaseSchema);
  return rc;
}

/*
** This routine is a no-op if the database schema is already initialized.
** Otherwise, the schema is loaded. An error code is returned.
*/
int sqlite3ReadSchema(Parse *pParse){
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
  if( ppStmt==0 ) return SQLITE_MISUSE_BKPT;
#endif
  *ppStmt = 0;
  if( !sqlite3SafetyCheckOk(db)||zSql==0 ){
    return SQLITE_MISUSE_BKPT;
  }
  sqlite3_mutex_enter(db->mutex);
  if( IsReuseSchema(db) ){
    if( (db->mDbFlags & DBFLAG_SchemaInuse)==0 ){
      db->mDbFlags |= DBFLAG_SchemaInuse;
      bReleaseSchema = 1;
    }
  }
  sqlite3BtreeEnterAll(db);
  do{
    /* Make multiple attempts to compile the SQL, until it either succeeds
    ** or encounters a permanent error.  A schema problem after one schema
    ** reset is considered a permanent error. */
    rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
    assert( rc==SQLITE_OK || *ppStmt==0 );
  }while( rc==SQLITE_ERROR_RETRY
       || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );

  sqlite3BtreeLeaveAll(db);

  if( bReleaseSchema ){
    db->mDbFlags &= ~DBFLAG_SchemaInuse;
    sqlite3SchemaReleaseAll(db);
  }

  rc = sqlite3ApiExit(db, rc);
  assert( (rc&db->errMask)==rc );
  sqlite3_mutex_leave(db->mutex);
  return rc;
}








<
<
<
|
<
<












|
<
<
<







761
762
763
764
765
766
767



768


769
770
771
772
773
774
775
776
777
778
779
780
781



782
783
784
785
786
787
788
  if( ppStmt==0 ) return SQLITE_MISUSE_BKPT;
#endif
  *ppStmt = 0;
  if( !sqlite3SafetyCheckOk(db)||zSql==0 ){
    return SQLITE_MISUSE_BKPT;
  }
  sqlite3_mutex_enter(db->mutex);



  bReleaseSchema = sqlite3LockReusableSchema(db);


  sqlite3BtreeEnterAll(db);
  do{
    /* Make multiple attempts to compile the SQL, until it either succeeds
    ** or encounters a permanent error.  A schema problem after one schema
    ** reset is considered a permanent error. */
    rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
    assert( rc==SQLITE_OK || *ppStmt==0 );
  }while( rc==SQLITE_ERROR_RETRY
       || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );

  sqlite3BtreeLeaveAll(db);

  sqlite3UnlockReusableSchema(db, bReleaseSchema);




  rc = sqlite3ApiExit(db, rc);
  assert( (rc&db->errMask)==rc );
  sqlite3_mutex_leave(db->mutex);
  return rc;
}

Changes to src/sqliteInt.h.
3258
3259
3260
3261
3262
3263
3264

3265
3266
3267
3268
3269
3270
3271
  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 */

  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.







>







3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
  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.
4325
4326
4327
4328
4329
4330
4331



4332
4333
4334
4335
4336
4337
4338
Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
void sqlite3KeyInfoUnref(KeyInfo*);
KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int);




#ifdef SQLITE_DEBUG
int sqlite3KeyInfoIsWriteable(KeyInfo*);
#endif
int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
  void (*)(sqlite3_context*,int,sqlite3_value **),
  void (*)(sqlite3_context*,int,sqlite3_value **), 







>
>
>







4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
void sqlite3KeyInfoUnref(KeyInfo*);
KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int);

void sqlite3UnlockReusableSchema(sqlite3 *db, int bRelease);
int sqlite3LockReusableSchema(sqlite3 *db);

#ifdef SQLITE_DEBUG
int sqlite3KeyInfoIsWriteable(KeyInfo*);
#endif
int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
  void (*)(sqlite3_context*,int,sqlite3_value **),
  void (*)(sqlite3_context*,int,sqlite3_value **), 
Changes to src/trigger.c.
51
52
53
54
55
56
57

58




59
60
61
62


63
64

65
66
67
68
69

70
71
72
73
74
75
76
  Trigger *pList = 0;                  /* List of triggers to return */

  if( pParse->disableTriggers ){
    return 0;
  }

  if( pTmpSchema!=pTab->pSchema ){

    HashElem *p;




    assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) );
    for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){
      Trigger *pTrig = (Trigger *)sqliteHashData(p);
      if( pTrig->pTabSchema==pTab->pSchema


       && 0==sqlite3StrICmp(pTrig->table, pTab->zName) 
      ){

        pTrig->pNext = (pList ? pList : pTab->pTrigger);
        pList = pTrig;
      }
    }
  }


  return (pList ? pList : pTab->pTrigger);
}

/*
** This is called by the parser when it sees a CREATE TRIGGER statement
** up to the point of the BEGIN before the trigger actions.  A Trigger







>

>
>
>
>
|


|
>
>
|

>





>







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  Trigger *pList = 0;                  /* List of triggers to return */

  if( pParse->disableTriggers ){
    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))
      ){
        if( 0==sqlite3StrICmp(pTrig->table, pTab->zName) 
      ){
          pTrig->pTabSchema = pTab->pSchema;
        pTrig->pNext = (pList ? pList : pTab->pTrigger);
        pList = pTrig;
      }
    }
  }
  }

  return (pList ? pList : pTab->pTrigger);
}

/*
** This is called by the parser when it sees a CREATE TRIGGER statement
** up to the point of the BEGIN before the trigger actions.  A Trigger
240
241
242
243
244
245
246




247
248
249
250
251
252
253

  /* 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);




  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;
  if( IN_RENAME_OBJECT ){
    sqlite3RenameTokenRemap(pParse, pTrigger->table, pTableName->a[0].zName);
    pTrigger->pWhen = pWhen;







>
>
>
>







249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266

  /* 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;
  if( IN_RENAME_OBJECT ){
    sqlite3RenameTokenRemap(pParse, pTrigger->table, pTableName->a[0].zName);
    pTrigger->pWhen = pWhen;
537
538
539
540
541
542
543

544
545
546
547
548
549
550
** Recursively delete a Trigger structure
*/
void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
  if( pTrigger==0 ) return;
  sqlite3DeleteTriggerStep(db, pTrigger->step_list);
  sqlite3DbFree(db, pTrigger->zName);
  sqlite3DbFree(db, pTrigger->table);

  sqlite3ExprDelete(db, pTrigger->pWhen);
  sqlite3IdListDelete(db, pTrigger->pColumns);
  sqlite3DbFree(db, pTrigger);
}

/*
** This function is called to drop a trigger from the database schema. 







>







550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
** Recursively delete a Trigger structure
*/
void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
  if( pTrigger==0 ) return;
  sqlite3DeleteTriggerStep(db, pTrigger->step_list);
  sqlite3DbFree(db, pTrigger->zName);
  sqlite3DbFree(db, pTrigger->table);
  sqlite3DbFree(db, pTrigger->zTabSchema);
  sqlite3ExprDelete(db, pTrigger->pWhen);
  sqlite3IdListDelete(db, pTrigger->pColumns);
  sqlite3DbFree(db, pTrigger);
}

/*
** This function is called to drop a trigger from the database schema. 
Changes to src/vdbe.c.
5757
5758
5759
5760
5761
5762
5763

5764
5765
5766
5767
5768
5769
5770
** then runs the new virtual machine.  It is thus a re-entrant opcode.
*/
case OP_ParseSchema: {
  int iDb;
  const char *zMaster;
  char *zSql;
  InitData initData;


  /* Any prepared statement that invokes this opcode will hold mutexes
  ** on every btree.  This is a prerequisite for invoking 
  ** sqlite3InitCallback().
  */
#ifdef SQLITE_DEBUG
  for(iDb=0; iDb<db->nDb; iDb++){







>







5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
** then runs the new virtual machine.  It is thus a re-entrant opcode.
*/
case OP_ParseSchema: {
  int iDb;
  const char *zMaster;
  char *zSql;
  InitData initData;
  int bRelease;

  /* Any prepared statement that invokes this opcode will hold mutexes
  ** on every btree.  This is a prerequisite for invoking 
  ** sqlite3InitCallback().
  */
#ifdef SQLITE_DEBUG
  for(iDb=0; iDb<db->nDb; iDb++){
5793
5794
5795
5796
5797
5798
5799








5800
5801
5802
5803
5804
5805

5806
5807
5808
5809
5810
5811
5812
    initData.mInitFlags = 0;
    zSql = sqlite3MPrintf(db,
       "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid",
       db->aDb[iDb].zDbSName, zMaster, pOp->p4.z);
    if( zSql==0 ){
      rc = SQLITE_NOMEM_BKPT;
    }else{








      assert( db->init.busy==0 );
      db->init.busy = 1;
      initData.rc = SQLITE_OK;
      initData.nInitRow = 0;
      assert( !db->mallocFailed );
      rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);

      if( rc==SQLITE_OK ) rc = initData.rc;
      if( rc==SQLITE_OK && initData.nInitRow==0 ){
        /* The OP_ParseSchema opcode with a non-NULL P4 argument should parse
        ** at least one SQL statement. Any less than that indicates that
        ** the sqlite_master table is corrupt. */
        rc = SQLITE_CORRUPT_BKPT;
      }







>
>
>
>
>
>
>
>






>







5794
5795
5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
5822
    initData.mInitFlags = 0;
    zSql = sqlite3MPrintf(db,
       "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid",
       db->aDb[iDb].zDbSName, zMaster, pOp->p4.z);
    if( zSql==0 ){
      rc = SQLITE_NOMEM_BKPT;
    }else{
      bRelease = sqlite3LockReusableSchema(db);
      if( IsReuseSchema(db) ){
        rc = sqlite3Init(db, &p->zErrMsg);
        if( rc ){
          sqlite3UnlockReusableSchema(db, bRelease);
          goto abort_due_to_error;
        }
      }
      assert( db->init.busy==0 );
      db->init.busy = 1;
      initData.rc = SQLITE_OK;
      initData.nInitRow = 0;
      assert( !db->mallocFailed );
      rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
      sqlite3UnlockReusableSchema(db, bRelease);
      if( rc==SQLITE_OK ) rc = initData.rc;
      if( rc==SQLITE_OK && initData.nInitRow==0 ){
        /* The OP_ParseSchema opcode with a non-NULL P4 argument should parse
        ** at least one SQL statement. Any less than that indicates that
        ** the sqlite_master table is corrupt. */
        rc = SQLITE_CORRUPT_BKPT;
      }
Changes to test/reuse1.test.
12
13
14
15
16
17
18

19
20
21
22
23
24
25
#


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



forcedelete test.db2
sqlite3 db2 test.db2

do_execsql_test 1.0 {
  CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE, z);
  CREATE INDEX i1 ON t1(z);







>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#


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

if 1 {

forcedelete test.db2
sqlite3 db2 test.db2

do_execsql_test 1.0 {
  CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE, z);
  CREATE INDEX i1 ON t1(z);
86
87
88
89
90
91
92
93







































































































































































































































































































94


  7  { DROP INDEX i1 }
  8  { DROP TABLE t1 }
  9  { DROP TRIGGER tr1 }
  10 { ANALYZE }
} {
  do_catchsql_test 1.5.$tn $sql {1 {attempt to modify read-only schema}}
}








































































































































































































































































































finish_test










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

>
>
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
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
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
  7  { DROP INDEX i1 }
  8  { DROP TABLE t1 }
  9  { DROP TRIGGER tr1 }
  10 { ANALYZE }
} {
  do_catchsql_test 1.5.$tn $sql {1 {attempt to modify read-only schema}}
}

#-------------------------------------------------------------------------
#
reset_db
forcedelete test.db2
ifcapable fts5 {
  do_execsql_test 2.0 {
    CREATE VIRTUAL TABLE ft USING fts5(a);
    INSERT INTO ft VALUES('one'), ('two'), ('three');
    ATTACH 'test.db2' AS aux;
    CREATE VIRTUAL TABLE aux.ft USING fts5(a);
    INSERT INTO aux.ft VALUES('aux1'), ('aux2'), ('aux3');
  }

  db close
  sqlite3 db  test.db -reuse-schema 1

  do_execsql_test 2.1 {
    ATTACH 'test.db2' AS aux;
    SELECT * FROM main.ft;
  } {one two three}

  do_execsql_test 2.2 {
    SELECT * FROM aux.ft;
  } {aux1 aux2 aux3}

  do_execsql_test 2.2 {
    SELECT * FROM aux.ft_content;
  } {1 aux1 2 aux2 3 aux3}
}

}

#-------------------------------------------------------------------------
#
reset_db
forcedelete test.db2
do_execsql_test 3.0 {
  CREATE TABLE t1(a PRIMARY KEY, b, c);
  CREATE VIEW v1 AS SELECT * FROM t1;
  CREATE TRIGGER v1_ins INSTEAD OF INSERT ON v1 BEGIN
    INSERT INTO t1 VALUES(new.a, new.b, new.c);
  END;
  CREATE TRIGGER v1_del INSTEAD OF DELETE ON v1 BEGIN
    DELETE FROM t1 WHERE a=old.a;
  END;
  CREATE TRIGGER v1_up INSTEAD OF UPDATE ON v1 BEGIN
    UPDATE t1 SET a=new.a, b=new.b, c=new.c WHERE a=old.a;
  END;
}
forcecopy test.db test.db2

do_test 3.1 {
  sqlite3 db2 test.db2
  execsql { INSERT INTO t1 VALUES(1, 2, 3) } db
  execsql { INSERT INTO t1 VALUES(4, 5, 6) } db2
  db2 close
  execsql { ATTACH 'test.db2' AS aux; }
} {}

do_execsql_test 3.2 {
  SELECT * FROM main.v1;
} {1 2 3}

do_execsql_test 3.3 {
  SELECT * FROM aux.v1;
} {4 5 6}

db close
sqlite3 db test.db -reuse-schema 1

do_execsql_test 3.4 { ATTACH 'test.db2' AS aux } {}
do_execsql_test 3.5 { SELECT * FROM main.v1 } {1 2 3}
do_execsql_test 3.6 { SELECT * FROM aux.v1  } {4 5 6}

do_execsql_test 3.7.1 { INSERT INTO aux.t1 VALUES(8, 9, 10); }
do_execsql_test 3.7.2 { SELECT * FROM main.v1 } {1 2 3}
do_execsql_test 3.7.3 { SELECT * FROM aux.v1  } {4 5 6 8 9 10}

do_execsql_test 3.8.1 { DELETE FROM aux.t1 WHERE b=5 }
do_execsql_test 3.8.2 { SELECT * FROM main.v1 } {1 2 3}
do_execsql_test 3.8.3 { SELECT * FROM aux.v1  } {8 9 10}

do_execsql_test 3.9.1 { UPDATE aux.t1 SET b='abc' }
do_execsql_test 3.9.2 { SELECT * FROM main.v1 } {1 2 3}
do_execsql_test 3.9.3 { SELECT * FROM aux.v1  } {8 abc 10}

do_execsql_test 3.10.1 { INSERT INTO aux.v1 VALUES(11, 12, 13) }
do_execsql_test 3.10.2 { SELECT * FROM main.v1 } {1 2 3}
do_execsql_test 3.10.3 { SELECT * FROM aux.v1  } {8 abc 10 11 12 13}

do_execsql_test 3.11.1 { DELETE FROM aux.v1 WHERE b='abc' }
do_execsql_test 3.11.2 { SELECT * FROM main.v1 } {1 2 3}
do_execsql_test 3.11.3 { SELECT * FROM aux.v1  } {11 12 13}

do_execsql_test 3.12.1 { UPDATE aux.v1 SET b='def' }
do_execsql_test 3.12.2 { SELECT * FROM main.v1 } {1 2 3}
do_execsql_test 3.12.3 { SELECT * FROM aux.v1  } {11 def 13}

do_execsql_test 3.13.1 {
  CREATE TEMP TRIGGER xyz AFTER INSERT ON aux.t1 BEGIN
    INSERT INTO v1 VALUES(new.a, new.b, new.c);
  END
}
do_execsql_test 3.13.2 {
  INSERT INTO aux.v1 VALUES('x', 'y', 'z');
}
do_execsql_test 3.13.3 {
  SELECT * FROM v1;
} {1 2 3 x y z}

#-------------------------------------------------------------------------
#
reset_db
forcedelete test.db2
do_execsql_test 4.0 {
  CREATE TABLE t1(a PRIMARY KEY, b, c UNIQUE);
  CREATE TABLE del(a, b, c);
  CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN
    INSERT INTO del VALUES(old.a, old.b, old.c);
  END;
}
forcecopy test.db test.db2

db close
sqlite3 db test.db -reuse-schema 1
execsql { 
  ATTACH 'test.db2' AS aux;
  PRAGMA recursive_triggers = 1;
}

do_execsql_test 4.1 {
  INSERT INTO main.t1 VALUES(1, 2, 3);
  INSERT INTO aux.t1 VALUES(4, 5, 6);
}

do_execsql_test 4.2.1 {
  INSERT OR REPLACE INTO aux.t1 VALUES('a', 'b', 6);
  SELECT * FROM aux.t1;
} {a b 6}
do_execsql_test 4.2.2 { SELECT * FROM aux.del  } {4 5 6}
do_execsql_test 4.2.3 { SELECT * FROM main.del } {}

do_execsql_test 4.3.1 {
  INSERT INTO aux.t1 VALUES('x', 'y', 'z');
  UPDATE OR REPLACE aux.t1 SET c='z' WHERE a='a';
} {}
do_execsql_test 4.3.2 { SELECT * FROM aux.del  } {4 5 6 x y z}
do_execsql_test 4.3.3 { SELECT * FROM main.del } {}

#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 5.0 {
  CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
  CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c);
  CREATE INDEX i1 ON t1(b);
  INSERT INTO t1 VALUES(1, 2, 3), (4, 5, 6);
  ANALYZE;
  PRAGMA writable_schema = 1;
  DELETE FROM sqlite_stat1;
}
db close
forcecopy test.db test.db2
sqlite3 db test.db -reuse-schema 1
execsql { ATTACH 'test.db2' AS aux }

foreach {tn sql} {
  1 { CREATE TABLE t3(x) }
  2 { DROP TABLE t2 }
  3 { CREATE INDEX i2 ON t2(b) }
  4 { DROP INDEX i1 }
  5 { ALTER TABLE t1 ADD COLUMN d }
  6 { ALTER TABLE t1 RENAME TO t3 }
  7 { ALTER TABLE t1 RENAME c TO d }
} {
  do_catchsql_test 5.1.$tn $sql {1 {attempt to modify read-only schema}}
}

do_execsql_test 5.2.1 { ANALYZE aux.t1 } {}
do_execsql_test 5.2.2 { SELECT * FROM aux.sqlite_stat1  } {t1 i1 {2 1}}
do_execsql_test 5.2.3 { SELECT * FROM main.sqlite_stat1 } {}

do_test 5.3.0 {
  sqlite3 db2 test.db2
  db2 eval { 
    PRAGMA writable_schema = 1;
    DELETE FROM sqlite_stat1;
  }
} {}

do_execsql_test 5.3.1 { SELECT * FROM aux.sqlite_stat1  } {}
do_execsql_test 5.3.2 { ANALYZE aux } {}
do_execsql_test 5.3.3 { SELECT * FROM aux.sqlite_stat1  } {t1 i1 {2 1}}
do_execsql_test 5.3.4 { SELECT * FROM main.sqlite_stat1 } {}

#-------------------------------------------------------------------------
# Attempting to run ANALYZE when the required sqlite_statXX functions
# are missing is an error (because it would modify the database schema).
#
reset_db
do_execsql_test 5.4 {
  CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
  CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c);
  CREATE INDEX i1 ON t1(b);
  INSERT INTO t1 VALUES(1, 2, 3), (4, 5, 6);
}
db close
sqlite3 db test.db -reuse-schema 1
breakpoint
foreach {tn sql} {
  1 { ANALYZE }
  2 { ANALYZE t1 }
  3 { ANALYZE i1 }
  4 { ANALYZE main }
  5 { ANALYZE main.t1 }
  6 { ANALYZE main.i1 }
} {
  do_catchsql_test 5.4.$tn $sql {1 {attempt to modify read-only schema}}
}

#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 6.0 {
  CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
  CREATE VIEW v1 AS SELECT * FROM t1;
}
db close
forcecopy test.db test.db2
sqlite3 db test.db -reuse-schema 1
execsql { ATTACH 'test.db2' AS aux }

do_execsql_test 6.1 {
  INSERT INTO main.t1(a) VALUES(1), (2), (3);
  INSERT INTO aux.t1(a) VALUES(4), (5), (6);
  CREATE TEMP TABLE t2(i,t);
  INSERT INTO t2 VALUES(2, 'two'), (5, 'five');
}

do_execsql_test 6.2 {
  SELECT t FROM t2 WHERE i IN (SELECT a FROM aux.t1)
} {five}
do_execsql_test 6.3 {
  SELECT t FROM t2 WHERE i IN (SELECT a FROM aux.v1)
} {five}

#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 7.0 {
  CREATE TABLE p1(a PRIMARY KEY, b);
  CREATE TABLE p2(a PRIMARY KEY, b);
  CREATE TABLE c1(x REFERENCES p1 ON UPDATE CASCADE ON DELETE CASCADE);
}

db close
forcecopy test.db test.db2
sqlite3 db test.db -reuse-schema 1
execsql { ATTACH 'test.db2' AS aux }

do_execsql_test 7.1 {
  INSERT INTO aux.p1 VALUES(1, 'one');
  INSERT INTO aux.p1 VALUES(2, 'two');
  PRAGMA foreign_keys = on;
}

do_execsql_test 7.2 {
  INSERT INTO aux.c1 VALUES(2);
}

do_execsql_test 7.3.1 {
  PRAGMA foreign_keys = off;
  INSERT INTO main.p2 SELECT * FROM aux.p1;
}
do_execsql_test 7.3.2 {
  SELECT * FROM main.p2;
} {1 one 2 two}

do_execsql_test 7.3.3 {
  INSERT INTO aux.p2 VALUES(1, 2);
}

do_execsql_test 7.3.4 {
  SELECT main.p2.a FROM main.p2, aux.p2;
} {1 2}

do_execsql_test 7.3.5 {
  SELECT * FROM main.p2, aux.p2;
} {1 one 1 2   2 two 1 2}

do_execsql_test 7.4 {
  SELECT count(*) FROM aux.p2;
} {1}


finish_test