/ Check-in [ed7c17ea]
Login

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

Overview
Comment:Fix a spurious SQLITE_CONSTRAINT error that may be returned by an incr-merge operation.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | fts4-incr-merge
Files: files | file ages | folders
SHA1: ed7c17ea165f6348506bd23ebc58c427bb65d697
User & Date: dan 2012-03-23 18:26:11
Context
2012-03-24
14:45
Merge auto-incr-merge with incr-merge branch. check-in: 1c68687a user: dan tags: fts4-incr-merge
02:20
An attempt at automatic incremental merging for FTS4. check-in: ed69434c user: drh tags: fts4-auto-incr-merge
2012-03-23
18:26
Fix a spurious SQLITE_CONSTRAINT error that may be returned by an incr-merge operation. check-in: ed7c17ea user: dan tags: fts4-incr-merge
15:38
Add a test to verify that sqlite3_total_changes() works with incr-merge operations. check-in: 1c72cecc user: dan tags: fts4-incr-merge
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts3/fts3.c.

  3408   3408   
  3409   3409   /*
  3410   3410   ** The xSavepoint() method.
  3411   3411   **
  3412   3412   ** Flush the contents of the pending-terms table to disk.
  3413   3413   */
  3414   3414   static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
         3415  +  int rc = SQLITE_OK;
  3415   3416     UNUSED_PARAMETER(iSavepoint);
  3416   3417     assert( ((Fts3Table *)pVtab)->inTransaction );
  3417   3418     assert( ((Fts3Table *)pVtab)->mxSavepoint < iSavepoint );
  3418   3419     TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
  3419         -  return fts3SyncMethod(pVtab);
         3420  +  if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){
         3421  +    rc = fts3SyncMethod(pVtab);
         3422  +  }
         3423  +  return rc;
  3420   3424   }
  3421   3425   
  3422   3426   /*
  3423   3427   ** The xRelease() method.
  3424   3428   **
  3425   3429   ** This is a no-op.
  3426   3430   */

Changes to ext/fts3/fts3Int.h.

   196    196     sqlite3_tokenizer *pTokenizer;  /* tokenizer for inserts and queries */
   197    197     char *zContentTbl;              /* content=xxx option, or NULL */
   198    198     char *zLanguageid;              /* languageid=xxx option, or NULL */
   199    199   
   200    200     /* Precompiled statements used by the implementation. Each of these 
   201    201     ** statements is run and reset within a single virtual table API call. 
   202    202     */
   203         -  sqlite3_stmt *aStmt[35];
          203  +  sqlite3_stmt *aStmt[36];
   204    204   
   205    205     char *zReadExprlist;
   206    206     char *zWriteExprlist;
   207    207   
   208    208     int nNodeSize;                  /* Soft limit for node size */
   209    209     u8 bHasStat;                    /* True if %_stat table exists */
   210    210     u8 bHasDocsize;                 /* True if %_docsize table exists */
   211    211     u8 bDescIdx;                    /* True if doclists are in reverse order */
          212  +  u8 bIgnoreSavepoint;            /* True to ignore xSavepoint invocations */
   212    213     int nPgsz;                      /* Page size for host database */
   213    214     char *zSegmentsTbl;             /* Name of %_segments table */
   214    215     sqlite3_blob *pSegments;        /* Blob handle open on %_segments table */
   215    216   
   216    217     /* 
   217    218     ** The following array of hash tables is used to buffer pending index 
   218    219     ** updates during transactions. All pending updates buffered at any one

Changes to ext/fts3/fts3_write.c.

   255    255   #define SQL_SELECT_ALL_PREFIX_LEVEL   24
   256    256   #define SQL_DELETE_ALL_TERMS_SEGDIR   25
   257    257   #define SQL_DELETE_SEGDIR_RANGE       26
   258    258   #define SQL_SELECT_ALL_LANGID         27
   259    259   #define SQL_FIND_MERGE_LEVEL          28
   260    260   #define SQL_MAX_LEAF_NODE_ESTIMATE    29
   261    261   #define SQL_DELETE_SEGDIR_ENTRY       30
   262         -#define SQL_SHIFT_SEGDIR_ENTRIES      31
          262  +#define SQL_SHIFT_SEGDIR_ENTRY        31
   263    263   #define SQL_SELECT_SEGDIR             32
   264    264   #define SQL_CHOMP_SEGDIR              33
   265    265   #define SQL_SEGMENT_IS_APPENDABLE     34
          266  +#define SQL_SELECT_INDEXES            35
   266    267   
   267    268   /*
   268    269   ** This function is used to obtain an SQLite prepared statement handle
   269    270   ** for the statement identified by the second argument. If successful,
   270    271   ** *pp is set to the requested statement handle and SQLITE_OK returned.
   271    272   ** Otherwise, an SQLite error code is returned and *pp is set to 0.
   272    273   **
................................................................................
   333    334   /* 29 */ "SELECT 2 * total(1 + leaves_end_block - start_block) "
   334    335            "  FROM %Q.'%q_segdir' WHERE level = ? AND idx < ?",
   335    336   
   336    337   /* SQL_DELETE_SEGDIR_ENTRY
   337    338   **   Delete the %_segdir entry on absolute level :1 with index :2.  */
   338    339   /* 30 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ? AND idx = ?",
   339    340   
   340         -/* SQL_SHIFT_SEGDIR_ENTRIES
   341         -**   Reduce by one the idx values of all segments on absolute level :1 with
   342         -**   an index greater than :2.  */
   343         -/* 31 */ "UPDATE %Q.'%q_segdir' SET idx = idx - 1 WHERE level = ? AND idx>:2",
          341  +/* SQL_SHIFT_SEGDIR_ENTRY
          342  +**   Modify the idx value for the segment with idx=:3 on absolute level :2
          343  +**   to :1.  */
          344  +/* 31 */ "UPDATE %Q.'%q_segdir' SET idx = ? WHERE level=? AND idx=?",
   344    345   
   345    346   /* SQL_SELECT_SEGDIR
   346    347   **   Read a single entry from the %_segdir table. The entry from absolute 
   347    348   **   level :1 with index value :2.  */
   348    349   /* 32 */  "SELECT idx, start_block, leaves_end_block, end_block, root "
   349    350               "FROM %Q.'%q_segdir' WHERE level = ? AND idx = ?",
   350    351   
