]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Following an incr-merge operation that does not completely consume its input segments...
authordan <dan@noemail.net>
Thu, 22 Mar 2012 16:48:12 +0000 (16:48 +0000)
committerdan <dan@noemail.net>
Thu, 22 Mar 2012 16:48:12 +0000 (16:48 +0000)
FossilOrigin-Name: ab0a4f44fb67e9f0cb82297b80e728ca58cdb0fb

ext/fts3/fts3_write.c
manifest
manifest.uuid
test/fts4merge.test

index 1218814b4a233b9339ca6c19fd2736714883983e..5b8fcc2ab25a02e60a97c62b57b216eeaafc6a7c 100644 (file)
@@ -66,6 +66,12 @@ int test_fts3_node_chunk_threshold = (4*1024)*4;
 # define FTS3_NODE_CHUNK_THRESHOLD (FTS3_NODE_CHUNKSIZE*4)
 #endif
 
+/*
+** The two values that may be meaningfully bound to the :1 parameter in
+** statements SQL_REPLACE_STAT and SQL_SELECT_STAT.
+*/
+#define FTS_STAT_DOCTOTAL      0
+#define FTS_STAT_INCRMERGEHINT 1
 
 /*
 ** If FTS_LOG_MERGES is defined, call sqlite3_log() to report each automatic
@@ -243,8 +249,8 @@ struct SegmentNode {
 #define SQL_DELETE_DOCSIZE            19
 #define SQL_REPLACE_DOCSIZE           20
 #define SQL_SELECT_DOCSIZE            21
-#define SQL_SELECT_DOCTOTAL           22
-#define SQL_REPLACE_DOCTOTAL          23
+#define SQL_SELECT_STAT               22
+#define SQL_REPLACE_STAT              23
 
 #define SQL_SELECT_ALL_PREFIX_LEVEL   24
 #define SQL_DELETE_ALL_TERMS_SEGDIR   25
@@ -305,8 +311,8 @@ static int fts3SqlStmt(
 /* 19 */  "DELETE FROM %Q.'%q_docsize' WHERE docid = ?",
 /* 20 */  "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)",
 /* 21 */  "SELECT size FROM %Q.'%q_docsize' WHERE docid=?",
-/* 22 */  "SELECT value FROM %Q.'%q_stat' WHERE id=0",
-/* 23 */  "REPLACE INTO %Q.'%q_stat' VALUES(0,?)",
+/* 22 */  "SELECT value FROM %Q.'%q_stat' WHERE id=?",
+/* 23 */  "REPLACE INTO %Q.'%q_stat' VALUES(?,?)",
 /* 24 */  "",
 /* 25 */  "",
 
