]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix some code and test coverage issues in fts5_index.c. fts5
authordan <dan@noemail.net>
Tue, 29 Mar 2016 21:19:04 +0000 (21:19 +0000)
committerdan <dan@noemail.net>
Tue, 29 Mar 2016 21:19:04 +0000 (21:19 +0000)
FossilOrigin-Name: 7635c68018ce1656a1c5d6eebaf8f3a8e8839b59

ext/fts5/fts5_index.c
ext/fts5/test/fts5_common.tcl
ext/fts5/test/fts5corrupt.test
ext/fts5/test/fts5faultC.test
ext/fts5/test/fts5rowid.test
ext/fts5/test/fts5simple3.test
ext/fts5/test/fts5synonym2.test
ext/fts5/test/fts5version.test
manifest
manifest.uuid

index 95e4fe3848ed7395c0b37567a20ad9b1ca42abc8..85ee7676a7b51f743d6a4817e6e0871c5c5d5df8 100644 (file)
@@ -266,7 +266,6 @@ typedef struct Fts5DlidxIter Fts5DlidxIter;
 typedef struct Fts5DlidxLvl Fts5DlidxLvl;
 typedef struct Fts5DlidxWriter Fts5DlidxWriter;
 typedef struct Fts5Iter Fts5Iter;
-typedef struct Fts5PageWriter Fts5PageWriter;
 typedef struct Fts5SegIter Fts5SegIter;
 typedef struct Fts5DoclistIter Fts5DoclistIter;
 typedef struct Fts5SegWriter Fts5SegWriter;
