/ Check-in [a6256980]
Login

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

Overview
Comment:Add experimental sqlite3_open_v2() flag SQLITE_OPEN_REUSE_SCHEMA. For sharing identical in-memory schema objects between connections.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | reuse-schema
Files: files | file ages | folders
SHA3-256: a625698048c05375f12ea8875892fdbf3518291f10917fe68dd2294280cd3b42
User & Date: dan 2017-08-09 20:35:10
Wiki:reuse-schema
Context
2018-10-08
18:58
Merge latest trunk changes into this branch. check-in: 2ac72114 user: dan tags: reuse-schema
2017-08-09
20:35
Add experimental sqlite3_open_v2() flag SQLITE_OPEN_REUSE_SCHEMA. For sharing identical in-memory schema objects between connections. check-in: a6256980 user: dan tags: reuse-schema
19:27
Various bug fixes for the new LSM1 virtual table design. check-in: 94434a25 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/alter.c.

   350    350   
   351    351     /* Drop the table and index from the internal schema.  */
   352    352     sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
   353    353   
   354    354     /* Reload the table, index and permanent trigger schemas. */
   355    355     zWhere = sqlite3MPrintf(pParse->db, "tbl_name=%Q", zName);
   356    356     if( !zWhere ) return;
   357         -  sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
          357  +  sqlite3VdbeAddParseSchemaOp(pParse, iDb, zWhere);
   358    358   
   359    359   #ifndef SQLITE_OMIT_TRIGGER
   360    360     /* Now, if the table is not stored in the temp database, reload any temp 
   361    361     ** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined. 
   362    362     */
   363    363     if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
   364         -    sqlite3VdbeAddParseSchemaOp(v, 1, zWhere);
          364  +    sqlite3VdbeAddParseSchemaOp(pParse, 1, zWhere);
   365    365     }
   366    366   #endif
   367    367   }
   368    368   
   369    369   /*
   370    370   ** Parameter zName is the name of a table that is about to be altered
   371    371   ** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN).

Changes to src/analyze.c.

  1310   1310     sqlite3 *db = pParse->db;
  1311   1311     Schema *pSchema = db->aDb[iDb].pSchema;    /* Schema of database iDb */
  1312   1312     HashElem *k;
  1313   1313     int iStatCur;
  1314   1314     int iMem;
  1315   1315     int iTab;
  1316   1316   
         1317  +  sqlite3SchemaWritable(pParse, iDb);
  1317   1318     sqlite3BeginWriteOperation(pParse, 0, iDb);
  1318   1319     iStatCur = pParse->nTab;
  1319   1320     pParse->nTab += 3;
  1320   1321     openStatTable(pParse, iDb, iStatCur, 0, 0);
  1321   1322     iMem = pParse->nMem+1;
  1322   1323     iTab = pParse->nTab;
  1323   1324     assert( sqlite3SchemaMutexHeld(db, iDb, 0) );

