| Index: third_party/sqlite/fts3.patch
|
| ===================================================================
|
| --- third_party/sqlite/fts3.patch (revision 0)
|
| +++ third_party/sqlite/fts3.patch (revision 0)
|
| @@ -0,0 +1,1925 @@
|
| +Index: ext/fts3/fts3_tokenizer.c
|
| +===================================================================
|
| +--- ext/fts3/fts3_tokenizer.c (revision 48758)
|
| ++++ 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
|
| +Index: ext/fts3/fts3.c
|
| +===================================================================
|
| +--- ext/fts3/fts3.c (revision 48758)
|
| ++++ ext/fts3/fts3.c (working copy)
|
| +@@ -271,6 +271,7 @@
|
| + ** deletions and duplications. This would basically be a forced merge
|
| + ** into a single segment.
|
| + */
|
| ++#define CHROMIUM_FTS3_CHANGES 1
|
| +
|
| + #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,65 @@
|
| +
|
| + 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;
|
| +
|
| +- pReader->pStmt = s;
|
| +- leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0),
|
| +- sqlite3_column_bytes(pReader->pStmt, 0),
|
| +- &pReader->leafReader);
|
| ++ 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;
|
| ++ }
|
| + }
|
| + return SQLITE_OK;
|
| + }
|
| +@@ -5256,11 +5490,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 +5505,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 +5584,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 +5607,7 @@
|
| + while( i-->0 ){
|
| + leavesReaderDestroy(&pReaders[i]);
|
| + }
|
| ++ sqlite3_reset(s); /* So we don't leave a lock. */
|
| + return rc;
|
| + }
|
| +
|
| +@@ -5369,14 +5631,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 +5704,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 +5754,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 +5803,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 +5855,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 +5886,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 +5953,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 +5982,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 +5995,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 +6023,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 +6077,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 +6131,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 +6149,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 +6197,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 +6218,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 +6587,7 @@
|
| + LeafWriter *pWriter){
|
| + int i, rc = SQLITE_OK;
|
| + DataBuffer doclist, merged, tmp;
|
| ++ const char *pData;
|
| +
|
| + /* Order the readers. */
|
| + i = nReaders;
|
| +@@ -6270,14 +6608,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 +6629,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 +6640,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 +6675,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 +6757,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 +6778,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 +6843,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 +7016,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 +7039,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 +7061,7 @@
|
| + dataBufferAppend(&dump, buf, strlen(buf));
|
| + }
|
| + plrDestroy(&plReader);
|
| ++ if( rc!= SQLITE_OK ) break;
|
| +
|
| + assert( dump.nData>0 );
|
| + dump.nData--; /* Overwrite trailing space. */
|
| +@@ -6690,6 +7070,10 @@
|
| + }
|
| + }
|
| + dlrDestroy(&dlReader);
|
| ++ if( rc!=SQLITE_OK ){
|
| ++ dataBufferDestroy(&dump);
|
| ++ return rc;
|
| ++ }
|
| +
|
| + assert( dump.nData>0 );
|
| + dump.nData--; /* Overwrite trailing space. */
|
| +@@ -6701,6 +7085,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 +7372,11 @@
|
| + ** module with sqlite.
|
| + */
|
| + if( SQLITE_OK==rc
|
| ++#if CHROMIUM_FTS3_CHANGES && !SQLITE_TEST
|
| ++ /* fts3_tokenizer() disabled for security reasons. */
|
| ++#else
|
| + && 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 48758)
|
| ++++ ext/fts3/fts3_icu.c (working copy)
|
| +@@ -198,7 +198,7 @@
|
| +
|
| + while( iStart<iEnd ){
|
| + int iWhite = iStart;
|
| +- U8_NEXT(pCsr->aChar, iWhite, pCsr->nChar, c);
|
| ++ U16_NEXT(pCsr->aChar, iWhite, pCsr->nChar, c);
|
| + if( u_isspace(c) ){
|
| + iStart = iWhite;
|
| + }else{
|
|
|