/ Check-in [651ef242]
Login

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

Overview
Comment:Do not invoke the xRollbackTo or xRelease methods of a virtual table without having first invoked an appropriate xSavepoint method. Add assert() statements to FTS3/4 to verify that this is happening in all cases.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 651ef24249d8c22c4f13e4c0bb98a60099cfd23a
User & Date: drh 2011-05-24 15:36:01
Context
2011-05-25
01:16
Changes to savepoint in virtual tables for simpler and more consistent operation. check-in: 92f26a8b user: drh tags: trunk
2011-05-24
18:49
If the fts4 option prefix=1 is specified, have the fts4 module maintain an index of prefixes as well as terms. check-in: b5bdc639 user: dan tags: fts3-prefix-search
15:36
Do not invoke the xRollbackTo or xRelease methods of a virtual table without having first invoked an appropriate xSavepoint method. Add assert() statements to FTS3/4 to verify that this is happening in all cases. check-in: 651ef242 user: drh tags: trunk
00:35
Make sure the savepoint index is correct one calls to sqlite3VtabSavepoint with SAVEPOINT_BEGIN. check-in: a9d09566 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts3/fts3.c.

   973    973     p->nPendingData = 0;
   974    974     p->azColumn = (char **)&p[1];
   975    975     p->pTokenizer = pTokenizer;
   976    976     p->nNodeSize = 1000;
   977    977     p->nMaxPendingData = FTS3_MAX_PENDING_DATA;
   978    978     p->bHasDocsize = (isFts4 && bNoDocsize==0);
   979    979     p->bHasStat = isFts4;
          980  +  TESTONLY( p->inTransaction = -1 );
          981  +  TESTONLY( p->mxSavepoint = -1 );
   980    982     fts3HashInit(&p->pendingTerms, FTS3_HASH_STRING, 1);
   981    983   
   982    984     /* Fill in the zName and zDb fields of the vtab structure. */
   983    985     zCsr = (char *)&p->azColumn[nCol];
   984    986     p->zName = zCsr;
   985    987     memcpy(zCsr, argv[2], nName);
   986    988     zCsr += nName;