Changes to src/build.c.

   513    513   }
   514    514   
   515    515   /*
   516    516   ** Reset the schema for the database at index iDb.  Also reset the
   517    517   ** TEMP schema.
   518    518   */
   519    519   void sqlite3ResetOneSchema(sqlite3 *db, int iDb){
   520         -  Db *pDb;
   521         -  assert( iDb<db->nDb );
   522         -
   523         -  /* Case 1:  Reset the single schema identified by iDb */
   524         -  pDb = &db->aDb[iDb];
   525         -  assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
          520  +  /* If any database other than TEMP is reset, then also reset TEMP
          521  +  ** since TEMP might be holding triggers that reference tables in the
          522  +  ** other database.  */
          523  +  Db *pDb = &db->aDb[1];
   526    524     assert( pDb->pSchema!=0 );
   527    525     sqlite3SchemaClear(pDb->pSchema);
   528    526   
   529         -  /* If any database other than TEMP is reset, then also reset TEMP
   530         -  ** since TEMP might be holding triggers that reference tables in the
   531         -  ** other database.
   532         -  */
          527  +  assert( iDb<db->nDb );
          528  +  assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
          529  +  assert( db->aDb[iDb].pSchema!=0 );
          530  +
   533    531     if( iDb!=1 ){
   534         -    pDb = &db->aDb[1];
   535         -    assert( pDb->pSchema!=0 );
   536         -    sqlite3SchemaClear(pDb->pSchema);
          532  +    sqlite3SchemaUnuse(db, iDb);
   537    533     }
   538    534     return;
   539    535   }
   540    536   
   541    537   /*
   542    538   ** Erase all schema information from all attached databases (including
   543    539   ** "main" and "temp") for a single database connection.
   544    540   */
   545    541   void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){
   546    542     int i;
   547    543     sqlite3BtreeEnterAll(db);
   548         -  for(i=0; i<db->nDb; i++){
   549         -    Db *pDb = &db->aDb[i];
   550         -    if( pDb->pSchema ){
   551         -      sqlite3SchemaClear(pDb->pSchema);
   552         -    }
          544  +  for(i=0; i<db->nDb; i = (i?i+1:2)){
          545  +    sqlite3SchemaUnuse(db, i);
   553    546     }
          547  +  sqlite3SchemaClear(db->aDb[1].pSchema);
          548  +
   554    549     db->mDbFlags &= ~DBFLAG_SchemaChange;
   555    550     sqlite3VtabUnlockList(db);
   556    551     sqlite3BtreeLeaveAll(db);
   557    552     sqlite3CollapseDatabaseArray(db);
   558    553   }
   559    554   
   560    555   /*
................................................................................
  2034   2029             pDb->zDbSName
  2035   2030           );
  2036   2031         }
  2037   2032       }
  2038   2033   #endif
  2039   2034   
  2040   2035       /* Reparse everything to update our internal data structures */
  2041         -    sqlite3VdbeAddParseSchemaOp(v, iDb,
         2036  +    sqlite3VdbeAddParseSchemaOp(pParse, iDb,
  2042   2037              sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName));
  2043   2038     }
  2044   2039   
  2045   2040   
  2046   2041     /* Add the table to the in-memory representation of the database.
  2047   2042     */
  2048   2043     if( db->init.busy ){
................................................................................
  2529   2524   
  2530   2525     if( pTab==0 ){
  2531   2526       if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
  2532   2527       goto exit_drop_table;
  2533   2528     }
  2534   2529     iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
  2535   2530     assert( iDb>=0 && iDb<db->nDb );
         2531  +  sqlite3SchemaWritable(pParse, iDb);
  2536   2532   
  2537   2533     /* If pTab is a virtual table, call ViewGetColumnNames() to ensure
  2538   2534     ** it is initialized.
  2539   2535     */
  2540   2536     if( IsVirtual(pTab) && sqlite3ViewGetColumnNames(pParse, pTab) ){
  2541   2537       goto exit_drop_table;
  2542   2538     }
................................................................................
  3392   3388   
  3393   3389       /* Fill the index with data and reparse the schema. Code an OP_Expire
  3394   3390       ** to invalidate all pre-compiled statements.
  3395   3391       */
  3396   3392       if( pTblName ){
  3397   3393         sqlite3RefillIndex(pParse, pIndex, iMem);
  3398   3394         sqlite3ChangeCookie(pParse, iDb);
  3399         -      sqlite3VdbeAddParseSchemaOp(v, iDb,
         3395  +      sqlite3VdbeAddParseSchemaOp(pParse, iDb,
  3400   3396            sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
  3401   3397         sqlite3VdbeAddOp0(v, OP_Expire);
  3402   3398       }
  3403   3399   
  3404   3400       sqlite3VdbeJumpHere(v, pIndex->tnum);
  3405   3401     }
  3406   3402   
................................................................................
  3511   3507     }
  3512   3508     if( pIndex->idxType!=SQLITE_IDXTYPE_APPDEF ){
  3513   3509       sqlite3ErrorMsg(pParse, "index associated with UNIQUE "
  3514   3510         "or PRIMARY KEY constraint cannot be dropped", 0);
  3515   3511       goto exit_drop_index;
  3516   3512     }
  3517   3513     iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
         3514  +  sqlite3SchemaWritable(pParse, iDb);
  3518   3515   #ifndef SQLITE_OMIT_AUTHORIZATION
  3519   3516     {
  3520   3517       int code = SQLITE_DROP_INDEX;
  3521   3518       Table *pTab = pIndex->pTable;
  3522   3519       const char *zDb = db->aDb[iDb].zDbSName;
  3523   3520       const char *zTab = SCHEMA_TABLE(iDb);
  3524   3521       if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){

Changes to src/callback.c.

   456    456     sqlite3HashClear(&pSchema->fkeyHash);
   457    457     pSchema->pSeqTab = 0;
   458    458     if( pSchema->schemaFlags & DB_SchemaLoaded ){
   459    459       pSchema->iGeneration++;
   460    460       pSchema->schemaFlags &= ~DB_SchemaLoaded;
   461    461     }
   462    462   }
          463  +
          464  +/*
          465  +** Global linked list of sharable Schema objects. Read and write access must
          466  +** be protected by the SQLITE_MUTEX_STATIC_MASTER mutex.
          467  +*/
          468  +static Schema *SQLITE_WSD sharedSchemaList = 0;
          469  +
          470  +/*
          471  +** Check that the schema of db iDb is writable (either because it is the temp
          472  +** db schema or because the db handle was opened without
          473  +** SQLITE_OPEN_REUSE_SCHEMA). If so, do nothing. Otherwise, leave an 
          474  +** error in the Parse object.
          475  +*/
          476  +void sqlite3SchemaWritable(Parse *pParse, int iDb){
          477  +  if( iDb!=1 && (pParse->db->openFlags & SQLITE_OPEN_REUSE_SCHEMA) ){
          478  +    sqlite3ErrorMsg(pParse, "attempt to modify read-only schema");
          479  +  }
          480  +}
          481  +
          482  +/*
          483  +** Replace the Schema object currently associated with database iDb with
          484  +** an empty schema object, ready to be populated. If there are multiple
          485  +** users of the Schema, this means decrementing the current objects ref
          486  +** count and replacing it with a pointer to a new, empty, object. Or, if
          487  +** the database handle passed as the first argument of the schema object 
          488  +** is the only user, remove it from the shared list (if applicable) and 
          489  +** clear it in place.
          490  +*/
          491  +void sqlite3SchemaUnuse(sqlite3 *db, int iDb){
          492  +  Db *pDb = &db->aDb[iDb];
          493  +  Schema *pSchema;
          494  +  assert( iDb!=1 );
          495  +  if( (pSchema = pDb->pSchema) ){
          496  +
          497  +    if( (db->openFlags & SQLITE_OPEN_REUSE_SCHEMA) ){
          498  +      sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
          499  +      assert( pSchema->nRef>=1 );
          500  +      if( pSchema->nRef==1 ){
          501  +        Schema **pp;
          502  +        for(pp=&sharedSchemaList; (*pp); pp=&(*pp)->pNext){
          503  +          if( *pp==pSchema ){
          504  +            *pp = pSchema->pNext;
          505  +            break;
          506  +          }
          507  +        }
          508  +        pSchema->pNext = 0;
          509  +      }else{
          510  +        assert( db->openFlags & SQLITE_OPEN_REUSE_SCHEMA );
          511  +        pSchema->nRef--;
          512  +        pDb->pSchema = pSchema = 0;
          513  +      }
          514  +      sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
          515  +    }
          516  +
          517  +    if( pSchema==0 ){
          518  +      db->aDb[iDb].pSchema = sqlite3SchemaGet(db, 0);
          519  +    }else{
          520  +      sqlite3SchemaClear(pSchema);
          521  +    }
          522  +  }
          523  +}
          524  +
          525  +/*
          526  +** The schema for database iDb of database handle db, which was opened
          527  +** with SQLITE_OPEN_REUSE_SCHEMA, has just been parsed. This function
          528  +** checks the global list (sharedSchemaList) for a matching schema and,
          529  +** if one is found, frees the newly parsed Schema object and adds a pointer 
          530  +** to the existing shared schema in its place. Or, if there is no matching
          531  +** schema in the list, then the new schema is added to it.
          532  +*/
          533  +void sqlite3SchemaReuse(sqlite3 *db, int iDb){
          534  +  Schema *pSchema = db->aDb[iDb].pSchema;
          535  +  Schema *p;
          536  +  assert( pSchema && iDb!=1 );
          537  +
          538  +  sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
          539  +  for(p=sharedSchemaList; p; p=p->pNext){
          540  +    if( p->cksum==pSchema->cksum 
          541  +        && p->schema_cookie==pSchema->schema_cookie 
          542  +      ){
          543  +      break;
          544  +    }
          545  +  }
          546  +  if( !p ){
          547  +    /* No matching schema was found. */
          548  +    pSchema->pNext = sharedSchemaList;
          549  +    sharedSchemaList = pSchema;
          550  +  }else{
          551  +    /* Found a matching schema. Increase its ref count. */
          552  +    p->nRef++;
          553  +  }
          554  +  sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
          555  +
          556  +  /* If a matching schema was found in the shared schema list, free the
          557  +  ** schema object just parsed, and add a pointer to the matching schema
          558  +  ** to the db handle.  */
          559  +  if( p ){
          560  +    sqlite3SchemaClear(pSchema);
          561  +    sqlite3DbFree(0, pSchema);
          562  +    db->aDb[iDb].pSchema = p;
          563  +  }
          564  +}
   463    565   
   464    566   /*
   465    567   ** Find and return the schema associated with a BTree.  Create
   466    568   ** a new one if necessary.
   467    569   */
   468    570   Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
   469    571     Schema * p;
   470         -  if( pBt ){
          572  +  if( pBt && (db->openFlags & SQLITE_OPEN_REUSE_SCHEMA)==0 ){
   471    573       p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear);
   472    574     }else{
   473    575       p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema));
   474    576     }
   475    577     if( !p ){
   476    578       sqlite3OomFault(db);
   477    579     }else if ( 0==p->file_format ){
   478    580       sqlite3HashInit(&p->tblHash);
   479    581       sqlite3HashInit(&p->idxHash);
   480    582       sqlite3HashInit(&p->trigHash);
   481    583       sqlite3HashInit(&p->fkeyHash);
   482    584       p->enc = SQLITE_UTF8;
          585  +    p->nRef = 1;
   483    586     }
   484    587     return p;
   485    588   }