@@ -356,13 +355,6 @@ struct Fts5Structure {
 /*
 ** An object of type Fts5SegWriter is used to write to segments.
 */
-struct Fts5PageWriter {
-  int pgno;                       /* Page number for this page */
-  int iPrevPgidx;                 /* Previous value written into pgidx */
-  Fts5Buffer buf;                 /* Buffer containing leaf data */
-  Fts5Buffer pgidx;               /* Buffer containing page-index */
-  Fts5Buffer term;                /* Buffer containing previous term on page */
-};
 struct Fts5DlidxWriter {
   int pgno;                       /* Page number for this page */
   int bPrevValid;                 /* True if iPrev is valid */
@@ -371,12 +363,15 @@ struct Fts5DlidxWriter {
 };
 struct Fts5SegWriter {
   int iSegid;                     /* Segid to write to */
-  Fts5PageWriter writer;          /* PageWriter object */
+  int pgno;                       /* Page number for current leaf page */
+  int iPrevPgidx;                 /* Previous value written into pgidx */
+  Fts5Buffer buf;                 /* Buffer of current leaf page data */
+  Fts5Buffer pgidx;               /* Buffer of current leaf page-index */
+  Fts5Buffer term;                /* Buffer containing previous term on leaf */
+
   i64 iPrevRowid;                 /* Previous rowid written to current leaf */
   u8 bFirstRowidInDoclist;        /* True if next rowid is first in doclist */
   u8 bFirstRowidInPage;           /* True if next rowid is first in page */
-  /* TODO1: Can use (writer.pgidx.n==0) instead of bFirstTermInPage */
-  u8 bFirstTermInPage;            /* True if next term will be first in leaf */
   int nLeafWritten;               /* Number of leaf pages written */
   int nEmpty;                     /* Number of contiguous term-less nodes */
 
@@ -1004,6 +999,14 @@ static Fts5Structure *fts5StructureReadUncached(Fts5Index *p){
   return pRet;
 }
 
+/*
+** Execute "PRAGMA db.data_version" and return the integer value that 
+** it returns.
+**
+** This function returns 0 if Fts5Index.rc is set to other than SQLITE_OK
+** when it is called. If an error occurs while executing the PRAGMA,
+** Fts5Index.rc is set to an SQLite error code before returning.
+*/
 static i64 fts5IndexDataVersion(Fts5Index *p){
   i64 iVersion = 0;
 
@@ -3047,6 +3050,20 @@ static void fts5PoslistFilterCallback(
   }
 }
 
+/*
+** This function is used to extract the position list associated with
+** the entry that segment iterator pSeg currently points to. If the
+** entire position list resides on a single leaf page, then this 
+** function invokes the xChunk callback exactly once. Or, if the position
+** list spans multiple leaves, xChunk is invoked once for each leaf.
+**
+** The first argument passed to the xChunk callback is a copy of the Fts5Index
+** pointer passed as the first argument to this function. Similarly, the 
+** second argument passed to xChunk is a copy of the third parameter passed
+** to this function. The third and fourth arguments passed to xChunk are
+** a pointer to a blob containing part of the position list and the size
+** in bytes there of.
+*/
 static void fts5ChunkIterate(
   Fts5Index *p,                   /* Index object */
   Fts5SegIter *pSeg,              /* Poslist of this iterator */
@@ -3060,7 +3077,7 @@ static void fts5ChunkIterate(
   int pgno = pSeg->iLeafPgno;
   int pgnoSave = 0;
 
-  /* This function does notmwork with detail=none databases. */
+  /* This function does not work with detail=none databases. */
   assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE );
 
   if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){
@@ -3734,7 +3751,7 @@ static void fts5WriteBtreeTerm(
 ){
   fts5WriteFlushBtree(p, pWriter);
   fts5BufferSet(&p->rc, &pWriter->btterm, nTerm, pTerm);
-  pWriter->iBtPage = pWriter->writer.pgno;
+  pWriter->iBtPage = pWriter->pgno;
 }
 
 /*
@@ -3757,10 +3774,16 @@ static void fts5WriteBtreeNoTerm(
   pWriter->nEmpty++;
 }
 
+/*
+** Buffer pBuf contains a doclist-index page. Return the first rowid 
+** value on the page.
+*/
 static i64 fts5DlidxExtractFirstRowid(Fts5Buffer *pBuf){
   i64 iRowid;
   int iOff;
 
+  /* Skip past the flags byte and the page number of the left-most child
+  ** page to the first rowid.  */
   iOff = 1 + fts5GetVarint(&pBuf->p[1], (u64*)&iRowid);
   fts5GetVarint(&pBuf->p[iOff], (u64*)&iRowid);
   return iRowid;
@@ -3768,8 +3791,8 @@ static i64 fts5DlidxExtractFirstRowid(Fts5Buffer *pBuf){
 
 /*
 ** Rowid iRowid has just been appended to the current leaf page. It is the
-** first on the page. This function appends an appropriate entry to the current
-** doclist-index.
+** first on the page. This function appends an appropriate entry to the
+** current doclist-index.
 */
 static void fts5WriteDlidxAppend(
   Fts5Index *p, 
@@ -3818,7 +3841,7 @@ static void fts5WriteDlidxAppend(
     if( pDlidx->bPrevValid ){
       iVal = iRowid - pDlidx->iPrev;
     }else{
-      i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno);
+      i64 iPgno = (i==0 ? pWriter->pgno : pDlidx[-1].pgno);
       assert( pDlidx->buf.n==0 );
       sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, !bDone);
       sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, iPgno);
@@ -3831,42 +3854,42 @@ static void fts5WriteDlidxAppend(
   }
 }
 
+/*
+** Flush the writer's current leaf page to disk. Initialize various
+** fields ready for the next leaf page.
+*/
 static void fts5WriteFlushLeaf(Fts5Index *p, Fts5SegWriter *pWriter){
-  static const u8 zero[] = { 0x00, 0x00, 0x00, 0x00 };
-  Fts5PageWriter *pPage = &pWriter->writer;
   i64 iRowid;
 
-  assert( (pPage->pgidx.n==0)==(pWriter->bFirstTermInPage) );
-
   /* Set the szLeaf header field. */
-  assert( 0==fts5GetU16(&pPage->buf.p[2]) );
-  fts5PutU16(&pPage->buf.p[2], (u16)pPage->buf.n);
+  assert( 0==fts5GetU16(&pWriter->buf.p[2]) );
+  fts5PutU16(&pWriter->buf.p[2], (u16)pWriter->buf.n);
 
-  if( pWriter->bFirstTermInPage ){
+  if( pWriter->pgidx.n==0 ){
     /* No term was written to this page. */
-    assert( pPage->pgidx.n==0 );
+    assert( pWriter->pgidx.n==0 );
     fts5WriteBtreeNoTerm(p, pWriter);
   }else{
     /* Append the pgidx to the page buffer. Set the szLeaf header field. */
-    fts5BufferAppendBlob(&p->rc, &pPage->buf, pPage->pgidx.n, pPage->pgidx.p);
+    fts5BufferSafeAppendBlob(&pWriter->buf, pWriter->pgidx.p, pWriter->pgidx.n);
   }
 
   /* Write the page out to disk */
-  iRowid = FTS5_SEGMENT_ROWID(pWriter->iSegid, pPage->pgno);
-  fts5DataWrite(p, iRowid, pPage->buf.p, pPage->buf.n);
+  iRowid = FTS5_SEGMENT_ROWID(pWriter->iSegid, pWriter->pgno);
+  fts5DataWrite(p, iRowid, pWriter->buf.p, pWriter->buf.n);
 
   /* Initialize the next page. */
-  fts5BufferZero(&pPage->buf);
-  fts5BufferZero(&pPage->pgidx);
-  fts5BufferAppendBlob(&p->rc, &pPage->buf, 4, zero);
-  pPage->iPrevPgidx = 0;
-  pPage->pgno++;
+  fts5BufferZero(&pWriter->buf);
+  fts5BufferZero(&pWriter->pgidx);
+  memset(pWriter->buf.p, 0, 4);
+  pWriter->buf.n = 4;
+  pWriter->iPrevPgidx = 0;
+  pWriter->pgno++;
 
   /* Increase the leaves written counter */
   pWriter->nLeafWritten++;
 
   /* The new leaf holds no terms or rowids */
-  pWriter->bFirstTermInPage = 1;
   pWriter->bFirstRowidInPage = 1;
 }
 
@@ -3883,34 +3906,24 @@ static void fts5WriteAppendTerm(
   int nTerm, const u8 *pTerm 
 ){
   int nPrefix;                    /* Bytes of prefix compression for term */
-  Fts5PageWriter *pPage = &pWriter->writer;
-  Fts5Buffer *pPgidx = &pWriter->writer.pgidx;
+  int iOff;
+  Fts5Buffer *pPgidx = &pWriter->pgidx;
 
   assert( p->rc==SQLITE_OK );
-  assert( pPage->buf.n>=4 );
-  assert( pPage->buf.n>4 || pWriter->bFirstTermInPage );
+  assert( pWriter->buf.n>=4 );
 
   /* If the current leaf page is full, flush it to disk. */
-  if( (pPage->buf.n + pPgidx->n + nTerm + 2)>=p->pConfig->pgsz ){
-    if( pPage->buf.n>4 ){
+  if( (pWriter->buf.n + pPgidx->n + nTerm + 2)>=p->pConfig->pgsz ){
+    if( pWriter->buf.n>4 ){
       fts5WriteFlushLeaf(p, pWriter);
     }
-    fts5BufferGrow(&p->rc, &pPage->buf, nTerm+FTS5_DATA_PADDING);
+    fts5BufferGrow(&p->rc, &pWriter->buf, nTerm+FTS5_DATA_PADDING);
   }
-  
-  /* TODO1: Updating pgidx here. */
-  pPgidx->n += sqlite3Fts5PutVarint(
-      &pPgidx->p[pPgidx->n], pPage->buf.n - pPage->iPrevPgidx
-  );
-  pPage->iPrevPgidx = pPage->buf.n;
-#if 0
-  fts5PutU16(&pPgidx->p[pPgidx->n], pPage->buf.n);
-  pPgidx->n += 2;
-#endif
 
-  if( pWriter->bFirstTermInPage ){
+  iOff = pWriter->buf.n;
+  if( pPgidx->n==0 ){
     nPrefix = 0;
-    if( pPage->pgno!=1 ){
+    if( pWriter->pgno!=1 ){
       /* This is the first term on a leaf that is not the leftmost leaf in
       ** the segment b-tree. In this case it is necessary to add a term to
       ** the b-tree hierarchy that is (a) larger than the largest term 
@@ -3919,37 +3932,38 @@ static void fts5WriteAppendTerm(
       ** byte longer than the longest prefix (pTerm/nTerm) shares with the
       ** previous term. 
       **
-      ** Usually, the previous term is available in pPage->term. The exception
+      ** Usually, the previous term is available in pWriter->term. The exception
       ** is if this is the first term written in an incremental-merge step.
       ** In this case the previous term is not available, so just write a
       ** copy of (pTerm/nTerm) into the parent node. This is slightly
       ** inefficient, but still correct.  */
       int n = nTerm;
-      if( pPage->term.n ){
-        n = 1 + fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm);
+      if( pWriter->term.n ){
+        n = 1 + fts5PrefixCompress(pWriter->term.n, pWriter->term.p, pTerm);
       }
       fts5WriteBtreeTerm(p, pWriter, n, pTerm);
-      pPage = &pWriter->writer;
     }
   }else{
-    nPrefix = fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm);
-    fts5BufferAppendVarint(&p->rc, &pPage->buf, nPrefix);
+    nPrefix = fts5PrefixCompress(pWriter->term.n, pWriter->term.p, pTerm);
+    fts5BufferAppendVarint(&p->rc, &pWriter->buf, nPrefix);
   }
 
+  fts5BufferSafeAppendVarint(pPgidx, iOff - pWriter->iPrevPgidx);
+  pWriter->iPrevPgidx = iOff;
+
   /* Append the number of bytes of new data, then the term data itself
   ** to the page. */
-  fts5BufferAppendVarint(&p->rc, &pPage->buf, nTerm - nPrefix);
-  fts5BufferAppendBlob(&p->rc, &pPage->buf, nTerm - nPrefix, &pTerm[nPrefix]);
+  fts5BufferAppendVarint(&p->rc, &pWriter->buf, nTerm - nPrefix);
+  fts5BufferAppendBlob(&p->rc, &pWriter->buf, nTerm - nPrefix, &pTerm[nPrefix]);
 
-  /* Update the Fts5PageWriter.term field. */
-  fts5BufferSet(&p->rc, &pPage->term, nTerm, pTerm);
-  pWriter->bFirstTermInPage = 0;
+  /* Update the Fts5SegWriter.term field. */
+  fts5BufferSet(&p->rc, &pWriter->term, nTerm, pTerm);
 
   pWriter->bFirstRowidInPage = 0;
   pWriter->bFirstRowidInDoclist = 1;
 
   assert( p->rc || (pWriter->nDlidx>0 && pWriter->aDlidx[0].buf.n==0) );
-  pWriter->aDlidx[0].pgno = pPage->pgno;
+  pWriter->aDlidx[0].pgno = pWriter->pgno;
 }
 
 /*
@@ -3961,9 +3975,8 @@ static void fts5WriteAppendRowid(
   i64 iRowid
 ){
   if( p->rc==SQLITE_OK ){
-    Fts5PageWriter *pPage = &pWriter->writer;
 
-    if( (pPage->buf.n + pPage->pgidx.n)>=p->pConfig->pgsz ){
+    if( (pWriter->buf.n + pWriter->pgidx.n)>=p->pConfig->pgsz ){
       fts5WriteFlushLeaf(p, pWriter);
     }
 
@@ -3971,16 +3984,17 @@ static void fts5WriteAppendRowid(
     ** rowid-pointer in the page-header. Also append a value to the dlidx
     ** buffer, in case a doclist-index is required.  */
     if( pWriter->bFirstRowidInPage ){
-      fts5PutU16(pPage->buf.p, (u16)pPage->buf.n);
+      fts5PutU16(pWriter->buf.p, (u16)pWriter->buf.n);
       fts5WriteDlidxAppend(p, pWriter, iRowid);
     }
 
     /* Write the rowid. */
     if( pWriter->bFirstRowidInDoclist || pWriter->bFirstRowidInPage ){
-      fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid);
+      fts5BufferSafeAppendVarint(&pWriter->buf, iRowid);
     }else{
       assert( p->rc || iRowid>pWriter->iPrevRowid );
-      fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid);
+      assert( pWriter->buf.n+9 <= pWriter->buf.nSpace );
+      fts5BufferSafeAppendVarint(&pWriter->buf, iRowid - pWriter->iPrevRowid);
     }
     pWriter->iPrevRowid = iRowid;
     pWriter->bFirstRowidInDoclist = 0;
@@ -3988,33 +4002,35 @@ static void fts5WriteAppendRowid(
   }
 }
 
+/*
+** Append position list data to the writers output.
+*/
 static void fts5WriteAppendPoslistData(
   Fts5Index *p, 
-  Fts5SegWriter *pWriter, 
-  const u8 *aData, 
-  int nData
+  Fts5SegWriter *pWriter,         /* Writer object to write to the output of */
+  const u8 *aData,                /* Buffer containing data to append */
+  int nData                       /* Size of buffer aData[] in bytes */
 ){
-  Fts5PageWriter *pPage = &pWriter->writer;
   const u8 *a = aData;
   int n = nData;
   
   assert( p->pConfig->pgsz>0 );
   while( p->rc==SQLITE_OK 
-     && (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz 
+     && (pWriter->buf.n + pWriter->pgidx.n + n)>=p->pConfig->pgsz 
   ){
-    int nReq = p->pConfig->pgsz - pPage->buf.n - pPage->pgidx.n;
+    int nReq = p->pConfig->pgsz - pWriter->buf.n - pWriter->pgidx.n;
     int nCopy = 0;
     while( nCopy<nReq ){
       i64 dummy;
       nCopy += fts5GetVarint(&a[nCopy], (u64*)&dummy);
     }
-    fts5BufferAppendBlob(&p->rc, &pPage->buf, nCopy, a);
+    fts5BufferAppendBlob(&p->rc, &pWriter->buf, nCopy, a);
     a += nCopy;
     n -= nCopy;
     fts5WriteFlushLeaf(p, pWriter);
   }
   if( n>0 ){
-    fts5BufferAppendBlob(&p->rc, &pPage->buf, n, a);
+    fts5BufferAppendBlob(&p->rc, &pWriter->buf, n, a);
   }
 }
 
@@ -4028,20 +4044,19 @@ static void fts5WriteFinish(
   int *pnLeaf                     /* OUT: Number of leaf pages in b-tree */
 ){
   int i;
-  Fts5PageWriter *pLeaf = &pWriter->writer;
   if( p->rc==SQLITE_OK ){
-    assert( pLeaf->pgno>=1 );
-    if( pLeaf->buf.n>4 ){
+    assert( pWriter->pgno>=1 );
+    if( pWriter->buf.n>4 ){
       fts5WriteFlushLeaf(p, pWriter);
     }
-    *pnLeaf = pLeaf->pgno-1;
-    if( pLeaf->pgno>1 ){
+    *pnLeaf = pWriter->pgno-1;
+    if( pWriter->pgno>1 ){
       fts5WriteFlushBtree(p, pWriter);
     }
   }
-  fts5BufferFree(&pLeaf->term);
-  fts5BufferFree(&pLeaf->buf);
-  fts5BufferFree(&pLeaf->pgidx);
+  fts5BufferFree(&pWriter->term);
+  fts5BufferFree(&pWriter->buf);
+  fts5BufferFree(&pWriter->pgidx);
   fts5BufferFree(&pWriter->btterm);
 
   for(i=0; i<pWriter->nDlidx; i++){
@@ -4050,6 +4065,10 @@ static void fts5WriteFinish(
   sqlite3_free(pWriter->aDlidx);
 }
 
+/*
+** Initialize the Fts5SegWriter object indicated by the second argument
+** to write to the segment with seg-id iSegid.
+*/
 static void fts5WriteInit(
   Fts5Index *p, 
   Fts5SegWriter *pWriter, 
@@ -4061,16 +4080,15 @@ static void fts5WriteInit(
   pWriter->iSegid = iSegid;
 
   fts5WriteDlidxGrow(p, pWriter, 1);
-  pWriter->writer.pgno = 1;
-  pWriter->bFirstTermInPage = 1;
+  pWriter->pgno = 1;
   pWriter->iBtPage = 1;
 
-  assert( pWriter->writer.buf.n==0 );
-  assert( pWriter->writer.pgidx.n==0 );
+  assert( pWriter->buf.n==0 );
+  assert( pWriter->pgidx.n==0 );
 
   /* Grow the two buffers to pgsz + padding bytes in size. */
-  sqlite3Fts5BufferSize(&p->rc, &pWriter->writer.pgidx, nBuffer);
-  sqlite3Fts5BufferSize(&p->rc, &pWriter->writer.buf, nBuffer);
+  sqlite3Fts5BufferSize(&p->rc, &pWriter->pgidx, nBuffer);
+  sqlite3Fts5BufferSize(&p->rc, &pWriter->buf, nBuffer);
 
   if( p->pIdxWriter==0 ){
     Fts5Config *pConfig = p->pConfig;
@@ -4082,8 +4100,8 @@ static void fts5WriteInit(
 
   if( p->rc==SQLITE_OK ){
     /* Initialize the 4-byte leaf-page header to 0x00. */
-    memset(pWriter->writer.buf.p, 0, 4);
-    pWriter->writer.buf.n = 4;
+    memset(pWriter->buf.p, 0, 4);
+    pWriter->buf.n = 4;
 
     /* Bind the current output segment id to the index-writer. This is an
     ** optimization over binding the same value over and over as rows are
@@ -4096,6 +4114,8 @@ static void fts5WriteInit(
 ** Iterator pIter was used to iterate through the input segments of on an
 ** incremental merge operation. This function is called if the incremental
 ** merge step has finished but the input has not been completely exhausted.
+** It removes (DELETEs) any input segment leaves that are no longer required
+** from the database.
 */
 static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){
   int i;
@@ -4154,6 +4174,11 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){
   fts5BufferFree(&buf);
 }
 
+/*
+** This function is used as an fts5ChunkIterate() callback when merging
+** levels. The chunk passed to this function is appended to the output
+** segment.
+*/
 static void fts5MergeChunkCallback(
   Fts5Index *p, 
   void *pCtx, 
@@ -4205,7 +4230,7 @@ static void fts5IndexMergeLevel(
     pSeg = &pLvlOut->aSeg[pLvlOut->nSeg-1];
 
     fts5WriteInit(p, &writer, pSeg->iSegid);
-    writer.writer.pgno = pSeg->pgnoLast+1;
+    writer.pgno = pSeg->pgnoLast+1;
     writer.iBtPage = 0;
   }else{
     int iSegid = fts5AllocateSegid(p, pStruct);
@@ -4265,15 +4290,15 @@ static void fts5IndexMergeLevel(
 
     if( eDetail==FTS5_DETAIL_NONE ){
       if( pSegIter->bDel ){
-        fts5BufferAppendVarint(&p->rc, &writer.writer.buf, 0);
+        fts5BufferAppendVarint(&p->rc, &writer.buf, 0);
         if( pSegIter->nPos>0 ){
-          fts5BufferAppendVarint(&p->rc, &writer.writer.buf, 0);
+          fts5BufferAppendVarint(&p->rc, &writer.buf, 0);
         }
       }
     }else{
       /* Append the position-list data to the output */
       nPos = pSegIter->nPos*2 + pSegIter->bDel;
-      fts5BufferAppendVarint(&p->rc, &writer.writer.buf, nPos);
+      fts5BufferAppendVarint(&p->rc, &writer.buf, nPos);
       fts5ChunkIterate(p, pSegIter, (void*)&writer, fts5MergeChunkCallback);
     }
   }
@@ -4471,8 +4496,8 @@ static void fts5IndexFlush(Fts5Index *p){
       Fts5SegWriter writer;
       fts5WriteInit(p, &writer, iSegid);
 
-      pBuf = &writer.writer.buf;
-      pPgidx = &writer.writer.pgidx;
+      pBuf = &writer.buf;
+      pPgidx = &writer.pgidx;
 
       /* fts5WriteInit() should have initialized the buffers to (most likely)
       ** the maximum space required. */
@@ -5061,10 +5086,11 @@ static void fts5SetupPrefixIter(
     xAppend = fts5AppendPoslist;
   }
 
+  assert( p->pStruct );
   aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
   pStruct = fts5StructureRead(p);
 
-  if( aBuf && pStruct ){
+  if( aBuf ){
     const int flags = FTS5INDEX_QUERY_SCAN 
                     | FTS5INDEX_QUERY_SKIPEMPTY 
                     | FTS5INDEX_QUERY_NOOUTPUT;
@@ -5365,6 +5391,8 @@ int sqlite3Fts5IndexQuery(
   /* If the QUERY_SCAN flag is set, all other flags must be clear. This
   ** flag is used by the fts5vocab module only. */
   assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN );
+  assert( p->rc==SQLITE_OK );
+  assert( p->pStruct!=0 || (flags & FTS5INDEX_QUERY_PREFIX)==0 );
 
   if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
     int iIdx = 0;                 /* Index to search */
@@ -5567,6 +5595,7 @@ int sqlite3Fts5IndexLoadConfig(Fts5Index *p){
 */
 int sqlite3Fts5IndexNewTrans(Fts5Index *p){
   assert( p->pStruct==0 || p->iStructVersion!=0 );
+  assert( p->rc==SQLITE_OK );
   if( p->pConfig->iCookie<0 || fts5IndexDataVersion(p)!=p->iStructVersion ){
     fts5StructureInvalidate(p);
   }
@@ -6457,40 +6486,6 @@ static void fts5DecodeFunction(
   fts5BufferFree(&s);
 }
 
-/*
-** The implementation of user-defined scalar function fts5_rowid().
-*/
-static void fts5RowidFunction(
-  sqlite3_context *pCtx,          /* Function call context */
-  int nArg,                       /* Number of args (always 2) */
-  sqlite3_value **apVal           /* Function arguments */
-){
-  const char *zArg;
-  if( nArg==0 ){
-    sqlite3_result_error(pCtx, "should be: fts5_rowid(subject, ....)", -1);
-  }else{
-    zArg = (const char*)sqlite3_value_text(apVal[0]);
-    if( 0==sqlite3_stricmp(zArg, "segment") ){
-      i64 iRowid;
-      int segid, pgno;
-      if( nArg!=3 ){
-        sqlite3_result_error(pCtx, 
-            "should be: fts5_rowid('segment', segid, pgno))", -1
-        );
-      }else{
-        segid = sqlite3_value_int(apVal[1]);
-        pgno = sqlite3_value_int(apVal[2]);
-        iRowid = FTS5_SEGMENT_ROWID(segid, pgno);
-        sqlite3_result_int64(pCtx, iRowid);
-      }
-    }else{
-      sqlite3_result_error(pCtx, 
-        "first arg to fts5_rowid() must be 'segment'" , -1
-      );
-    }
-  }
-}
-
 /*
 ** This is called as part of registering the FTS5 module with database
 ** connection db. It registers several user-defined scalar functions useful
@@ -6511,11 +6506,6 @@ int sqlite3Fts5IndexInit(sqlite3 *db){
     );
   }
 
-  if( rc==SQLITE_OK ){
-    rc = sqlite3_create_function(
-        db, "fts5_rowid", -1, SQLITE_UTF8, 0, fts5RowidFunction, 0, 0
-    );
-  }
   return rc;
 }
 
index 0f371dcfd9d90e2e61cc3908cd05ccf157c3b83e..50d514dcb88762a07a9f5a6fcbb1a1e4ea018ffa 100644 (file)
@@ -645,3 +645,10 @@ proc fts5_tclnum_register {db} {
 # End of tokenizer code.
 #-------------------------------------------------------------------------
 
+proc fts5_rowid_func {segid pgno} { return [expr ($segid<<37) + $pgno] }
+proc register_fts5_rowid {db} {
+  $db func fts5_rowid -argcount 2 fts5_rowid_func
+}
+
+
+
index edaafb23793c1527318534ebd194ca15726b2d9c..b9d087384d0deff08856d56cef8974c73b5faf39 100644 (file)
@@ -41,18 +41,20 @@ db_save
 do_execsql_test 1.2 { INSERT INTO t1(t1) VALUES('integrity-check') }
 set segid [lindex [fts5_level_segids t1] 0]
 
+register_fts5_rowid db
 do_test 1.3 {
   execsql {
-    DELETE FROM t1_data WHERE rowid = fts5_rowid('segment', $segid, 4);
+    DELETE FROM t1_data WHERE rowid = fts5_rowid($segid, 4);
   }
   catchsql { INSERT INTO t1(t1) VALUES('integrity-check') }
 } {1 {database disk image is malformed}}
 
 do_test 1.4 {
   db_restore_and_reopen
+  register_fts5_rowid db
   execsql {
     UPDATE t1_data set block = X'00000000' || substr(block, 5) WHERE
-    rowid = fts5_rowid('segment', $segid, 4);
+    rowid = fts5_rowid($segid, 4);
   }
   catchsql { INSERT INTO t1(t1) VALUES('integrity-check') }
 } {1 {database disk image is malformed}}
index a9c94a44c0d6b5e164cfe581695c4bca6598c5a9..df51bf9a51639bdc64993ca23985413fcdeb6da3 100644 (file)
@@ -17,14 +17,13 @@ source $testdir/malloc_common.tcl
 set testprefix fts5faultC
 return_if_no_fts5
 
+if 1 {
+
 #--------------------------------------------------------------------------
 # Test that if an OOM error occurs while trying to set a configuration
 # option, the in-memory and on-disk configurations are not left in an 
 # inconsistent state.
 #
-
-
-
 proc posrowid {cmd} { $cmd xRowid }
 proc negrowid {cmd} { expr -1 * [$cmd xRowid] }
 
@@ -73,7 +72,50 @@ do_faultsim_test 1.1 -faults oom-* -prep {
 }
 
 
+#--------------------------------------------------------------------------
+
+reset_db
+do_execsql_test 2.0 {
+  CREATE VIRTUAL TABLE t1 USING fts5(y);
+}
+do_faultsim_test 2.1 -faults * -prep {
+  sqlite3 db test.db
+  execsql { SELECT * FROM t1('x') }
+} -body {
+  execsql { SELECT * FROM t1('x') }
+  execsql { SELECT * FROM t1('x') }
+} -test {
+  faultsim_test_result [list 0 {}]
+}
+do_faultsim_test 2.2 -faults * -prep {
+  sqlite3 db test.db
+  execsql { INSERT INTO t1 VALUES('yy') }
+} -body {
+  execsql { INSERT INTO t1 VALUES('yy') }
+  execsql { INSERT INTO t1 VALUES('yy') }
+} -test {
+  faultsim_test_result [list 0 {}]
+}
 
+#--------------------------------------------------------------------------
+}
+reset_db
+do_execsql_test 3.0 {
+  CREATE VIRTUAL TABLE t1 USING fts5(y);
+}
+faultsim_save_and_close
+do_faultsim_test 3.1 -faults * -prep {
+  faultsim_restore_and_reopen
+  execsql { 
+    BEGIN;
+      INSERT INTO t1 VALUES('x y z');
+      INSERT INTO t1 VALUES('a b c');
+  }
+} -body {
+  execsql { INSERT INTO t1(t1) VALUES('optimize') }
+} -test {
+  faultsim_test_result [list 0 {}]
+}
 
 finish_test
 
index 19590cdf0d819c9cf1208d38c58df09a433192a1..7a24a1287fa5feb9e8d2481b7c0e3a8aaf4b6e65 100644 (file)
 source [file join [file dirname [info script]] fts5_common.tcl]
 set testprefix fts5rowid
 
-# If SQLITE_ENABLE_FTS5 is defined, omit this file.
-ifcapable !fts5 {
-  finish_test
-  return
-}
-
-do_catchsql_test 1.1 {
-  SELECT fts5_rowid()
-} {1 {should be: fts5_rowid(subject, ....)}}
-
-do_catchsql_test 1.2 {
-  SELECT fts5_rowid('segment')
-} {1 {should be: fts5_rowid('segment', segid, pgno))}}
-
-do_execsql_test 1.3 {
-  SELECT fts5_rowid('segment', 1, 1)
-} {137438953473}
-
-do_catchsql_test 1.4 {
-  SELECT fts5_rowid('nosucharg');
-} {1 {first arg to fts5_rowid() must be 'segment'}} 
-
+return_if_no_fts5
 
 #-------------------------------------------------------------------------
 # Tests of the fts5_decode() function.
 #
 reset_db
+register_fts5_rowid db
 do_execsql_test 2.1 { 
   CREATE VIRTUAL TABLE x1 USING fts5(a, b);
   INSERT INTO x1(x1, rank) VALUES('pgsz', 32);
@@ -92,7 +72,7 @@ do_execsql_test 2.6 {
 #} $res
 
 do_execsql_test 2.8 {
-  SELECT fts5_decode(fts5_rowid('segment', 1000, 1), X'AB')
+  SELECT fts5_decode(fts5_rowid(1000, 1), X'AB')
 } {corrupt}
 
 #-------------------------------------------------------------------------
index 3d956459f944d0beb44987ba72ac47844317e60b..8ed0e12b824ecd2e4f1c6c105233e291255b3d80 100644 (file)
@@ -96,5 +96,35 @@ do_execsql_test 4.1 { SELECT * FROM sqlite_master } {}
 do_execsql_test 4.2 { COMMIT }
 do_execsql_test 4.3 { SELECT * FROM sqlite_master } {}
 
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 5.0 {
+  CREATE VIRTUAL TABLE t1 USING fts5(a);
+  INSERT INTO t1 VALUES('vtable constructor failed');
+} {}
+do_execsql_test 5.1 {
+  SELECT quote(block) FROM t1_data WHERE rowid=10;
+} {
+  X'000000000101010001010101'
+}
+do_test 5.3 {
+  sqlite3 db2 test.db
+  register_fts5_rowid db2
+  db2 eval {
+    UPDATE t1_data SET block = X'000000000101010001A7080101' WHERE rowid=10;
+    UPDATE t1_data SET rowid=fts5_rowid(5000, 1) WHERE rowid=fts5_rowid(1,1);
+    UPDATE t1_idx SET segid=5000 WHERE segid=1;
+  }
+  db2 close
+} {}
+do_execsql_test 5.4 {
+  SELECT rowid FROM t1('cons*');
+} {1}
+breakpoint
+do_execsql_test 5.5 {
+  INSERT INTO t1 VALUES('hello world');
+}
+
 finish_test
 
index 7e92822c33f6bfd3a034bbd702b21054f6e05a52..6344a321c3ecda6adee6a2723be13970889da5a7 100644 (file)
@@ -42,13 +42,13 @@ proc fts5_test_bothlist {cmd} {
 }
 sqlite3_fts5_create_function db fts5_test_bothlist fts5_test_bothlist
 
-proc fts5_rowid {cmd} { expr [$cmd xColumnText -1] }
-sqlite3_fts5_create_function db fts5_rowid fts5_rowid
+proc rowid_func {cmd} { expr [$cmd xColumnText -1] }
+sqlite3_fts5_create_function db rowid_func rowid_func
 
 do_execsql_test 1.$tok.0.1 "
   CREATE VIRTUAL TABLE ss USING fts5(a, b, 
        tokenize='tclnum $tok', detail=%DETAIL%);
-  INSERT INTO ss(ss, rank) VALUES('rank', 'fts5_rowid()');
+  INSERT INTO ss(ss, rank) VALUES('rank', 'rowid_func()');
 "
 
 do_execsql_test 1.$tok.0.2 {
index 7e4d74d114d9ec3690fe816c38d0111869dfbc9c..265b5959a37a83d05ea23f9b4714c0bbb0d2350c 100644 (file)
@@ -59,6 +59,25 @@ do_test 1.7 {
   catchsql { SELECT * FROM t1 WHERE t1 MATCH 'a' }
 } {1 {invalid fts5 file format (found 0, expected 4) - run 'rebuild'}}
 
+#---------------------------------------------------------------------------
+
+do_execsql_test 2.1 {
+  CREATE VIRTUAL TABLE t2 USING fts5(two);
+  INSERT INTO t2 VALUES('a b c d');
+}
+
+do_test 2.2 {
+  sqlite3 db2 test.db
+  execsql {
+    INSERT INTO t2(t2, rank) VALUES('pgsz', 512);
+    UPDATE t2_config SET v=5 WHERE k='version';
+  } db2
+  db2 close
+} {}
+
+do_catchsql_test 2.3 {
+  SELECT * FROM t2('b + c');
+} {1 {SQL logic error or missing database}}
 
 finish_test
 
index 38bb6957dc7c45b848d5f3c1b2a6d3aaf1996895..4fd5b4c81e001d4b02d1b100eb0e867256d9aae6 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sfurther\stests\sfor\ssavepoint\srollback.\sFix\svarious\scode\sissues\sand\sadd\smissing\scomments\sin\sfts5_index.c.
-D 2016-03-28T20:13:25.897
+C Fix\ssome\scode\sand\stest\scoverage\sissues\sin\sfts5_index.c.
+D 2016-03-29T21:19:04.017
 F Makefile.in f53429fb2f313c099283659d0df6f20f932c861f
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc df0bf9ff7f8b3f4dd9fb4cc43f92fe58f6ec5c66
@@ -104,7 +104,7 @@ F ext/fts5/fts5_buffer.c 4c1502d4c956cd092c89ce4480867f9d8bf325cd
 F ext/fts5/fts5_config.c 5af9c360e99669d29f06492c370892394aba0857
 F ext/fts5/fts5_expr.c 5ca4bafe29aa3d27683c90e836192e4aefd20a3f
 F ext/fts5/fts5_hash.c f3a7217c86eb8f272871be5f6aa1b6798960a337
-F ext/fts5/fts5_index.c 48fed29450d8d38b0c9ee5acc97d9360f35d9b01
+F ext/fts5/fts5_index.c 870fa81c7d08dae287f843cddf8809c5665f3f3e
 F ext/fts5/fts5_main.c 1e1e6e2d6df6b224fe9b2c75bcbe265c73b8712d
 F ext/fts5/fts5_storage.c e0aa8509e01eb22ae8e198c1de9c3200755c0d94
 F ext/fts5/fts5_tcl.c f8731e0508299bd43f1a2eff7dbeaac870768966
@@ -116,7 +116,7 @@ F ext/fts5/fts5_varint.c a5aceacda04dafcbae725413d7a16818ecd65738
 F ext/fts5/fts5_vocab.c dba72ca393d71c2588548b51380387f6b44c77a8
 F ext/fts5/fts5parse.y fcc5e92e570d38cab38488b2109cbf67468923b2
 F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
-F ext/fts5/test/fts5_common.tcl b01c584144b5064f30e6c648145a2dd6bc440841
+F ext/fts5/test/fts5_common.tcl 7bae6b2a93bd62be99cc78930c4806afcc152b4b
 F ext/fts5/test/fts5aa.test bd2d88182b9f7f30d300044048ad14683306b745
 F ext/fts5/test/fts5ab.test 30325a89453280160106be411bba3acf138e6d1b
 F ext/fts5/test/fts5ac.test 55cad4275a1f5acabfe14d8442a8046b47e49e5f
@@ -139,7 +139,7 @@ F ext/fts5/test/fts5columnsize.test a8cfef21ffa1c264b9f670a7d94eeaccb5341c07
 F ext/fts5/test/fts5config.test 7788b9c058074d640dfcdd81d97b6a9480000368
 F ext/fts5/test/fts5conflict.test 26f4e46c4d31e16221794832a990dc4e30e18de5
 F ext/fts5/test/fts5content.test 9a952c95518a14182dc3b59e3c8fa71cda82a4e1
-F ext/fts5/test/fts5corrupt.test c2ad090192708150d50d961278df10ae7a4b8b62
+F ext/fts5/test/fts5corrupt.test 0d0dbfa10a134ab710f14b9a25a3922e93e43ac3
 F ext/fts5/test/fts5corrupt2.test 26c0a39dd9ff73207e6229f83b50b21d37c7658c
 F ext/fts5/test/fts5corrupt3.test f77f65e386231daf62902466b40ff998b2c8ce4f
 F ext/fts5/test/fts5detail.test ef5c690535a797413acaf5ad9b8ab5d49972df69
@@ -159,7 +159,7 @@ F ext/fts5/test/fts5fault8.test 6785af34bd1760de74e2824ea9c161965af78f85
 F ext/fts5/test/fts5fault9.test e10e395428a9ea0596ebe752ff7123d16ab78e08
 F ext/fts5/test/fts5faultA.test fa5d59c0ff62b7125cd14eee38ded1c46e15a7ea
 F ext/fts5/test/fts5faultB.test 92ae906284062bf081b6c854afa54dcb1aa9ef88
-F ext/fts5/test/fts5faultC.test 10da76c6b69df05ff9095c7e56dc3a4b8d0e8f20
+F ext/fts5/test/fts5faultC.test c1eaa71dbf832190a797772682ada0c63af88d58
 F ext/fts5/test/fts5full.test 6f6143af0c6700501d9fd597189dfab1555bb741
 F ext/fts5/test/fts5fuzz1.test bece4695fc169b61ab236ada7931c6e4942cbef9
 F ext/fts5/test/fts5hash.test 06f9309ccb4d5050a131594e9e47d0b21456837d
@@ -180,12 +180,12 @@ F ext/fts5/test/fts5query.test f5ec25f5f2fbb70033424113cdffc101b1985a40
 F ext/fts5/test/fts5rank.test 7e9e64eac7245637f6f2033aec4b292aaf611aab
 F ext/fts5/test/fts5rebuild.test 03935f617ace91ed23a6099c7c74d905227ff29b
 F ext/fts5/test/fts5restart.test c17728fdea26e7d0f617d22ad5b4b2862b994c17
-F ext/fts5/test/fts5rowid.test 16908a99d6efc9ba21081b4f2b86b3fc699839a6
+F ext/fts5/test/fts5rowid.test 4092994f3b4b94c071b1866e6854aa705bbc4c1f
 F ext/fts5/test/fts5simple.test f157c8b068f5e68473ad86bfe220b07a84e0209f
 F ext/fts5/test/fts5simple2.test 98377ae1ff7749a42c21fe1a139c1ed312522c46
-F ext/fts5/test/fts5simple3.test 5e00bc009aa0a62190e795383d969b8f31dbde15
+F ext/fts5/test/fts5simple3.test 9508bbfce687d521853a33cb9e096783cfd637b3
 F ext/fts5/test/fts5synonym.test 6475d189c2e20d60795808f83e36bf9318708d48
-F ext/fts5/test/fts5synonym2.test aa4c43bd3b691ff80f658cb064f5ab40690e834e
+F ext/fts5/test/fts5synonym2.test 29335811b8bd6113915b711adbd69b943ce9d301
 F ext/fts5/test/fts5tok1.test beb894c6f3468f10a574302f69ebe4436b0287c7
 F ext/fts5/test/fts5tok2.test dcacb32d4a2a3f0dd3215d4a3987f78ae4be21a2
 F ext/fts5/test/fts5tokenizer.test ea4df698b35cc427ebf2ba22829d0e28386d8c89
@@ -194,7 +194,7 @@ F ext/fts5/test/fts5unicode2.test c1dd890ba32b7609adba78e420faa847abe43b59
 F ext/fts5/test/fts5unicode3.test 35c3d02aa7acf7d43d8de3bfe32c15ba96e8928e
 F ext/fts5/test/fts5unindexed.test e9539d5b78c677315e7ed8ea911d4fd25437c680
 F ext/fts5/test/fts5update.test 57c7012a7919889048947addae10e0613df45529
-F ext/fts5/test/fts5version.test 978f59541d8cef7e8591f8be2115ec5ccb863e2e
+F ext/fts5/test/fts5version.test 64c07b0a3d951db35b7984a051144ac499a01f00
 F ext/fts5/test/fts5vocab.test 480d780aa6b699816c5066225fbd86f3a0239477
 F ext/fts5/tool/fts5speed.tcl b0056f91a55b2d1a3684ec05729de92b042e2f85
 F ext/fts5/tool/fts5txt2db.tcl 526a9979c963f1c54fd50976a05a502e533a4c59
@@ -1460,7 +1460,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 06039d901ad680b8d5abdf31c3799bd971750b5d
-R 4b4e165ed2fcd56f69de6ef3da9c9fa1
+P a805c6f7ea59a74ba3110a058ba6eb9dda8058a7
+R 74c0771422d93ac3c0fc9105cf2a64de
 U dan
-Z 91b6074a9c111559bae6cc0a64059b03
+Z fe5545e02974667cb03b9c402c34ddc2
index 5d5004e44bb096ffc442fce89513b89604bf2006..bf9e810b8b2feafeefb045a5cbbff1acf0ea94fa 100644 (file)
@@ -1 +1 @@
-a805c6f7ea59a74ba3110a058ba6eb9dda8058a7
\ No newline at end of file
+7635c68018ce1656a1c5d6eebaf8f3a8e8839b59
\ No newline at end of file