................................................................................
  3270   3272   }
  3271   3273   
  3272   3274   /*
  3273   3275   ** Implementation of xBegin() method. This is a no-op.
  3274   3276   */
  3275   3277   static int fts3BeginMethod(sqlite3_vtab *pVtab){
  3276   3278     UNUSED_PARAMETER(pVtab);
  3277         -  assert( ((Fts3Table *)pVtab)->nPendingData==0 );
         3279  +  TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
         3280  +  assert( p->nPendingData==0 );
         3281  +  assert( p->inTransaction!=1 );
         3282  +  TESTONLY( p->inTransaction = 1 );
         3283  +  TESTONLY( p->mxSavepoint = -1; );
  3278   3284     return SQLITE_OK;
  3279   3285   }
  3280   3286   
  3281   3287   /*
  3282   3288   ** Implementation of xCommit() method. This is a no-op. The contents of
  3283   3289   ** the pending-terms hash-table have already been flushed into the database
  3284   3290   ** by fts3SyncMethod().
  3285   3291   */
  3286   3292   static int fts3CommitMethod(sqlite3_vtab *pVtab){
  3287   3293     UNUSED_PARAMETER(pVtab);
  3288         -  assert( ((Fts3Table *)pVtab)->nPendingData==0 );
         3294  +  TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
         3295  +  assert( p->nPendingData==0 );
         3296  +  assert( p->inTransaction!=0 );
         3297  +  TESTONLY( p->inTransaction = 0 );
         3298  +  TESTONLY( p->mxSavepoint = -1; );
  3289   3299     return SQLITE_OK;
  3290   3300   }
  3291   3301   
  3292   3302   /*
  3293   3303   ** Implementation of xRollback(). Discard the contents of the pending-terms
  3294   3304   ** hash-table. Any changes made to the database are reverted by SQLite.
  3295   3305   */
  3296   3306   static int fts3RollbackMethod(sqlite3_vtab *pVtab){
  3297         -  sqlite3Fts3PendingTermsClear((Fts3Table *)pVtab);
         3307  +  Fts3Table *p = (Fts3Table*)pVtab;
         3308  +  sqlite3Fts3PendingTermsClear(p);
         3309  +  assert( p->inTransaction!=0 );
         3310  +  TESTONLY( p->inTransaction = 0 );
         3311  +  TESTONLY( p->mxSavepoint = -1; );
  3298   3312     return SQLITE_OK;
  3299   3313   }
  3300   3314   
  3301   3315   /*
  3302   3316   ** Load the doclist associated with expression pExpr to pExpr->aDoclist.
  3303   3317   ** The loaded doclist contains positions as well as the document ids.
  3304   3318   ** This is used by the matchinfo(), snippet() and offsets() auxillary
................................................................................
  3646   3660       "ALTER TABLE %Q.'%q_segdir'   RENAME TO '%q_segdir';",
  3647   3661       p->zDb, p->zName, zName
  3648   3662     );
  3649   3663     return rc;
  3650   3664   }
  3651   3665   
  3652   3666   static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
  3653         -  return sqlite3Fts3PendingTermsFlush((Fts3Table *)pVtab);
         3667  +  Fts3Table *p = (Fts3Table*)pVtab;
         3668  +  assert( p->inTransaction );
         3669  +  assert( p->mxSavepoint < iSavepoint );
         3670  +  TESTONLY( p->mxSavepoint = iSavepoint );
         3671  +  return sqlite3Fts3PendingTermsFlush(p);
  3654   3672   }
  3655   3673   static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
         3674  +  TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
         3675  +  assert( p->inTransaction );
         3676  +  assert( p->mxSavepoint >= iSavepoint );
         3677  +  TESTONLY( p->mxSavepoint = iSavepoint>0 ? iSavepoint-1 : 0 );
  3656   3678     return SQLITE_OK;
  3657   3679   }
  3658   3680   static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
  3659         -  sqlite3Fts3PendingTermsClear((Fts3Table *)pVtab);
         3681  +  Fts3Table *p = (Fts3Table*)pVtab;
         3682  +  assert( p->inTransaction );
         3683  +  assert( p->mxSavepoint >= iSavepoint );
         3684  +  TESTONLY( p->mxSavepoint = iSavepoint );
         3685  +  sqlite3Fts3PendingTermsClear(p);
  3660   3686     return SQLITE_OK;
  3661   3687   }
  3662   3688   
  3663   3689   static const sqlite3_module fts3Module = {
  3664   3690     /* iVersion      */ 2,
  3665   3691     /* xCreate       */ fts3CreateMethod,
  3666   3692     /* xConnect      */ fts3ConnectMethod,

Changes to ext/fts3/fts3Int.h.

    88     88   /*
    89     89   ** Internal types used by SQLite.
    90     90   */
    91     91   typedef unsigned char u8;         /* 1-byte (or larger) unsigned integer */
    92     92   typedef short int i16;            /* 2-byte (or larger) signed integer */
    93     93   typedef unsigned int u32;         /* 4-byte unsigned integer */
    94     94   typedef sqlite3_uint64 u64;       /* 8-byte unsigned integer */
           95  +
    95     96   /*
    96     97   ** Macro used to suppress compiler warnings for unused parameters.
    97     98   */
    98     99   #define UNUSED_PARAMETER(x) (void)(x)
          100  +
          101  +/*
          102  +** Activate assert() only if SQLITE_TEST is enabled.
          103  +*/
          104  +#if !defined(NDEBUG) && !defined(SQLITE_DEBUG) 
          105  +# define NDEBUG 1
          106  +#endif
          107  +
          108  +/*
          109  +** The TESTONLY macro is used to enclose variable declarations or
          110  +** other bits of code that are needed to support the arguments
          111  +** within testcase() and assert() macros.
          112  +*/
          113  +#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
          114  +# define TESTONLY(X)  X
          115  +#else
          116  +# define TESTONLY(X)
    99    117   #endif
          118  +
          119  +#endif /* SQLITE_AMALGAMATION */
   100    120   
   101    121   typedef struct Fts3Table Fts3Table;
   102    122   typedef struct Fts3Cursor Fts3Cursor;
   103    123   typedef struct Fts3Expr Fts3Expr;
   104    124   typedef struct Fts3Phrase Fts3Phrase;
   105    125   typedef struct Fts3PhraseToken Fts3PhraseToken;
   106    126   