................................................................................
   353    354   **   entry located on absolute level :3 with index :4.  */
   354    355   /* 33 */  "UPDATE %Q.'%q_segdir' SET start_block = ?, root = ?"
   355    356               "WHERE level = ? AND idx = ?",
   356    357   
   357    358   /* SQL_SEGMENT_IS_APPENDABLE
   358    359   **   Return a single row if the segment with end_block=? is appendable. Or
   359    360   **   no rows otherwise.  */
   360         -/* 34 */  "SELECT 1 FROM %Q.'%q_segments' WHERE blockid=? AND block IS NULL"
          361  +/* 34 */  "SELECT 1 FROM %Q.'%q_segments' WHERE blockid=? AND block IS NULL",
          362  +
          363  +/* SQL_SELECT_INDEXES
          364  +**   Return the list of valid segment indexes for absolute level ?  */
          365  +/* 35 */  "SELECT idx FROM %Q.'%q_segdir' WHERE level=? ORDER BY 1 ASC"
   361    366     };
   362    367     int rc = SQLITE_OK;
   363    368     sqlite3_stmt *pStmt;
   364    369   
   365    370     assert( SizeofArray(azSql)==SizeofArray(p->aStmt) );
   366    371     assert( eStmt<SizeofArray(azSql) && eStmt>=0 );
   367    372     
