Index: third_party/sqlite/fts3.patch |
diff --git a/third_party/sqlite/fts3.patch b/third_party/sqlite/fts3.patch |
index 622626a3aa09bd35dd1fe2830e31fc750323a905..07fdb1a6afcb12b295146b5560dd8af558cd8f85 100644 |
--- a/third_party/sqlite/fts3.patch |
+++ b/third_party/sqlite/fts3.patch |
@@ -1,8 +1,8 @@ |
-Index: ext/fts3/fts3.c |
-=================================================================== |
---- ext/fts3/fts3.c (revision 48811) |
-+++ ext/fts3/fts3.c (working copy) |
-@@ -271,6 +271,7 @@ |
+diff --git ext/fts3/fts3.c ext/fts3/fts3.c |
+index 20da051..71e22ae 100644 |
+--- ext/fts3/fts3.c |
++++ ext/fts3/fts3.c |
+@@ -291,6 +291,7 @@ |
** deletions and duplications. This would basically be a forced merge |
** into a single segment. |
*/ |
@@ -10,1884 +10,22 @@ Index: ext/fts3/fts3.c |
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) |
-@@ -313,6 +314,16 @@ |
- # define FTSTRACE(A) |
- #endif |
- |
-+#if 0 |
-+/* Useful to set breakpoints. See main.c sqlite3Corrupt(). */ |
-+static int fts3Corrupt(void){ |
-+ return SQLITE_CORRUPT; |
-+} |
-+# define SQLITE_CORRUPT_BKPT fts3Corrupt() |
-+#else |
-+# define SQLITE_CORRUPT_BKPT SQLITE_CORRUPT |
-+#endif |
-+ |
- /* It is not safe to call isspace(), tolower(), or isalnum() on |
- ** hi-bit-set characters. This is the same solution used in the |
- ** tokenizer. |
-@@ -401,30 +412,41 @@ |
- /* Read a 64-bit variable-length integer from memory starting at p[0]. |
- * Return the number of bytes read, or 0 on error. |
- * The value is stored in *v. */ |
--static int fts3GetVarint(const char *p, sqlite_int64 *v){ |
-+static int fts3GetVarintSafe(const char *p, sqlite_int64 *v, int max){ |
- const unsigned char *q = (const unsigned char *) p; |
- sqlite_uint64 x = 0, y = 1; |
-- while( (*q & 0x80) == 0x80 ){ |
-+ if( max>VARINT_MAX ) max = VARINT_MAX; |
-+ while( max && (*q & 0x80) == 0x80 ){ |
-+ max--; |
- x += y * (*q++ & 0x7f); |
- y <<= 7; |
-- if( q - (unsigned char *)p >= VARINT_MAX ){ /* bad data */ |
-- assert( 0 ); |
-- return 0; |
-- } |
- } |
-+ if( !max ){ |
-+ assert( 0 ); |
-+ return 0; /* tried to read too much; bad data */ |
-+ } |
- x += y * (*q++); |
- *v = (sqlite_int64) x; |
- return (int) (q - (unsigned char *)p); |
- } |
- |
--static int fts3GetVarint32(const char *p, int *pi){ |
-+static int fts3GetVarint(const char *p, sqlite_int64 *v){ |
-+ return fts3GetVarintSafe(p, v, VARINT_MAX); |
-+} |
-+ |
-+static int fts3GetVarint32Safe(const char *p, int *pi, int max){ |
- sqlite_int64 i; |
-- int ret = fts3GetVarint(p, &i); |
-+ int ret = fts3GetVarintSafe(p, &i, max); |
-+ if( !ret ) return ret; |
- *pi = (int) i; |
- assert( *pi==i ); |
- return ret; |
- } |
- |
-+static int fts3GetVarint32(const char* p, int *pi){ |
-+ return fts3GetVarint32Safe(p, pi, VARINT_MAX); |
-+} |
-+ |
- /*******************************************************************/ |
- /* DataBuffer is used to collect data into a buffer in piecemeal |
- ** fashion. It implements the usual distinction between amount of |
-@@ -593,7 +615,7 @@ |
- |
- static int dlrAtEnd(DLReader *pReader){ |
- assert( pReader->nData>=0 ); |
-- return pReader->nData==0; |
-+ return pReader->nData<=0; |
- } |
- static sqlite_int64 dlrDocid(DLReader *pReader){ |
- assert( !dlrAtEnd(pReader) ); |
-@@ -617,7 +639,8 @@ |
- */ |
- static const char *dlrPosData(DLReader *pReader){ |
- sqlite_int64 iDummy; |
-- int n = fts3GetVarint(pReader->pData, &iDummy); |
-+ int n = fts3GetVarintSafe(pReader->pData, &iDummy, pReader->nElement); |
-+ if( !n ) return NULL; |
- assert( !dlrAtEnd(pReader) ); |
- return pReader->pData+n; |
- } |
-@@ -627,7 +650,7 @@ |
- assert( !dlrAtEnd(pReader) ); |
- return pReader->nElement-n; |
- } |
--static void dlrStep(DLReader *pReader){ |
-+static int dlrStep(DLReader *pReader){ |
- assert( !dlrAtEnd(pReader) ); |
- |
- /* Skip past current doclist element. */ |
-@@ -636,32 +659,44 @@ |
- pReader->nData -= pReader->nElement; |
- |
- /* If there is more data, read the next doclist element. */ |
-- if( pReader->nData!=0 ){ |
-+ if( pReader->nData>0 ){ |
- sqlite_int64 iDocidDelta; |
-- int iDummy, n = fts3GetVarint(pReader->pData, &iDocidDelta); |
-+ int nTotal = 0; |
-+ int iDummy, n = fts3GetVarintSafe(pReader->pData, &iDocidDelta, pReader->nData); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ nTotal += n; |
- pReader->iDocid += iDocidDelta; |
- if( pReader->iType>=DL_POSITIONS ){ |
-- assert( n<pReader->nData ); |
- while( 1 ){ |
-- n += fts3GetVarint32(pReader->pData+n, &iDummy); |
-- assert( n<=pReader->nData ); |
-+ n = fts3GetVarint32Safe(pReader->pData+nTotal, &iDummy, pReader->nData-nTotal); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ nTotal += n; |
- if( iDummy==POS_END ) break; |
- if( iDummy==POS_COLUMN ){ |
-- n += fts3GetVarint32(pReader->pData+n, &iDummy); |
-- assert( n<pReader->nData ); |
-+ n = fts3GetVarint32Safe(pReader->pData+nTotal, &iDummy, pReader->nData-nTotal); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ nTotal += n; |
- }else if( pReader->iType==DL_POSITIONS_OFFSETS ){ |
-- n += fts3GetVarint32(pReader->pData+n, &iDummy); |
-- n += fts3GetVarint32(pReader->pData+n, &iDummy); |
-- assert( n<pReader->nData ); |
-+ n = fts3GetVarint32Safe(pReader->pData+nTotal, &iDummy, pReader->nData-nTotal); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ nTotal += n; |
-+ n = fts3GetVarint32Safe(pReader->pData+nTotal, &iDummy, pReader->nData-nTotal); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ nTotal += n; |
- } |
- } |
- } |
-- pReader->nElement = n; |
-+ pReader->nElement = nTotal; |
- assert( pReader->nElement<=pReader->nData ); |
- } |
-+ return SQLITE_OK; |
- } |
--static void dlrInit(DLReader *pReader, DocListType iType, |
-- const char *pData, int nData){ |
-+static void dlrDestroy(DLReader *pReader){ |
-+ SCRAMBLE(pReader); |
-+} |
-+static int dlrInit(DLReader *pReader, DocListType iType, |
-+ const char *pData, int nData){ |
-+ int rc; |
- assert( pData!=NULL && nData!=0 ); |
- pReader->iType = iType; |
- pReader->pData = pData; |
-@@ -670,11 +705,10 @@ |
- pReader->iDocid = 0; |
- |
- /* Load the first element's data. There must be a first element. */ |
-- dlrStep(pReader); |
-+ rc = dlrStep(pReader); |
-+ if( rc!=SQLITE_OK ) dlrDestroy(pReader); |
-+ return rc; |
- } |
--static void dlrDestroy(DLReader *pReader){ |
-- SCRAMBLE(pReader); |
--} |
- |
- #ifndef NDEBUG |
- /* Verify that the doclist can be validly decoded. Also returns the |
-@@ -760,9 +794,9 @@ |
- /* TODO(shess) This has become just a helper for docListMerge. |
- ** Consider a refactor to make this cleaner. |
- */ |
--static void dlwAppend(DLWriter *pWriter, |
-- const char *pData, int nData, |
-- sqlite_int64 iFirstDocid, sqlite_int64 iLastDocid){ |
-+static int dlwAppend(DLWriter *pWriter, |
-+ const char *pData, int nData, |
-+ sqlite_int64 iFirstDocid, sqlite_int64 iLastDocid){ |
- sqlite_int64 iDocid = 0; |
- char c[VARINT_MAX]; |
- int nFirstOld, nFirstNew; /* Old and new varint len of first docid. */ |
-@@ -771,7 +805,8 @@ |
- #endif |
- |
- /* Recode the initial docid as delta from iPrevDocid. */ |
-- nFirstOld = fts3GetVarint(pData, &iDocid); |
-+ nFirstOld = fts3GetVarintSafe(pData, &iDocid, nData); |
-+ if( !nFirstOld ) return SQLITE_CORRUPT_BKPT; |
- assert( nFirstOld<nData || (nFirstOld==nData && pWriter->iType==DL_DOCIDS) ); |
- nFirstNew = fts3PutVarint(c, iFirstDocid-pWriter->iPrevDocid); |
- |
-@@ -792,10 +827,11 @@ |
- dataBufferAppend(pWriter->b, c, nFirstNew); |
- } |
- pWriter->iPrevDocid = iLastDocid; |
-+ return SQLITE_OK; |
- } |
--static void dlwCopy(DLWriter *pWriter, DLReader *pReader){ |
-- dlwAppend(pWriter, dlrDocData(pReader), dlrDocDataBytes(pReader), |
-- dlrDocid(pReader), dlrDocid(pReader)); |
-+static int dlwCopy(DLWriter *pWriter, DLReader *pReader){ |
-+ return dlwAppend(pWriter, dlrDocData(pReader), dlrDocDataBytes(pReader), |
-+ dlrDocid(pReader), dlrDocid(pReader)); |
- } |
- static void dlwAdd(DLWriter *pWriter, sqlite_int64 iDocid){ |
- char c[VARINT_MAX]; |
-@@ -856,45 +892,61 @@ |
- assert( !plrAtEnd(pReader) ); |
- return pReader->iEndOffset; |
- } |
--static void plrStep(PLReader *pReader){ |
-- int i, n; |
-+static int plrStep(PLReader *pReader){ |
-+ int i, n, nTotal = 0; |
- |
- assert( !plrAtEnd(pReader) ); |
- |
-- if( pReader->nData==0 ){ |
-+ if( pReader->nData<=0 ){ |
- pReader->pData = NULL; |
-- return; |
-+ return SQLITE_OK; |
- } |
- |
-- n = fts3GetVarint32(pReader->pData, &i); |
-+ n = fts3GetVarint32Safe(pReader->pData, &i, pReader->nData); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ nTotal += n; |
- if( i==POS_COLUMN ){ |
-- n += fts3GetVarint32(pReader->pData+n, &pReader->iColumn); |
-+ n = fts3GetVarint32Safe(pReader->pData+nTotal, &pReader->iColumn, pReader->nData-nTotal); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ nTotal += n; |
- pReader->iPosition = 0; |
- pReader->iStartOffset = 0; |
-- n += fts3GetVarint32(pReader->pData+n, &i); |
-+ n = fts3GetVarint32Safe(pReader->pData+nTotal, &i, pReader->nData-nTotal); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ nTotal += n; |
- } |
- /* Should never see adjacent column changes. */ |
- assert( i!=POS_COLUMN ); |
- |
- if( i==POS_END ){ |
-+ assert( nTotal<=pReader->nData ); |
- pReader->nData = 0; |
- pReader->pData = NULL; |
-- return; |
-+ return SQLITE_OK; |
- } |
- |
- pReader->iPosition += i-POS_BASE; |
- if( pReader->iType==DL_POSITIONS_OFFSETS ){ |
-- n += fts3GetVarint32(pReader->pData+n, &i); |
-+ n = fts3GetVarint32Safe(pReader->pData+nTotal, &i, pReader->nData-nTotal); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ nTotal += n; |
- pReader->iStartOffset += i; |
-- n += fts3GetVarint32(pReader->pData+n, &i); |
-+ n = fts3GetVarint32Safe(pReader->pData+nTotal, &i, pReader->nData-nTotal); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ nTotal += n; |
- pReader->iEndOffset = pReader->iStartOffset+i; |
- } |
-- assert( n<=pReader->nData ); |
-- pReader->pData += n; |
-- pReader->nData -= n; |
-+ assert( nTotal<=pReader->nData ); |
-+ pReader->pData += nTotal; |
-+ pReader->nData -= nTotal; |
-+ return SQLITE_OK; |
- } |
- |
--static void plrInit(PLReader *pReader, DLReader *pDLReader){ |
-+static void plrDestroy(PLReader *pReader){ |
-+ SCRAMBLE(pReader); |
-+} |
-+static int plrInit(PLReader *pReader, DLReader *pDLReader){ |
-+ int rc; |
- pReader->pData = dlrPosData(pDLReader); |
- pReader->nData = dlrPosDataLen(pDLReader); |
- pReader->iType = pDLReader->iType; |
-@@ -902,11 +954,10 @@ |
- pReader->iPosition = 0; |
- pReader->iStartOffset = 0; |
- pReader->iEndOffset = 0; |
-- plrStep(pReader); |
-+ rc = plrStep(pReader); |
-+ if( rc!=SQLITE_OK ) plrDestroy(pReader); |
-+ return rc; |
- } |
--static void plrDestroy(PLReader *pReader){ |
-- SCRAMBLE(pReader); |
--} |
- |
- /*******************************************************************/ |
- /* PLWriter is used in constructing a document's position list. As a |
-@@ -1091,14 +1142,16 @@ |
- ** deletion will be trimmed, and will thus not effect a deletion |
- ** during the merge. |
- */ |
--static void docListTrim(DocListType iType, const char *pData, int nData, |
-- int iColumn, DocListType iOutType, DataBuffer *out){ |
-+static int docListTrim(DocListType iType, const char *pData, int nData, |
-+ int iColumn, DocListType iOutType, DataBuffer *out){ |
- DLReader dlReader; |
- DLWriter dlWriter; |
-+ int rc; |
- |
- assert( iOutType<=iType ); |
- |
-- dlrInit(&dlReader, iType, pData, nData); |
-+ rc = dlrInit(&dlReader, iType, pData, nData); |
-+ if( rc!=SQLITE_OK ) return rc; |
- dlwInit(&dlWriter, iOutType, out); |
- |
- while( !dlrAtEnd(&dlReader) ){ |
-@@ -1106,7 +1159,8 @@ |
- PLWriter plWriter; |
- int match = 0; |
- |
-- plrInit(&plReader, &dlReader); |
-+ rc = plrInit(&plReader, &dlReader); |
-+ if( rc!=SQLITE_OK ) break; |
- |
- while( !plrAtEnd(&plReader) ){ |
- if( iColumn==-1 || plrColumn(&plReader)==iColumn ){ |
-@@ -1117,7 +1171,11 @@ |
- plwAdd(&plWriter, plrColumn(&plReader), plrPosition(&plReader), |
- plrStartOffset(&plReader), plrEndOffset(&plReader)); |
- } |
-- plrStep(&plReader); |
-+ rc = plrStep(&plReader); |
-+ if( rc!=SQLITE_OK ){ |
-+ plrDestroy(&plReader); |
-+ goto err; |
-+ } |
- } |
- if( match ){ |
- plwTerminate(&plWriter); |
-@@ -1125,10 +1183,13 @@ |
- } |
- |
- plrDestroy(&plReader); |
-- dlrStep(&dlReader); |
-+ rc = dlrStep(&dlReader); |
-+ if( rc!=SQLITE_OK ) break; |
- } |
-+err: |
- dlwDestroy(&dlWriter); |
- dlrDestroy(&dlReader); |
-+ return rc; |
- } |
- |
- /* Used by docListMerge() to keep doclists in the ascending order by |
-@@ -1185,19 +1246,20 @@ |
- /* TODO(shess) nReaders must be <= MERGE_COUNT. This should probably |
- ** be fixed. |
- */ |
--static void docListMerge(DataBuffer *out, |
-- DLReader *pReaders, int nReaders){ |
-+static int docListMerge(DataBuffer *out, |
-+ DLReader *pReaders, int nReaders){ |
- OrderedDLReader readers[MERGE_COUNT]; |
- DLWriter writer; |
- int i, n; |
- const char *pStart = 0; |
- int nStart = 0; |
- sqlite_int64 iFirstDocid = 0, iLastDocid = 0; |
-+ int rc = SQLITE_OK; |
- |
- assert( nReaders>0 ); |
- if( nReaders==1 ){ |
- dataBufferAppend(out, dlrDocData(pReaders), dlrAllDataBytes(pReaders)); |
-- return; |
-+ return SQLITE_OK; |
- } |
- |
- assert( nReaders<=MERGE_COUNT ); |
-@@ -1230,20 +1292,23 @@ |
- nStart += dlrDocDataBytes(readers[0].pReader); |
- }else{ |
- if( pStart!=0 ){ |
-- dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid); |
-+ rc = dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid); |
-+ if( rc!=SQLITE_OK ) goto err; |
- } |
- pStart = dlrDocData(readers[0].pReader); |
- nStart = dlrDocDataBytes(readers[0].pReader); |
- iFirstDocid = iDocid; |
- } |
- iLastDocid = iDocid; |
-- dlrStep(readers[0].pReader); |
-+ rc = dlrStep(readers[0].pReader); |
-+ if( rc!= SQLITE_OK ) goto err; |
- |
- /* Drop all of the older elements with the same docid. */ |
- for(i=1; i<nReaders && |
- !dlrAtEnd(readers[i].pReader) && |
- dlrDocid(readers[i].pReader)==iDocid; i++){ |
-- dlrStep(readers[i].pReader); |
-+ rc = dlrStep(readers[i].pReader); |
-+ if( rc!=SQLITE_OK ) goto err; |
- } |
- |
- /* Get the readers back into order. */ |
-@@ -1253,8 +1318,10 @@ |
- } |
- |
- /* Copy over any remaining elements. */ |
-- if( nStart>0 ) dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid); |
-+ if( nStart>0 ) rc = dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid); |
-+err: |
- dlwDestroy(&writer); |
-+ return rc; |
- } |
- |
- /* Helper function for posListUnion(). Compares the current position |
-@@ -1290,30 +1357,40 @@ |
- ** work with any doclist type, though both inputs and the output |
- ** should be the same type. |
- */ |
--static void posListUnion(DLReader *pLeft, DLReader *pRight, DLWriter *pOut){ |
-+static int posListUnion(DLReader *pLeft, DLReader *pRight, DLWriter *pOut){ |
- PLReader left, right; |
- PLWriter writer; |
-+ int rc; |
- |
- assert( dlrDocid(pLeft)==dlrDocid(pRight) ); |
- assert( pLeft->iType==pRight->iType ); |
- assert( pLeft->iType==pOut->iType ); |
- |
-- plrInit(&left, pLeft); |
-- plrInit(&right, pRight); |
-+ rc = plrInit(&left, pLeft); |
-+ if( rc!=SQLITE_OK ) return rc; |
-+ rc = plrInit(&right, pRight); |
-+ if( rc!=SQLITE_OK ){ |
-+ plrDestroy(&left); |
-+ return rc; |
-+ } |
- plwInit(&writer, pOut, dlrDocid(pLeft)); |
- |
- while( !plrAtEnd(&left) || !plrAtEnd(&right) ){ |
- int c = posListCmp(&left, &right); |
- if( c<0 ){ |
- plwCopy(&writer, &left); |
-- plrStep(&left); |
-+ rc = plrStep(&left); |
-+ if( rc!=SQLITE_OK ) break; |
- }else if( c>0 ){ |
- plwCopy(&writer, &right); |
-- plrStep(&right); |
-+ rc = plrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- }else{ |
- plwCopy(&writer, &left); |
-- plrStep(&left); |
-- plrStep(&right); |
-+ rc = plrStep(&left); |
-+ if( rc!=SQLITE_OK ) break; |
-+ rc = plrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- } |
- } |
- |
-@@ -1321,56 +1398,75 @@ |
- plwDestroy(&writer); |
- plrDestroy(&left); |
- plrDestroy(&right); |
-+ return rc; |
- } |
- |
- /* Write the union of doclists in pLeft and pRight to pOut. For |
- ** docids in common between the inputs, the union of the position |
- ** lists is written. Inputs and outputs are always type DL_DEFAULT. |
- */ |
--static void docListUnion( |
-+static int docListUnion( |
- const char *pLeft, int nLeft, |
- const char *pRight, int nRight, |
- DataBuffer *pOut /* Write the combined doclist here */ |
- ){ |
- DLReader left, right; |
- DLWriter writer; |
-+ int rc; |
- |
- if( nLeft==0 ){ |
- if( nRight!=0) dataBufferAppend(pOut, pRight, nRight); |
-- return; |
-+ return SQLITE_OK; |
- } |
- if( nRight==0 ){ |
- dataBufferAppend(pOut, pLeft, nLeft); |
-- return; |
-+ return SQLITE_OK; |
- } |
- |
-- dlrInit(&left, DL_DEFAULT, pLeft, nLeft); |
-- dlrInit(&right, DL_DEFAULT, pRight, nRight); |
-+ rc = dlrInit(&left, DL_DEFAULT, pLeft, nLeft); |
-+ if( rc!=SQLITE_OK ) return rc; |
-+ rc = dlrInit(&right, DL_DEFAULT, pRight, nRight); |
-+ if( rc!=SQLITE_OK){ |
-+ dlrDestroy(&left); |
-+ return rc; |
-+ } |
- dlwInit(&writer, DL_DEFAULT, pOut); |
- |
- while( !dlrAtEnd(&left) || !dlrAtEnd(&right) ){ |
- if( dlrAtEnd(&right) ){ |
-- dlwCopy(&writer, &left); |
-- dlrStep(&left); |
-+ rc = dlwCopy(&writer, &left); |
-+ if( rc!=SQLITE_OK) break; |
-+ rc = dlrStep(&left); |
-+ if( rc!=SQLITE_OK) break; |
- }else if( dlrAtEnd(&left) ){ |
-- dlwCopy(&writer, &right); |
-- dlrStep(&right); |
-+ rc = dlwCopy(&writer, &right); |
-+ if( rc!=SQLITE_OK ) break; |
-+ rc = dlrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- }else if( dlrDocid(&left)<dlrDocid(&right) ){ |
-- dlwCopy(&writer, &left); |
-- dlrStep(&left); |
-+ rc = dlwCopy(&writer, &left); |
-+ if( rc!=SQLITE_OK ) break; |
-+ rc = dlrStep(&left); |
-+ if( rc!=SQLITE_OK ) break; |
- }else if( dlrDocid(&left)>dlrDocid(&right) ){ |
-- dlwCopy(&writer, &right); |
-- dlrStep(&right); |
-+ rc = dlwCopy(&writer, &right); |
-+ if( rc!=SQLITE_OK ) break; |
-+ rc = dlrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- }else{ |
-- posListUnion(&left, &right, &writer); |
-- dlrStep(&left); |
-- dlrStep(&right); |
-+ rc = posListUnion(&left, &right, &writer); |
-+ if( rc!=SQLITE_OK ) break; |
-+ rc = dlrStep(&left); |
-+ if( rc!=SQLITE_OK ) break; |
-+ rc = dlrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- } |
- } |
- |
- dlrDestroy(&left); |
- dlrDestroy(&right); |
- dlwDestroy(&writer); |
-+ return rc; |
- } |
- |
- /* |
-@@ -1394,7 +1490,7 @@ |
- ** from pLeft instead of pRight. In the example above, the positions "5" |
- ** and "20" would be added instead of "6" and "21". |
- */ |
--static void posListPhraseMerge( |
-+static int posListPhraseMerge( |
- DLReader *pLeft, |
- DLReader *pRight, |
- int nNear, |
-@@ -1404,20 +1500,29 @@ |
- PLReader left, right; |
- PLWriter writer; |
- int match = 0; |
-+ int rc; |
- |
- assert( dlrDocid(pLeft)==dlrDocid(pRight) ); |
- assert( pOut->iType!=DL_POSITIONS_OFFSETS ); |
- |
-- plrInit(&left, pLeft); |
-- plrInit(&right, pRight); |
-+ rc = plrInit(&left, pLeft); |
-+ if( rc!=SQLITE_OK ) return rc; |
-+ rc = plrInit(&right, pRight); |
-+ if( rc!=SQLITE_OK ){ |
-+ plrDestroy(&left); |
-+ return rc; |
-+ } |
- |
- while( !plrAtEnd(&left) && !plrAtEnd(&right) ){ |
- if( plrColumn(&left)<plrColumn(&right) ){ |
-- plrStep(&left); |
-+ rc = plrStep(&left); |
-+ if( rc!=SQLITE_OK ) break; |
- }else if( plrColumn(&left)>plrColumn(&right) ){ |
-- plrStep(&right); |
-+ rc = plrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- }else if( plrPosition(&left)>=plrPosition(&right) ){ |
-- plrStep(&right); |
-+ rc = plrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- }else{ |
- if( (plrPosition(&right)-plrPosition(&left))<=(nNear+1) ){ |
- if( !match ){ |
-@@ -1429,9 +1534,11 @@ |
- }else{ |
- plwAdd(&writer, plrColumn(&left), plrPosition(&left), 0, 0); |
- } |
-- plrStep(&right); |
-+ rc = plrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- }else{ |
-- plrStep(&left); |
-+ rc = plrStep(&left); |
-+ if( rc!=SQLITE_OK ) break; |
- } |
- } |
- } |
-@@ -1443,6 +1550,7 @@ |
- |
- plrDestroy(&left); |
- plrDestroy(&right); |
-+ return rc; |
- } |
- |
- /* |
-@@ -1496,7 +1604,7 @@ |
- ** iType controls the type of data written to pOut. If iType is |
- ** DL_POSITIONS, the positions are those from pRight. |
- */ |
--static void docListPhraseMerge( |
-+static int docListPhraseMerge( |
- const char *pLeft, int nLeft, |
- const char *pRight, int nRight, |
- int nNear, /* 0 for a phrase merge, non-zero for a NEAR merge */ |
-@@ -1506,43 +1614,63 @@ |
- ){ |
- DLReader left, right; |
- DLWriter writer; |
-+ int rc; |
- |
-- if( nLeft==0 || nRight==0 ) return; |
-+ /* These two buffers are used in the 'while', but are declared here |
-+ ** to simplify error-handling. |
-+ */ |
-+ DataBuffer one = {0, 0, 0}; |
-+ DataBuffer two = {0, 0, 0}; |
- |
-+ if( nLeft==0 || nRight==0 ) return SQLITE_OK; |
-+ |
- assert( iType!=DL_POSITIONS_OFFSETS ); |
- |
-- dlrInit(&left, DL_POSITIONS, pLeft, nLeft); |
-- dlrInit(&right, DL_POSITIONS, pRight, nRight); |
-+ rc = dlrInit(&left, DL_POSITIONS, pLeft, nLeft); |
-+ if( rc!=SQLITE_OK ) return rc; |
-+ rc = dlrInit(&right, DL_POSITIONS, pRight, nRight); |
-+ if( rc!=SQLITE_OK ){ |
-+ dlrDestroy(&left); |
-+ return rc; |
-+ } |
- dlwInit(&writer, iType, pOut); |
- |
- while( !dlrAtEnd(&left) && !dlrAtEnd(&right) ){ |
- if( dlrDocid(&left)<dlrDocid(&right) ){ |
-- dlrStep(&left); |
-+ rc = dlrStep(&left); |
-+ if( rc!=SQLITE_OK ) goto err; |
- }else if( dlrDocid(&right)<dlrDocid(&left) ){ |
-- dlrStep(&right); |
-+ rc = dlrStep(&right); |
-+ if( rc!=SQLITE_OK ) goto err; |
- }else{ |
- if( nNear==0 ){ |
-- posListPhraseMerge(&left, &right, 0, 0, &writer); |
-+ rc = posListPhraseMerge(&left, &right, 0, 0, &writer); |
-+ if( rc!=SQLITE_OK ) goto err; |
- }else{ |
- /* This case occurs when two terms (simple terms or phrases) are |
- * connected by a NEAR operator, span (nNear+1). i.e. |
- * |
- * '"terrible company" NEAR widget' |
- */ |
-- DataBuffer one = {0, 0, 0}; |
-- DataBuffer two = {0, 0, 0}; |
-- |
- DLWriter dlwriter2; |
-- DLReader dr1 = {0, 0, 0, 0, 0}; |
-+ DLReader dr1 = {0, 0, 0, 0, 0}; |
- DLReader dr2 = {0, 0, 0, 0, 0}; |
- |
- dlwInit(&dlwriter2, iType, &one); |
-- posListPhraseMerge(&right, &left, nNear-3+nPhrase, 1, &dlwriter2); |
-+ rc = posListPhraseMerge(&right, &left, nNear-3+nPhrase, 1, &dlwriter2); |
-+ if( rc!=SQLITE_OK ) goto err; |
- dlwInit(&dlwriter2, iType, &two); |
-- posListPhraseMerge(&left, &right, nNear-1, 0, &dlwriter2); |
-+ rc = posListPhraseMerge(&left, &right, nNear-1, 0, &dlwriter2); |
-+ if( rc!=SQLITE_OK ) goto err; |
- |
-- if( one.nData) dlrInit(&dr1, iType, one.pData, one.nData); |
-- if( two.nData) dlrInit(&dr2, iType, two.pData, two.nData); |
-+ if( one.nData){ |
-+ rc = dlrInit(&dr1, iType, one.pData, one.nData); |
-+ if( rc!=SQLITE_OK ) goto err; |
-+ } |
-+ if( two.nData){ |
-+ rc = dlrInit(&dr2, iType, two.pData, two.nData); |
-+ if( rc!=SQLITE_OK ) goto err; |
-+ } |
- |
- if( !dlrAtEnd(&dr1) || !dlrAtEnd(&dr2) ){ |
- PLReader pr1 = {0}; |
-@@ -1551,161 +1679,211 @@ |
- PLWriter plwriter; |
- plwInit(&plwriter, &writer, dlrDocid(dlrAtEnd(&dr1)?&dr2:&dr1)); |
- |
-- if( one.nData ) plrInit(&pr1, &dr1); |
-- if( two.nData ) plrInit(&pr2, &dr2); |
-+ if( one.nData ){ |
-+ rc = plrInit(&pr1, &dr1); |
-+ if( rc!=SQLITE_OK ) goto err; |
-+ } |
-+ if( two.nData ){ |
-+ rc = plrInit(&pr2, &dr2); |
-+ if( rc!=SQLITE_OK ) goto err; |
-+ } |
- while( !plrAtEnd(&pr1) || !plrAtEnd(&pr2) ){ |
- int iCompare = plrCompare(&pr1, &pr2); |
- switch( iCompare ){ |
- case -1: |
- plwCopy(&plwriter, &pr1); |
-- plrStep(&pr1); |
-+ rc = plrStep(&pr1); |
-+ if( rc!=SQLITE_OK ) goto err; |
- break; |
- case 1: |
- plwCopy(&plwriter, &pr2); |
-- plrStep(&pr2); |
-+ rc = plrStep(&pr2); |
-+ if( rc!=SQLITE_OK ) goto err; |
- break; |
- case 0: |
- plwCopy(&plwriter, &pr1); |
-- plrStep(&pr1); |
-- plrStep(&pr2); |
-+ rc = plrStep(&pr1); |
-+ if( rc!=SQLITE_OK ) goto err; |
-+ rc = plrStep(&pr2); |
-+ if( rc!=SQLITE_OK ) goto err; |
- break; |
- } |
- } |
- plwTerminate(&plwriter); |
- } |
-- dataBufferDestroy(&one); |
-- dataBufferDestroy(&two); |
-+ dataBufferReset(&one); |
-+ dataBufferReset(&two); |
- } |
-- dlrStep(&left); |
-- dlrStep(&right); |
-+ rc = dlrStep(&left); |
-+ if( rc!=SQLITE_OK ) goto err; |
-+ rc = dlrStep(&right); |
-+ if( rc!=SQLITE_OK ) goto err; |
- } |
- } |
- |
-+err: |
-+ dataBufferDestroy(&one); |
-+ dataBufferDestroy(&two); |
- dlrDestroy(&left); |
- dlrDestroy(&right); |
- dlwDestroy(&writer); |
-+ return rc; |
- } |
- |
- /* We have two DL_DOCIDS doclists: pLeft and pRight. |
- ** Write the intersection of these two doclists into pOut as a |
- ** DL_DOCIDS doclist. |
- */ |
--static void docListAndMerge( |
-+static int docListAndMerge( |
- const char *pLeft, int nLeft, |
- const char *pRight, int nRight, |
- DataBuffer *pOut /* Write the combined doclist here */ |
- ){ |
- DLReader left, right; |
- DLWriter writer; |
-+ int rc; |
- |
-- if( nLeft==0 || nRight==0 ) return; |
-+ if( nLeft==0 || nRight==0 ) return SQLITE_OK; |
- |
-- dlrInit(&left, DL_DOCIDS, pLeft, nLeft); |
-- dlrInit(&right, DL_DOCIDS, pRight, nRight); |
-+ rc = dlrInit(&left, DL_DOCIDS, pLeft, nLeft); |
-+ if( rc!=SQLITE_OK ) return rc; |
-+ rc = dlrInit(&right, DL_DOCIDS, pRight, nRight); |
-+ if( rc!=SQLITE_OK ){ |
-+ dlrDestroy(&left); |
-+ return rc; |
-+ } |
- dlwInit(&writer, DL_DOCIDS, pOut); |
- |
- while( !dlrAtEnd(&left) && !dlrAtEnd(&right) ){ |
- if( dlrDocid(&left)<dlrDocid(&right) ){ |
-- dlrStep(&left); |
-+ rc = dlrStep(&left); |
-+ if( rc!=SQLITE_OK ) break; |
- }else if( dlrDocid(&right)<dlrDocid(&left) ){ |
-- dlrStep(&right); |
-+ rc = dlrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- }else{ |
- dlwAdd(&writer, dlrDocid(&left)); |
-- dlrStep(&left); |
-- dlrStep(&right); |
-+ rc = dlrStep(&left); |
-+ if( rc!=SQLITE_OK ) break; |
-+ rc = dlrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- } |
- } |
- |
- dlrDestroy(&left); |
- dlrDestroy(&right); |
- dlwDestroy(&writer); |
-+ return rc; |
- } |
- |
- /* We have two DL_DOCIDS doclists: pLeft and pRight. |
- ** Write the union of these two doclists into pOut as a |
- ** DL_DOCIDS doclist. |
- */ |
--static void docListOrMerge( |
-+static int docListOrMerge( |
- const char *pLeft, int nLeft, |
- const char *pRight, int nRight, |
- DataBuffer *pOut /* Write the combined doclist here */ |
- ){ |
- DLReader left, right; |
- DLWriter writer; |
-+ int rc; |
- |
- if( nLeft==0 ){ |
- if( nRight!=0 ) dataBufferAppend(pOut, pRight, nRight); |
-- return; |
-+ return SQLITE_OK; |
- } |
- if( nRight==0 ){ |
- dataBufferAppend(pOut, pLeft, nLeft); |
-- return; |
-+ return SQLITE_OK; |
- } |
- |
-- dlrInit(&left, DL_DOCIDS, pLeft, nLeft); |
-- dlrInit(&right, DL_DOCIDS, pRight, nRight); |
-+ rc = dlrInit(&left, DL_DOCIDS, pLeft, nLeft); |
-+ if( rc!=SQLITE_OK ) return rc; |
-+ rc = dlrInit(&right, DL_DOCIDS, pRight, nRight); |
-+ if( rc!=SQLITE_OK ){ |
-+ dlrDestroy(&left); |
-+ return rc; |
-+ } |
- dlwInit(&writer, DL_DOCIDS, pOut); |
- |
- while( !dlrAtEnd(&left) || !dlrAtEnd(&right) ){ |
- if( dlrAtEnd(&right) ){ |
- dlwAdd(&writer, dlrDocid(&left)); |
-- dlrStep(&left); |
-+ rc = dlrStep(&left); |
-+ if( rc!=SQLITE_OK ) break; |
- }else if( dlrAtEnd(&left) ){ |
- dlwAdd(&writer, dlrDocid(&right)); |
-- dlrStep(&right); |
-+ rc = dlrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- }else if( dlrDocid(&left)<dlrDocid(&right) ){ |
- dlwAdd(&writer, dlrDocid(&left)); |
-- dlrStep(&left); |
-+ rc = dlrStep(&left); |
-+ if( rc!=SQLITE_OK ) break; |
- }else if( dlrDocid(&right)<dlrDocid(&left) ){ |
- dlwAdd(&writer, dlrDocid(&right)); |
-- dlrStep(&right); |
-+ rc = dlrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- }else{ |
- dlwAdd(&writer, dlrDocid(&left)); |
-- dlrStep(&left); |
-- dlrStep(&right); |
-+ rc = dlrStep(&left); |
-+ if( rc!=SQLITE_OK ) break; |
-+ rc = dlrStep(&right); |
-+ if( rc!=SQLITE_OK ) break; |
- } |
- } |
- |
- dlrDestroy(&left); |
- dlrDestroy(&right); |
- dlwDestroy(&writer); |
-+ return rc; |
- } |
- |
- /* We have two DL_DOCIDS doclists: pLeft and pRight. |
- ** Write into pOut as DL_DOCIDS doclist containing all documents that |
- ** occur in pLeft but not in pRight. |
- */ |
--static void docListExceptMerge( |
-+static int docListExceptMerge( |
- const char *pLeft, int nLeft, |
- const char *pRight, int nRight, |
- DataBuffer *pOut /* Write the combined doclist here */ |
- ){ |
- DLReader left, right; |
- DLWriter writer; |
-+ int rc; |
- |
-- if( nLeft==0 ) return; |
-+ if( nLeft==0 ) return SQLITE_OK; |
- if( nRight==0 ){ |
- dataBufferAppend(pOut, pLeft, nLeft); |
-- return; |
-+ return SQLITE_OK; |
- } |
- |
-- dlrInit(&left, DL_DOCIDS, pLeft, nLeft); |
-- dlrInit(&right, DL_DOCIDS, pRight, nRight); |
-+ rc = dlrInit(&left, DL_DOCIDS, pLeft, nLeft); |
-+ if( rc!=SQLITE_OK ) return rc; |
-+ rc = dlrInit(&right, DL_DOCIDS, pRight, nRight); |
-+ if( rc!=SQLITE_OK ){ |
-+ dlrDestroy(&left); |
-+ return rc; |
-+ } |
- dlwInit(&writer, DL_DOCIDS, pOut); |
- |
- while( !dlrAtEnd(&left) ){ |
- while( !dlrAtEnd(&right) && dlrDocid(&right)<dlrDocid(&left) ){ |
-- dlrStep(&right); |
-+ rc = dlrStep(&right); |
-+ if( rc!=SQLITE_OK ) goto err; |
- } |
- if( dlrAtEnd(&right) || dlrDocid(&left)<dlrDocid(&right) ){ |
- dlwAdd(&writer, dlrDocid(&left)); |
- } |
-- dlrStep(&left); |
-+ rc = dlrStep(&left); |
-+ if( rc!=SQLITE_OK ) break; |
- } |
- |
-+err: |
- dlrDestroy(&left); |
- dlrDestroy(&right); |
- dlwDestroy(&writer); |
-+ return rc; |
- } |
- |
- static char *string_dup_n(const char *s, int n){ |
-@@ -1858,7 +2036,7 @@ |
- /* SEGDIR_MAX_INDEX */ "select max(idx) from %_segdir where level = ?", |
- /* SEGDIR_SET */ "insert into %_segdir values (?, ?, ?, ?, ?, ?)", |
- /* SEGDIR_SELECT_LEVEL */ |
-- "select start_block, leaves_end_block, root from %_segdir " |
-+ "select start_block, leaves_end_block, root, idx from %_segdir " |
- " where level = ? order by idx", |
- /* SEGDIR_SPAN */ |
- "select min(start_block), max(end_block) from %_segdir " |
-@@ -3680,16 +3858,19 @@ |
- return SQLITE_OK; |
- } |
- rc = sqlite3_bind_int64(c->pStmt, 1, dlrDocid(&c->reader)); |
-- dlrStep(&c->reader); |
- if( rc!=SQLITE_OK ) return rc; |
-+ rc = dlrStep(&c->reader); |
-+ if( rc!=SQLITE_OK ) return rc; |
- /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */ |
- rc = sqlite3_step(c->pStmt); |
- if( rc==SQLITE_ROW ){ /* the case we expect */ |
- c->eof = 0; |
- return SQLITE_OK; |
- } |
-- /* an error occurred; abort */ |
-- return rc==SQLITE_DONE ? SQLITE_ERROR : rc; |
-+ /* Corrupt if the index refers to missing document. */ |
-+ if( rc==SQLITE_DONE ) return SQLITE_CORRUPT_BKPT; |
-+ |
-+ return rc; |
- } |
- } |
- |
-@@ -3739,11 +3920,12 @@ |
- if( ii==(pPhrase->nToken-1) ){ |
- eType = eListType; |
- } |
-- docListPhraseMerge( |
-+ rc = docListPhraseMerge( |
- res.pData, res.nData, tmp.pData, tmp.nData, 0, 0, eType, pResult |
- ); |
- dataBufferDestroy(&res); |
- dataBufferDestroy(&tmp); |
-+ if( rc!= SQLITE_OK ) return rc; |
- } |
- } |
- } |
-@@ -3798,21 +3980,21 @@ |
- assert( pExpr->pRight->eType==FTSQUERY_PHRASE ); |
- assert( pLeft->eType==FTSQUERY_PHRASE ); |
- nToken = pLeft->pPhrase->nToken + pExpr->pRight->pPhrase->nToken; |
-- docListPhraseMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData, |
-+ rc = docListPhraseMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData, |
- pExpr->nNear+1, nToken, eType, pRes |
- ); |
- break; |
- } |
- case FTSQUERY_NOT: { |
-- docListExceptMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData,pRes); |
-+ rc = docListExceptMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData,pRes); |
- break; |
- } |
- case FTSQUERY_AND: { |
-- docListAndMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData, pRes); |
-+ rc = docListAndMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData, pRes); |
- break; |
- } |
- case FTSQUERY_OR: { |
-- docListOrMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData, pRes); |
-+ rc = docListOrMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData, pRes); |
- break; |
- } |
- } |
-@@ -4469,22 +4651,19 @@ |
- SCRAMBLE(pReader); |
- } |
- |
--/* TODO(shess) The assertions are great, but what if we're in NDEBUG |
--** and the blob is empty or otherwise contains suspect data? |
--*/ |
--static void interiorReaderInit(const char *pData, int nData, |
-- InteriorReader *pReader){ |
-+static int interiorReaderInit(const char *pData, int nData, |
-+ InteriorReader *pReader){ |
- int n, nTerm; |
- |
-- /* Require at least the leading flag byte */ |
-+ /* These conditions are checked and met by the callers. */ |
- assert( nData>0 ); |
- assert( pData[0]!='\0' ); |
- |
- CLEAR(pReader); |
- |
- /* Decode the base blockid, and set the cursor to the first term. */ |
-- n = fts3GetVarint(pData+1, &pReader->iBlockid); |
-- assert( 1+n<=nData ); |
-+ n = fts3GetVarintSafe(pData+1, &pReader->iBlockid, nData-1); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
- pReader->pData = pData+1+n; |
- pReader->nData = nData-(1+n); |
- |
-@@ -4495,17 +4674,18 @@ |
- if( pReader->nData==0 ){ |
- dataBufferInit(&pReader->term, 0); |
- }else{ |
-- n = fts3GetVarint32(pReader->pData, &nTerm); |
-+ n = fts3GetVarint32Safe(pReader->pData, &nTerm, pReader->nData); |
-+ if( !n || nTerm<0 || nTerm>pReader->nData-n) return SQLITE_CORRUPT_BKPT; |
- dataBufferInit(&pReader->term, nTerm); |
- dataBufferReplace(&pReader->term, pReader->pData+n, nTerm); |
-- assert( n+nTerm<=pReader->nData ); |
- pReader->pData += n+nTerm; |
- pReader->nData -= n+nTerm; |
- } |
-+ return SQLITE_OK; |
- } |
- |
- static int interiorReaderAtEnd(InteriorReader *pReader){ |
-- return pReader->term.nData==0; |
-+ return pReader->term.nData<=0; |
- } |
- |
- static sqlite_int64 interiorReaderCurrentBlockid(InteriorReader *pReader){ |
-@@ -4522,7 +4702,7 @@ |
- } |
- |
- /* Step forward to the next term in the node. */ |
--static void interiorReaderStep(InteriorReader *pReader){ |
-+static int interiorReaderStep(InteriorReader *pReader){ |
- assert( !interiorReaderAtEnd(pReader) ); |
- |
- /* If the last term has been read, signal eof, else construct the |
-@@ -4533,18 +4713,26 @@ |
- }else{ |
- int n, nPrefix, nSuffix; |
- |
-- n = fts3GetVarint32(pReader->pData, &nPrefix); |
-- n += fts3GetVarint32(pReader->pData+n, &nSuffix); |
-+ n = fts3GetVarint32Safe(pReader->pData, &nPrefix, pReader->nData); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ pReader->nData -= n; |
-+ pReader->pData += n; |
-+ n = fts3GetVarint32Safe(pReader->pData, &nSuffix, pReader->nData); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ pReader->nData -= n; |
-+ pReader->pData += n; |
-+ if( nSuffix<0 || nSuffix>pReader->nData ) return SQLITE_CORRUPT_BKPT; |
-+ if( nPrefix<0 || nPrefix>pReader->term.nData ) return SQLITE_CORRUPT_BKPT; |
- |
- /* Truncate the current term and append suffix data. */ |
- pReader->term.nData = nPrefix; |
-- dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix); |
-+ dataBufferAppend(&pReader->term, pReader->pData, nSuffix); |
- |
-- assert( n+nSuffix<=pReader->nData ); |
-- pReader->pData += n+nSuffix; |
-- pReader->nData -= n+nSuffix; |
-+ pReader->pData += nSuffix; |
-+ pReader->nData -= nSuffix; |
- } |
- pReader->iBlockid++; |
-+ return SQLITE_OK; |
- } |
- |
- /* Compare the current term to pTerm[nTerm], returning strcmp-style |
-@@ -4916,7 +5104,8 @@ |
- n = fts3PutVarint(c, nData); |
- dataBufferAppend(&pWriter->data, c, n); |
- |
-- docListMerge(&pWriter->data, pReaders, nReaders); |
-+ rc = docListMerge(&pWriter->data, pReaders, nReaders); |
-+ if( rc!=SQLITE_OK ) return rc; |
- ASSERT_VALID_DOCLIST(DL_DEFAULT, |
- pWriter->data.pData+iDoclistData+n, |
- pWriter->data.nData-iDoclistData-n, NULL); |
-@@ -5026,7 +5215,8 @@ |
- int rc; |
- DLReader reader; |
- |
-- dlrInit(&reader, DL_DEFAULT, pData, nData); |
-+ rc = dlrInit(&reader, DL_DEFAULT, pData, nData); |
-+ if( rc!=SQLITE_OK ) return rc; |
- rc = leafWriterStepMerge(v, pWriter, pTerm, nTerm, &reader, 1); |
- dlrDestroy(&reader); |
- |
-@@ -5071,38 +5261,41 @@ |
- static const char *leafReaderData(LeafReader *pReader){ |
- int n, nData; |
- assert( pReader->term.nData>0 ); |
-- n = fts3GetVarint32(pReader->pData, &nData); |
-+ n = fts3GetVarint32Safe(pReader->pData, &nData, pReader->nData); |
-+ if( !n || nData>pReader->nData-n ) return NULL; |
- return pReader->pData+n; |
- } |
- |
--static void leafReaderInit(const char *pData, int nData, |
-- LeafReader *pReader){ |
-+static int leafReaderInit(const char *pData, int nData, |
-+ LeafReader *pReader){ |
- int nTerm, n; |
- |
-+ /* All callers check this precondition. */ |
- assert( nData>0 ); |
- assert( pData[0]=='\0' ); |
- |
- CLEAR(pReader); |
- |
- /* Read the first term, skipping the header byte. */ |
-- n = fts3GetVarint32(pData+1, &nTerm); |
-+ n = fts3GetVarint32Safe(pData+1, &nTerm, nData-1); |
-+ if( !n || nTerm<0 || nTerm>nData-1-n ) return SQLITE_CORRUPT_BKPT; |
- dataBufferInit(&pReader->term, nTerm); |
- dataBufferReplace(&pReader->term, pData+1+n, nTerm); |
- |
- /* Position after the first term. */ |
-- assert( 1+n+nTerm<nData ); |
- pReader->pData = pData+1+n+nTerm; |
- pReader->nData = nData-1-n-nTerm; |
-+ return SQLITE_OK; |
- } |
- |
- /* Step the reader forward to the next term. */ |
--static void leafReaderStep(LeafReader *pReader){ |
-+static int leafReaderStep(LeafReader *pReader){ |
- int n, nData, nPrefix, nSuffix; |
- assert( !leafReaderAtEnd(pReader) ); |
- |
- /* Skip previous entry's data block. */ |
-- n = fts3GetVarint32(pReader->pData, &nData); |
-- assert( n+nData<=pReader->nData ); |
-+ n = fts3GetVarint32Safe(pReader->pData, &nData, pReader->nData); |
-+ if( !n || nData<0 || nData>pReader->nData-n ) return SQLITE_CORRUPT_BKPT; |
- pReader->pData += n+nData; |
- pReader->nData -= n+nData; |
- |
-@@ -5110,15 +5303,23 @@ |
- /* Construct the new term using a prefix from the old term plus a |
- ** suffix from the leaf data. |
- */ |
-- n = fts3GetVarint32(pReader->pData, &nPrefix); |
-- n += fts3GetVarint32(pReader->pData+n, &nSuffix); |
-- assert( n+nSuffix<pReader->nData ); |
-+ n = fts3GetVarint32Safe(pReader->pData, &nPrefix, pReader->nData); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ pReader->nData -= n; |
-+ pReader->pData += n; |
-+ n = fts3GetVarint32Safe(pReader->pData, &nSuffix, pReader->nData); |
-+ if( !n ) return SQLITE_CORRUPT_BKPT; |
-+ pReader->nData -= n; |
-+ pReader->pData += n; |
-+ if( nSuffix<0 || nSuffix>pReader->nData ) return SQLITE_CORRUPT_BKPT; |
-+ if( nPrefix<0 || nPrefix>pReader->term.nData ) return SQLITE_CORRUPT_BKPT; |
- pReader->term.nData = nPrefix; |
-- dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix); |
-+ dataBufferAppend(&pReader->term, pReader->pData, nSuffix); |
- |
-- pReader->pData += n+nSuffix; |
-- pReader->nData -= n+nSuffix; |
-+ pReader->pData += nSuffix; |
-+ pReader->nData -= nSuffix; |
- } |
-+ return SQLITE_OK; |
- } |
- |
- /* strcmp-style comparison of pReader's current term against pTerm. |
-@@ -5222,32 +5423,67 @@ |
- |
- dataBufferInit(&pReader->rootData, 0); |
- if( iStartBlockid==0 ){ |
-+ int rc; |
-+ /* Corrupt if this can't be a leaf node. */ |
-+ if( pRootData==NULL || nRootData<1 || pRootData[0]!='\0' ){ |
-+ return SQLITE_CORRUPT_BKPT; |
-+ } |
- /* Entire leaf level fit in root data. */ |
- dataBufferReplace(&pReader->rootData, pRootData, nRootData); |
-- leafReaderInit(pReader->rootData.pData, pReader->rootData.nData, |
-- &pReader->leafReader); |
-+ rc = leafReaderInit(pReader->rootData.pData, pReader->rootData.nData, |
-+ &pReader->leafReader); |
-+ if( rc!=SQLITE_OK ){ |
-+ dataBufferDestroy(&pReader->rootData); |
-+ return rc; |
-+ } |
- }else{ |
- sqlite3_stmt *s; |
- int rc = sql_get_leaf_statement(v, idx, &s); |
- if( rc!=SQLITE_OK ) return rc; |
- |
- rc = sqlite3_bind_int64(s, 1, iStartBlockid); |
-- if( rc!=SQLITE_OK ) return rc; |
-+ if( rc!=SQLITE_OK ) goto err; |
- |
- rc = sqlite3_bind_int64(s, 2, iEndBlockid); |
-- if( rc!=SQLITE_OK ) return rc; |
-+ if( rc!=SQLITE_OK ) goto err; |
- |
- rc = sqlite3_step(s); |
-+ |
-+ /* Corrupt if interior node referenced missing leaf node. */ |
- if( rc==SQLITE_DONE ){ |
-- pReader->eof = 1; |
-- return SQLITE_OK; |
-+ rc = SQLITE_CORRUPT_BKPT; |
-+ goto err; |
- } |
-- if( rc!=SQLITE_ROW ) return rc; |
- |
-+ if( rc!=SQLITE_ROW ) goto err; |
-+ rc = SQLITE_OK; |
-+ |
-+ /* Corrupt if leaf data isn't a blob. */ |
-+ if( sqlite3_column_type(s, 0)!=SQLITE_BLOB ){ |
-+ rc = SQLITE_CORRUPT_BKPT; |
-+ }else{ |
-+ const char *pLeafData = sqlite3_column_blob(s, 0); |
-+ int nLeafData = sqlite3_column_bytes(s, 0); |
-+ |
-+ /* Corrupt if this can't be a leaf node. */ |
-+ if( pLeafData==NULL || nLeafData<1 || pLeafData[0]!='\0' ){ |
-+ rc = SQLITE_CORRUPT_BKPT; |
-+ }else{ |
-+ rc = leafReaderInit(pLeafData, nLeafData, &pReader->leafReader); |
-+ } |
-+ } |
-+ |
-+ err: |
-+ if( rc!=SQLITE_OK ){ |
-+ if( idx==-1 ){ |
-+ sqlite3_finalize(s); |
-+ }else{ |
-+ sqlite3_reset(s); |
-+ } |
-+ return rc; |
-+ } |
-+ |
- pReader->pStmt = s; |
-- leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0), |
-- sqlite3_column_bytes(pReader->pStmt, 0), |
-- &pReader->leafReader); |
- } |
- return SQLITE_OK; |
- } |
-@@ -5256,11 +5492,12 @@ |
- ** end of the current leaf, step forward to the next leaf block. |
- */ |
- static int leavesReaderStep(fulltext_vtab *v, LeavesReader *pReader){ |
-+ int rc; |
- assert( !leavesReaderAtEnd(pReader) ); |
-- leafReaderStep(&pReader->leafReader); |
-+ rc = leafReaderStep(&pReader->leafReader); |
-+ if( rc!=SQLITE_OK ) return rc; |
- |
- if( leafReaderAtEnd(&pReader->leafReader) ){ |
-- int rc; |
- if( pReader->rootData.pData ){ |
- pReader->eof = 1; |
- return SQLITE_OK; |
-@@ -5270,10 +5507,25 @@ |
- pReader->eof = 1; |
- return rc==SQLITE_DONE ? SQLITE_OK : rc; |
- } |
-- leafReaderDestroy(&pReader->leafReader); |
-- leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0), |
-- sqlite3_column_bytes(pReader->pStmt, 0), |
-- &pReader->leafReader); |
-+ |
-+ /* Corrupt if leaf data isn't a blob. */ |
-+ if( sqlite3_column_type(pReader->pStmt, 0)!=SQLITE_BLOB ){ |
-+ return SQLITE_CORRUPT_BKPT; |
-+ }else{ |
-+ LeafReader tmp; |
-+ const char *pLeafData = sqlite3_column_blob(pReader->pStmt, 0); |
-+ int nLeafData = sqlite3_column_bytes(pReader->pStmt, 0); |
-+ |
-+ /* Corrupt if this can't be a leaf node. */ |
-+ if( pLeafData==NULL || nLeafData<1 || pLeafData[0]!='\0' ){ |
-+ return SQLITE_CORRUPT_BKPT; |
-+ } |
-+ |
-+ rc = leafReaderInit(pLeafData, nLeafData, &tmp); |
-+ if( rc!=SQLITE_OK ) return rc; |
-+ leafReaderDestroy(&pReader->leafReader); |
-+ pReader->leafReader = tmp; |
-+ } |
- } |
- return SQLITE_OK; |
- } |
-@@ -5334,8 +5586,19 @@ |
- sqlite_int64 iEnd = sqlite3_column_int64(s, 1); |
- const char *pRootData = sqlite3_column_blob(s, 2); |
- int nRootData = sqlite3_column_bytes(s, 2); |
-+ sqlite_int64 iIndex = sqlite3_column_int64(s, 3); |
- |
-- assert( i<MERGE_COUNT ); |
-+ /* Corrupt if we get back different types than we stored. */ |
-+ /* Also corrupt if the index is not sequential starting at 0. */ |
-+ if( sqlite3_column_type(s, 0)!=SQLITE_INTEGER || |
-+ sqlite3_column_type(s, 1)!=SQLITE_INTEGER || |
-+ sqlite3_column_type(s, 2)!=SQLITE_BLOB || |
-+ i!=iIndex || |
-+ i>=MERGE_COUNT ){ |
-+ rc = SQLITE_CORRUPT_BKPT; |
-+ break; |
-+ } |
-+ |
- rc = leavesReaderInit(v, i, iStart, iEnd, pRootData, nRootData, |
- &pReaders[i]); |
- if( rc!=SQLITE_OK ) break; |
-@@ -5346,6 +5609,7 @@ |
- while( i-->0 ){ |
- leavesReaderDestroy(&pReaders[i]); |
- } |
-+ sqlite3_reset(s); /* So we don't leave a lock. */ |
- return rc; |
- } |
- |
-@@ -5369,14 +5633,27 @@ |
- DLReader dlReaders[MERGE_COUNT]; |
- const char *pTerm = leavesReaderTerm(pReaders); |
- int i, nTerm = leavesReaderTermBytes(pReaders); |
-+ int rc; |
- |
- assert( nReaders<=MERGE_COUNT ); |
- |
- for(i=0; i<nReaders; i++){ |
-- dlrInit(&dlReaders[i], DL_DEFAULT, |
-- leavesReaderData(pReaders+i), |
-- leavesReaderDataBytes(pReaders+i)); |
-+ const char *pData = leavesReaderData(pReaders+i); |
-+ if( pData==NULL ){ |
-+ rc = SQLITE_CORRUPT_BKPT; |
-+ break; |
-+ } |
-+ rc = dlrInit(&dlReaders[i], DL_DEFAULT, |
-+ pData, |
-+ leavesReaderDataBytes(pReaders+i)); |
-+ if( rc!=SQLITE_OK ) break; |
- } |
-+ if( rc!=SQLITE_OK ){ |
-+ while( i-->0 ){ |
-+ dlrDestroy(&dlReaders[i]); |
-+ } |
-+ return rc; |
-+ } |
- |
- return leafWriterStepMerge(v, pWriter, pTerm, nTerm, dlReaders, nReaders); |
- } |
-@@ -5429,10 +5706,14 @@ |
- memset(&lrs, '\0', sizeof(lrs)); |
- rc = leavesReadersInit(v, iLevel, lrs, &i); |
- if( rc!=SQLITE_OK ) return rc; |
-- assert( i==MERGE_COUNT ); |
- |
- leafWriterInit(iLevel+1, idx, &writer); |
- |
-+ if( i!=MERGE_COUNT ){ |
-+ rc = SQLITE_CORRUPT_BKPT; |
-+ goto err; |
-+ } |
-+ |
- /* Since leavesReaderReorder() pushes readers at eof to the end, |
- ** when the first reader is empty, all will be empty. |
- */ |
-@@ -5475,12 +5756,14 @@ |
- } |
- |
- /* Accumulate the union of *acc and *pData into *acc. */ |
--static void docListAccumulateUnion(DataBuffer *acc, |
-- const char *pData, int nData) { |
-+static int docListAccumulateUnion(DataBuffer *acc, |
-+ const char *pData, int nData) { |
- DataBuffer tmp = *acc; |
-+ int rc; |
- dataBufferInit(acc, tmp.nData+nData); |
-- docListUnion(tmp.pData, tmp.nData, pData, nData, acc); |
-+ rc = docListUnion(tmp.pData, tmp.nData, pData, nData, acc); |
- dataBufferDestroy(&tmp); |
-+ return rc; |
- } |
- |
- /* TODO(shess) It might be interesting to explore different merge |
-@@ -5522,8 +5805,13 @@ |
- int c = leafReaderTermCmp(&pReader->leafReader, pTerm, nTerm, isPrefix); |
- if( c>0 ) break; /* Past any possible matches. */ |
- if( c==0 ){ |
-+ int iBuffer, nData; |
- const char *pData = leavesReaderData(pReader); |
-- int iBuffer, nData = leavesReaderDataBytes(pReader); |
-+ if( pData==NULL ){ |
-+ rc = SQLITE_CORRUPT_BKPT; |
-+ break; |
-+ } |
-+ nData = leavesReaderDataBytes(pReader); |
- |
- /* Find the first empty buffer. */ |
- for(iBuffer=0; iBuffer<nBuffers; ++iBuffer){ |
-@@ -5569,11 +5857,13 @@ |
- ** with pData/nData. |
- */ |
- dataBufferSwap(p, pAcc); |
-- docListAccumulateUnion(pAcc, pData, nData); |
-+ rc = docListAccumulateUnion(pAcc, pData, nData); |
-+ if( rc!=SQLITE_OK ) goto err; |
- |
- /* Accumulate remaining doclists into pAcc. */ |
- for(++p; p<pAcc; ++p){ |
-- docListAccumulateUnion(pAcc, p->pData, p->nData); |
-+ rc = docListAccumulateUnion(pAcc, p->pData, p->nData); |
-+ if( rc!=SQLITE_OK ) goto err; |
- |
- /* dataBufferReset() could allow a large doclist to blow up |
- ** our memory requirements. |
-@@ -5598,13 +5888,15 @@ |
- if( out->nData==0 ){ |
- dataBufferSwap(out, &(pBuffers[iBuffer])); |
- }else{ |
-- docListAccumulateUnion(out, pBuffers[iBuffer].pData, |
-- pBuffers[iBuffer].nData); |
-+ rc = docListAccumulateUnion(out, pBuffers[iBuffer].pData, |
-+ pBuffers[iBuffer].nData); |
-+ if( rc!=SQLITE_OK ) break; |
- } |
- } |
- } |
- } |
- |
-+err: |
- while( nBuffers-- ){ |
- dataBufferDestroy(&(pBuffers[nBuffers])); |
- } |
-@@ -5663,20 +5955,26 @@ |
- ** node. Consider whether breaking symmetry is worthwhile. I suspect |
- ** it is not worthwhile. |
- */ |
--static void getChildrenContaining(const char *pData, int nData, |
-- const char *pTerm, int nTerm, int isPrefix, |
-- sqlite_int64 *piStartChild, |
-- sqlite_int64 *piEndChild){ |
-+static int getChildrenContaining(const char *pData, int nData, |
-+ const char *pTerm, int nTerm, int isPrefix, |
-+ sqlite_int64 *piStartChild, |
-+ sqlite_int64 *piEndChild){ |
- InteriorReader reader; |
-+ int rc; |
- |
- assert( nData>1 ); |
- assert( *pData!='\0' ); |
-- interiorReaderInit(pData, nData, &reader); |
-+ rc = interiorReaderInit(pData, nData, &reader); |
-+ if( rc!=SQLITE_OK ) return rc; |
- |
- /* Scan for the first child which could contain pTerm/nTerm. */ |
- while( !interiorReaderAtEnd(&reader) ){ |
- if( interiorReaderTermCmp(&reader, pTerm, nTerm, 0)>0 ) break; |
-- interiorReaderStep(&reader); |
-+ rc = interiorReaderStep(&reader); |
-+ if( rc!=SQLITE_OK ){ |
-+ interiorReaderDestroy(&reader); |
-+ return rc; |
-+ } |
- } |
- *piStartChild = interiorReaderCurrentBlockid(&reader); |
- |
-@@ -5686,7 +5984,11 @@ |
- */ |
- while( !interiorReaderAtEnd(&reader) ){ |
- if( interiorReaderTermCmp(&reader, pTerm, nTerm, isPrefix)>0 ) break; |
-- interiorReaderStep(&reader); |
-+ rc = interiorReaderStep(&reader); |
-+ if( rc!=SQLITE_OK ){ |
-+ interiorReaderDestroy(&reader); |
-+ return rc; |
-+ } |
- } |
- *piEndChild = interiorReaderCurrentBlockid(&reader); |
- |
-@@ -5695,6 +5997,7 @@ |
- /* Children must ascend, and if !prefix, both must be the same. */ |
- assert( *piEndChild>=*piStartChild ); |
- assert( isPrefix || *piStartChild==*piEndChild ); |
-+ return rc; |
- } |
- |
- /* Read block at iBlockid and pass it with other params to |
-@@ -5722,12 +6025,32 @@ |
- if( rc!=SQLITE_OK ) return rc; |
- |
- rc = sqlite3_step(s); |
-- if( rc==SQLITE_DONE ) return SQLITE_ERROR; |
-+ /* Corrupt if interior node references missing child node. */ |
-+ if( rc==SQLITE_DONE ) return SQLITE_CORRUPT_BKPT; |
- if( rc!=SQLITE_ROW ) return rc; |
- |
-- getChildrenContaining(sqlite3_column_blob(s, 0), sqlite3_column_bytes(s, 0), |
-- pTerm, nTerm, isPrefix, piStartChild, piEndChild); |
-+ /* Corrupt if child node isn't a blob. */ |
-+ if( sqlite3_column_type(s, 0)!=SQLITE_BLOB ){ |
-+ sqlite3_reset(s); /* So we don't leave a lock. */ |
-+ return SQLITE_CORRUPT_BKPT; |
-+ }else{ |
-+ const char *pData = sqlite3_column_blob(s, 0); |
-+ int nData = sqlite3_column_bytes(s, 0); |
- |
-+ /* Corrupt if child is not a valid interior node. */ |
-+ if( pData==NULL || nData<1 || pData[0]=='\0' ){ |
-+ sqlite3_reset(s); /* So we don't leave a lock. */ |
-+ return SQLITE_CORRUPT_BKPT; |
-+ } |
-+ |
-+ rc = getChildrenContaining(pData, nData, pTerm, nTerm, |
-+ isPrefix, piStartChild, piEndChild); |
-+ if( rc!=SQLITE_OK ){ |
-+ sqlite3_reset(s); |
-+ return rc; |
-+ } |
-+ } |
-+ |
- /* We expect only one row. We must execute another sqlite3_step() |
- * to complete the iteration; otherwise the table will remain |
- * locked. */ |
-@@ -5756,8 +6079,9 @@ |
- /* Process pData as an interior node, then loop down the tree |
- ** until we find the set of leaf nodes to scan for the term. |
- */ |
-- getChildrenContaining(pData, nData, pTerm, nTerm, isPrefix, |
-- &iStartChild, &iEndChild); |
-+ rc = getChildrenContaining(pData, nData, pTerm, nTerm, isPrefix, |
-+ &iStartChild, &iEndChild); |
-+ if( rc!=SQLITE_OK ) return rc; |
- while( iStartChild>iLeavesEnd ){ |
- sqlite_int64 iNextStart, iNextEnd; |
- rc = loadAndGetChildrenContaining(v, iStartChild, pTerm, nTerm, isPrefix, |
-@@ -5809,7 +6133,8 @@ |
- DataBuffer result; |
- int rc; |
- |
-- assert( nData>1 ); |
-+ /* Corrupt if segment root can't be valid. */ |
-+ if( pData==NULL || nData<1 ) return SQLITE_CORRUPT_BKPT; |
- |
- /* This code should never be called with buffered updates. */ |
- assert( v->nPendingData<0 ); |
-@@ -5826,16 +6151,21 @@ |
- DataBuffer merged; |
- DLReader readers[2]; |
- |
-- dlrInit(&readers[0], DL_DEFAULT, out->pData, out->nData); |
-- dlrInit(&readers[1], DL_DEFAULT, result.pData, result.nData); |
-- dataBufferInit(&merged, out->nData+result.nData); |
-- docListMerge(&merged, readers, 2); |
-- dataBufferDestroy(out); |
-- *out = merged; |
-- dlrDestroy(&readers[0]); |
-- dlrDestroy(&readers[1]); |
-+ rc = dlrInit(&readers[0], DL_DEFAULT, out->pData, out->nData); |
-+ if( rc==SQLITE_OK ){ |
-+ rc = dlrInit(&readers[1], DL_DEFAULT, result.pData, result.nData); |
-+ if( rc==SQLITE_OK ){ |
-+ dataBufferInit(&merged, out->nData+result.nData); |
-+ rc = docListMerge(&merged, readers, 2); |
-+ dataBufferDestroy(out); |
-+ *out = merged; |
-+ dlrDestroy(&readers[1]); |
-+ } |
-+ dlrDestroy(&readers[0]); |
-+ } |
- } |
- } |
-+ |
- dataBufferDestroy(&result); |
- return rc; |
- } |
-@@ -5869,11 +6199,20 @@ |
- const char *pData = sqlite3_column_blob(s, 2); |
- const int nData = sqlite3_column_bytes(s, 2); |
- const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1); |
-+ |
-+ /* Corrupt if we get back different types than we stored. */ |
-+ if( sqlite3_column_type(s, 1)!=SQLITE_INTEGER || |
-+ sqlite3_column_type(s, 2)!=SQLITE_BLOB ){ |
-+ rc = SQLITE_CORRUPT_BKPT; |
-+ goto err; |
-+ } |
-+ |
- rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, isPrefix, |
- &doclist); |
- if( rc!=SQLITE_OK ) goto err; |
- } |
- if( rc==SQLITE_DONE ){ |
-+ rc = SQLITE_OK; |
- if( doclist.nData!=0 ){ |
- /* TODO(shess) The old term_select_all() code applied the column |
- ** restrict as we merged segments, leading to smaller buffers. |
-@@ -5881,13 +6220,13 @@ |
- ** system is checked in. |
- */ |
- if( iColumn==v->nColumn) iColumn = -1; |
-- docListTrim(DL_DEFAULT, doclist.pData, doclist.nData, |
-- iColumn, iType, out); |
-+ rc = docListTrim(DL_DEFAULT, doclist.pData, doclist.nData, |
-+ iColumn, iType, out); |
- } |
-- rc = SQLITE_OK; |
- } |
- |
- err: |
-+ sqlite3_reset(s); /* So we don't leave a lock. */ |
- dataBufferDestroy(&doclist); |
- return rc; |
- } |
-@@ -6250,6 +6589,7 @@ |
- LeafWriter *pWriter){ |
- int i, rc = SQLITE_OK; |
- DataBuffer doclist, merged, tmp; |
-+ const char *pData; |
- |
- /* Order the readers. */ |
- i = nReaders; |
-@@ -6270,14 +6610,20 @@ |
- if( 0!=optLeavesReaderTermCmp(&readers[0], &readers[i]) ) break; |
- } |
- |
-+ pData = optLeavesReaderData(&readers[0]); |
-+ if( pData==NULL ){ |
-+ rc = SQLITE_CORRUPT_BKPT; |
-+ break; |
-+ } |
-+ |
- /* Special-case for no merge. */ |
- if( i==1 ){ |
- /* Trim deletions from the doclist. */ |
- dataBufferReset(&merged); |
-- docListTrim(DL_DEFAULT, |
-- optLeavesReaderData(&readers[0]), |
-- optLeavesReaderDataBytes(&readers[0]), |
-- -1, DL_DEFAULT, &merged); |
-+ rc = docListTrim(DL_DEFAULT, pData, |
-+ optLeavesReaderDataBytes(&readers[0]), |
-+ -1, DL_DEFAULT, &merged); |
-+ if( rc!=SQLITE_OK ) break; |
- }else{ |
- DLReader dlReaders[MERGE_COUNT]; |
- int iReader, nReaders; |
-@@ -6285,9 +6631,10 @@ |
- /* Prime the pipeline with the first reader's doclist. After |
- ** one pass index 0 will reference the accumulated doclist. |
- */ |
-- dlrInit(&dlReaders[0], DL_DEFAULT, |
-- optLeavesReaderData(&readers[0]), |
-- optLeavesReaderDataBytes(&readers[0])); |
-+ rc = dlrInit(&dlReaders[0], DL_DEFAULT, |
-+ pData, |
-+ optLeavesReaderDataBytes(&readers[0])); |
-+ if( rc!=SQLITE_OK ) break; |
- iReader = 1; |
- |
- assert( iReader<i ); /* Must execute the loop at least once. */ |
-@@ -6295,24 +6642,34 @@ |
- /* Merge 16 inputs per pass. */ |
- for( nReaders=1; iReader<i && nReaders<MERGE_COUNT; |
- iReader++, nReaders++ ){ |
-- dlrInit(&dlReaders[nReaders], DL_DEFAULT, |
-- optLeavesReaderData(&readers[iReader]), |
-- optLeavesReaderDataBytes(&readers[iReader])); |
-+ pData = optLeavesReaderData(&readers[iReader]); |
-+ if( pData==NULL ){ |
-+ rc = SQLITE_CORRUPT_BKPT; |
-+ break; |
-+ } |
-+ rc = dlrInit(&dlReaders[nReaders], DL_DEFAULT, pData, |
-+ optLeavesReaderDataBytes(&readers[iReader])); |
-+ if( rc!=SQLITE_OK ) break; |
- } |
- |
- /* Merge doclists and swap result into accumulator. */ |
-- dataBufferReset(&merged); |
-- docListMerge(&merged, dlReaders, nReaders); |
-- tmp = merged; |
-- merged = doclist; |
-- doclist = tmp; |
-+ if( rc==SQLITE_OK ){ |
-+ dataBufferReset(&merged); |
-+ rc = docListMerge(&merged, dlReaders, nReaders); |
-+ tmp = merged; |
-+ merged = doclist; |
-+ doclist = tmp; |
-+ } |
- |
- while( nReaders-- > 0 ){ |
- dlrDestroy(&dlReaders[nReaders]); |
- } |
- |
-+ if( rc!=SQLITE_OK ) goto err; |
-+ |
- /* Accumulated doclist to reader 0 for next pass. */ |
-- dlrInit(&dlReaders[0], DL_DEFAULT, doclist.pData, doclist.nData); |
-+ rc = dlrInit(&dlReaders[0], DL_DEFAULT, doclist.pData, doclist.nData); |
-+ if( rc!=SQLITE_OK ) goto err; |
- } |
- |
- /* Destroy reader that was left in the pipeline. */ |
-@@ -6320,8 +6677,9 @@ |
- |
- /* Trim deletions from the doclist. */ |
- dataBufferReset(&merged); |
-- docListTrim(DL_DEFAULT, doclist.pData, doclist.nData, |
-- -1, DL_DEFAULT, &merged); |
-+ rc = docListTrim(DL_DEFAULT, doclist.pData, doclist.nData, |
-+ -1, DL_DEFAULT, &merged); |
-+ if( rc!=SQLITE_OK ) goto err; |
- } |
- |
- /* Only pass doclists with hits (skip if all hits deleted). */ |
-@@ -6401,6 +6759,14 @@ |
- const char *pRootData = sqlite3_column_blob(s, 2); |
- int nRootData = sqlite3_column_bytes(s, 2); |
- |
-+ /* Corrupt if we get back different types than we stored. */ |
-+ if( sqlite3_column_type(s, 0)!=SQLITE_INTEGER || |
-+ sqlite3_column_type(s, 1)!=SQLITE_INTEGER || |
-+ sqlite3_column_type(s, 2)!=SQLITE_BLOB ){ |
-+ rc = SQLITE_CORRUPT_BKPT; |
-+ break; |
-+ } |
-+ |
- assert( i<nReaders ); |
- rc = leavesReaderInit(v, -1, iStart, iEnd, pRootData, nRootData, |
- &readers[i].reader); |
-@@ -6414,6 +6780,8 @@ |
- if( rc==SQLITE_DONE ){ |
- assert( i==nReaders ); |
- rc = optimizeInternal(v, readers, nReaders, &writer); |
-+ }else{ |
-+ sqlite3_reset(s); /* So we don't leave a lock. */ |
- } |
- |
- while( i-- > 0 ){ |
-@@ -6477,9 +6845,18 @@ |
- const sqlite_int64 iEndBlockid = sqlite3_column_int64(s, 1); |
- const char *pRootData = sqlite3_column_blob(s, 2); |
- const int nRootData = sqlite3_column_bytes(s, 2); |
-+ int rc; |
- LeavesReader reader; |
-- int rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid, |
-- pRootData, nRootData, &reader); |
-+ |
-+ /* Corrupt if we get back different types than we stored. */ |
-+ if( sqlite3_column_type(s, 0)!=SQLITE_INTEGER || |
-+ sqlite3_column_type(s, 1)!=SQLITE_INTEGER || |
-+ sqlite3_column_type(s, 2)!=SQLITE_BLOB ){ |
-+ return SQLITE_CORRUPT_BKPT; |
-+ } |
-+ |
-+ rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid, |
-+ pRootData, nRootData, &reader); |
- if( rc!=SQLITE_OK ) return rc; |
- |
- while( rc==SQLITE_OK && !leavesReaderAtEnd(&reader) ){ |
-@@ -6641,16 +7018,19 @@ |
- const char *pData, int nData){ |
- DataBuffer dump; |
- DLReader dlReader; |
-+ int rc; |
- |
- assert( pData!=NULL && nData>0 ); |
- |
-+ rc = dlrInit(&dlReader, DL_DEFAULT, pData, nData); |
-+ if( rc!=SQLITE_OK ) return rc; |
- dataBufferInit(&dump, 0); |
-- dlrInit(&dlReader, DL_DEFAULT, pData, nData); |
-- for( ; !dlrAtEnd(&dlReader); dlrStep(&dlReader) ){ |
-+ for( ; rc==SQLITE_OK && !dlrAtEnd(&dlReader); rc = dlrStep(&dlReader) ){ |
- char buf[256]; |
- PLReader plReader; |
- |
-- plrInit(&plReader, &dlReader); |
-+ rc = plrInit(&plReader, &dlReader); |
-+ if( rc!=SQLITE_OK ) break; |
- if( DL_DEFAULT==DL_DOCIDS || plrAtEnd(&plReader) ){ |
- sqlite3_snprintf(sizeof(buf), buf, "[%lld] ", dlrDocid(&dlReader)); |
- dataBufferAppend(&dump, buf, strlen(buf)); |
-@@ -6661,7 +7041,8 @@ |
- dlrDocid(&dlReader), iColumn); |
- dataBufferAppend(&dump, buf, strlen(buf)); |
- |
-- for( ; !plrAtEnd(&plReader); plrStep(&plReader) ){ |
-+ for( ; !plrAtEnd(&plReader); rc = plrStep(&plReader) ){ |
-+ if( rc!=SQLITE_OK ) break; |
- if( plrColumn(&plReader)!=iColumn ){ |
- iColumn = plrColumn(&plReader); |
- sqlite3_snprintf(sizeof(buf), buf, "] %d[", iColumn); |
-@@ -6682,6 +7063,7 @@ |
- dataBufferAppend(&dump, buf, strlen(buf)); |
- } |
- plrDestroy(&plReader); |
-+ if( rc!= SQLITE_OK ) break; |
- |
- assert( dump.nData>0 ); |
- dump.nData--; /* Overwrite trailing space. */ |
-@@ -6690,6 +7072,10 @@ |
- } |
- } |
- dlrDestroy(&dlReader); |
-+ if( rc!=SQLITE_OK ){ |
-+ dataBufferDestroy(&dump); |
-+ return rc; |
-+ } |
- |
- assert( dump.nData>0 ); |
- dump.nData--; /* Overwrite trailing space. */ |
-@@ -6701,6 +7087,7 @@ |
- sqlite3_result_text(pContext, dump.pData, dump.nData, sqlite3_free); |
- dump.pData = NULL; |
- dump.nData = dump.nCapacity = 0; |
-+ return SQLITE_OK; |
- } |
- |
- /* Implements dump_doclist() for use in inspecting the fts3 index from |
-@@ -6987,7 +7374,11 @@ |
+@@ -1226,7 +1227,13 @@ static int fts3ScanInteriorNode( |
+ isFirstTerm = 0; |
+ zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix); |
+ |
+- if( nPrefix<0 || nSuffix<0 || &zCsr[nSuffix]>zEnd ){ |
++ /* NOTE(shess): Previous code checked for negative nPrefix and |
++ ** nSuffix and suffix overrunning zEnd. Additionally corrupt if |
++ ** the prefix is longer than the previous term, or if the suffix |
++ ** causes overflow. |
++ */ |
++ if( nPrefix<0 || nSuffix<0 || nPrefix>nBuffer |
++ || &zCsr[nSuffix]<zCsr || &zCsr[nSuffix]>zEnd ){ |
+ rc = SQLITE_CORRUPT; |
+ goto finish_scan; |
+ } |
+@@ -3646,7 +3660,11 @@ int sqlite3Fts3Init(sqlite3 *db){ |
** module with sqlite. |
*/ |
if( SQLITE_OK==rc |
@@ -1897,13 +35,29 @@ Index: ext/fts3/fts3.c |
&& SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) |
+#endif |
&& SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) |
- && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", -1)) |
- && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", -1)) |
-Index: ext/fts3/fts3_icu.c |
-=================================================================== |
---- ext/fts3/fts3_icu.c (revision 48811) |
-+++ ext/fts3/fts3_icu.c (working copy) |
-@@ -198,7 +198,7 @@ |
+ && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) |
+ && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 1)) |
+@@ -3656,11 +3674,15 @@ int sqlite3Fts3Init(sqlite3 *db){ |
+ rc = sqlite3_create_module_v2( |
+ db, "fts3", &fts3Module, (void *)pHash, hashDestroy |
+ ); |
++#if CHROMIUM_FTS3_CHANGES && !SQLITE_TEST |
++ /* Disable fts4 pending review. */ |
++#else |
+ if( rc==SQLITE_OK ){ |
+ rc = sqlite3_create_module_v2( |
+ db, "fts4", &fts3Module, (void *)pHash, 0 |
+ ); |
+ } |
++#endif |
+ return rc; |
+ } |
+ |
+diff --git ext/fts3/fts3_icu.c ext/fts3/fts3_icu.c |
+index 85390d3..a75b14a 100644 |
+--- ext/fts3/fts3_icu.c |
++++ ext/fts3/fts3_icu.c |
+@@ -198,7 +198,7 @@ static int icuNext( |
while( iStart<iEnd ){ |
int iWhite = iStart; |
@@ -1912,15 +66,3 @@ Index: ext/fts3/fts3_icu.c |
if( u_isspace(c) ){ |
iStart = iWhite; |
}else{ |
-Index: ext/fts3/fts3_tokenizer.c |
-=================================================================== |
---- ext/fts3/fts3_tokenizer.c (revision 48811) |
-+++ ext/fts3/fts3_tokenizer.c (working copy) |
-@@ -33,6 +33,7 @@ |
- #include "fts3_hash.h" |
- #include "fts3_tokenizer.h" |
- #include <assert.h> |
-+#include <stddef.h> |
- |
- /* |
- ** Implementation of the SQL scalar function for accessing the underlying |