/ Check-in [36c04dd1]
Login

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

Overview
Comment:Add a generation counter to the Schema object and enhance OP_VerifySchema to also check the Schema generation. Fix for ticket [f7b4edece25c99].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 36c04dd1695f0899b53ce58738181b146fc005ed
User & Date: drh 2011-03-18 21:55:46
Context
2011-03-19
02:04
Add a test case to verify that ticket [f7b4edece25c994857] is fixed. check-in: eedbcf0a user: drh tags: trunk
2011-03-18
21:55
Add a generation counter to the Schema object and enhance OP_VerifySchema to also check the Schema generation. Fix for ticket [f7b4edece25c99]. check-in: 36c04dd1 user: drh tags: trunk
2011-03-17
16:45
Update the implementation of ".testctrl" in the command-line shell to use a look-up table rather than a long sequence of if-elses. Shorten source code lines of shell.c to 80 characters or less. check-in: 54bacb95 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/build.c.

   152    152         int iDb;
   153    153         sqlite3VdbeJumpHere(v, pParse->cookieGoto-1);
   154    154         for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){
   155    155           if( (mask & pParse->cookieMask)==0 ) continue;
   156    156           sqlite3VdbeUsesBtree(v, iDb);
   157    157           sqlite3VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0);
   158    158           if( db->init.busy==0 ){
   159         -          sqlite3VdbeAddOp2(v,OP_VerifyCookie, iDb, pParse->cookieValue[iDb]);
          159  +          sqlite3VdbeAddOp3(v, OP_VerifyCookie,
          160  +                            iDb, pParse->cookieValue[iDb],
          161  +                            db->aDb[iDb].pSchema->iGeneration);
   160    162           }
   161    163         }
   162    164   #ifndef SQLITE_OMIT_VIRTUALTABLE
   163    165         {
   164    166           int i;
   165    167           for(i=0; i<pParse->nVtabLock; i++){
   166    168             char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]);