Changes to src/main.c.

  1153   1153     /* Close all database connections */
  1154   1154     for(j=0; j<db->nDb; j++){
  1155   1155       struct Db *pDb = &db->aDb[j];
  1156   1156       if( pDb->pBt ){
  1157   1157         sqlite3BtreeClose(pDb->pBt);
  1158   1158         pDb->pBt = 0;
  1159   1159         if( j!=1 ){
         1160  +        if( db->openFlags & SQLITE_OPEN_REUSE_SCHEMA ){
         1161  +          sqlite3SchemaUnuse(db, j);
         1162  +          sqlite3DbFree(0, pDb->pSchema);
         1163  +        }
  1160   1164           pDb->pSchema = 0;
  1161   1165         }
  1162   1166       }
  1163   1167     }
  1164   1168     /* Clear the TEMP schema separately and last */
  1165   1169     if( db->aDb[1].pSchema ){
  1166   1170       sqlite3SchemaClear(db->aDb[1].pSchema);

Changes to src/prepare.c.

    31     31       z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj);
    32     32       if( zExtra ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
    33     33       sqlite3DbFree(db, *pData->pzErrMsg);
    34     34       *pData->pzErrMsg = z;
    35     35     }
    36     36     pData->rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_CORRUPT_BKPT;
    37     37   }
           38  +
           39  +/*
           40  +** Update the Schema.cksum checksum to account for the database object
           41  +** specified by the three arguments following the first.
           42  +*/
           43  +void schemaUpdateChecksum(
           44  +  Schema *pSchema,                /* Schema object being parsed */
           45  +  const char *zName,              /* Name of new database object */
           46  +  const char *zRoot,              /* Root page of new database object */
           47  +  const char *zSql                /* SQL used to create new database object */
           48  +){
           49  +  int i;
           50  +  u64 cksum = pSchema->cksum;
           51  +  if( zName ){
           52  +    for(i=0; zName[i]; i++) cksum += (cksum<<3) + zName[i];
           53  +  }
           54  +  if( zRoot ) for(i=0; zRoot[i]; i++) cksum += (cksum<<3) + zRoot[i];
           55  +  if( zSql ) for(i=0; zSql[i]; i++) cksum += (cksum<<3) + zSql[i];
           56  +  pSchema->cksum = cksum;
           57  +}
    38     58   
    39     59   /*
    40     60   ** This is the callback routine for the code that initializes the
    41     61   ** database.  See sqlite3Init() below for additional information.
    42     62   ** This routine is also called from the OP_ParseSchema opcode of the VDBE.
    43     63   **
    44     64   ** Each callback contains the following information:
................................................................................
   117    137         ** safely ignore the index on the permanent table.
   118    138         */
   119    139         /* Do Nothing */;
   120    140       }else if( sqlite3GetInt32(argv[1], &pIndex->tnum)==0 ){
   121    141         corruptSchema(pData, argv[0], "invalid rootpage");
   122    142       }
   123    143     }
          144  +
          145  +  if( iDb!=1 && (db->openFlags & SQLITE_OPEN_REUSE_SCHEMA) ){
          146  +    /* If this schema might be used by multiple connections, ensure that
          147  +    ** the affinity string is allocated here. Otherwise, there might be
          148  +    ** a race condition where two threads attempt to allocate it
          149  +    ** simultaneously.  */
          150  +    Index *pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName);
          151  +    if( pIndex ) sqlite3IndexAffinityStr(db, pIndex);
          152  +    schemaUpdateChecksum(db->aDb[iDb].pSchema, argv[0], argv[1], argv[2]);
          153  +  }
   124    154     return 0;
   125    155   }
   126    156   
   127    157   /*
   128    158   ** Attempt to read the database schema and initialize internal
   129    159   ** data structures for a single database file.  The index of the
   130    160   ** database file is given by iDb.  iDb==0 is used for the main
................................................................................
   320    350       ** of the schema was loaded before the error occurred. The primary
   321    351       ** purpose of this is to allow access to the sqlite_master table
   322    352       ** even when its contents have been corrupted.
   323    353       */
   324    354       DbSetProperty(db, iDb, DB_SchemaLoaded);
   325    355       rc = SQLITE_OK;
   326    356     }
          357  +
          358  +  if( iDb!=1 && (db->openFlags & SQLITE_OPEN_REUSE_SCHEMA) ){
          359  +    sqlite3SchemaReuse(db, iDb);
          360  +  }
   327    361   
   328    362     /* Jump here for an error that occurs after successfully allocating
   329    363     ** curMain and calling sqlite3BtreeEnter(). For an error that occurs
   330    364     ** before that point, jump to error_out.
   331    365     */
   332    366   initone_error_out:
   333    367     if( openedTransaction ){

Changes to src/sqlite.h.in.

   551    551   #define SQLITE_OPEN_NOMUTEX          0x00008000  /* Ok for sqlite3_open_v2() */
   552    552   #define SQLITE_OPEN_FULLMUTEX        0x00010000  /* Ok for sqlite3_open_v2() */
   553    553   #define SQLITE_OPEN_SHAREDCACHE      0x00020000  /* Ok for sqlite3_open_v2() */
   554    554   #define SQLITE_OPEN_PRIVATECACHE     0x00040000  /* Ok for sqlite3_open_v2() */
   555    555   #define SQLITE_OPEN_WAL              0x00080000  /* VFS only */
   556    556   
   557    557   /* Reserved:                         0x00F00000 */
          558  +#define SQLITE_OPEN_REUSE_SCHEMA     0x01000000  /* Ok for sqlite3_open_v2() */
          559  +
   558    560   
   559    561   /*
   560    562   ** CAPI3REF: Device Characteristics
   561    563   **
   562    564   ** The xDeviceCharacteristics method of the [sqlite3_io_methods]
   563    565   ** object returns an integer which is a vector of these
   564    566   ** bit values expressing I/O characteristics of the mass storage

Changes to src/sqliteInt.h.

  1185   1185     Hash trigHash;       /* All triggers indexed by name */
  1186   1186     Hash fkeyHash;       /* All foreign keys by referenced table name */
  1187   1187     Table *pSeqTab;      /* The sqlite_sequence table used by AUTOINCREMENT */
  1188   1188     u8 file_format;      /* Schema format version for this file */
  1189   1189     u8 enc;              /* Text encoding used by this database */
  1190   1190     u16 schemaFlags;     /* Flags associated with this schema */
  1191   1191     int cache_size;      /* Number of pages to use in the cache */
         1192  +
         1193  +  int nRef;            /* Number of connections using this schema */
         1194  +  u64 cksum;           /* Checksum for this database schema */
         1195  +  Schema *pNext;       /* Next schema in shared schema list */
  1192   1196   };
  1193   1197   
  1194   1198   /*
  1195   1199   ** These macros can be used to test, set, or clear bits in the
  1196   1200   ** Db.pSchema->flags field.
  1197   1201   */
  1198   1202   #define DbHasProperty(D,I,P)     (((D)->aDb[I].pSchema->schemaFlags&(P))==(P))