@@ -318,7 +324,7 @@ static int fts3SqlStmt(
 ** of the oldest level in the db that contains at least ? segments. Or,
 ** if no level in the FTS index contains more than ? segments, the statement
 ** returns zero rows.  */
-/* 28 */ "SELECT level FROM %Q.'%q_segdir' GROUP BY level HAVING count(*)>?"
+/* 28 */ "SELECT level FROM %Q.'%q_segdir' GROUP BY level HAVING count(*)>=?"
          "  ORDER BY (level %% 1024) DESC LIMIT 1",
 
 /* Estimate the upper limit on the number of leaf nodes in a new segment
@@ -392,20 +398,15 @@ static int fts3SqlStmt(
 
 static int fts3SelectDocsize(
   Fts3Table *pTab,                /* FTS3 table handle */
-  int eStmt,                      /* Either SQL_SELECT_DOCSIZE or DOCTOTAL */
   sqlite3_int64 iDocid,           /* Docid to bind for SQL_SELECT_DOCSIZE */
   sqlite3_stmt **ppStmt           /* OUT: Statement handle */
 ){
   sqlite3_stmt *pStmt = 0;        /* Statement requested from fts3SqlStmt() */
   int rc;                         /* Return code */
 
-  assert( eStmt==SQL_SELECT_DOCSIZE || eStmt==SQL_SELECT_DOCTOTAL );
-
-  rc = fts3SqlStmt(pTab, eStmt, &pStmt, 0);
+  rc = fts3SqlStmt(pTab, SQL_SELECT_DOCSIZE, &pStmt, 0);
   if( rc==SQLITE_OK ){
-    if( eStmt==SQL_SELECT_DOCSIZE ){
-      sqlite3_bind_int64(pStmt, 1, iDocid);
-    }
+    sqlite3_bind_int64(pStmt, 1, iDocid);
     rc = sqlite3_step(pStmt);
     if( rc!=SQLITE_ROW || sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){
       rc = sqlite3_reset(pStmt);
@@ -424,7 +425,21 @@ int sqlite3Fts3SelectDoctotal(
   Fts3Table *pTab,                /* Fts3 table handle */
   sqlite3_stmt **ppStmt           /* OUT: Statement handle */
 ){
-  return fts3SelectDocsize(pTab, SQL_SELECT_DOCTOTAL, 0, ppStmt);
+  sqlite3_stmt *pStmt = 0;
+  int rc;
+  rc = fts3SqlStmt(pTab, SQL_SELECT_STAT, &pStmt, 0);
+  if( rc==SQLITE_OK ){
+    sqlite3_bind_int(pStmt, 1, FTS_STAT_DOCTOTAL);
+    if( sqlite3_step(pStmt)!=SQLITE_ROW
+     || sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB
+    ){
+      rc = sqlite3_reset(pStmt);
+      if( rc==SQLITE_OK ) rc = FTS_CORRUPT_VTAB;
+      pStmt = 0;
+    }
+  }
+  *ppStmt = pStmt;
+  return rc;
 }
 
 int sqlite3Fts3SelectDocsize(
@@ -432,7 +447,7 @@ int sqlite3Fts3SelectDocsize(
   sqlite3_int64 iDocid,           /* Docid to read size data for */
   sqlite3_stmt **ppStmt           /* OUT: Statement handle */
 ){
-  return fts3SelectDocsize(pTab, SQL_SELECT_DOCSIZE, iDocid, ppStmt);
+  return fts3SelectDocsize(pTab, iDocid, ppStmt);
 }
 
 /*
@@ -3094,12 +3109,13 @@ static void fts3UpdateDocTotals(
     return;
   }
   pBlob = (char*)&a[nStat];
-  rc = fts3SqlStmt(p, SQL_SELECT_DOCTOTAL, &pStmt, 0);
+  rc = fts3SqlStmt(p, SQL_SELECT_STAT, &pStmt, 0);
   if( rc ){
     sqlite3_free(a);
     *pRC = rc;
     return;
   }
+  sqlite3_bind_int(pStmt, 1, FTS_STAT_DOCTOTAL);
   if( sqlite3_step(pStmt)==SQLITE_ROW ){
     fts3DecodeIntArray(nStat, a,
          sqlite3_column_blob(pStmt, 0),
@@ -3123,13 +3139,14 @@ static void fts3UpdateDocTotals(
     a[i+1] = x;
   }
   fts3EncodeIntArray(nStat, a, pBlob, &nBlob);
-  rc = fts3SqlStmt(p, SQL_REPLACE_DOCTOTAL, &pStmt, 0);
+  rc = fts3SqlStmt(p, SQL_REPLACE_STAT, &pStmt, 0);
   if( rc ){
     sqlite3_free(a);
     *pRC = rc;
     return;
   }
-  sqlite3_bind_blob(pStmt, 1, pBlob, nBlob, SQLITE_STATIC);
+  sqlite3_bind_int(pStmt, 1, FTS_STAT_DOCTOTAL);
+  sqlite3_bind_blob(pStmt, 2, pBlob, nBlob, SQLITE_STATIC);
   sqlite3_step(pStmt);
   *pRC = sqlite3_reset(pStmt);
   sqlite3_free(a);
@@ -3277,13 +3294,13 @@ static int fts3IncrmergeCsr(
     rc = SQLITE_NOMEM;
   }else{
     memset(pCsr->apSegment, 0, nByte);
-    pCsr->nSegment = nSeg;
     rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0);
   }
   if( rc==SQLITE_OK ){
     int i;
     int rc2;
     sqlite3_bind_int64(pStmt, 1, iAbsLevel);
+    assert( pCsr->nSegment==0 );
     for(i=0; rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW && i<nSeg; i++){
       rc = sqlite3Fts3SegReaderNew(i, 0,
           sqlite3_column_int64(pStmt, 1),        /* segdir.start_block */
@@ -3293,6 +3310,7 @@ static int fts3IncrmergeCsr(
           sqlite3_column_bytes(pStmt, 4),        /* segdir.root */
           &pCsr->apSegment[i]
       );
+      pCsr->nSegment++;
     }
     rc2 = sqlite3_reset(pStmt);
     if( rc==SQLITE_OK ) rc = rc2;
@@ -3970,11 +3988,37 @@ static int fts3IncrmergeLoad(
   return rc;
 }
 
+/*
+** Determine the largest segment index value that exists within absolute
+** level iAbsLevel+1. If no error occurs, set *piIdx to this value plus
+** one before returning SQLITE_OK. Or, if there are no segments at all 
+** within level iAbsLevel, set *piIdx to zero.
+**
+** If an error occurs, return an SQLite error code. The final value of
+** *piIdx is undefined in this case.
+*/
+static int fts3IncrmergeOutputIdx( 
+  Fts3Table *p,                   /* FTS Table handle */
+  sqlite3_int64 iAbsLevel,        /* Absolute index of input segments */
+  int *piIdx                      /* OUT: Next free index at iAbsLevel+1 */
+){
+  int rc;
+  sqlite3_stmt *pOutputIdx = 0;   /* SQL used to find output index */
+
+  rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pOutputIdx, 0);
+  if( rc==SQLITE_OK ){
+    sqlite3_bind_int64(pOutputIdx, 1, iAbsLevel+1);
+    sqlite3_step(pOutputIdx);
+    *piIdx = sqlite3_column_int(pOutputIdx, 0);
+    rc = sqlite3_reset(pOutputIdx);
+  }
+
+  return rc;
+}
+
 /* 
-** Either allocate an output segment or locate an existing appendable 
-** output segment to append to. An "appendable" output segment is
-** slightly different to a normal one, as the required range of keys in
-** the %_segments table must be allocated up front.
+** Allocate an appendable output segment on absolute level iAbsLevel+1
+** with idx value iIdx.
 **
 ** In the %_segdir table, a segment is defined by the values in three
 ** columns:
@@ -4001,33 +4045,15 @@ static int fts3IncrmergeLoad(
 static int fts3IncrmergeWriter( 
   Fts3Table *p,                   /* Fts3 table handle */
   sqlite3_int64 iAbsLevel,        /* Absolute level of input segments */
+  int iIdx,                       /* Index of new output segment */
   Fts3MultiSegReader *pCsr,       /* Cursor that data will be read from */
   IncrmergeWriter *pWriter        /* Populate this object */
 ){
   int rc;                         /* Return Code */
   int i;                          /* Iterator variable */
   int nLeafEst;                   /* Blocks allocated for leaf nodes */
-  int iIdx;                       /* Index of output segment */
   sqlite3_stmt *pLeafEst = 0;     /* SQL used to determine nLeafEst */
   sqlite3_stmt *pFirstBlock = 0;  /* SQL used to determine first block */
-  sqlite3_stmt *pOutputIdx = 0;   /* SQL used to find output index */
-  const char *zKey = pCsr->zTerm; /* First key to be appended to output */
-  int nKey = pCsr->nTerm;         /* Size of zKey in bytes */
-
-  rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pOutputIdx, 0);
-  if( rc==SQLITE_OK ){
-    sqlite3_bind_int64(pOutputIdx, 1, iAbsLevel+1);
-    sqlite3_step(pOutputIdx);
-    iIdx = sqlite3_column_int(pOutputIdx, 0) - 1;
-    rc = sqlite3_reset(pOutputIdx);
-  }
-  if( rc!=SQLITE_OK ) return rc;
-
-  assert( zKey );
-  assert( pWriter->nLeafEst==0 );
-  rc = fts3IncrmergeLoad(p, iAbsLevel, iIdx, zKey, nKey, pWriter);
-  if( rc!=SQLITE_OK || pWriter->nLeafEst ) return rc;
-  iIdx++;
 
   /* Calculate nLeafEst. */
   rc = fts3SqlStmt(p, SQL_MAX_LEAF_NODE_ESTIMATE, &pLeafEst, 0);
@@ -4190,7 +4216,7 @@ static int fts3TruncateSegment(
   sqlite3_int64 iAbsLevel,        /* Absolute level of segment to modify */
   int iIdx,                       /* Index within level of segment to modify */
   const char *zTerm,              /* Remove terms smaller than this */
-  int nTerm                       /* Number of bytes in buffer zTerm */
+  int nTerm                      /* Number of bytes in buffer zTerm */
 ){
   int rc = SQLITE_OK;             /* Return code */
   Blob root = {0,0,0};            /* New root page image */
@@ -4198,22 +4224,22 @@ static int fts3TruncateSegment(
   sqlite3_int64 iBlock = 0;       /* Block id */
   sqlite3_int64 iNewStart = 0;    /* New value for iStartBlock */
   sqlite3_int64 iOldStart = 0;    /* Old value for iStartBlock */
-  int rc2;                        /* sqlite3_reset() return code */
   sqlite3_stmt *pFetch = 0;       /* Statement used to fetch segdir */
 
-  assert( p->aStmt[SQL_SELECT_SEGDIR] );
-  pFetch = p->aStmt[SQL_SELECT_SEGDIR];
-
-  sqlite3_bind_int64(pFetch, 1, iAbsLevel);
-  sqlite3_bind_int(pFetch, 2, iIdx);
-  if( SQLITE_ROW==sqlite3_step(pFetch) ){
-    const char *aRoot = sqlite3_column_blob(pFetch, 4);
-    int nRoot = sqlite3_column_bytes(pFetch, 4);
-    iOldStart = sqlite3_column_int64(pFetch, 1);
-    rc = fts3TruncateNode(aRoot, nRoot, &root, zTerm, nTerm, &iBlock);
+  rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR, &pFetch, 0);
+  if( rc==SQLITE_OK ){
+    int rc2;                      /* sqlite3_reset() return code */
+    sqlite3_bind_int64(pFetch, 1, iAbsLevel);
+    sqlite3_bind_int(pFetch, 2, iIdx);
+    if( SQLITE_ROW==sqlite3_step(pFetch) ){
+      const char *aRoot = sqlite3_column_blob(pFetch, 4);
+      int nRoot = sqlite3_column_bytes(pFetch, 4);
+      iOldStart = sqlite3_column_int64(pFetch, 1);
+      rc = fts3TruncateNode(aRoot, nRoot, &root, zTerm, nTerm, &iBlock);
+    }
+    rc2 = sqlite3_reset(pFetch);
+    if( rc==SQLITE_OK ) rc = rc2;
   }
-  rc2 = sqlite3_reset(pFetch);
-  if( rc==SQLITE_OK ) rc = rc2;
 
   while( rc==SQLITE_OK && iBlock ){
     char *aBlock = 0;
@@ -4273,9 +4299,11 @@ static int fts3TruncateSegment(
 static int fts3IncrmergeChomp(
   Fts3Table *p,                   /* FTS table handle */
   sqlite3_int64 iAbsLevel,        /* Absolute level containing segments */
-  Fts3MultiSegReader *pCsr        /* Chomp all segments opened by this cursor */
+  Fts3MultiSegReader *pCsr,       /* Chomp all segments opened by this cursor */
+  int *pnRem                      /* Number of segments not deleted */
 ){
   int i;
+  int nRem = 0;
   int rc = SQLITE_OK;
 
   for(i=pCsr->nSegment-1; i>=0 && rc==SQLITE_OK; i--){
@@ -4296,6 +4324,7 @@ static int fts3IncrmergeChomp(
       if( rc==SQLITE_OK ){
         rc = fts3RemoveSegdirEntry(p, iAbsLevel, pSeg->iIdx);
       }
+      *pnRem = 0;
     }else{
       /* The incremental merge did not copy all the data from this 
       ** segment to the upper level. The segment is modified in place
@@ -4303,7 +4332,74 @@ static int fts3IncrmergeChomp(
       const char *zTerm = pSeg->zTerm;
       int nTerm = pSeg->nTerm;
       rc = fts3TruncateSegment(p, iAbsLevel, pSeg->iIdx, zTerm, nTerm);
+      nRem++;
+    }
+  }
+
+  *pnRem = nRem;
+  return rc;
+}
+
+/*
+** Store an incr-merge hint in the database.
+*/
+static int fts3IncrmergeHintStore(
+  Fts3Table *p,                   /* FTS3 table handle */
+  sqlite3_int64 iAbsLevel,        /* Absolute level to read input data from */
+  int nMerge                      /* Number of segments to merge */
+){
+  char aBlob[FTS3_VARINT_MAX * 2];
+  int nBlob = 0;
+  int rc;
+  sqlite3_stmt *pReplace = 0;
+
+  assert( p->bHasStat );
+  nBlob += sqlite3Fts3PutVarint(&aBlob[nBlob], iAbsLevel);
+  nBlob += sqlite3Fts3PutVarint(&aBlob[nBlob], nMerge);
+
+  rc = fts3SqlStmt(p, SQL_REPLACE_STAT, &pReplace, 0);
+  if( rc==SQLITE_OK ){
+    sqlite3_bind_int(pReplace, 1, FTS_STAT_INCRMERGEHINT);
+    sqlite3_bind_blob(pReplace, 2, aBlob, nBlob, SQLITE_TRANSIENT);
+    sqlite3_step(pReplace);
+    rc = sqlite3_reset(pReplace);
+  }
+
+  return rc;
+}
+
+/*
+** Load an incr-merge hint from the database.
+**
+** The incr-merge hint, if one exists, is stored in the rowid==1 row of
+** the %_stat table.
+*/
+static int fts3IncrmergeHintLoad(
+  Fts3Table *p,                   /* FTS3 table handle */
+  sqlite3_int64 *piAbsLevel,      /* Absolute level to read input data from */
+  int *pnMerge                    /* Number of segments to merge */
+){
+  sqlite3_stmt *pSelect = 0;
+  int rc;
+
+  *pnMerge = 0;
+  *piAbsLevel = 0;
+
+  rc = fts3SqlStmt(p, SQL_SELECT_STAT, &pSelect, 0);
+  if( rc==SQLITE_OK ){
+    sqlite3_bind_int(pSelect, 1, FTS_STAT_INCRMERGEHINT);
+    if( SQLITE_ROW==sqlite3_step(pSelect) ){
+      char *aHint = sqlite3_column_blob(pSelect, 0);
+      int nHint = sqlite3_column_bytes(pSelect, 0);
+      if( aHint ){
+        int i;
+        char aBlob[FTS3_VARINT_MAX * 2];
+        memcpy(aBlob, aHint, MAX(sizeof(aBlob), nHint));
+        i = sqlite3Fts3GetVarint(aBlob, piAbsLevel);
+        sqlite3Fts3GetVarint32(&aBlob[i], pnMerge);
+      }
     }
+    rc = sqlite3_reset(pSelect);
   }
 
   return rc;
@@ -4319,31 +4415,48 @@ static int fts3IncrmergeChomp(
 ** quota of nMerge leaf blocks.
 */
 static int fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
-  int rc = SQLITE_OK;             /* Return code */
+  int rc;                         /* Return code */
   int nRem = nMerge;              /* Number of leaf pages yet to  be written */
+  int bUseHint = 1;               /* True if hint has not yet been attempted */
+  sqlite3_int64 iHintAbsLevel = 0;/* Hint level */
+  int nHintSeg = 0;               /* Hint number of segments */
+  int nSeg = 0;                   /* Number of input segments */
+  sqlite3_int64 iAbsLevel = 0;    /* Absolute level number to work on */
 
   assert( nMin>=2 );
 
+  rc = fts3IncrmergeHintLoad(p, &iHintAbsLevel, &nHintSeg);
+  if( nHintSeg==0 ) bUseHint = 0;
+
   while( rc==SQLITE_OK && nRem>0 ){
-    sqlite3_int64 iAbsLevel;        /* Absolute level number to work on */
-    sqlite3_stmt *pFindLevel = 0;   /* SQL used to determine iAbsLevel */
     Fts3MultiSegReader *pCsr;       /* Cursor used to read input data */
     Fts3SegFilter *pFilter;         /* Filter used with cursor pCsr */
     IncrmergeWriter *pWriter;       /* Writer object */
     const int nAlloc = sizeof(*pCsr) + sizeof(*pFilter) + sizeof(*pWriter);
 
-    /* Determine which level to merge segments from. Any level, from any
-    ** prefix or language index may be selected. Stack variable iAbsLevel 
-    ** is set to the absolute level number of the level to merge from.  */
-    rc = fts3SqlStmt(p, SQL_FIND_MERGE_LEVEL, &pFindLevel, 0);
-    sqlite3_bind_int(pFindLevel, 1, nMin);
-    if( sqlite3_step(pFindLevel)!=SQLITE_ROW ){
-      /* There are no levels with nMin or more segments. Or an error has
-      ** occurred. Either way, exit early.  */
-      return sqlite3_reset(pFindLevel);
+    if( bUseHint ){
+      iAbsLevel = iHintAbsLevel;
+      nSeg = nHintSeg;
+    }else{
+      sqlite3_stmt *pFindLevel = 0; /* SQL used to determine iAbsLevel */
+
+      /* Determine which level to merge segments from. Any level, from any
+      ** prefix or language index may be selected. Stack variable iAbsLevel 
+      ** is set to the absolute level number of the level to merge from.  */
+      rc = fts3SqlStmt(p, SQL_FIND_MERGE_LEVEL, &pFindLevel, 0);
+      sqlite3_bind_int(pFindLevel, 1, nMin);
+      if( sqlite3_step(pFindLevel)!=SQLITE_ROW ){
+        /* There are no levels with nMin or more segments. Or an error has
+         ** occurred. Either way, exit early.  */
+        rc = sqlite3_reset(pFindLevel);
+        iAbsLevel = 0;
+        nSeg = 0;
+        break;
+      }
+      iAbsLevel = sqlite3_column_int64(pFindLevel, 0);
+      nSeg = nMin;
+      sqlite3_reset(pFindLevel);
     }
-    iAbsLevel = sqlite3_column_int64(pFindLevel, 0);
-    sqlite3_reset(pFindLevel);
 
     /* Allocate space for the cursor, filter and writer objects */
     pWriter = (IncrmergeWriter *)sqlite3_malloc(nAlloc);
@@ -4352,36 +4465,56 @@ static int fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
     pFilter = (Fts3SegFilter *)&pWriter[1];
     pCsr = (Fts3MultiSegReader *)&pFilter[1];
 
-    /* Open a cursor to iterate through the contents of indexes 0 and 1 of
-    ** the selected absolute level. */
+    /* Open a cursor to iterate through the contents of the oldest nSeg 
+    ** indexes of absolute level iAbsLevel. If this cursor is opened using 
+    ** the 'hint' parameters, it is possible that there are less than nSeg
+    ** segments available in level iAbsLevel. In this case, no work is
+    ** done on iAbsLevel - fall through to the next iteration of the loop 
+    ** to start work on some other level.  */
     pFilter->flags = FTS3_SEGMENT_REQUIRE_POS;
-    rc = fts3IncrmergeCsr(p, iAbsLevel, nMin, pCsr);
-    fts3LogMerge(nMin, iAbsLevel);
-    if( rc==SQLITE_OK ){
-      rc = sqlite3Fts3SegReaderStart(p, pCsr, pFilter);
-    }
-    if( rc==SQLITE_OK ){
-      if( SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pCsr)) ){
-        rc = fts3IncrmergeWriter(p, iAbsLevel, pCsr, pWriter);
+    rc = fts3IncrmergeCsr(p, iAbsLevel, nSeg, pCsr);
+    if( pCsr->nSegment==nSeg && SQLITE_OK==rc
+     && SQLITE_OK ==(rc = sqlite3Fts3SegReaderStart(p, pCsr, pFilter))
+     && SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pCsr))
+    ){
+      int iIdx = 0;               /* Largest idx in level (iAbsLevel+1) */
+      rc = fts3IncrmergeOutputIdx(p, iAbsLevel, &iIdx);
+      if( rc==SQLITE_OK ){
+        if( bUseHint && iIdx>0 ){
+          const char *zKey = pCsr->zTerm;
+          int nKey = pCsr->nTerm;
+          rc = fts3IncrmergeLoad(p, iAbsLevel, iIdx-1, zKey, nKey, pWriter);
+        }else{
+          rc = fts3IncrmergeWriter(p, iAbsLevel, iIdx, pCsr, pWriter);
+        }
+      }
+
+      if( rc==SQLITE_OK && pWriter->nLeafEst ){
+        fts3LogMerge(nSeg, iAbsLevel);
+        do {
+          rc = fts3IncrmergeAppend(p, pWriter, pCsr);
+          if( rc==SQLITE_OK ) rc = sqlite3Fts3SegReaderStep(p, pCsr);
+          if( pWriter->nWork>=nRem && rc==SQLITE_ROW ) rc = SQLITE_OK;
+        }while( rc==SQLITE_ROW );
+
+        /* Update or delete the input segments */
         if( rc==SQLITE_OK ){
-          do {
-            rc = fts3IncrmergeAppend(p, pWriter, pCsr);
-            if( rc==SQLITE_OK ) rc = sqlite3Fts3SegReaderStep(p, pCsr);
-            if( pWriter->nWork>=nRem && rc==SQLITE_ROW ) rc = SQLITE_OK;
-          }while( rc==SQLITE_ROW );
+          nRem -= (1 + pWriter->nWork);
+          rc = fts3IncrmergeChomp(p, iAbsLevel, pCsr, &nSeg);
         }
       }
-    }
-    fts3IncrmergeRelease(p, pWriter, &rc);
-    nRem -= (1 + pWriter->nWork);
 
-    /* Update or delete the input segments */
-    if( rc==SQLITE_OK ){
-      rc = fts3IncrmergeChomp(p, iAbsLevel, pCsr);
+      fts3IncrmergeRelease(p, pWriter, &rc);
     }
 
     sqlite3Fts3SegReaderFinish(pCsr);
     sqlite3_free(pWriter);
+    bUseHint = 0;
+  }
+
+  /* Write the hint values into the %_stat table for the next incr-merger */
+  if( rc==SQLITE_OK && (iAbsLevel!=iHintAbsLevel || nHintSeg!=nSeg) ){
+    rc = fts3IncrmergeHintStore(p, iAbsLevel, nSeg);
   }
 
   return rc;
index 13ef1aed2cdcc03871c7911b0002d4590a58c8f2..7fb243a8e375c1391569db3219efe642a0188c9b 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sfts4merge3.test,\sfor\stesting\sthat\solder\sversions\sof\sFTS4\smay\sinteroperate\swith\sincr-merge\scapable\sversions.
-D 2012-03-21T14:34:23.781
+C Following\san\sincr-merge\soperation\sthat\sdoes\snot\scompletely\sconsume\sits\sinput\ssegments,\sstore\scontext\sin\sthe\srowid==1\srow\sof\sthe\s%_stat\stable\sthat\sallows\sthe\snext\sincr-merge\sto\spick\sup\swhere\sthe\sprevious\sleft\soff.
+D 2012-03-22T16:48:12.328
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 2f37e468503dbe79d35c9f6dffcf3fae1ae9ec20
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -78,7 +78,7 @@ F ext/fts3/fts3_test.c 6b7cc68aef4efb084e1449f7d20c4b20d3bdf6b4
 F ext/fts3/fts3_tokenizer.c 3da7254a9881f7e270ab28e2004e0d22b3212bce
 F ext/fts3/fts3_tokenizer.h 66dec98e365854b6cd2d54f1a96bb6d428fc5a68
 F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
-F ext/fts3/fts3_write.c a78deea7b575b244d50eea1fdb9bf228c0dd00bb
+F ext/fts3/fts3_write.c 62ada731e3beef4977386a91240c5f80a1db884e
 F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
 F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
@@ -498,7 +498,7 @@ F test/fts3sort.test 95be0b19d7e41c44b29014f13ea8bddd495fd659
 F test/fts4aa.test 6e7f90420b837b2c685f3bcbe84c868492d40a68
 F test/fts4content.test 17b2360f7d1a9a7e5aa8022783f5c5731b6dfd4f
 F test/fts4langid.test 24a6e41063b416bbdf371ff6b4476fa41c194aa7
-F test/fts4merge.test 1c3389a3be18498934baf76afe67663d8b4b5e42
+F test/fts4merge.test 8e092654ab874afcc4a25f2255497eeb37da74e9
 F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891
 F test/fts4merge3.test 2d2008772f45afc617fc34d10bcafbc8de1ca2ae
 F test/func.test 6c5ce11e3a0021ca3c0649234e2d4454c89110ca
@@ -996,7 +996,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
 F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
-P f61d5fb0281381228eb1a12a233bacaeb26b12a3
-R 0ef159b3f0bfab79746087f966c7f8a3
+P 903ec5126dd981da6d7bab45c568f34b99446159
+R 36f0dde084aa3ea3b70c7d9b0328ed18
 U dan
-Z 5c824893eaa42ecaf0e347109e073f8a
+Z c1efcc23c92abcdb78f85442afae1924
index 4039183245145074ae649c718428313dae820d00..2894e9a6a25d57ad9bcadf04295f2b89e391c5b9 100644 (file)
@@ -1 +1 @@
-903ec5126dd981da6d7bab45c568f34b99446159
\ No newline at end of file
+ab0a4f44fb67e9f0cb82297b80e728ca58cdb0fb
\ No newline at end of file
index 9f7e05b0ed5fb206945aa3fcb7d95e8f5d66e8d0..91f3e04f8bcd8f9045024226f0886108e1fcc375 100644 (file)
@@ -63,9 +63,7 @@ for {set i 0} {$i<100} {incr i} {
 do_execsql_test 1.5 { 
   SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level 
 } {
-  0 {0 1 2 3} 
-  1 {0 1 2} 
-  2 0
+  2 {0 1}
   3 0
 }
 
@@ -114,12 +112,55 @@ do_execsql_test 3.3 {
   SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level 
 } {
   0 0 
-  1 {0 1} 
-  2 0 
-  3 {0 1} 
-  4 {0 1} 
-  5 0
+  2 0
+  3 0 
+  4 0
+  6 0
 }
 
-finish_test
+#-------------------------------------------------------------------------
+# Test cases 4.*
+#
+reset_db
+do_execsql_test 4.1 {
+  PRAGMA page_size = 512;
+  CREATE VIRTUAL TABLE t4 USING fts4;
+  PRAGMA main.page_size;
+} {512}
+
+do_test 4.2 {
+  foreach x {a c b d e f g h i j k l m n o p} {
+    execsql "INSERT INTO t4 VALUES('[string repeat $x 600]')"
+  }
+  execsql {SELECT level, group_concat(idx, ' ') FROM t4_segdir GROUP BY level}
+} {0 {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15}}
 
+foreach {tn expect} {
+  1  "0 {0 1 2 3 4 5 6 7 8 9 10 11 12 13} 1 0"
+  2  "0 {0 1 2 3 4 5 6 7 8 9 10 11 12}    1 0"
+  3  "0 {0 1 2 3 4 5 6 7 8 9 10 11}       1 0"
+  4  "0 {0 1 2 3 4 5 6 7 8 9 10}          1 0"
+  5  "0 {0 1 2 3 4 5 6 7 8 9}             1 0"
+  6  "0 {0 1 2 3 4 5 6 7 8}               1 0"
+  7  "0 {0 1 2 3 4 5 6 7}                 1 0"
+  8  "0 {0 1 2 3 4 5 6}                   1 0"
+  9  "0 {0 1 2 3 4 5}                     1 0"
+} {
+  do_execsql_test 4.3.$tn {
+    INSERT INTO t4(t4) VALUES('merge=1,16');
+    SELECT level, group_concat(idx, ' ') FROM t4_segdir GROUP BY level;
+  } $expect
+}
+
+do_execsql_test 4.4.1 {
+  SELECT quote(value) FROM t4_stat WHERE rowid=1
+} {X'0006'}
+
+do_execsql_test 4.4.2 {
+  DELETE FROM t4_stat WHERE rowid=1;
+  INSERT INTO t4(t4) VALUES('merge=1,12');
+  SELECT level, group_concat(idx, ' ') FROM t4_segdir GROUP BY level;
+} "0 {0 1 2 3 4 5}                     1 0"
+
+
+finish_test