................................................................................
  4109   4114   static int fts3RemoveSegdirEntry(
  4110   4115     Fts3Table *p,                   /* FTS3 table handle */
  4111   4116     sqlite3_int64 iAbsLevel,        /* Absolute level to delete from */
  4112   4117     int iIdx                        /* Index of %_segdir entry to delete */
  4113   4118   ){
  4114   4119     int rc;                         /* Return code */
  4115   4120     sqlite3_stmt *pDelete = 0;      /* DELETE statement */
  4116         -  sqlite3_stmt *pUpdate = 0;      /* UPDATE statement */
  4117   4121   
  4118   4122     rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_ENTRY, &pDelete, 0);
  4119   4123     if( rc==SQLITE_OK ){
  4120   4124       sqlite3_bind_int64(pDelete, 1, iAbsLevel);
  4121   4125       sqlite3_bind_int(pDelete, 2, iIdx);
  4122   4126       sqlite3_step(pDelete);
  4123   4127       rc = sqlite3_reset(pDelete);
  4124   4128     }
         4129  +
         4130  +  return rc;
         4131  +}
         4132  +
         4133  +/*
         4134  +** One or more segments have just been removed from absolute level iAbsLevel.
         4135  +** Update the 'idx' values of the remaining segments in the level so that
         4136  +** the idx values are a contiguous sequence starting from 0.
         4137  +*/
         4138  +static int fts3RepackSegdirLevel(
         4139  +  Fts3Table *p,                   /* FTS3 table handle */
         4140  +  sqlite3_int64 iAbsLevel         /* Absolute level to repack */
         4141  +){
         4142  +  int rc;                         /* Return code */
         4143  +  int *aIdx = 0;                  /* Array of remaining idx values */
         4144  +  int nIdx = 0;                   /* Valid entries in aIdx[] */
         4145  +  int nAlloc = 0;                 /* Allocated size of aIdx[] */
         4146  +  int i;                          /* Iterator variable */
         4147  +  sqlite3_stmt *pSelect = 0;      /* Select statement to read idx values */
         4148  +  sqlite3_stmt *pUpdate = 0;      /* Update statement to modify idx values */
         4149  +
         4150  +  rc = fts3SqlStmt(p, SQL_SELECT_INDEXES, &pSelect, 0);
         4151  +  if( rc==SQLITE_OK ){
         4152  +    int rc2;
         4153  +    sqlite3_bind_int64(pSelect, 1, iAbsLevel);
         4154  +    while( SQLITE_ROW==sqlite3_step(pSelect) ){
         4155  +      if( nIdx>=nAlloc ){
         4156  +        int *aNew;
         4157  +        nAlloc += 16;
         4158  +        aNew = sqlite3_realloc(aIdx, nAlloc*sizeof(int));
         4159  +        if( !aNew ){
         4160  +          rc = SQLITE_NOMEM;
         4161  +          break;
         4162  +        }
         4163  +        aIdx = aNew;
         4164  +      }
         4165  +      aIdx[nIdx++] = sqlite3_column_int(pSelect, 0);
         4166  +    }
         4167  +    rc2 = sqlite3_reset(pSelect);
         4168  +    if( rc==SQLITE_OK ) rc = rc2;
         4169  +  }
  4125   4170   
  4126   4171     if( rc==SQLITE_OK ){
  4127         -    rc = fts3SqlStmt(p, SQL_SHIFT_SEGDIR_ENTRIES, &pUpdate, 0);
         4172  +    rc = fts3SqlStmt(p, SQL_SHIFT_SEGDIR_ENTRY, &pUpdate, 0);
  4128   4173     }
  4129   4174     if( rc==SQLITE_OK ){
  4130         -    sqlite3_bind_int64(pUpdate, 1, iAbsLevel);
  4131         -    sqlite3_bind_int(pUpdate, 2, iIdx);
  4132         -    sqlite3_step(pUpdate);
  4133         -    rc = sqlite3_reset(pUpdate);
         4175  +    sqlite3_bind_int64(pUpdate, 2, iAbsLevel);
  4134   4176     }
  4135   4177   
         4178  +  assert( p->bIgnoreSavepoint==0 );
         4179  +  p->bIgnoreSavepoint = 1;
         4180  +  for(i=0; rc==SQLITE_OK && i<nIdx; i++){
         4181  +    if( aIdx[i]!=i ){
         4182  +      sqlite3_bind_int(pUpdate, 3, aIdx[i]);
         4183  +      sqlite3_bind_int(pUpdate, 1, i);
         4184  +      sqlite3_step(pUpdate);
         4185  +      rc = sqlite3_reset(pUpdate);
         4186  +    }
         4187  +  }
         4188  +  p->bIgnoreSavepoint = 0;
         4189  +
         4190  +  sqlite3_free(aIdx);
  4136   4191     return rc;
  4137   4192   }
  4138   4193   
  4139   4194   static void fts3StartNode(Blob *pNode, int iHeight, sqlite3_int64 iChild){
  4140   4195     pNode->a[0] = (char)iHeight;
  4141   4196     if( iChild ){
  4142   4197       assert( pNode->nAlloc>=1+sqlite3Fts3VarintLen(iChild) );
................................................................................
  4330   4385         ** so that it contains no keys smaller than zTerm/nTerm. */ 
  4331   4386         const char *zTerm = pSeg->zTerm;
  4332   4387         int nTerm = pSeg->nTerm;
  4333   4388         rc = fts3TruncateSegment(p, iAbsLevel, pSeg->iIdx, zTerm, nTerm);
  4334   4389         nRem++;
  4335   4390       }
  4336   4391     }
         4392  +
         4393  +  if( rc==SQLITE_OK && nRem!=pCsr->nSegment ){
         4394  +    rc = fts3RepackSegdirLevel(p, iAbsLevel);
         4395  +  }
  4337   4396   
  4338   4397     *pnRem = nRem;
  4339   4398     return rc;
  4340   4399   }
  4341   4400   
  4342   4401   /*
  4343   4402   ** Store an incr-merge hint in the database.