Changes to src/callback.c.

   423    423     for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
   424    424       Table *pTab = sqliteHashData(pElem);
   425    425       sqlite3DeleteTable(0, pTab);
   426    426     }
   427    427     sqlite3HashClear(&temp1);
   428    428     sqlite3HashClear(&pSchema->fkeyHash);
   429    429     pSchema->pSeqTab = 0;
   430         -  pSchema->flags &= ~DB_SchemaLoaded;
          430  +  if( pSchema->flags & DB_SchemaLoaded ){
          431  +    pSchema->iGeneration++;
          432  +    pSchema->flags &= ~DB_SchemaLoaded;
          433  +  }
   431    434   }
   432    435   
   433    436   /*
   434    437   ** Find and return the schema associated with a BTree.  Create
   435    438   ** a new one if necessary.
   436    439   */
   437    440   Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){

Changes to src/sqliteInt.h.

   667    667   };
   668    668   
   669    669   /*
   670    670   ** An instance of the following structure stores a database schema.
   671    671   */
   672    672   struct Schema {
   673    673     int schema_cookie;   /* Database schema version number for this file */
          674  +  int iGeneration;     /* Generation counter.  Incremented with each change */
   674    675     Hash tblHash;        /* All tables indexed by name */
   675    676     Hash idxHash;        /* All (named) indices indexed by name */
   676    677     Hash trigHash;       /* All triggers indexed by name */
   677    678     Hash fkeyHash;       /* All foreign keys by referenced table name */
   678    679     Table *pSeqTab;      /* The sqlite_sequence table used by AUTOINCREMENT */
   679    680     u8 file_format;      /* Schema format version for this file */
   680    681     u8 enc;              /* Text encoding used by this database */

Changes to src/vdbe.c.

  2886   2886       ** schema is changed.  Ticket #1644 */
  2887   2887       sqlite3ExpirePreparedStatements(db);
  2888   2888       p->expired = 0;
  2889   2889     }
  2890   2890     break;
  2891   2891   }
  2892   2892   
  2893         -/* Opcode: VerifyCookie P1 P2 *
         2893  +/* Opcode: VerifyCookie P1 P2 P3 * *
  2894   2894   **
  2895   2895   ** Check the value of global database parameter number 0 (the
  2896         -** schema version) and make sure it is equal to P2.  
         2896  +** schema version) and make sure it is equal to P2 and that the
         2897  +** generation counter on the local schema parse equals P3.
         2898  +**
  2897   2899   ** P1 is the database number which is 0 for the main database file
  2898   2900   ** and 1 for the file holding temporary tables and some higher number
  2899   2901   ** for auxiliary databases.
  2900   2902   **
  2901   2903   ** The cookie changes its value whenever the database schema changes.
  2902   2904   ** This operation is used to detect when that the cookie has changed
  2903   2905   ** and that the current process needs to reread the schema.
................................................................................
  2904   2906   **
  2905   2907   ** Either a transaction needs to have been started or an OP_Open needs
  2906   2908   ** to be executed (to establish a read lock) before this opcode is
  2907   2909   ** invoked.
  2908   2910   */
  2909   2911   case OP_VerifyCookie: {
  2910   2912     int iMeta;
         2913  +  int iGen;
  2911   2914     Btree *pBt;
         2915  +
  2912   2916     assert( pOp->p1>=0 && pOp->p1<db->nDb );
  2913   2917     assert( (p->btreeMask & (1<<pOp->p1))!=0 );
  2914   2918     pBt = db->aDb[pOp->p1].pBt;
  2915   2919     if( pBt ){
  2916   2920       sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta);
         2921  +    iGen = db->aDb[pOp->p1].pSchema->iGeneration;
  2917   2922     }else{
  2918   2923       iMeta = 0;
  2919   2924     }
  2920         -  if( iMeta!=pOp->p2 ){
         2925  +  if( iMeta!=pOp->p2 || iGen!=pOp->p3 ){
  2921   2926       sqlite3DbFree(db, p->zErrMsg);
  2922   2927       p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
  2923   2928       /* If the schema-cookie from the database file matches the cookie 
  2924   2929       ** stored with the in-memory representation of the schema, do
  2925   2930       ** not reload the schema from the database file.
  2926   2931       **
  2927   2932       ** If virtual-tables are in use, this is not just an optimization.

Changes to src/vdbeblob.c.

   262    262         /* Configure the OP_Transaction */
   263    263         sqlite3VdbeChangeP1(v, 0, iDb);
   264    264         sqlite3VdbeChangeP2(v, 0, flags);
   265    265   
   266    266         /* Configure the OP_VerifyCookie */
   267    267         sqlite3VdbeChangeP1(v, 1, iDb);
   268    268         sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie);
          269  +      sqlite3VdbeChangeP3(v, 1, pTab->pSchema->iGeneration);
   269    270   
   270    271         /* Make sure a mutex is held on the table to be accessed */
   271    272         sqlite3VdbeUsesBtree(v, iDb); 
   272    273   
   273    274         /* Configure the OP_TableLock instruction */
   274    275   #ifdef SQLITE_OMIT_SHARED_CACHE
   275    276         sqlite3VdbeChangeToNoop(v, 2, 1);

Changes to test/capi3.test.

   647    647   } {0}
   648    648   do_test capi3-6.1 {
   649    649     db cache flush
   650    650     sqlite3_close $DB
   651    651   } {SQLITE_BUSY}
   652    652   do_test capi3-6.2 {
   653    653     sqlite3_step $STMT
   654         -} {SQLITE_ROW}
   655         -check_data $STMT capi3-6.3 {INTEGER} {1} {1.0} {1}
          654  +} {SQLITE_ERROR}
          655  +#check_data $STMT capi3-6.3 {INTEGER} {1} {1.0} {1}
   656    656   do_test capi3-6.3 {
   657    657     sqlite3_finalize $STMT
   658         -} {SQLITE_OK}
          658  +} {SQLITE_SCHEMA}
   659    659   do_test capi3-6.4-misuse {
   660    660     db cache flush
   661    661     sqlite3_close $DB
   662    662   } {SQLITE_OK}
   663    663   db close
   664    664   
   665    665   # This procedure sets the value of the file-format in file 'test.db'