OLD | NEW |
(Empty) | |
| 1 diff -ru ext-orig/fts2/fts2.c ext/fts2/fts2.c |
| 2 --- ext-orig/fts2/fts2.c 2009-09-04 13:37:41.000000000 -0700 |
| 3 +++ ext/fts2/fts2.c 2009-09-30 14:48:14.000000000 -0700 |
| 4 @@ -37,6 +37,20 @@ |
| 5 ** This is an SQLite module implementing full-text search. |
| 6 */ |
| 7 |
| 8 +/* TODO(shess): To make it easier to spot changes without groveling |
| 9 +** through changelogs, I've defined GEARS_FTS2_CHANGES to call them |
| 10 +** out, and I will document them here. On imports, these changes |
| 11 +** should be reviewed to make sure they are still present, or are |
| 12 +** dropped as appropriate. |
| 13 +** |
| 14 +** SQLite core adds the custom function fts2_tokenizer() to be used |
| 15 +** for defining new tokenizers. The second parameter is a vtable |
| 16 +** pointer encoded as a blob. Obviously this cannot be exposed to |
| 17 +** Gears callers for security reasons. It could be suppressed in the |
| 18 +** authorizer, but for now I have simply commented the definition out. |
| 19 +*/ |
| 20 +#define GEARS_FTS2_CHANGES 1 |
| 21 + |
| 22 /* |
| 23 ** The code in this file is only compiled if: |
| 24 ** |
| 25 @@ -335,6 +349,16 @@ |
| 26 # define TRACE(A) |
| 27 #endif |
| 28 |
| 29 +#if 0 |
| 30 +/* Useful to set breakpoints. See main.c sqlite3Corrupt(). */ |
| 31 +static int fts2Corrupt(void){ |
| 32 + return SQLITE_CORRUPT; |
| 33 +} |
| 34 +# define SQLITE_CORRUPT_BKPT fts2Corrupt() |
| 35 +#else |
| 36 +# define SQLITE_CORRUPT_BKPT SQLITE_CORRUPT |
| 37 +#endif |
| 38 + |
| 39 /* It is not safe to call isspace(), tolower(), or isalnum() on |
| 40 ** hi-bit-set characters. This is the same solution used in the |
| 41 ** tokenizer. |
| 42 @@ -423,30 +447,41 @@ |
| 43 /* Read a 64-bit variable-length integer from memory starting at p[0]. |
| 44 * Return the number of bytes read, or 0 on error. |
| 45 * The value is stored in *v. */ |
| 46 -static int getVarint(const char *p, sqlite_int64 *v){ |
| 47 +static int getVarintSafe(const char *p, sqlite_int64 *v, int max){ |
| 48 const unsigned char *q = (const unsigned char *) p; |
| 49 sqlite_uint64 x = 0, y = 1; |
| 50 - while( (*q & 0x80) == 0x80 ){ |
| 51 + if( max>VARINT_MAX ) max = VARINT_MAX; |
| 52 + while( max && (*q & 0x80) == 0x80 ){ |
| 53 + max--; |
| 54 x += y * (*q++ & 0x7f); |
| 55 y <<= 7; |
| 56 - if( q - (unsigned char *)p >= VARINT_MAX ){ /* bad data */ |
| 57 - assert( 0 ); |
| 58 - return 0; |
| 59 - } |
| 60 + } |
| 61 + if ( !max ){ |
| 62 + assert( 0 ); |
| 63 + return 0; /* tried to read too much; bad data */ |
| 64 } |
| 65 x += y * (*q++); |
| 66 *v = (sqlite_int64) x; |
| 67 return (int) (q - (unsigned char *)p); |
| 68 } |
| 69 |
| 70 -static int getVarint32(const char *p, int *pi){ |
| 71 +static int getVarint(const char *p, sqlite_int64 *v){ |
| 72 + return getVarintSafe(p, v, VARINT_MAX); |
| 73 +} |
| 74 + |
| 75 +static int getVarint32Safe(const char *p, int *pi, int max){ |
| 76 sqlite_int64 i; |
| 77 - int ret = getVarint(p, &i); |
| 78 + int ret = getVarintSafe(p, &i, max); |
| 79 + if( !ret ) return ret; |
| 80 *pi = (int) i; |
| 81 assert( *pi==i ); |
| 82 return ret; |
| 83 } |
| 84 |
| 85 +static int getVarint32(const char* p, int *pi){ |
| 86 + return getVarint32Safe(p, pi, VARINT_MAX); |
| 87 +} |
| 88 + |
| 89 /*******************************************************************/ |
| 90 /* DataBuffer is used to collect data into a buffer in piecemeal |
| 91 ** fashion. It implements the usual distinction between amount of |
| 92 @@ -615,7 +650,7 @@ |
| 93 |
| 94 static int dlrAtEnd(DLReader *pReader){ |
| 95 assert( pReader->nData>=0 ); |
| 96 - return pReader->nData==0; |
| 97 + return pReader->nData<=0; |
| 98 } |
| 99 static sqlite_int64 dlrDocid(DLReader *pReader){ |
| 100 assert( !dlrAtEnd(pReader) ); |
| 101 @@ -639,7 +674,8 @@ |
| 102 */ |
| 103 static const char *dlrPosData(DLReader *pReader){ |
| 104 sqlite_int64 iDummy; |
| 105 - int n = getVarint(pReader->pData, &iDummy); |
| 106 + int n = getVarintSafe(pReader->pData, &iDummy, pReader->nElement); |
| 107 + if( !n ) return NULL; |
| 108 assert( !dlrAtEnd(pReader) ); |
| 109 return pReader->pData+n; |
| 110 } |
| 111 @@ -649,7 +685,7 @@ |
| 112 assert( !dlrAtEnd(pReader) ); |
| 113 return pReader->nElement-n; |
| 114 } |
| 115 -static void dlrStep(DLReader *pReader){ |
| 116 +static int dlrStep(DLReader *pReader){ |
| 117 assert( !dlrAtEnd(pReader) ); |
| 118 |
| 119 /* Skip past current doclist element. */ |
| 120 @@ -658,32 +694,48 @@ |
| 121 pReader->nData -= pReader->nElement; |
| 122 |
| 123 /* If there is more data, read the next doclist element. */ |
| 124 - if( pReader->nData!=0 ){ |
| 125 + if( pReader->nData>0 ){ |
| 126 sqlite_int64 iDocidDelta; |
| 127 - int iDummy, n = getVarint(pReader->pData, &iDocidDelta); |
| 128 + int nTotal = 0; |
| 129 + int iDummy, n = getVarintSafe(pReader->pData, &iDocidDelta, pReader->nData)
; |
| 130 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 131 + nTotal += n; |
| 132 pReader->iDocid += iDocidDelta; |
| 133 if( pReader->iType>=DL_POSITIONS ){ |
| 134 - assert( n<pReader->nData ); |
| 135 while( 1 ){ |
| 136 - n += getVarint32(pReader->pData+n, &iDummy); |
| 137 - assert( n<=pReader->nData ); |
| 138 + n = getVarint32Safe(pReader->pData+nTotal, &iDummy, |
| 139 + pReader->nData-nTotal); |
| 140 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 141 + nTotal += n; |
| 142 if( iDummy==POS_END ) break; |
| 143 if( iDummy==POS_COLUMN ){ |
| 144 - n += getVarint32(pReader->pData+n, &iDummy); |
| 145 - assert( n<pReader->nData ); |
| 146 + n = getVarint32Safe(pReader->pData+nTotal, &iDummy, |
| 147 + pReader->nData-nTotal); |
| 148 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 149 + nTotal += n; |
| 150 }else if( pReader->iType==DL_POSITIONS_OFFSETS ){ |
| 151 - n += getVarint32(pReader->pData+n, &iDummy); |
| 152 - n += getVarint32(pReader->pData+n, &iDummy); |
| 153 - assert( n<pReader->nData ); |
| 154 + n = getVarint32Safe(pReader->pData+nTotal, &iDummy, |
| 155 + pReader->nData-nTotal); |
| 156 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 157 + nTotal += n; |
| 158 + n = getVarint32Safe(pReader->pData+nTotal, &iDummy, |
| 159 + pReader->nData-nTotal); |
| 160 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 161 + nTotal += n; |
| 162 } |
| 163 } |
| 164 } |
| 165 - pReader->nElement = n; |
| 166 + pReader->nElement = nTotal; |
| 167 assert( pReader->nElement<=pReader->nData ); |
| 168 } |
| 169 + return SQLITE_OK; |
| 170 } |
| 171 -static void dlrInit(DLReader *pReader, DocListType iType, |
| 172 - const char *pData, int nData){ |
| 173 +static void dlrDestroy(DLReader *pReader){ |
| 174 + SCRAMBLE(pReader); |
| 175 +} |
| 176 +static int dlrInit(DLReader *pReader, DocListType iType, |
| 177 + const char *pData, int nData){ |
| 178 + int rc; |
| 179 assert( pData!=NULL && nData!=0 ); |
| 180 pReader->iType = iType; |
| 181 pReader->pData = pData; |
| 182 @@ -692,10 +744,9 @@ |
| 183 pReader->iDocid = 0; |
| 184 |
| 185 /* Load the first element's data. There must be a first element. */ |
| 186 - dlrStep(pReader); |
| 187 -} |
| 188 -static void dlrDestroy(DLReader *pReader){ |
| 189 - SCRAMBLE(pReader); |
| 190 + rc = dlrStep(pReader); |
| 191 + if( rc!=SQLITE_OK ) dlrDestroy(pReader); |
| 192 + return rc; |
| 193 } |
| 194 |
| 195 #ifndef NDEBUG |
| 196 @@ -782,9 +833,9 @@ |
| 197 /* TODO(shess) This has become just a helper for docListMerge. |
| 198 ** Consider a refactor to make this cleaner. |
| 199 */ |
| 200 -static void dlwAppend(DLWriter *pWriter, |
| 201 - const char *pData, int nData, |
| 202 - sqlite_int64 iFirstDocid, sqlite_int64 iLastDocid){ |
| 203 +static int dlwAppend(DLWriter *pWriter, |
| 204 + const char *pData, int nData, |
| 205 + sqlite_int64 iFirstDocid, sqlite_int64 iLastDocid){ |
| 206 sqlite_int64 iDocid = 0; |
| 207 char c[VARINT_MAX]; |
| 208 int nFirstOld, nFirstNew; /* Old and new varint len of first docid. */ |
| 209 @@ -793,7 +844,8 @@ |
| 210 #endif |
| 211 |
| 212 /* Recode the initial docid as delta from iPrevDocid. */ |
| 213 - nFirstOld = getVarint(pData, &iDocid); |
| 214 + nFirstOld = getVarintSafe(pData, &iDocid, nData); |
| 215 + if( !nFirstOld ) return SQLITE_CORRUPT_BKPT; |
| 216 assert( nFirstOld<nData || (nFirstOld==nData && pWriter->iType==DL_DOCIDS) ); |
| 217 nFirstNew = putVarint(c, iFirstDocid-pWriter->iPrevDocid); |
| 218 |
| 219 @@ -814,10 +866,11 @@ |
| 220 dataBufferAppend(pWriter->b, c, nFirstNew); |
| 221 } |
| 222 pWriter->iPrevDocid = iLastDocid; |
| 223 + return SQLITE_OK; |
| 224 } |
| 225 -static void dlwCopy(DLWriter *pWriter, DLReader *pReader){ |
| 226 - dlwAppend(pWriter, dlrDocData(pReader), dlrDocDataBytes(pReader), |
| 227 - dlrDocid(pReader), dlrDocid(pReader)); |
| 228 +static int dlwCopy(DLWriter *pWriter, DLReader *pReader){ |
| 229 + return dlwAppend(pWriter, dlrDocData(pReader), dlrDocDataBytes(pReader), |
| 230 + dlrDocid(pReader), dlrDocid(pReader)); |
| 231 } |
| 232 static void dlwAdd(DLWriter *pWriter, sqlite_int64 iDocid){ |
| 233 char c[VARINT_MAX]; |
| 234 @@ -878,45 +931,63 @@ |
| 235 assert( !plrAtEnd(pReader) ); |
| 236 return pReader->iEndOffset; |
| 237 } |
| 238 -static void plrStep(PLReader *pReader){ |
| 239 - int i, n; |
| 240 +static int plrStep(PLReader *pReader){ |
| 241 + int i, n, nTotal = 0; |
| 242 |
| 243 assert( !plrAtEnd(pReader) ); |
| 244 |
| 245 - if( pReader->nData==0 ){ |
| 246 + if( pReader->nData<=0 ){ |
| 247 pReader->pData = NULL; |
| 248 - return; |
| 249 + return SQLITE_OK; |
| 250 } |
| 251 |
| 252 - n = getVarint32(pReader->pData, &i); |
| 253 + n = getVarint32Safe(pReader->pData, &i, pReader->nData); |
| 254 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 255 + nTotal += n; |
| 256 if( i==POS_COLUMN ){ |
| 257 - n += getVarint32(pReader->pData+n, &pReader->iColumn); |
| 258 + n = getVarint32Safe(pReader->pData+nTotal, &pReader->iColumn, |
| 259 + pReader->nData-nTotal); |
| 260 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 261 + nTotal += n; |
| 262 pReader->iPosition = 0; |
| 263 pReader->iStartOffset = 0; |
| 264 - n += getVarint32(pReader->pData+n, &i); |
| 265 + n = getVarint32Safe(pReader->pData+nTotal, &i, pReader->nData-nTotal); |
| 266 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 267 + nTotal += n; |
| 268 } |
| 269 /* Should never see adjacent column changes. */ |
| 270 assert( i!=POS_COLUMN ); |
| 271 |
| 272 if( i==POS_END ){ |
| 273 + assert( nTotal<=pReader->nData ); |
| 274 pReader->nData = 0; |
| 275 pReader->pData = NULL; |
| 276 - return; |
| 277 + return SQLITE_OK; |
| 278 } |
| 279 |
| 280 pReader->iPosition += i-POS_BASE; |
| 281 if( pReader->iType==DL_POSITIONS_OFFSETS ){ |
| 282 - n += getVarint32(pReader->pData+n, &i); |
| 283 + n = getVarint32Safe(pReader->pData+nTotal, &i, pReader->nData-nTotal); |
| 284 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 285 + nTotal += n; |
| 286 pReader->iStartOffset += i; |
| 287 - n += getVarint32(pReader->pData+n, &i); |
| 288 + n = getVarint32Safe(pReader->pData+nTotal, &i, pReader->nData-nTotal); |
| 289 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 290 + nTotal += n; |
| 291 pReader->iEndOffset = pReader->iStartOffset+i; |
| 292 } |
| 293 - assert( n<=pReader->nData ); |
| 294 - pReader->pData += n; |
| 295 - pReader->nData -= n; |
| 296 + assert( nTotal<=pReader->nData ); |
| 297 + pReader->pData += nTotal; |
| 298 + pReader->nData -= nTotal; |
| 299 + return SQLITE_OK; |
| 300 } |
| 301 |
| 302 -static void plrInit(PLReader *pReader, DLReader *pDLReader){ |
| 303 +static void plrDestroy(PLReader *pReader){ |
| 304 + SCRAMBLE(pReader); |
| 305 +} |
| 306 + |
| 307 +static int plrInit(PLReader *pReader, DLReader *pDLReader){ |
| 308 + int rc; |
| 309 pReader->pData = dlrPosData(pDLReader); |
| 310 pReader->nData = dlrPosDataLen(pDLReader); |
| 311 pReader->iType = pDLReader->iType; |
| 312 @@ -924,10 +995,9 @@ |
| 313 pReader->iPosition = 0; |
| 314 pReader->iStartOffset = 0; |
| 315 pReader->iEndOffset = 0; |
| 316 - plrStep(pReader); |
| 317 -} |
| 318 -static void plrDestroy(PLReader *pReader){ |
| 319 - SCRAMBLE(pReader); |
| 320 + rc = plrStep(pReader); |
| 321 + if( rc!=SQLITE_OK ) plrDestroy(pReader); |
| 322 + return rc; |
| 323 } |
| 324 |
| 325 /*******************************************************************/ |
| 326 @@ -1113,14 +1183,16 @@ |
| 327 ** deletion will be trimmed, and will thus not effect a deletion |
| 328 ** during the merge. |
| 329 */ |
| 330 -static void docListTrim(DocListType iType, const char *pData, int nData, |
| 331 - int iColumn, DocListType iOutType, DataBuffer *out){ |
| 332 +static int docListTrim(DocListType iType, const char *pData, int nData, |
| 333 + int iColumn, DocListType iOutType, DataBuffer *out){ |
| 334 DLReader dlReader; |
| 335 DLWriter dlWriter; |
| 336 + int rc; |
| 337 |
| 338 assert( iOutType<=iType ); |
| 339 |
| 340 - dlrInit(&dlReader, iType, pData, nData); |
| 341 + rc = dlrInit(&dlReader, iType, pData, nData); |
| 342 + if( rc!=SQLITE_OK ) return rc; |
| 343 dlwInit(&dlWriter, iOutType, out); |
| 344 |
| 345 while( !dlrAtEnd(&dlReader) ){ |
| 346 @@ -1128,7 +1200,8 @@ |
| 347 PLWriter plWriter; |
| 348 int match = 0; |
| 349 |
| 350 - plrInit(&plReader, &dlReader); |
| 351 + rc = plrInit(&plReader, &dlReader); |
| 352 + if( rc!=SQLITE_OK ) break; |
| 353 |
| 354 while( !plrAtEnd(&plReader) ){ |
| 355 if( iColumn==-1 || plrColumn(&plReader)==iColumn ){ |
| 356 @@ -1139,7 +1212,11 @@ |
| 357 plwAdd(&plWriter, plrColumn(&plReader), plrPosition(&plReader), |
| 358 plrStartOffset(&plReader), plrEndOffset(&plReader)); |
| 359 } |
| 360 - plrStep(&plReader); |
| 361 + rc = plrStep(&plReader); |
| 362 + if( rc!=SQLITE_OK ){ |
| 363 + plrDestroy(&plReader); |
| 364 + goto err; |
| 365 + } |
| 366 } |
| 367 if( match ){ |
| 368 plwTerminate(&plWriter); |
| 369 @@ -1147,10 +1224,13 @@ |
| 370 } |
| 371 |
| 372 plrDestroy(&plReader); |
| 373 - dlrStep(&dlReader); |
| 374 + rc = dlrStep(&dlReader); |
| 375 + if( rc!=SQLITE_OK ) break; |
| 376 } |
| 377 +err: |
| 378 dlwDestroy(&dlWriter); |
| 379 dlrDestroy(&dlReader); |
| 380 + return rc; |
| 381 } |
| 382 |
| 383 /* Used by docListMerge() to keep doclists in the ascending order by |
| 384 @@ -1207,19 +1287,20 @@ |
| 385 /* TODO(shess) nReaders must be <= MERGE_COUNT. This should probably |
| 386 ** be fixed. |
| 387 */ |
| 388 -static void docListMerge(DataBuffer *out, |
| 389 - DLReader *pReaders, int nReaders){ |
| 390 +static int docListMerge(DataBuffer *out, |
| 391 + DLReader *pReaders, int nReaders){ |
| 392 OrderedDLReader readers[MERGE_COUNT]; |
| 393 DLWriter writer; |
| 394 int i, n; |
| 395 const char *pStart = 0; |
| 396 int nStart = 0; |
| 397 sqlite_int64 iFirstDocid = 0, iLastDocid = 0; |
| 398 + int rc = SQLITE_OK; |
| 399 |
| 400 assert( nReaders>0 ); |
| 401 if( nReaders==1 ){ |
| 402 dataBufferAppend(out, dlrDocData(pReaders), dlrAllDataBytes(pReaders)); |
| 403 - return; |
| 404 + return SQLITE_OK; |
| 405 } |
| 406 |
| 407 assert( nReaders<=MERGE_COUNT ); |
| 408 @@ -1252,20 +1333,23 @@ |
| 409 nStart += dlrDocDataBytes(readers[0].pReader); |
| 410 }else{ |
| 411 if( pStart!=0 ){ |
| 412 - dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid); |
| 413 + rc = dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid); |
| 414 + if( rc!=SQLITE_OK ) goto err; |
| 415 } |
| 416 pStart = dlrDocData(readers[0].pReader); |
| 417 nStart = dlrDocDataBytes(readers[0].pReader); |
| 418 iFirstDocid = iDocid; |
| 419 } |
| 420 iLastDocid = iDocid; |
| 421 - dlrStep(readers[0].pReader); |
| 422 + rc = dlrStep(readers[0].pReader); |
| 423 + if( rc!=SQLITE_OK ) goto err; |
| 424 |
| 425 /* Drop all of the older elements with the same docid. */ |
| 426 for(i=1; i<nReaders && |
| 427 !dlrAtEnd(readers[i].pReader) && |
| 428 dlrDocid(readers[i].pReader)==iDocid; i++){ |
| 429 - dlrStep(readers[i].pReader); |
| 430 + rc = dlrStep(readers[i].pReader); |
| 431 + if( rc!=SQLITE_OK ) goto err; |
| 432 } |
| 433 |
| 434 /* Get the readers back into order. */ |
| 435 @@ -1275,8 +1359,11 @@ |
| 436 } |
| 437 |
| 438 /* Copy over any remaining elements. */ |
| 439 - if( nStart>0 ) dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid); |
| 440 + if( nStart>0 ) |
| 441 + rc = dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid); |
| 442 +err: |
| 443 dlwDestroy(&writer); |
| 444 + return rc; |
| 445 } |
| 446 |
| 447 /* Helper function for posListUnion(). Compares the current position |
| 448 @@ -1312,30 +1399,40 @@ |
| 449 ** work with any doclist type, though both inputs and the output |
| 450 ** should be the same type. |
| 451 */ |
| 452 -static void posListUnion(DLReader *pLeft, DLReader *pRight, DLWriter *pOut){ |
| 453 +static int posListUnion(DLReader *pLeft, DLReader *pRight, DLWriter *pOut){ |
| 454 PLReader left, right; |
| 455 PLWriter writer; |
| 456 + int rc; |
| 457 |
| 458 assert( dlrDocid(pLeft)==dlrDocid(pRight) ); |
| 459 assert( pLeft->iType==pRight->iType ); |
| 460 assert( pLeft->iType==pOut->iType ); |
| 461 |
| 462 - plrInit(&left, pLeft); |
| 463 - plrInit(&right, pRight); |
| 464 + rc = plrInit(&left, pLeft); |
| 465 + if( rc != SQLITE_OK ) return rc; |
| 466 + rc = plrInit(&right, pRight); |
| 467 + if( rc != SQLITE_OK ){ |
| 468 + plrDestroy(&left); |
| 469 + return rc; |
| 470 + } |
| 471 plwInit(&writer, pOut, dlrDocid(pLeft)); |
| 472 |
| 473 while( !plrAtEnd(&left) || !plrAtEnd(&right) ){ |
| 474 int c = posListCmp(&left, &right); |
| 475 if( c<0 ){ |
| 476 plwCopy(&writer, &left); |
| 477 - plrStep(&left); |
| 478 + rc = plrStep(&left); |
| 479 + if( rc != SQLITE_OK ) break; |
| 480 }else if( c>0 ){ |
| 481 plwCopy(&writer, &right); |
| 482 - plrStep(&right); |
| 483 + rc = plrStep(&right); |
| 484 + if( rc != SQLITE_OK ) break; |
| 485 }else{ |
| 486 plwCopy(&writer, &left); |
| 487 - plrStep(&left); |
| 488 - plrStep(&right); |
| 489 + rc = plrStep(&left); |
| 490 + if( rc != SQLITE_OK ) break; |
| 491 + rc = plrStep(&right); |
| 492 + if( rc != SQLITE_OK ) break; |
| 493 } |
| 494 } |
| 495 |
| 496 @@ -1343,56 +1440,75 @@ |
| 497 plwDestroy(&writer); |
| 498 plrDestroy(&left); |
| 499 plrDestroy(&right); |
| 500 + return rc; |
| 501 } |
| 502 |
| 503 /* Write the union of doclists in pLeft and pRight to pOut. For |
| 504 ** docids in common between the inputs, the union of the position |
| 505 ** lists is written. Inputs and outputs are always type DL_DEFAULT. |
| 506 */ |
| 507 -static void docListUnion( |
| 508 +static int docListUnion( |
| 509 const char *pLeft, int nLeft, |
| 510 const char *pRight, int nRight, |
| 511 DataBuffer *pOut /* Write the combined doclist here */ |
| 512 ){ |
| 513 DLReader left, right; |
| 514 DLWriter writer; |
| 515 + int rc; |
| 516 |
| 517 if( nLeft==0 ){ |
| 518 if( nRight!=0) dataBufferAppend(pOut, pRight, nRight); |
| 519 - return; |
| 520 + return SQLITE_OK; |
| 521 } |
| 522 if( nRight==0 ){ |
| 523 dataBufferAppend(pOut, pLeft, nLeft); |
| 524 - return; |
| 525 + return SQLITE_OK; |
| 526 } |
| 527 |
| 528 - dlrInit(&left, DL_DEFAULT, pLeft, nLeft); |
| 529 - dlrInit(&right, DL_DEFAULT, pRight, nRight); |
| 530 + rc = dlrInit(&left, DL_DEFAULT, pLeft, nLeft); |
| 531 + if( rc!=SQLITE_OK ) return rc; |
| 532 + rc = dlrInit(&right, DL_DEFAULT, pRight, nRight); |
| 533 + if( rc!=SQLITE_OK ){ |
| 534 + dlrDestroy(&left); |
| 535 + return rc; |
| 536 + } |
| 537 dlwInit(&writer, DL_DEFAULT, pOut); |
| 538 |
| 539 while( !dlrAtEnd(&left) || !dlrAtEnd(&right) ){ |
| 540 if( dlrAtEnd(&right) ){ |
| 541 - dlwCopy(&writer, &left); |
| 542 - dlrStep(&left); |
| 543 + rc = dlwCopy(&writer, &left); |
| 544 + if( rc!=SQLITE_OK ) break; |
| 545 + rc = dlrStep(&left); |
| 546 + if( rc!=SQLITE_OK ) break; |
| 547 }else if( dlrAtEnd(&left) ){ |
| 548 - dlwCopy(&writer, &right); |
| 549 - dlrStep(&right); |
| 550 + rc = dlwCopy(&writer, &right); |
| 551 + if( rc!=SQLITE_OK ) break; |
| 552 + rc = dlrStep(&right); |
| 553 + if( rc!=SQLITE_OK ) break; |
| 554 }else if( dlrDocid(&left)<dlrDocid(&right) ){ |
| 555 - dlwCopy(&writer, &left); |
| 556 - dlrStep(&left); |
| 557 + rc = dlwCopy(&writer, &left); |
| 558 + if( rc!=SQLITE_OK ) break; |
| 559 + rc = dlrStep(&left); |
| 560 + if( rc!=SQLITE_OK ) break; |
| 561 }else if( dlrDocid(&left)>dlrDocid(&right) ){ |
| 562 - dlwCopy(&writer, &right); |
| 563 - dlrStep(&right); |
| 564 + rc = dlwCopy(&writer, &right); |
| 565 + if( rc!=SQLITE_OK ) break; |
| 566 + rc = dlrStep(&right); |
| 567 + if( rc!=SQLITE_OK ) break; |
| 568 }else{ |
| 569 - posListUnion(&left, &right, &writer); |
| 570 - dlrStep(&left); |
| 571 - dlrStep(&right); |
| 572 + rc = posListUnion(&left, &right, &writer); |
| 573 + if( rc!=SQLITE_OK ) break; |
| 574 + rc = dlrStep(&left); |
| 575 + if( rc!=SQLITE_OK ) break; |
| 576 + rc = dlrStep(&right); |
| 577 + if( rc!=SQLITE_OK ) break; |
| 578 } |
| 579 } |
| 580 |
| 581 dlrDestroy(&left); |
| 582 dlrDestroy(&right); |
| 583 dlwDestroy(&writer); |
| 584 + return rc; |
| 585 } |
| 586 |
| 587 /* pLeft and pRight are DLReaders positioned to the same docid. |
| 588 @@ -1407,35 +1523,47 @@ |
| 589 ** include the positions from pRight that are one more than a |
| 590 ** position in pLeft. In other words: pRight.iPos==pLeft.iPos+1. |
| 591 */ |
| 592 -static void posListPhraseMerge(DLReader *pLeft, DLReader *pRight, |
| 593 - DLWriter *pOut){ |
| 594 +static int posListPhraseMerge(DLReader *pLeft, DLReader *pRight, |
| 595 + DLWriter *pOut){ |
| 596 PLReader left, right; |
| 597 PLWriter writer; |
| 598 int match = 0; |
| 599 + int rc; |
| 600 |
| 601 assert( dlrDocid(pLeft)==dlrDocid(pRight) ); |
| 602 assert( pOut->iType!=DL_POSITIONS_OFFSETS ); |
| 603 |
| 604 - plrInit(&left, pLeft); |
| 605 - plrInit(&right, pRight); |
| 606 + rc = plrInit(&left, pLeft); |
| 607 + if( rc!=SQLITE_OK ) return rc; |
| 608 + rc = plrInit(&right, pRight); |
| 609 + if( rc!=SQLITE_OK ){ |
| 610 + plrDestroy(&left); |
| 611 + return rc; |
| 612 + } |
| 613 |
| 614 while( !plrAtEnd(&left) && !plrAtEnd(&right) ){ |
| 615 if( plrColumn(&left)<plrColumn(&right) ){ |
| 616 - plrStep(&left); |
| 617 + rc = plrStep(&left); |
| 618 + if( rc!=SQLITE_OK ) break; |
| 619 }else if( plrColumn(&left)>plrColumn(&right) ){ |
| 620 - plrStep(&right); |
| 621 + rc = plrStep(&right); |
| 622 + if( rc!=SQLITE_OK ) break; |
| 623 }else if( plrPosition(&left)+1<plrPosition(&right) ){ |
| 624 - plrStep(&left); |
| 625 + rc = plrStep(&left); |
| 626 + if( rc!=SQLITE_OK ) break; |
| 627 }else if( plrPosition(&left)+1>plrPosition(&right) ){ |
| 628 - plrStep(&right); |
| 629 + rc = plrStep(&right); |
| 630 + if( rc!=SQLITE_OK ) break; |
| 631 }else{ |
| 632 if( !match ){ |
| 633 plwInit(&writer, pOut, dlrDocid(pLeft)); |
| 634 match = 1; |
| 635 } |
| 636 plwAdd(&writer, plrColumn(&right), plrPosition(&right), 0, 0); |
| 637 - plrStep(&left); |
| 638 - plrStep(&right); |
| 639 + rc = plrStep(&left); |
| 640 + if( rc!=SQLITE_OK ) break; |
| 641 + rc = plrStep(&right); |
| 642 + if( rc!=SQLITE_OK ) break; |
| 643 } |
| 644 } |
| 645 |
| 646 @@ -1446,6 +1574,7 @@ |
| 647 |
| 648 plrDestroy(&left); |
| 649 plrDestroy(&right); |
| 650 + return rc; |
| 651 } |
| 652 |
| 653 /* We have two doclists with positions: pLeft and pRight. |
| 654 @@ -1457,7 +1586,7 @@ |
| 655 ** iType controls the type of data written to pOut. If iType is |
| 656 ** DL_POSITIONS, the positions are those from pRight. |
| 657 */ |
| 658 -static void docListPhraseMerge( |
| 659 +static int docListPhraseMerge( |
| 660 const char *pLeft, int nLeft, |
| 661 const char *pRight, int nRight, |
| 662 DocListType iType, |
| 663 @@ -1465,152 +1594,198 @@ |
| 664 ){ |
| 665 DLReader left, right; |
| 666 DLWriter writer; |
| 667 + int rc; |
| 668 |
| 669 - if( nLeft==0 || nRight==0 ) return; |
| 670 + if( nLeft==0 || nRight==0 ) return SQLITE_OK; |
| 671 |
| 672 assert( iType!=DL_POSITIONS_OFFSETS ); |
| 673 |
| 674 - dlrInit(&left, DL_POSITIONS, pLeft, nLeft); |
| 675 - dlrInit(&right, DL_POSITIONS, pRight, nRight); |
| 676 + rc = dlrInit(&left, DL_POSITIONS, pLeft, nLeft); |
| 677 + if( rc!=SQLITE_OK ) return rc; |
| 678 + rc = dlrInit(&right, DL_POSITIONS, pRight, nRight); |
| 679 + if( rc!=SQLITE_OK ){ |
| 680 + dlrDestroy(&left); |
| 681 + return rc; |
| 682 + } |
| 683 dlwInit(&writer, iType, pOut); |
| 684 |
| 685 while( !dlrAtEnd(&left) && !dlrAtEnd(&right) ){ |
| 686 if( dlrDocid(&left)<dlrDocid(&right) ){ |
| 687 - dlrStep(&left); |
| 688 + rc = dlrStep(&left); |
| 689 + if( rc!=SQLITE_OK ) break; |
| 690 }else if( dlrDocid(&right)<dlrDocid(&left) ){ |
| 691 - dlrStep(&right); |
| 692 + rc = dlrStep(&right); |
| 693 + if( rc!=SQLITE_OK ) break; |
| 694 }else{ |
| 695 - posListPhraseMerge(&left, &right, &writer); |
| 696 - dlrStep(&left); |
| 697 - dlrStep(&right); |
| 698 + rc = posListPhraseMerge(&left, &right, &writer); |
| 699 + if( rc!=SQLITE_OK ) break; |
| 700 + rc = dlrStep(&left); |
| 701 + if( rc!=SQLITE_OK ) break; |
| 702 + rc = dlrStep(&right); |
| 703 + if( rc!=SQLITE_OK ) break; |
| 704 } |
| 705 } |
| 706 |
| 707 dlrDestroy(&left); |
| 708 dlrDestroy(&right); |
| 709 dlwDestroy(&writer); |
| 710 + return rc; |
| 711 } |
| 712 |
| 713 /* We have two DL_DOCIDS doclists: pLeft and pRight. |
| 714 ** Write the intersection of these two doclists into pOut as a |
| 715 ** DL_DOCIDS doclist. |
| 716 */ |
| 717 -static void docListAndMerge( |
| 718 +static int docListAndMerge( |
| 719 const char *pLeft, int nLeft, |
| 720 const char *pRight, int nRight, |
| 721 DataBuffer *pOut /* Write the combined doclist here */ |
| 722 ){ |
| 723 DLReader left, right; |
| 724 DLWriter writer; |
| 725 + int rc; |
| 726 |
| 727 - if( nLeft==0 || nRight==0 ) return; |
| 728 + if( nLeft==0 || nRight==0 ) return SQLITE_OK; |
| 729 |
| 730 - dlrInit(&left, DL_DOCIDS, pLeft, nLeft); |
| 731 - dlrInit(&right, DL_DOCIDS, pRight, nRight); |
| 732 + rc = dlrInit(&left, DL_DOCIDS, pLeft, nLeft); |
| 733 + if( rc!=SQLITE_OK ) return rc; |
| 734 + rc = dlrInit(&right, DL_DOCIDS, pRight, nRight); |
| 735 + if( rc!=SQLITE_OK ){ |
| 736 + dlrDestroy(&left); |
| 737 + return rc; |
| 738 + } |
| 739 dlwInit(&writer, DL_DOCIDS, pOut); |
| 740 |
| 741 while( !dlrAtEnd(&left) && !dlrAtEnd(&right) ){ |
| 742 if( dlrDocid(&left)<dlrDocid(&right) ){ |
| 743 - dlrStep(&left); |
| 744 + rc = dlrStep(&left); |
| 745 + if( rc!=SQLITE_OK ) break; |
| 746 }else if( dlrDocid(&right)<dlrDocid(&left) ){ |
| 747 - dlrStep(&right); |
| 748 + rc = dlrStep(&right); |
| 749 + if( rc!=SQLITE_OK ) break; |
| 750 }else{ |
| 751 dlwAdd(&writer, dlrDocid(&left)); |
| 752 - dlrStep(&left); |
| 753 - dlrStep(&right); |
| 754 + rc = dlrStep(&left); |
| 755 + if( rc!=SQLITE_OK ) break; |
| 756 + rc = dlrStep(&right); |
| 757 + if( rc!=SQLITE_OK ) break; |
| 758 } |
| 759 } |
| 760 |
| 761 dlrDestroy(&left); |
| 762 dlrDestroy(&right); |
| 763 dlwDestroy(&writer); |
| 764 + return rc; |
| 765 } |
| 766 |
| 767 /* We have two DL_DOCIDS doclists: pLeft and pRight. |
| 768 ** Write the union of these two doclists into pOut as a |
| 769 ** DL_DOCIDS doclist. |
| 770 */ |
| 771 -static void docListOrMerge( |
| 772 +static int docListOrMerge( |
| 773 const char *pLeft, int nLeft, |
| 774 const char *pRight, int nRight, |
| 775 DataBuffer *pOut /* Write the combined doclist here */ |
| 776 ){ |
| 777 DLReader left, right; |
| 778 DLWriter writer; |
| 779 + int rc; |
| 780 |
| 781 if( nLeft==0 ){ |
| 782 if( nRight!=0 ) dataBufferAppend(pOut, pRight, nRight); |
| 783 - return; |
| 784 + return SQLITE_OK; |
| 785 } |
| 786 if( nRight==0 ){ |
| 787 dataBufferAppend(pOut, pLeft, nLeft); |
| 788 - return; |
| 789 + return SQLITE_OK; |
| 790 } |
| 791 |
| 792 - dlrInit(&left, DL_DOCIDS, pLeft, nLeft); |
| 793 - dlrInit(&right, DL_DOCIDS, pRight, nRight); |
| 794 + rc = dlrInit(&left, DL_DOCIDS, pLeft, nLeft); |
| 795 + if( rc!=SQLITE_OK ) return rc; |
| 796 + rc = dlrInit(&right, DL_DOCIDS, pRight, nRight); |
| 797 + if( rc!=SQLITE_OK ){ |
| 798 + dlrDestroy(&left); |
| 799 + return rc; |
| 800 + } |
| 801 dlwInit(&writer, DL_DOCIDS, pOut); |
| 802 |
| 803 while( !dlrAtEnd(&left) || !dlrAtEnd(&right) ){ |
| 804 if( dlrAtEnd(&right) ){ |
| 805 dlwAdd(&writer, dlrDocid(&left)); |
| 806 - dlrStep(&left); |
| 807 + rc = dlrStep(&left); |
| 808 + if( rc!=SQLITE_OK ) break; |
| 809 }else if( dlrAtEnd(&left) ){ |
| 810 dlwAdd(&writer, dlrDocid(&right)); |
| 811 - dlrStep(&right); |
| 812 + rc = dlrStep(&right); |
| 813 + if( rc!=SQLITE_OK ) break; |
| 814 }else if( dlrDocid(&left)<dlrDocid(&right) ){ |
| 815 dlwAdd(&writer, dlrDocid(&left)); |
| 816 - dlrStep(&left); |
| 817 + rc = dlrStep(&left); |
| 818 + if( rc!=SQLITE_OK ) break; |
| 819 }else if( dlrDocid(&right)<dlrDocid(&left) ){ |
| 820 dlwAdd(&writer, dlrDocid(&right)); |
| 821 - dlrStep(&right); |
| 822 + rc = dlrStep(&right); |
| 823 + if( rc!=SQLITE_OK ) break; |
| 824 }else{ |
| 825 dlwAdd(&writer, dlrDocid(&left)); |
| 826 - dlrStep(&left); |
| 827 - dlrStep(&right); |
| 828 + rc = dlrStep(&left); |
| 829 + if( rc!=SQLITE_OK ) break; |
| 830 + rc = dlrStep(&right); |
| 831 + if( rc!=SQLITE_OK ) break; |
| 832 } |
| 833 } |
| 834 |
| 835 dlrDestroy(&left); |
| 836 dlrDestroy(&right); |
| 837 dlwDestroy(&writer); |
| 838 + return rc; |
| 839 } |
| 840 |
| 841 /* We have two DL_DOCIDS doclists: pLeft and pRight. |
| 842 ** Write into pOut as DL_DOCIDS doclist containing all documents that |
| 843 ** occur in pLeft but not in pRight. |
| 844 */ |
| 845 -static void docListExceptMerge( |
| 846 +static int docListExceptMerge( |
| 847 const char *pLeft, int nLeft, |
| 848 const char *pRight, int nRight, |
| 849 DataBuffer *pOut /* Write the combined doclist here */ |
| 850 ){ |
| 851 DLReader left, right; |
| 852 DLWriter writer; |
| 853 + int rc; |
| 854 |
| 855 - if( nLeft==0 ) return; |
| 856 + if( nLeft==0 ) return SQLITE_OK; |
| 857 if( nRight==0 ){ |
| 858 dataBufferAppend(pOut, pLeft, nLeft); |
| 859 - return; |
| 860 + return SQLITE_OK; |
| 861 } |
| 862 |
| 863 - dlrInit(&left, DL_DOCIDS, pLeft, nLeft); |
| 864 - dlrInit(&right, DL_DOCIDS, pRight, nRight); |
| 865 + rc = dlrInit(&left, DL_DOCIDS, pLeft, nLeft); |
| 866 + if( rc!=SQLITE_OK ) return rc; |
| 867 + rc = dlrInit(&right, DL_DOCIDS, pRight, nRight); |
| 868 + if( rc!=SQLITE_OK ){ |
| 869 + dlrDestroy(&left); |
| 870 + return rc; |
| 871 + } |
| 872 dlwInit(&writer, DL_DOCIDS, pOut); |
| 873 |
| 874 while( !dlrAtEnd(&left) ){ |
| 875 while( !dlrAtEnd(&right) && dlrDocid(&right)<dlrDocid(&left) ){ |
| 876 - dlrStep(&right); |
| 877 + rc = dlrStep(&right); |
| 878 + if( rc!=SQLITE_OK ) goto err; |
| 879 } |
| 880 if( dlrAtEnd(&right) || dlrDocid(&left)<dlrDocid(&right) ){ |
| 881 dlwAdd(&writer, dlrDocid(&left)); |
| 882 } |
| 883 - dlrStep(&left); |
| 884 + rc = dlrStep(&left); |
| 885 + if( rc!=SQLITE_OK ) break; |
| 886 } |
| 887 |
| 888 +err: |
| 889 dlrDestroy(&left); |
| 890 dlrDestroy(&right); |
| 891 dlwDestroy(&writer); |
| 892 + return rc; |
| 893 } |
| 894 |
| 895 static char *string_dup_n(const char *s, int n){ |
| 896 @@ -1814,7 +1989,7 @@ |
| 897 /* SEGDIR_MAX_INDEX */ "select max(idx) from %_segdir where level = ?", |
| 898 /* SEGDIR_SET */ "insert into %_segdir values (?, ?, ?, ?, ?, ?)", |
| 899 /* SEGDIR_SELECT_LEVEL */ |
| 900 - "select start_block, leaves_end_block, root from %_segdir " |
| 901 + "select start_block, leaves_end_block, root, idx from %_segdir " |
| 902 " where level = ? order by idx", |
| 903 /* SEGDIR_SPAN */ |
| 904 "select min(start_block), max(end_block) from %_segdir " |
| 905 @@ -3413,7 +3588,8 @@ |
| 906 return SQLITE_OK; |
| 907 } |
| 908 rc = sqlite3_bind_int64(c->pStmt, 1, dlrDocid(&c->reader)); |
| 909 - dlrStep(&c->reader); |
| 910 + if( rc!=SQLITE_OK ) return rc; |
| 911 + rc = dlrStep(&c->reader); |
| 912 if( rc!=SQLITE_OK ) return rc; |
| 913 /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */ |
| 914 rc = sqlite3_step(c->pStmt); |
| 915 @@ -3421,8 +3597,11 @@ |
| 916 c->eof = 0; |
| 917 return SQLITE_OK; |
| 918 } |
| 919 - /* an error occurred; abort */ |
| 920 - return rc==SQLITE_DONE ? SQLITE_ERROR : rc; |
| 921 + |
| 922 + /* Corrupt if the index refers to missing document. */ |
| 923 + if( rc==SQLITE_DONE ) return SQLITE_CORRUPT_BKPT; |
| 924 + |
| 925 + return rc; |
| 926 } |
| 927 } |
| 928 |
| 929 @@ -3470,14 +3649,18 @@ |
| 930 return rc; |
| 931 } |
| 932 dataBufferInit(&new, 0); |
| 933 - docListPhraseMerge(left.pData, left.nData, right.pData, right.nData, |
| 934 - i<pQTerm->nPhrase ? DL_POSITIONS : DL_DOCIDS, &new); |
| 935 + rc = docListPhraseMerge(left.pData, left.nData, right.pData, right.nData, |
| 936 + i<pQTerm->nPhrase ? DL_POSITIONS : DL_DOCIDS, &new)
; |
| 937 dataBufferDestroy(&left); |
| 938 dataBufferDestroy(&right); |
| 939 + if( rc!=SQLITE_OK ){ |
| 940 + dataBufferDestroy(&new); |
| 941 + return rc; |
| 942 + } |
| 943 left = new; |
| 944 } |
| 945 *pResult = left; |
| 946 - return SQLITE_OK; |
| 947 + return rc; |
| 948 } |
| 949 |
| 950 /* Add a new term pTerm[0..nTerm-1] to the query *q. |
| 951 @@ -3544,6 +3727,7 @@ |
| 952 int firstIndex = pQuery->nTerms; |
| 953 int iCol; |
| 954 int nTerm = 1; |
| 955 + int iEndLast = -1; |
| 956 |
| 957 int rc = pModule->xOpen(pTokenizer, pSegment, nSegment, &pCursor); |
| 958 if( rc!=SQLITE_OK ) return rc; |
| 959 @@ -3568,6 +3752,20 @@ |
| 960 pQuery->nextIsOr = 1; |
| 961 continue; |
| 962 } |
| 963 + |
| 964 + /* |
| 965 + * The ICU tokenizer considers '*' a break character, so the code below |
| 966 + * sets isPrefix correctly, but since that code doesn't eat the '*', the |
| 967 + * ICU tokenizer returns it as the next token. So eat it here until a |
| 968 + * better solution presents itself. |
| 969 + */ |
| 970 + if( pQuery->nTerms>0 && nToken==1 && pSegment[iBegin]=='*' && |
| 971 + iEndLast==iBegin){ |
| 972 + pQuery->pTerms[pQuery->nTerms-1].isPrefix = 1; |
| 973 + continue; |
| 974 + } |
| 975 + iEndLast = iEnd; |
| 976 + |
| 977 queryAdd(pQuery, pToken, nToken); |
| 978 if( !inPhrase && iBegin>0 && pSegment[iBegin-1]=='-' ){ |
| 979 pQuery->pTerms[pQuery->nTerms-1].isNot = 1; |
| 980 @@ -3707,18 +3905,30 @@ |
| 981 return rc; |
| 982 } |
| 983 dataBufferInit(&new, 0); |
| 984 - docListOrMerge(right.pData, right.nData, or.pData, or.nData, &new); |
| 985 + rc = docListOrMerge(right.pData, right.nData, or.pData, or.nData, &new); |
| 986 dataBufferDestroy(&right); |
| 987 dataBufferDestroy(&or); |
| 988 + if( rc!=SQLITE_OK ){ |
| 989 + if( i!=nNot ) dataBufferDestroy(&left); |
| 990 + queryClear(pQuery); |
| 991 + dataBufferDestroy(&new); |
| 992 + return rc; |
| 993 + } |
| 994 right = new; |
| 995 } |
| 996 if( i==nNot ){ /* first term processed. */ |
| 997 left = right; |
| 998 }else{ |
| 999 dataBufferInit(&new, 0); |
| 1000 - docListAndMerge(left.pData, left.nData, right.pData, right.nData, &new); |
| 1001 + rc = docListAndMerge(left.pData, left.nData, |
| 1002 + right.pData, right.nData, &new); |
| 1003 dataBufferDestroy(&right); |
| 1004 dataBufferDestroy(&left); |
| 1005 + if( rc!=SQLITE_OK ){ |
| 1006 + queryClear(pQuery); |
| 1007 + dataBufferDestroy(&new); |
| 1008 + return rc; |
| 1009 + } |
| 1010 left = new; |
| 1011 } |
| 1012 } |
| 1013 @@ -3738,9 +3948,15 @@ |
| 1014 return rc; |
| 1015 } |
| 1016 dataBufferInit(&new, 0); |
| 1017 - docListExceptMerge(left.pData, left.nData, right.pData, right.nData, &new); |
| 1018 + rc = docListExceptMerge(left.pData, left.nData, |
| 1019 + right.pData, right.nData, &new); |
| 1020 dataBufferDestroy(&right); |
| 1021 dataBufferDestroy(&left); |
| 1022 + if( rc!=SQLITE_OK ){ |
| 1023 + queryClear(pQuery); |
| 1024 + dataBufferDestroy(&new); |
| 1025 + return rc; |
| 1026 + } |
| 1027 left = new; |
| 1028 } |
| 1029 |
| 1030 @@ -3834,7 +4050,8 @@ |
| 1031 rc = fulltextQuery(v, idxNum-QUERY_FULLTEXT, zQuery, -1, &c->result, &c->
q); |
| 1032 if( rc!=SQLITE_OK ) return rc; |
| 1033 if( c->result.nData!=0 ){ |
| 1034 - dlrInit(&c->reader, DL_DOCIDS, c->result.pData, c->result.nData); |
| 1035 + rc = dlrInit(&c->reader, DL_DOCIDS, c->result.pData, c->result.nData); |
| 1036 + if( rc!=SQLITE_OK ) return rc; |
| 1037 } |
| 1038 break; |
| 1039 } |
| 1040 @@ -4335,22 +4552,19 @@ |
| 1041 SCRAMBLE(pReader); |
| 1042 } |
| 1043 |
| 1044 -/* TODO(shess) The assertions are great, but what if we're in NDEBUG |
| 1045 -** and the blob is empty or otherwise contains suspect data? |
| 1046 -*/ |
| 1047 -static void interiorReaderInit(const char *pData, int nData, |
| 1048 - InteriorReader *pReader){ |
| 1049 +static int interiorReaderInit(const char *pData, int nData, |
| 1050 + InteriorReader *pReader){ |
| 1051 int n, nTerm; |
| 1052 |
| 1053 - /* Require at least the leading flag byte */ |
| 1054 + /* These conditions are checked and met by the callers. */ |
| 1055 assert( nData>0 ); |
| 1056 assert( pData[0]!='\0' ); |
| 1057 |
| 1058 CLEAR(pReader); |
| 1059 |
| 1060 /* Decode the base blockid, and set the cursor to the first term. */ |
| 1061 - n = getVarint(pData+1, &pReader->iBlockid); |
| 1062 - assert( 1+n<=nData ); |
| 1063 + n = getVarintSafe(pData+1, &pReader->iBlockid, nData-1); |
| 1064 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 1065 pReader->pData = pData+1+n; |
| 1066 pReader->nData = nData-(1+n); |
| 1067 |
| 1068 @@ -4361,17 +4575,18 @@ |
| 1069 if( pReader->nData==0 ){ |
| 1070 dataBufferInit(&pReader->term, 0); |
| 1071 }else{ |
| 1072 - n = getVarint32(pReader->pData, &nTerm); |
| 1073 + n = getVarint32Safe(pReader->pData, &nTerm, pReader->nData); |
| 1074 + if( !n || nTerm<0 || nTerm>pReader->nData-n) return SQLITE_CORRUPT_BKPT; |
| 1075 dataBufferInit(&pReader->term, nTerm); |
| 1076 dataBufferReplace(&pReader->term, pReader->pData+n, nTerm); |
| 1077 - assert( n+nTerm<=pReader->nData ); |
| 1078 pReader->pData += n+nTerm; |
| 1079 pReader->nData -= n+nTerm; |
| 1080 } |
| 1081 + return SQLITE_OK; |
| 1082 } |
| 1083 |
| 1084 static int interiorReaderAtEnd(InteriorReader *pReader){ |
| 1085 - return pReader->term.nData==0; |
| 1086 + return pReader->term.nData<=0; |
| 1087 } |
| 1088 |
| 1089 static sqlite_int64 interiorReaderCurrentBlockid(InteriorReader *pReader){ |
| 1090 @@ -4388,7 +4603,7 @@ |
| 1091 } |
| 1092 |
| 1093 /* Step forward to the next term in the node. */ |
| 1094 -static void interiorReaderStep(InteriorReader *pReader){ |
| 1095 +static int interiorReaderStep(InteriorReader *pReader){ |
| 1096 assert( !interiorReaderAtEnd(pReader) ); |
| 1097 |
| 1098 /* If the last term has been read, signal eof, else construct the |
| 1099 @@ -4399,18 +4614,26 @@ |
| 1100 }else{ |
| 1101 int n, nPrefix, nSuffix; |
| 1102 |
| 1103 - n = getVarint32(pReader->pData, &nPrefix); |
| 1104 - n += getVarint32(pReader->pData+n, &nSuffix); |
| 1105 + n = getVarint32Safe(pReader->pData, &nPrefix, pReader->nData); |
| 1106 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 1107 + pReader->nData -= n; |
| 1108 + pReader->pData += n; |
| 1109 + n = getVarint32Safe(pReader->pData, &nSuffix, pReader->nData); |
| 1110 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 1111 + pReader->nData -= n; |
| 1112 + pReader->pData += n; |
| 1113 + if( nSuffix<0 || nSuffix>pReader->nData ) return SQLITE_CORRUPT_BKPT; |
| 1114 + if( nPrefix<0 || nPrefix>pReader->term.nData ) return SQLITE_CORRUPT_BKPT; |
| 1115 |
| 1116 /* Truncate the current term and append suffix data. */ |
| 1117 pReader->term.nData = nPrefix; |
| 1118 - dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix); |
| 1119 + dataBufferAppend(&pReader->term, pReader->pData, nSuffix); |
| 1120 |
| 1121 - assert( n+nSuffix<=pReader->nData ); |
| 1122 - pReader->pData += n+nSuffix; |
| 1123 - pReader->nData -= n+nSuffix; |
| 1124 + pReader->pData += nSuffix; |
| 1125 + pReader->nData -= nSuffix; |
| 1126 } |
| 1127 pReader->iBlockid++; |
| 1128 + return SQLITE_OK; |
| 1129 } |
| 1130 |
| 1131 /* Compare the current term to pTerm[nTerm], returning strcmp-style |
| 1132 @@ -4782,7 +5005,8 @@ |
| 1133 n = putVarint(c, nData); |
| 1134 dataBufferAppend(&pWriter->data, c, n); |
| 1135 |
| 1136 - docListMerge(&pWriter->data, pReaders, nReaders); |
| 1137 + rc = docListMerge(&pWriter->data, pReaders, nReaders); |
| 1138 + if( rc!= SQLITE_OK ) return rc; |
| 1139 ASSERT_VALID_DOCLIST(DL_DEFAULT, |
| 1140 pWriter->data.pData+iDoclistData+n, |
| 1141 pWriter->data.nData-iDoclistData-n, NULL); |
| 1142 @@ -4892,7 +5116,8 @@ |
| 1143 int rc; |
| 1144 DLReader reader; |
| 1145 |
| 1146 - dlrInit(&reader, DL_DEFAULT, pData, nData); |
| 1147 + rc = dlrInit(&reader, DL_DEFAULT, pData, nData); |
| 1148 + if( rc!=SQLITE_OK ) return rc; |
| 1149 rc = leafWriterStepMerge(v, pWriter, pTerm, nTerm, &reader, 1); |
| 1150 dlrDestroy(&reader); |
| 1151 |
| 1152 @@ -4937,38 +5162,40 @@ |
| 1153 static const char *leafReaderData(LeafReader *pReader){ |
| 1154 int n, nData; |
| 1155 assert( pReader->term.nData>0 ); |
| 1156 - n = getVarint32(pReader->pData, &nData); |
| 1157 + n = getVarint32Safe(pReader->pData, &nData, pReader->nData); |
| 1158 + if( !n || nData>pReader->nData-n ) return NULL; |
| 1159 return pReader->pData+n; |
| 1160 } |
| 1161 |
| 1162 -static void leafReaderInit(const char *pData, int nData, |
| 1163 - LeafReader *pReader){ |
| 1164 +static int leafReaderInit(const char *pData, int nData, LeafReader *pReader){ |
| 1165 int nTerm, n; |
| 1166 |
| 1167 + /* All callers check this precondition. */ |
| 1168 assert( nData>0 ); |
| 1169 assert( pData[0]=='\0' ); |
| 1170 |
| 1171 CLEAR(pReader); |
| 1172 |
| 1173 /* Read the first term, skipping the header byte. */ |
| 1174 - n = getVarint32(pData+1, &nTerm); |
| 1175 + n = getVarint32Safe(pData+1, &nTerm, nData-1); |
| 1176 + if( !n || nTerm<0 || nTerm>nData-1-n ) return SQLITE_CORRUPT_BKPT; |
| 1177 dataBufferInit(&pReader->term, nTerm); |
| 1178 dataBufferReplace(&pReader->term, pData+1+n, nTerm); |
| 1179 |
| 1180 /* Position after the first term. */ |
| 1181 - assert( 1+n+nTerm<nData ); |
| 1182 pReader->pData = pData+1+n+nTerm; |
| 1183 pReader->nData = nData-1-n-nTerm; |
| 1184 + return SQLITE_OK; |
| 1185 } |
| 1186 |
| 1187 /* Step the reader forward to the next term. */ |
| 1188 -static void leafReaderStep(LeafReader *pReader){ |
| 1189 +static int leafReaderStep(LeafReader *pReader){ |
| 1190 int n, nData, nPrefix, nSuffix; |
| 1191 assert( !leafReaderAtEnd(pReader) ); |
| 1192 |
| 1193 /* Skip previous entry's data block. */ |
| 1194 - n = getVarint32(pReader->pData, &nData); |
| 1195 - assert( n+nData<=pReader->nData ); |
| 1196 + n = getVarint32Safe(pReader->pData, &nData, pReader->nData); |
| 1197 + if( !n || nData<0 || nData>pReader->nData-n ) return SQLITE_CORRUPT_BKPT; |
| 1198 pReader->pData += n+nData; |
| 1199 pReader->nData -= n+nData; |
| 1200 |
| 1201 @@ -4976,15 +5203,23 @@ |
| 1202 /* Construct the new term using a prefix from the old term plus a |
| 1203 ** suffix from the leaf data. |
| 1204 */ |
| 1205 - n = getVarint32(pReader->pData, &nPrefix); |
| 1206 - n += getVarint32(pReader->pData+n, &nSuffix); |
| 1207 - assert( n+nSuffix<pReader->nData ); |
| 1208 + n = getVarint32Safe(pReader->pData, &nPrefix, pReader->nData); |
| 1209 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 1210 + pReader->nData -= n; |
| 1211 + pReader->pData += n; |
| 1212 + n = getVarint32Safe(pReader->pData, &nSuffix, pReader->nData); |
| 1213 + if( !n ) return SQLITE_CORRUPT_BKPT; |
| 1214 + pReader->nData -= n; |
| 1215 + pReader->pData += n; |
| 1216 + if( nSuffix<0 || nSuffix>pReader->nData ) return SQLITE_CORRUPT_BKPT; |
| 1217 + if( nPrefix<0 || nPrefix>pReader->term.nData ) return SQLITE_CORRUPT_BKPT; |
| 1218 pReader->term.nData = nPrefix; |
| 1219 - dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix); |
| 1220 + dataBufferAppend(&pReader->term, pReader->pData, nSuffix); |
| 1221 |
| 1222 - pReader->pData += n+nSuffix; |
| 1223 - pReader->nData -= n+nSuffix; |
| 1224 + pReader->pData += nSuffix; |
| 1225 + pReader->nData -= nSuffix; |
| 1226 } |
| 1227 + return SQLITE_OK; |
| 1228 } |
| 1229 |
| 1230 /* strcmp-style comparison of pReader's current term against pTerm. |
| 1231 @@ -5077,6 +5312,9 @@ |
| 1232 ** the leaf data was entirely contained in the root), or from the |
| 1233 ** stream of blocks between iStartBlockid and iEndBlockid, inclusive. |
| 1234 */ |
| 1235 +/* TODO(shess): Figure out a means of indicating how many leaves are |
| 1236 +** expected, for purposes of detecting corruption. |
| 1237 +*/ |
| 1238 static int leavesReaderInit(fulltext_vtab *v, |
| 1239 int idx, |
| 1240 sqlite_int64 iStartBlockid, |
| 1241 @@ -5088,32 +5326,67 @@ |
| 1242 |
| 1243 dataBufferInit(&pReader->rootData, 0); |
| 1244 if( iStartBlockid==0 ){ |
| 1245 + int rc; |
| 1246 + /* Corrupt if this can't be a leaf node. */ |
| 1247 + if( pRootData==NULL || nRootData<1 || pRootData[0]!='\0' ){ |
| 1248 + return SQLITE_CORRUPT_BKPT; |
| 1249 + } |
| 1250 /* Entire leaf level fit in root data. */ |
| 1251 dataBufferReplace(&pReader->rootData, pRootData, nRootData); |
| 1252 - leafReaderInit(pReader->rootData.pData, pReader->rootData.nData, |
| 1253 - &pReader->leafReader); |
| 1254 + rc = leafReaderInit(pReader->rootData.pData, pReader->rootData.nData, |
| 1255 + &pReader->leafReader); |
| 1256 + if( rc!=SQLITE_OK ){ |
| 1257 + dataBufferDestroy(&pReader->rootData); |
| 1258 + return rc; |
| 1259 + } |
| 1260 }else{ |
| 1261 sqlite3_stmt *s; |
| 1262 int rc = sql_get_leaf_statement(v, idx, &s); |
| 1263 if( rc!=SQLITE_OK ) return rc; |
| 1264 |
| 1265 rc = sqlite3_bind_int64(s, 1, iStartBlockid); |
| 1266 - if( rc!=SQLITE_OK ) return rc; |
| 1267 + if( rc!=SQLITE_OK ) goto err; |
| 1268 |
| 1269 rc = sqlite3_bind_int64(s, 2, iEndBlockid); |
| 1270 - if( rc!=SQLITE_OK ) return rc; |
| 1271 + if( rc!=SQLITE_OK ) goto err; |
| 1272 |
| 1273 rc = sqlite3_step(s); |
| 1274 + |
| 1275 + /* Corrupt if interior node referenced missing leaf node. */ |
| 1276 if( rc==SQLITE_DONE ){ |
| 1277 - pReader->eof = 1; |
| 1278 - return SQLITE_OK; |
| 1279 + rc = SQLITE_CORRUPT_BKPT; |
| 1280 + goto err; |
| 1281 + } |
| 1282 + |
| 1283 + if( rc!=SQLITE_ROW ) goto err; |
| 1284 + rc = SQLITE_OK; |
| 1285 + |
| 1286 + /* Corrupt if leaf data isn't a blob. */ |
| 1287 + if( sqlite3_column_type(s, 0)!=SQLITE_BLOB ){ |
| 1288 + rc = SQLITE_CORRUPT_BKPT; |
| 1289 + }else{ |
| 1290 + const char *pLeafData = sqlite3_column_blob(s, 0); |
| 1291 + int nLeafData = sqlite3_column_bytes(s, 0); |
| 1292 + |
| 1293 + /* Corrupt if this can't be a leaf node. */ |
| 1294 + if( pLeafData==NULL || nLeafData<1 || pLeafData[0]!='\0' ){ |
| 1295 + rc = SQLITE_CORRUPT_BKPT; |
| 1296 + }else{ |
| 1297 + rc = leafReaderInit(pLeafData, nLeafData, &pReader->leafReader); |
| 1298 + } |
| 1299 + } |
| 1300 + |
| 1301 + err: |
| 1302 + if( rc!=SQLITE_OK ){ |
| 1303 + if( idx==-1 ){ |
| 1304 + sqlite3_finalize(s); |
| 1305 + }else{ |
| 1306 + sqlite3_reset(s); |
| 1307 + } |
| 1308 + return rc; |
| 1309 } |
| 1310 - if( rc!=SQLITE_ROW ) return rc; |
| 1311 |
| 1312 pReader->pStmt = s; |
| 1313 - leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0), |
| 1314 - sqlite3_column_bytes(pReader->pStmt, 0), |
| 1315 - &pReader->leafReader); |
| 1316 } |
| 1317 return SQLITE_OK; |
| 1318 } |
| 1319 @@ -5122,11 +5395,12 @@ |
| 1320 ** end of the current leaf, step forward to the next leaf block. |
| 1321 */ |
| 1322 static int leavesReaderStep(fulltext_vtab *v, LeavesReader *pReader){ |
| 1323 + int rc; |
| 1324 assert( !leavesReaderAtEnd(pReader) ); |
| 1325 - leafReaderStep(&pReader->leafReader); |
| 1326 + rc = leafReaderStep(&pReader->leafReader); |
| 1327 + if( rc!=SQLITE_OK ) return rc; |
| 1328 |
| 1329 if( leafReaderAtEnd(&pReader->leafReader) ){ |
| 1330 - int rc; |
| 1331 if( pReader->rootData.pData ){ |
| 1332 pReader->eof = 1; |
| 1333 return SQLITE_OK; |
| 1334 @@ -5136,10 +5410,25 @@ |
| 1335 pReader->eof = 1; |
| 1336 return rc==SQLITE_DONE ? SQLITE_OK : rc; |
| 1337 } |
| 1338 - leafReaderDestroy(&pReader->leafReader); |
| 1339 - leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0), |
| 1340 - sqlite3_column_bytes(pReader->pStmt, 0), |
| 1341 - &pReader->leafReader); |
| 1342 + |
| 1343 + /* Corrupt if leaf data isn't a blob. */ |
| 1344 + if( sqlite3_column_type(pReader->pStmt, 0)!=SQLITE_BLOB ){ |
| 1345 + return SQLITE_CORRUPT_BKPT; |
| 1346 + }else{ |
| 1347 + LeafReader tmp; |
| 1348 + const char *pLeafData = sqlite3_column_blob(pReader->pStmt, 0); |
| 1349 + int nLeafData = sqlite3_column_bytes(pReader->pStmt, 0); |
| 1350 + |
| 1351 + /* Corrupt if this can't be a leaf node. */ |
| 1352 + if( pLeafData==NULL || nLeafData<1 || pLeafData[0]!='\0' ){ |
| 1353 + return SQLITE_CORRUPT_BKPT; |
| 1354 + } |
| 1355 + |
| 1356 + rc = leafReaderInit(pLeafData, nLeafData, &tmp); |
| 1357 + if( rc!=SQLITE_OK ) return rc; |
| 1358 + leafReaderDestroy(&pReader->leafReader); |
| 1359 + pReader->leafReader = tmp; |
| 1360 + } |
| 1361 } |
| 1362 return SQLITE_OK; |
| 1363 } |
| 1364 @@ -5200,8 +5489,19 @@ |
| 1365 sqlite_int64 iEnd = sqlite3_column_int64(s, 1); |
| 1366 const char *pRootData = sqlite3_column_blob(s, 2); |
| 1367 int nRootData = sqlite3_column_bytes(s, 2); |
| 1368 + sqlite_int64 iIndex = sqlite3_column_int64(s, 3); |
| 1369 + |
| 1370 + /* Corrupt if we get back different types than we stored. */ |
| 1371 + /* Also corrupt if the index is not sequential starting at 0. */ |
| 1372 + if( sqlite3_column_type(s, 0)!=SQLITE_INTEGER || |
| 1373 + sqlite3_column_type(s, 1)!=SQLITE_INTEGER || |
| 1374 + sqlite3_column_type(s, 2)!=SQLITE_BLOB || |
| 1375 + i!=iIndex || |
| 1376 + i>=MERGE_COUNT ){ |
| 1377 + rc = SQLITE_CORRUPT_BKPT; |
| 1378 + break; |
| 1379 + } |
| 1380 |
| 1381 - assert( i<MERGE_COUNT ); |
| 1382 rc = leavesReaderInit(v, i, iStart, iEnd, pRootData, nRootData, |
| 1383 &pReaders[i]); |
| 1384 if( rc!=SQLITE_OK ) break; |
| 1385 @@ -5212,6 +5512,7 @@ |
| 1386 while( i-->0 ){ |
| 1387 leavesReaderDestroy(&pReaders[i]); |
| 1388 } |
| 1389 + sqlite3_reset(s); /* So we don't leave a lock. */ |
| 1390 return rc; |
| 1391 } |
| 1392 |
| 1393 @@ -5235,13 +5536,26 @@ |
| 1394 DLReader dlReaders[MERGE_COUNT]; |
| 1395 const char *pTerm = leavesReaderTerm(pReaders); |
| 1396 int i, nTerm = leavesReaderTermBytes(pReaders); |
| 1397 + int rc; |
| 1398 |
| 1399 assert( nReaders<=MERGE_COUNT ); |
| 1400 |
| 1401 for(i=0; i<nReaders; i++){ |
| 1402 - dlrInit(&dlReaders[i], DL_DEFAULT, |
| 1403 - leavesReaderData(pReaders+i), |
| 1404 - leavesReaderDataBytes(pReaders+i)); |
| 1405 + const char *pData = leavesReaderData(pReaders+i); |
| 1406 + if( pData==NULL ){ |
| 1407 + rc = SQLITE_CORRUPT_BKPT; |
| 1408 + break; |
| 1409 + } |
| 1410 + rc = dlrInit(&dlReaders[i], DL_DEFAULT, |
| 1411 + pData, |
| 1412 + leavesReaderDataBytes(pReaders+i)); |
| 1413 + if( rc!=SQLITE_OK ) break; |
| 1414 + } |
| 1415 + if( rc!=SQLITE_OK ){ |
| 1416 + while( i-->0 ){ |
| 1417 + dlrDestroy(&dlReaders[i]); |
| 1418 + } |
| 1419 + return rc; |
| 1420 } |
| 1421 |
| 1422 return leafWriterStepMerge(v, pWriter, pTerm, nTerm, dlReaders, nReaders); |
| 1423 @@ -5295,10 +5609,14 @@ |
| 1424 memset(&lrs, '\0', sizeof(lrs)); |
| 1425 rc = leavesReadersInit(v, iLevel, lrs, &i); |
| 1426 if( rc!=SQLITE_OK ) return rc; |
| 1427 - assert( i==MERGE_COUNT ); |
| 1428 |
| 1429 leafWriterInit(iLevel+1, idx, &writer); |
| 1430 |
| 1431 + if( i!=MERGE_COUNT ){ |
| 1432 + rc = SQLITE_CORRUPT_BKPT; |
| 1433 + goto err; |
| 1434 + } |
| 1435 + |
| 1436 /* Since leavesReaderReorder() pushes readers at eof to the end, |
| 1437 ** when the first reader is empty, all will be empty. |
| 1438 */ |
| 1439 @@ -5341,12 +5659,14 @@ |
| 1440 } |
| 1441 |
| 1442 /* Accumulate the union of *acc and *pData into *acc. */ |
| 1443 -static void docListAccumulateUnion(DataBuffer *acc, |
| 1444 - const char *pData, int nData) { |
| 1445 +static int docListAccumulateUnion(DataBuffer *acc, |
| 1446 + const char *pData, int nData) { |
| 1447 DataBuffer tmp = *acc; |
| 1448 + int rc; |
| 1449 dataBufferInit(acc, tmp.nData+nData); |
| 1450 - docListUnion(tmp.pData, tmp.nData, pData, nData, acc); |
| 1451 + rc = docListUnion(tmp.pData, tmp.nData, pData, nData, acc); |
| 1452 dataBufferDestroy(&tmp); |
| 1453 + return rc; |
| 1454 } |
| 1455 |
| 1456 /* TODO(shess) It might be interesting to explore different merge |
| 1457 @@ -5388,8 +5708,13 @@ |
| 1458 int c = leafReaderTermCmp(&pReader->leafReader, pTerm, nTerm, isPrefix); |
| 1459 if( c>0 ) break; /* Past any possible matches. */ |
| 1460 if( c==0 ){ |
| 1461 + int iBuffer, nData; |
| 1462 const char *pData = leavesReaderData(pReader); |
| 1463 - int iBuffer, nData = leavesReaderDataBytes(pReader); |
| 1464 + if( pData==NULL ){ |
| 1465 + rc = SQLITE_CORRUPT_BKPT; |
| 1466 + break; |
| 1467 + } |
| 1468 + nData = leavesReaderDataBytes(pReader); |
| 1469 |
| 1470 /* Find the first empty buffer. */ |
| 1471 for(iBuffer=0; iBuffer<nBuffers; ++iBuffer){ |
| 1472 @@ -5435,11 +5760,13 @@ |
| 1473 ** with pData/nData. |
| 1474 */ |
| 1475 dataBufferSwap(p, pAcc); |
| 1476 - docListAccumulateUnion(pAcc, pData, nData); |
| 1477 + rc = docListAccumulateUnion(pAcc, pData, nData); |
| 1478 + if( rc!=SQLITE_OK ) goto err; |
| 1479 |
| 1480 /* Accumulate remaining doclists into pAcc. */ |
| 1481 for(++p; p<pAcc; ++p){ |
| 1482 - docListAccumulateUnion(pAcc, p->pData, p->nData); |
| 1483 + rc = docListAccumulateUnion(pAcc, p->pData, p->nData); |
| 1484 + if( rc!=SQLITE_OK ) goto err; |
| 1485 |
| 1486 /* dataBufferReset() could allow a large doclist to blow up |
| 1487 ** our memory requirements. |
| 1488 @@ -5464,13 +5791,15 @@ |
| 1489 if( out->nData==0 ){ |
| 1490 dataBufferSwap(out, &(pBuffers[iBuffer])); |
| 1491 }else{ |
| 1492 - docListAccumulateUnion(out, pBuffers[iBuffer].pData, |
| 1493 - pBuffers[iBuffer].nData); |
| 1494 + rc = docListAccumulateUnion(out, pBuffers[iBuffer].pData, |
| 1495 + pBuffers[iBuffer].nData); |
| 1496 + if( rc!=SQLITE_OK ) break; |
| 1497 } |
| 1498 } |
| 1499 } |
| 1500 } |
| 1501 |
| 1502 +err: |
| 1503 while( nBuffers-- ){ |
| 1504 dataBufferDestroy(&(pBuffers[nBuffers])); |
| 1505 } |
| 1506 @@ -5529,20 +5858,26 @@ |
| 1507 ** node. Consider whether breaking symmetry is worthwhile. I suspect |
| 1508 ** it is not worthwhile. |
| 1509 */ |
| 1510 -static void getChildrenContaining(const char *pData, int nData, |
| 1511 - const char *pTerm, int nTerm, int isPrefix, |
| 1512 - sqlite_int64 *piStartChild, |
| 1513 - sqlite_int64 *piEndChild){ |
| 1514 +static int getChildrenContaining(const char *pData, int nData, |
| 1515 + const char *pTerm, int nTerm, int isPrefix, |
| 1516 + sqlite_int64 *piStartChild, |
| 1517 + sqlite_int64 *piEndChild){ |
| 1518 InteriorReader reader; |
| 1519 + int rc; |
| 1520 |
| 1521 assert( nData>1 ); |
| 1522 assert( *pData!='\0' ); |
| 1523 - interiorReaderInit(pData, nData, &reader); |
| 1524 + rc = interiorReaderInit(pData, nData, &reader); |
| 1525 + if( rc!=SQLITE_OK ) return rc; |
| 1526 |
| 1527 /* Scan for the first child which could contain pTerm/nTerm. */ |
| 1528 while( !interiorReaderAtEnd(&reader) ){ |
| 1529 if( interiorReaderTermCmp(&reader, pTerm, nTerm, 0)>0 ) break; |
| 1530 - interiorReaderStep(&reader); |
| 1531 + rc = interiorReaderStep(&reader); |
| 1532 + if( rc!=SQLITE_OK ){ |
| 1533 + interiorReaderDestroy(&reader); |
| 1534 + return rc; |
| 1535 + } |
| 1536 } |
| 1537 *piStartChild = interiorReaderCurrentBlockid(&reader); |
| 1538 |
| 1539 @@ -5552,7 +5887,11 @@ |
| 1540 */ |
| 1541 while( !interiorReaderAtEnd(&reader) ){ |
| 1542 if( interiorReaderTermCmp(&reader, pTerm, nTerm, isPrefix)>0 ) break; |
| 1543 - interiorReaderStep(&reader); |
| 1544 + rc = interiorReaderStep(&reader); |
| 1545 + if( rc!=SQLITE_OK ){ |
| 1546 + interiorReaderDestroy(&reader); |
| 1547 + return rc; |
| 1548 + } |
| 1549 } |
| 1550 *piEndChild = interiorReaderCurrentBlockid(&reader); |
| 1551 |
| 1552 @@ -5561,6 +5900,7 @@ |
| 1553 /* Children must ascend, and if !prefix, both must be the same. */ |
| 1554 assert( *piEndChild>=*piStartChild ); |
| 1555 assert( isPrefix || *piStartChild==*piEndChild ); |
| 1556 + return rc; |
| 1557 } |
| 1558 |
| 1559 /* Read block at iBlockid and pass it with other params to |
| 1560 @@ -5588,11 +5928,31 @@ |
| 1561 if( rc!=SQLITE_OK ) return rc; |
| 1562 |
| 1563 rc = sqlite3_step(s); |
| 1564 - if( rc==SQLITE_DONE ) return SQLITE_ERROR; |
| 1565 + /* Corrupt if interior node references missing child node. */ |
| 1566 + if( rc==SQLITE_DONE ) return SQLITE_CORRUPT_BKPT; |
| 1567 if( rc!=SQLITE_ROW ) return rc; |
| 1568 |
| 1569 - getChildrenContaining(sqlite3_column_blob(s, 0), sqlite3_column_bytes(s, 0), |
| 1570 - pTerm, nTerm, isPrefix, piStartChild, piEndChild); |
| 1571 + /* Corrupt if child node isn't a blob. */ |
| 1572 + if( sqlite3_column_type(s, 0)!=SQLITE_BLOB ){ |
| 1573 + sqlite3_reset(s); /* So we don't leave a lock. */ |
| 1574 + return SQLITE_CORRUPT_BKPT; |
| 1575 + }else{ |
| 1576 + const char *pData = sqlite3_column_blob(s, 0); |
| 1577 + int nData = sqlite3_column_bytes(s, 0); |
| 1578 + |
| 1579 + /* Corrupt if child is not a valid interior node. */ |
| 1580 + if( pData==NULL || nData<1 || pData[0]=='\0' ){ |
| 1581 + sqlite3_reset(s); /* So we don't leave a lock. */ |
| 1582 + return SQLITE_CORRUPT_BKPT; |
| 1583 + } |
| 1584 + |
| 1585 + rc = getChildrenContaining(pData, nData, pTerm, nTerm, |
| 1586 + isPrefix, piStartChild, piEndChild); |
| 1587 + if( rc!=SQLITE_OK ){ |
| 1588 + sqlite3_reset(s); |
| 1589 + return rc; |
| 1590 + } |
| 1591 + } |
| 1592 |
| 1593 /* We expect only one row. We must execute another sqlite3_step() |
| 1594 * to complete the iteration; otherwise the table will remain |
| 1595 @@ -5622,8 +5982,9 @@ |
| 1596 /* Process pData as an interior node, then loop down the tree |
| 1597 ** until we find the set of leaf nodes to scan for the term. |
| 1598 */ |
| 1599 - getChildrenContaining(pData, nData, pTerm, nTerm, isPrefix, |
| 1600 - &iStartChild, &iEndChild); |
| 1601 + rc = getChildrenContaining(pData, nData, pTerm, nTerm, isPrefix, |
| 1602 + &iStartChild, &iEndChild); |
| 1603 + if( rc!=SQLITE_OK ) return rc; |
| 1604 while( iStartChild>iLeavesEnd ){ |
| 1605 sqlite_int64 iNextStart, iNextEnd; |
| 1606 rc = loadAndGetChildrenContaining(v, iStartChild, pTerm, nTerm, isPrefix, |
| 1607 @@ -5675,7 +6036,8 @@ |
| 1608 DataBuffer result; |
| 1609 int rc; |
| 1610 |
| 1611 - assert( nData>1 ); |
| 1612 + /* Corrupt if segment root can't be valid. */ |
| 1613 + if( pData==NULL || nData<1 ) return SQLITE_CORRUPT_BKPT; |
| 1614 |
| 1615 /* This code should never be called with buffered updates. */ |
| 1616 assert( v->nPendingData<0 ); |
| 1617 @@ -5692,16 +6054,21 @@ |
| 1618 DataBuffer merged; |
| 1619 DLReader readers[2]; |
| 1620 |
| 1621 - dlrInit(&readers[0], DL_DEFAULT, out->pData, out->nData); |
| 1622 - dlrInit(&readers[1], DL_DEFAULT, result.pData, result.nData); |
| 1623 - dataBufferInit(&merged, out->nData+result.nData); |
| 1624 - docListMerge(&merged, readers, 2); |
| 1625 - dataBufferDestroy(out); |
| 1626 - *out = merged; |
| 1627 - dlrDestroy(&readers[0]); |
| 1628 - dlrDestroy(&readers[1]); |
| 1629 + rc = dlrInit(&readers[0], DL_DEFAULT, out->pData, out->nData); |
| 1630 + if( rc==SQLITE_OK ){ |
| 1631 + rc = dlrInit(&readers[1], DL_DEFAULT, result.pData, result.nData); |
| 1632 + if( rc==SQLITE_OK ){ |
| 1633 + dataBufferInit(&merged, out->nData+result.nData); |
| 1634 + rc = docListMerge(&merged, readers, 2); |
| 1635 + dataBufferDestroy(out); |
| 1636 + *out = merged; |
| 1637 + dlrDestroy(&readers[1]); |
| 1638 + } |
| 1639 + dlrDestroy(&readers[0]); |
| 1640 + } |
| 1641 } |
| 1642 } |
| 1643 + |
| 1644 dataBufferDestroy(&result); |
| 1645 return rc; |
| 1646 } |
| 1647 @@ -5729,11 +6096,20 @@ |
| 1648 const char *pData = sqlite3_column_blob(s, 2); |
| 1649 const int nData = sqlite3_column_bytes(s, 2); |
| 1650 const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1); |
| 1651 + |
| 1652 + /* Corrupt if we get back different types than we stored. */ |
| 1653 + if( sqlite3_column_type(s, 1)!=SQLITE_INTEGER || |
| 1654 + sqlite3_column_type(s, 2)!=SQLITE_BLOB ){ |
| 1655 + rc = SQLITE_CORRUPT_BKPT; |
| 1656 + goto err; |
| 1657 + } |
| 1658 + |
| 1659 rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, isPrefix, |
| 1660 &doclist); |
| 1661 if( rc!=SQLITE_OK ) goto err; |
| 1662 } |
| 1663 if( rc==SQLITE_DONE ){ |
| 1664 + rc = SQLITE_OK; |
| 1665 if( doclist.nData!=0 ){ |
| 1666 /* TODO(shess) The old term_select_all() code applied the column |
| 1667 ** restrict as we merged segments, leading to smaller buffers. |
| 1668 @@ -5741,13 +6117,13 @@ |
| 1669 ** system is checked in. |
| 1670 */ |
| 1671 if( iColumn==v->nColumn) iColumn = -1; |
| 1672 - docListTrim(DL_DEFAULT, doclist.pData, doclist.nData, |
| 1673 - iColumn, iType, out); |
| 1674 + rc = docListTrim(DL_DEFAULT, doclist.pData, doclist.nData, |
| 1675 + iColumn, iType, out); |
| 1676 } |
| 1677 - rc = SQLITE_OK; |
| 1678 } |
| 1679 |
| 1680 err: |
| 1681 + sqlite3_reset(s); /* So we don't leave a lock. */ |
| 1682 dataBufferDestroy(&doclist); |
| 1683 return rc; |
| 1684 } |
| 1685 @@ -6089,6 +6465,7 @@ |
| 1686 LeafWriter *pWriter){ |
| 1687 int i, rc = SQLITE_OK; |
| 1688 DataBuffer doclist, merged, tmp; |
| 1689 + const char *pData; |
| 1690 |
| 1691 /* Order the readers. */ |
| 1692 i = nReaders; |
| 1693 @@ -6109,14 +6486,21 @@ |
| 1694 if( 0!=optLeavesReaderTermCmp(&readers[0], &readers[i]) ) break; |
| 1695 } |
| 1696 |
| 1697 + pData = optLeavesReaderData(&readers[0]); |
| 1698 + if( pData==NULL ){ |
| 1699 + rc = SQLITE_CORRUPT_BKPT; |
| 1700 + break; |
| 1701 + } |
| 1702 + |
| 1703 /* Special-case for no merge. */ |
| 1704 if( i==1 ){ |
| 1705 /* Trim deletions from the doclist. */ |
| 1706 dataBufferReset(&merged); |
| 1707 - docListTrim(DL_DEFAULT, |
| 1708 - optLeavesReaderData(&readers[0]), |
| 1709 - optLeavesReaderDataBytes(&readers[0]), |
| 1710 - -1, DL_DEFAULT, &merged); |
| 1711 + rc = docListTrim(DL_DEFAULT, |
| 1712 + pData, |
| 1713 + optLeavesReaderDataBytes(&readers[0]), |
| 1714 + -1, DL_DEFAULT, &merged); |
| 1715 + if( rc!= SQLITE_OK ) break; |
| 1716 }else{ |
| 1717 DLReader dlReaders[MERGE_COUNT]; |
| 1718 int iReader, nReaders; |
| 1719 @@ -6124,9 +6508,10 @@ |
| 1720 /* Prime the pipeline with the first reader's doclist. After |
| 1721 ** one pass index 0 will reference the accumulated doclist. |
| 1722 */ |
| 1723 - dlrInit(&dlReaders[0], DL_DEFAULT, |
| 1724 - optLeavesReaderData(&readers[0]), |
| 1725 - optLeavesReaderDataBytes(&readers[0])); |
| 1726 + rc = dlrInit(&dlReaders[0], DL_DEFAULT, |
| 1727 + pData, |
| 1728 + optLeavesReaderDataBytes(&readers[0])); |
| 1729 + if( rc!=SQLITE_OK ) break; |
| 1730 iReader = 1; |
| 1731 |
| 1732 assert( iReader<i ); /* Must execute the loop at least once. */ |
| 1733 @@ -6134,24 +6519,35 @@ |
| 1734 /* Merge 16 inputs per pass. */ |
| 1735 for( nReaders=1; iReader<i && nReaders<MERGE_COUNT; |
| 1736 iReader++, nReaders++ ){ |
| 1737 - dlrInit(&dlReaders[nReaders], DL_DEFAULT, |
| 1738 - optLeavesReaderData(&readers[iReader]), |
| 1739 - optLeavesReaderDataBytes(&readers[iReader])); |
| 1740 + pData = optLeavesReaderData(&readers[iReader]); |
| 1741 + if( pData == NULL ){ |
| 1742 + rc = SQLITE_CORRUPT_BKPT; |
| 1743 + break; |
| 1744 + } |
| 1745 + rc = dlrInit(&dlReaders[nReaders], DL_DEFAULT, |
| 1746 + pData, |
| 1747 + optLeavesReaderDataBytes(&readers[iReader])); |
| 1748 + if( rc != SQLITE_OK ) break; |
| 1749 } |
| 1750 |
| 1751 /* Merge doclists and swap result into accumulator. */ |
| 1752 - dataBufferReset(&merged); |
| 1753 - docListMerge(&merged, dlReaders, nReaders); |
| 1754 - tmp = merged; |
| 1755 - merged = doclist; |
| 1756 - doclist = tmp; |
| 1757 + if( rc==SQLITE_OK ){ |
| 1758 + dataBufferReset(&merged); |
| 1759 + rc = docListMerge(&merged, dlReaders, nReaders); |
| 1760 + tmp = merged; |
| 1761 + merged = doclist; |
| 1762 + doclist = tmp; |
| 1763 + } |
| 1764 |
| 1765 while( nReaders-- > 0 ){ |
| 1766 dlrDestroy(&dlReaders[nReaders]); |
| 1767 } |
| 1768 |
| 1769 + if( rc!=SQLITE_OK ) goto err; |
| 1770 + |
| 1771 /* Accumulated doclist to reader 0 for next pass. */ |
| 1772 - dlrInit(&dlReaders[0], DL_DEFAULT, doclist.pData, doclist.nData); |
| 1773 + rc = dlrInit(&dlReaders[0], DL_DEFAULT, doclist.pData, doclist.nData); |
| 1774 + if( rc!=SQLITE_OK ) goto err; |
| 1775 } |
| 1776 |
| 1777 /* Destroy reader that was left in the pipeline. */ |
| 1778 @@ -6159,8 +6555,9 @@ |
| 1779 |
| 1780 /* Trim deletions from the doclist. */ |
| 1781 dataBufferReset(&merged); |
| 1782 - docListTrim(DL_DEFAULT, doclist.pData, doclist.nData, |
| 1783 - -1, DL_DEFAULT, &merged); |
| 1784 + rc = docListTrim(DL_DEFAULT, doclist.pData, doclist.nData, |
| 1785 + -1, DL_DEFAULT, &merged); |
| 1786 + if( rc!=SQLITE_OK ) goto err; |
| 1787 } |
| 1788 |
| 1789 /* Only pass doclists with hits (skip if all hits deleted). */ |
| 1790 @@ -6240,6 +6637,14 @@ |
| 1791 const char *pRootData = sqlite3_column_blob(s, 2); |
| 1792 int nRootData = sqlite3_column_bytes(s, 2); |
| 1793 |
| 1794 + /* Corrupt if we get back different types than we stored. */ |
| 1795 + if( sqlite3_column_type(s, 0)!=SQLITE_INTEGER || |
| 1796 + sqlite3_column_type(s, 1)!=SQLITE_INTEGER || |
| 1797 + sqlite3_column_type(s, 2)!=SQLITE_BLOB ){ |
| 1798 + rc = SQLITE_CORRUPT_BKPT; |
| 1799 + break; |
| 1800 + } |
| 1801 + |
| 1802 assert( i<nReaders ); |
| 1803 rc = leavesReaderInit(v, -1, iStart, iEnd, pRootData, nRootData, |
| 1804 &readers[i].reader); |
| 1805 @@ -6253,6 +6658,8 @@ |
| 1806 if( rc==SQLITE_DONE ){ |
| 1807 assert( i==nReaders ); |
| 1808 rc = optimizeInternal(v, readers, nReaders, &writer); |
| 1809 + }else{ |
| 1810 + sqlite3_reset(s); /* So we don't leave a lock. */ |
| 1811 } |
| 1812 |
| 1813 while( i-- > 0 ){ |
| 1814 @@ -6316,9 +6723,18 @@ |
| 1815 const sqlite_int64 iEndBlockid = sqlite3_column_int64(s, 1); |
| 1816 const char *pRootData = sqlite3_column_blob(s, 2); |
| 1817 const int nRootData = sqlite3_column_bytes(s, 2); |
| 1818 + int rc; |
| 1819 LeavesReader reader; |
| 1820 - int rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid, |
| 1821 - pRootData, nRootData, &reader); |
| 1822 + |
| 1823 + /* Corrupt if we get back different types than we stored. */ |
| 1824 + if( sqlite3_column_type(s, 0)!=SQLITE_INTEGER || |
| 1825 + sqlite3_column_type(s, 1)!=SQLITE_INTEGER || |
| 1826 + sqlite3_column_type(s, 2)!=SQLITE_BLOB ){ |
| 1827 + return SQLITE_CORRUPT_BKPT; |
| 1828 + } |
| 1829 + |
| 1830 + rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid, |
| 1831 + pRootData, nRootData, &reader); |
| 1832 if( rc!=SQLITE_OK ) return rc; |
| 1833 |
| 1834 while( rc==SQLITE_OK && !leavesReaderAtEnd(&reader) ){ |
| 1835 @@ -6480,16 +6896,19 @@ |
| 1836 const char *pData, int nData){ |
| 1837 DataBuffer dump; |
| 1838 DLReader dlReader; |
| 1839 + int rc; |
| 1840 |
| 1841 assert( pData!=NULL && nData>0 ); |
| 1842 |
| 1843 + rc = dlrInit(&dlReader, DL_DEFAULT, pData, nData); |
| 1844 + if( rc!=SQLITE_OK ) return rc; |
| 1845 dataBufferInit(&dump, 0); |
| 1846 - dlrInit(&dlReader, DL_DEFAULT, pData, nData); |
| 1847 - for( ; !dlrAtEnd(&dlReader); dlrStep(&dlReader) ){ |
| 1848 + for( ; rc==SQLITE_OK && !dlrAtEnd(&dlReader); rc = dlrStep(&dlReader) ){ |
| 1849 char buf[256]; |
| 1850 PLReader plReader; |
| 1851 |
| 1852 - plrInit(&plReader, &dlReader); |
| 1853 + rc = plrInit(&plReader, &dlReader); |
| 1854 + if( rc!=SQLITE_OK ) break; |
| 1855 if( DL_DEFAULT==DL_DOCIDS || plrAtEnd(&plReader) ){ |
| 1856 sqlite3_snprintf(sizeof(buf), buf, "[%lld] ", dlrDocid(&dlReader)); |
| 1857 dataBufferAppend(&dump, buf, strlen(buf)); |
| 1858 @@ -6500,7 +6919,8 @@ |
| 1859 dlrDocid(&dlReader), iColumn); |
| 1860 dataBufferAppend(&dump, buf, strlen(buf)); |
| 1861 |
| 1862 - for( ; !plrAtEnd(&plReader); plrStep(&plReader) ){ |
| 1863 + for( ; !plrAtEnd(&plReader); rc = plrStep(&plReader) ){ |
| 1864 + if( rc!=SQLITE_OK ) break; |
| 1865 if( plrColumn(&plReader)!=iColumn ){ |
| 1866 iColumn = plrColumn(&plReader); |
| 1867 sqlite3_snprintf(sizeof(buf), buf, "] %d[", iColumn); |
| 1868 @@ -6521,6 +6941,7 @@ |
| 1869 dataBufferAppend(&dump, buf, strlen(buf)); |
| 1870 } |
| 1871 plrDestroy(&plReader); |
| 1872 + if( rc!= SQLITE_OK ) break; |
| 1873 |
| 1874 assert( dump.nData>0 ); |
| 1875 dump.nData--; /* Overwrite trailing space. */ |
| 1876 @@ -6529,6 +6950,10 @@ |
| 1877 } |
| 1878 } |
| 1879 dlrDestroy(&dlReader); |
| 1880 + if( rc!=SQLITE_OK ){ |
| 1881 + dataBufferDestroy(&dump); |
| 1882 + return rc; |
| 1883 + } |
| 1884 |
| 1885 assert( dump.nData>0 ); |
| 1886 dump.nData--; /* Overwrite trailing space. */ |
| 1887 @@ -6540,6 +6965,7 @@ |
| 1888 sqlite3_result_text(pContext, dump.pData, dump.nData, sqlite3_free); |
| 1889 dump.pData = NULL; |
| 1890 dump.nData = dump.nCapacity = 0; |
| 1891 + return SQLITE_OK; |
| 1892 } |
| 1893 |
| 1894 /* Implements dump_doclist() for use in inspecting the fts2 index from |
| 1895 @@ -6822,7 +7248,11 @@ |
| 1896 ** module with sqlite. |
| 1897 */ |
| 1898 if( SQLITE_OK==rc |
| 1899 +#if GEARS_FTS2_CHANGES && !SQLITE_TEST |
| 1900 + /* fts2_tokenizer() disabled for security reasons. */ |
| 1901 +#else |
| 1902 && SQLITE_OK==(rc = sqlite3Fts2InitHashTable(db, pHash, "fts2_tokenizer")) |
| 1903 +#endif |
| 1904 && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) |
| 1905 && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", -1)) |
| 1906 && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", -1)) |
| 1907 diff -ru ext-orig/fts2/fts2_icu.c ext/fts2/fts2_icu.c |
| 1908 --- ext-orig/fts2/fts2_icu.c 2009-09-03 13:32:06.000000000 -0700 |
| 1909 +++ ext/fts2/fts2_icu.c 2009-09-18 14:39:41.000000000 -0700 |
| 1910 @@ -198,7 +198,7 @@ |
| 1911 |
| 1912 while( iStart<iEnd ){ |
| 1913 int iWhite = iStart; |
| 1914 - U8_NEXT(pCsr->aChar, iWhite, pCsr->nChar, c); |
| 1915 + U16_NEXT(pCsr->aChar, iWhite, pCsr->nChar, c); |
| 1916 if( u_isspace(c) ){ |
| 1917 iStart = iWhite; |
| 1918 }else{ |
| 1919 diff -ru ext-orig/fts2/fts2_tokenizer.c ext/fts2/fts2_tokenizer.c |
| 1920 --- ext-orig/fts2/fts2_tokenizer.c 2009-09-03 13:32:06.000000000 -0700 |
| 1921 +++ ext/fts2/fts2_tokenizer.c 2009-09-18 14:39:41.000000000 -0700 |
| 1922 @@ -33,6 +33,7 @@ |
| 1923 #include "fts2_hash.h" |
| 1924 #include "fts2_tokenizer.h" |
| 1925 #include <assert.h> |
| 1926 +#include <stddef.h> |
| 1927 |
| 1928 /* |
| 1929 ** Implementation of the SQL scalar function for accessing the underlying |
OLD | NEW |