................................................................................
   147    167     ** automatically. Variable iPrevDocid is the docid of the most recently
   148    168     ** inserted record.
   149    169     */
   150    170     int nMaxPendingData;
   151    171     int nPendingData;
   152    172     sqlite_int64 iPrevDocid;
   153    173     Fts3Hash pendingTerms;
          174  +
          175  +#if defined(SQLITE_DEBUG)
          176  +  /* State variables used for validating that the transaction control
          177  +  ** methods of the virtual table are called at appropriate times.  These
          178  +  ** values do not contribution to the FTS computation; they are used for
          179  +  ** verifying the SQLite core.
          180  +  */
          181  +  int inTransaction;     /* True after xBegin but before xCommit/xRollback */
          182  +  int mxSavepoint;       /* Largest valid xSavepoint integer */
          183  +#endif
   154    184   };
   155    185   
   156    186   /*
   157    187   ** When the core wants to read from the virtual table, it creates a
   158    188   ** virtual table cursor (an instance of the following structure) using
   159    189   ** the xOpen method. Cursors are destroyed using the xClose method.
   160    190   */

Changes to src/sqliteInt.h.

  1231   1231   */
  1232   1232   struct VTable {
  1233   1233     sqlite3 *db;              /* Database connection associated with this table */
  1234   1234     Module *pMod;             /* Pointer to module implementation */
  1235   1235     sqlite3_vtab *pVtab;      /* Pointer to vtab instance */
  1236   1236     int nRef;                 /* Number of pointers to this structure */
  1237   1237     u8 bConstraint;           /* True if constraints are supported */
         1238  +  u8 bInSavepoint;          /* True if within a SAVEPOINT */
  1238   1239     VTable *pNext;            /* Next in linked list (see above) */
  1239   1240   };
  1240   1241   
  1241   1242   /*
  1242   1243   ** Each SQL table is represented in memory by an instance of the
  1243   1244   ** following structure.
  1244   1245   **

Changes to src/vtab.c.

   869    869   int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
   870    870     int rc = SQLITE_OK;
   871    871   
   872    872     assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN );
   873    873     if( db->aVTrans ){
   874    874       int i;
   875    875       for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
   876         -      const sqlite3_module *pMod = db->aVTrans[i]->pMod->pModule;
   877         -      if( pMod->iVersion>=2 ){
          876  +      VTable *pVTab = db->aVTrans[i];
          877  +      const sqlite3_module *pMod = pVTab->pMod->pModule;
          878  +      if( pMod->iVersion>=2 && (pVTab->bInSavepoint || op==SAVEPOINT_BEGIN) ){
   878    879           int (*xMethod)(sqlite3_vtab *, int);
   879    880           switch( op ){
   880    881             case SAVEPOINT_BEGIN:
   881    882               xMethod = pMod->xSavepoint;
          883  +            pVTab->bInSavepoint = 1;
   882    884               break;
   883    885             case SAVEPOINT_ROLLBACK:
   884    886               xMethod = pMod->xRollbackTo;
   885    887               break;
   886    888             default:
   887    889               xMethod = pMod->xRelease;
   888    890               break;