................................................................................
  4081   4085   int sqlite3FindDbName(sqlite3 *, const char *);
  4082   4086   int sqlite3AnalysisLoad(sqlite3*,int iDB);
  4083   4087   void sqlite3DeleteIndexSamples(sqlite3*,Index*);
  4084   4088   void sqlite3DefaultRowEst(Index*);
  4085   4089   void sqlite3RegisterLikeFunctions(sqlite3*, int);
  4086   4090   int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
  4087   4091   void sqlite3SchemaClear(void *);
         4092  +void sqlite3SchemaUnuse(sqlite3*, int);
         4093  +void sqlite3SchemaReuse(sqlite3*, int);
         4094  +void sqlite3SchemaWritable(Parse*, int);
  4088   4095   Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
  4089   4096   int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
  4090   4097   KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
  4091   4098   void sqlite3KeyInfoUnref(KeyInfo*);
  4092   4099   KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
  4093   4100   KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
  4094   4101   #ifdef SQLITE_DEBUG

Changes to src/tclsqlite.c.

  3424   3424         int b;
  3425   3425         if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
  3426   3426         if( b ){
  3427   3427           flags |= SQLITE_OPEN_URI;
  3428   3428         }else{
  3429   3429           flags &= ~SQLITE_OPEN_URI;
  3430   3430         }
         3431  +    }else if( strcmp(zArg, "-reuse-schema")==0 ){
         3432  +      int b;
         3433  +      if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
         3434  +      if( b ){
         3435  +        flags |= SQLITE_OPEN_REUSE_SCHEMA;
         3436  +      }else{
         3437  +        flags &= ~SQLITE_OPEN_REUSE_SCHEMA;
         3438  +      }
  3431   3439       }else{
  3432   3440         Tcl_AppendResult(interp, "unknown option: ", zArg, (char*)0);
  3433   3441         return TCL_ERROR;
  3434   3442       }
  3435   3443     }
  3436   3444     if( objc<3 || (objc&1)!=1 ){
  3437   3445       Tcl_WrongNumArgs(interp, 1, objv,
  3438   3446         "HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?"
  3439   3447         " ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?"
         3448  +      " ?-reuse-schema BOOLEAN?"
  3440   3449   #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_CODEC_FROM_TCL)
  3441   3450         " ?-key CODECKEY?"
  3442   3451   #endif
  3443   3452       );
  3444   3453       return TCL_ERROR;
  3445   3454     }
  3446   3455     zErrMsg = 0;

Changes to src/trigger.c.

   309    309       testcase( z==0 );
   310    310       sqlite3NestedParse(pParse,
   311    311          "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')",
   312    312          db->aDb[iDb].zDbSName, MASTER_NAME, zName,
   313    313          pTrig->table, z);
   314    314       sqlite3DbFree(db, z);
   315    315       sqlite3ChangeCookie(pParse, iDb);
   316         -    sqlite3VdbeAddParseSchemaOp(v, iDb,
          316  +    sqlite3VdbeAddParseSchemaOp(pParse, iDb,
   317    317           sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName));
   318    318     }
   319    319   
   320    320     if( db->init.busy ){
   321    321       Trigger *pLink = pTrig;
   322    322       Hash *pHash = &db->aDb[iDb].pSchema->trigHash;
   323    323       assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
................................................................................
   534    534     Table   *pTable;
   535    535     Vdbe *v;
   536    536     sqlite3 *db = pParse->db;
   537    537     int iDb;
   538    538   
   539    539     iDb = sqlite3SchemaToIndex(pParse->db, pTrigger->pSchema);
   540    540     assert( iDb>=0 && iDb<db->nDb );
          541  +  sqlite3SchemaWritable(pParse, iDb);
   541    542     pTable = tableOfTrigger(pTrigger);
   542    543     assert( pTable );
   543    544     assert( pTable->pSchema==pTrigger->pSchema || iDb==1 );
   544    545   #ifndef SQLITE_OMIT_AUTHORIZATION
   545    546     {
   546    547       int code = SQLITE_DROP_TRIGGER;
   547    548       const char *zDb = db->aDb[iDb].zDbSName;

Changes to src/vacuum.c.

   113    113       ** The buggy behavior is required for binary compatibility with some
   114    114       ** legacy applications. */
   115    115       iDb = sqlite3FindDb(pParse->db, pNm);
   116    116       if( iDb<0 ) iDb = 0;
   117    117   #endif
   118    118     }
   119    119     if( iDb!=1 ){
          120  +    sqlite3SchemaWritable(pParse, iDb);
   120    121       sqlite3VdbeAddOp1(v, OP_Vacuum, iDb);
   121    122       sqlite3VdbeUsesBtree(v, iDb);
   122    123     }
   123    124     return;
   124    125   }
   125    126   
   126    127   /*

Changes to src/vdbe.h.

   193    193     void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N);
   194    194     void sqlite3VdbeVerifyNoResultRow(Vdbe *p);
   195    195   #else
   196    196   # define sqlite3VdbeVerifyNoMallocRequired(A,B)
   197    197   # define sqlite3VdbeVerifyNoResultRow(A)
   198    198   #endif
   199    199   VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
   200         -void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
          200  +void sqlite3VdbeAddParseSchemaOp(Parse*,int,char*);
   201    201   void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
   202    202   void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
   203    203   void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
   204    204   void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
   205    205   void sqlite3VdbeChangeP5(Vdbe*, u16 P5);
   206    206   void sqlite3VdbeJumpHere(Vdbe*, int addr);
   207    207   int sqlite3VdbeChangeToNoop(Vdbe*, int addr);

Changes to src/vdbeaux.c.

   305    305   ** Add an OP_ParseSchema opcode.  This routine is broken out from
   306    306   ** sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees
   307    307   ** as having been used.
   308    308   **
   309    309   ** The zWhere string must have been obtained from sqlite3_malloc().
   310    310   ** This routine will take ownership of the allocated memory.
   311    311   */
   312         -void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){
          312  +void sqlite3VdbeAddParseSchemaOp(Parse *pParse, int iDb, char *zWhere){
          313  +  Vdbe *p = pParse->pVdbe;
   313    314     int j;
          315  +  sqlite3SchemaWritable(pParse, iDb);
   314    316     sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
   315    317     for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
   316    318   }
   317    319   
   318    320   /*
   319    321   ** Add an opcode that includes the p4 value as an integer.
   320    322   */

Changes to src/vtab.c.

   430    430       );
   431    431       sqlite3DbFree(db, zStmt);
   432    432       v = sqlite3GetVdbe(pParse);
   433    433       sqlite3ChangeCookie(pParse, iDb);
   434    434   
   435    435       sqlite3VdbeAddOp0(v, OP_Expire);
   436    436       zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName);
   437         -    sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
          437  +    sqlite3VdbeAddParseSchemaOp(pParse, iDb, zWhere);
   438    438   
   439    439       iReg = ++pParse->nMem;
   440    440       sqlite3VdbeLoadString(v, iReg, pTab->zName);
   441    441       sqlite3VdbeAddOp2(v, OP_VCreate, iDb, iReg);
   442    442     }
   443    443   
   444    444     /* If we are rereading the sqlite_master table create the in-memory

Added test/reuse1.test.

            1  +# 2017 August 9
            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  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +set testprefix reuse1
           18  +
           19  +
           20  +forcedelete test.db2
           21  +sqlite3 db2 test.db2
           22  +
           23  +do_execsql_test 1.0 {
           24  +  CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE, z);
           25  +  CREATE INDEX i1 ON t1(z);
           26  +  PRAGMA schema_version;
           27  +} {2}
           28  +
           29  +do_execsql_test -db db2 1.1 {
           30  +  CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE, z);
           31  +  CREATE INDEX i1 ON t1(z);
           32  +  PRAGMA schema_version;
           33  +} {2}
           34  +
           35  +do_test 1.2 {
           36  +  db close
           37  +  db2 close
           38  +  sqlite3 db2 test.db2 -reuse-schema 1
           39  +  sqlite3 db  test.db -reuse-schema 1
           40  +} {}
           41  +
           42  +do_execsql_test -db db2 1.3.1 {
           43  +  INSERT INTO t1 VALUES(1, 2, 3);
           44  +  INSERT INTO t1 VALUES(4, 5, 6);
           45  +}
           46  +
           47  +do_execsql_test 1.3.2 {
           48  +  SELECT * FROM t1;
           49  +  PRAGMA integrity_check;
           50  +} {ok}
           51  +
           52  +do_execsql_test -db db2 1.3.3 {
           53  +  SELECT * FROM t1;
           54  +  PRAGMA integrity_check;
           55  +} {1 2 3 4 5 6 ok}
           56  +
           57  +sqlite3 db3 test.db2
           58  +do_execsql_test -db db3 1.4.1 {
           59  +  ALTER TABLE t1 ADD COLUMN a;
           60  +}
           61  +do_execsql_test -db db2 1.4.2 {
           62  +  SELECT * FROM t1;
           63  +} {1 2 3 {} 4 5 6 {}}
           64  +do_execsql_test 1.4.3 {
           65  +  SELECT * FROM t1;
           66  +} {}
           67  +
           68  +db3 close
           69  +sqlite3 db3 test.db
           70  +do_execsql_test -db db3 1.5.0 {
           71  +  CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
           72  +    SELECT 1, 2, 3;
           73  +  END;
           74  +}
           75  +
           76  +# Check that the schema cannot be modified if the db was opened with
           77  +# SQLITE_OPEN_REUSE_SCHEMA.
           78  +#
           79  +foreach {tn sql} {
           80  +  1  { CREATE TABLE t2(x, y) }
           81  +  2  { CREATE INDEX i2 ON t1(z) }
           82  +  3  { CREATE VIEW v2 AS SELECT * FROM t2 }
           83  +  4  { ALTER TABLE t1 RENAME TO t3 }
           84  +  5  { ALTER TABLE t1 ADD COLUMN xyz }
           85  +  6  { VACUUM }
           86  +  7  { DROP INDEX i1 }
           87  +  8  { DROP TABLE t1 }
           88  +  9  { DROP TRIGGER tr1 }
           89  +  10 { ANALYZE }
           90  +} {
           91  +  do_catchsql_test 1.5.$tn $sql {1 {attempt to modify read-only schema}}
           92  +}
           93  +
           94  +finish_test

Changes to test/tclsqlite.test.

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