Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Updates to snippet() and offsets() functions of FTS3 so that they work sanely following an OOM fault. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
b939a37a8ce296785a300e79ab9d3d87 |
User & Date: | drh 2009-11-28 21:33:21 |
Context
2009-11-30
| ||
08:55 | Add test cases for examples recently added to documentation file fts3.html. check-in: 498922cc user: dan tags: trunk | |
2009-11-28
| ||
21:33 | Updates to snippet() and offsets() functions of FTS3 so that they work sanely following an OOM fault. check-in: b939a37a user: drh tags: trunk | |
17:23 | Change FTS3 to detect when the RHS of the MATCH opertor encounters an OOM during string format conversion and report back an SQLITE_NOMEM error. check-in: 31eed4f8 user: drh tags: trunk | |
Changes
Changes to ext/fts3/fts3_snippet.c.
42 42 }; 43 43 44 44 45 45 /* It is not safe to call isspace(), tolower(), or isalnum() on 46 46 ** hi-bit-set characters. This is the same solution used in the 47 47 ** tokenizer. 48 48 */ 49 -/* TODO(shess) The snippet-generation code should be using the 50 -** tokenizer-generated tokens rather than doing its own local 51 -** tokenization. 52 -*/ 53 -/* TODO(shess) Is __isascii() a portable version of (c&0x80)==0? */ 54 -static int safe_isspace(char c){ 49 +static int fts3snippetIsspace(char c){ 55 50 return (c&0x80)==0 ? isspace(c) : 0; 56 51 } 57 -static int safe_isalnum(char c){ 58 - return (c&0x80)==0 ? isalnum(c) : 0; 59 -} 60 - 61 -/*******************************************************************/ 62 -/* DataBuffer is used to collect data into a buffer in piecemeal 63 -** fashion. It implements the usual distinction between amount of 64 -** data currently stored (nData) and buffer capacity (nCapacity). 65 -** 66 -** dataBufferInit - create a buffer with given initial capacity. 67 -** dataBufferReset - forget buffer's data, retaining capacity. 68 -** dataBufferSwap - swap contents of two buffers. 69 -** dataBufferExpand - expand capacity without adding data. 70 -** dataBufferAppend - append data. 71 -** dataBufferAppend2 - append two pieces of data at once. 72 -** dataBufferReplace - replace buffer's data. 73 -*/ 74 -typedef struct DataBuffer { 75 - char *pData; /* Pointer to malloc'ed buffer. */ 76 - int nCapacity; /* Size of pData buffer. */ 77 - int nData; /* End of data loaded into pData. */ 78 -} DataBuffer; 79 - 80 -static void dataBufferInit(DataBuffer *pBuffer, int nCapacity){ 81 - assert( nCapacity>=0 ); 82 - pBuffer->nData = 0; 83 - pBuffer->nCapacity = nCapacity; 84 - pBuffer->pData = nCapacity==0 ? NULL : sqlite3_malloc(nCapacity); 85 -} 86 -static void dataBufferReset(DataBuffer *pBuffer){ 87 - pBuffer->nData = 0; 88 -} 89 -static void dataBufferExpand(DataBuffer *pBuffer, int nAddCapacity){ 90 - assert( nAddCapacity>0 ); 91 - /* TODO(shess) Consider expanding more aggressively. Note that the 92 - ** underlying malloc implementation may take care of such things for 93 - ** us already. 94 - */ 95 - if( pBuffer->nData+nAddCapacity>pBuffer->nCapacity ){ 96 - pBuffer->nCapacity = pBuffer->nData+nAddCapacity; 97 - pBuffer->pData = sqlite3_realloc(pBuffer->pData, pBuffer->nCapacity); 98 - } 99 -} 100 -static void dataBufferAppend(DataBuffer *pBuffer, 101 - const char *pSource, int nSource){ 102 - assert( nSource>0 && pSource!=NULL ); 103 - dataBufferExpand(pBuffer, nSource); 104 - memcpy(pBuffer->pData+pBuffer->nData, pSource, nSource); 105 - pBuffer->nData += nSource; 106 -} 107 -static void dataBufferAppend2(DataBuffer *pBuffer, 108 - const char *pSource1, int nSource1, 109 - const char *pSource2, int nSource2){ 110 - assert( nSource1>0 && pSource1!=NULL ); 111 - assert( nSource2>0 && pSource2!=NULL ); 112 - dataBufferExpand(pBuffer, nSource1+nSource2); 113 - memcpy(pBuffer->pData+pBuffer->nData, pSource1, nSource1); 114 - memcpy(pBuffer->pData+pBuffer->nData+nSource1, pSource2, nSource2); 115 - pBuffer->nData += nSource1+nSource2; 116 -} 117 -static void dataBufferReplace(DataBuffer *pBuffer, 118 - const char *pSource, int nSource){ 119 - dataBufferReset(pBuffer); 120 - dataBufferAppend(pBuffer, pSource, nSource); 121 -} 122 - 123 - 124 -/* StringBuffer is a null-terminated version of DataBuffer. */ 52 + 53 + 54 +/* 55 +** A StringBuffer object holds a zero-terminated string that grows 56 +** arbitrarily by appending. Space to hold the string is obtained 57 +** from sqlite3_malloc(). After any memory allocation failure, 58 +** StringBuffer.z is set to NULL and no further allocation is attempted. 59 +*/ 125 60 typedef struct StringBuffer { 126 - DataBuffer b; /* Includes null terminator. */ 61 + char *z; /* Text of the string. Space from malloc. */ 62 + int nUsed; /* Number bytes of z[] used, not counting \000 terminator */ 63 + int nAlloc; /* Bytes allocated for z[] */ 127 64 } StringBuffer; 128 65 129 -static void initStringBuffer(StringBuffer *sb){ 130 - dataBufferInit(&sb->b, 100); 131 - dataBufferReplace(&sb->b, "", 1); 132 -} 133 -static int stringBufferLength(StringBuffer *sb){ 134 - return sb->b.nData-1; 135 -} 136 -static char *stringBufferData(StringBuffer *sb){ 137 - return sb->b.pData; 138 -} 139 - 140 -static void nappend(StringBuffer *sb, const char *zFrom, int nFrom){ 141 - assert( sb->b.nData>0 ); 142 - if( nFrom>0 ){ 143 - sb->b.nData--; 144 - dataBufferAppend2(&sb->b, zFrom, nFrom, "", 1); 145 - } 146 -} 147 -static void append(StringBuffer *sb, const char *zFrom){ 148 - nappend(sb, zFrom, strlen(zFrom)); 149 -} 150 - 151 -static int endsInWhiteSpace(StringBuffer *p){ 152 - return stringBufferLength(p)>0 && 153 - safe_isspace(stringBufferData(p)[stringBufferLength(p)-1]); 66 + 67 +/* 68 +** Initialize a new StringBuffer. 69 +*/ 70 +static void fts3SnippetSbInit(StringBuffer *p){ 71 + p->nAlloc = 100; 72 + p->nUsed = 0; 73 + p->z = sqlite3_malloc( p->nAlloc ); 74 +} 75 + 76 +/* 77 +** Append text to the string buffer. 78 +*/ 79 +static void fts3SnippetAppend(StringBuffer *p, const char *zNew, int nNew){ 80 + if( p->z==0 ) return; 81 + if( nNew<0 ) nNew = strlen(zNew); 82 + if( p->nUsed + nNew >= p->nAlloc ){ 83 + int nAlloc; 84 + char *zNew; 85 + 86 + nAlloc = p->nUsed + nNew + p->nAlloc; 87 + zNew = sqlite3_realloc(p->z, nAlloc); 88 + if( zNew==0 ){ 89 + sqlite3_free(p->z); 90 + p->z = 0; 91 + return; 92 + } 93 + p->z = zNew; 94 + p->nAlloc = nAlloc; 95 + } 96 + memcpy(&p->z[p->nUsed], zNew, nNew); 97 + p->nUsed += nNew; 98 + p->z[p->nUsed] = 0; 154 99 } 155 100 156 101 /* If the StringBuffer ends in something other than white space, add a 157 102 ** single space character to the end. 158 103 */ 159 -static void appendWhiteSpace(StringBuffer *p){ 160 - if( stringBufferLength(p)==0 ) return; 161 - if( !endsInWhiteSpace(p) ) append(p, " "); 104 +static void fts3SnippetAppendWhiteSpace(StringBuffer *p){ 105 + if( p->z && p->nUsed && !fts3snippetIsspace(p->z[p->nUsed-1]) ){ 106 + fts3SnippetAppend(p, " ", 1); 107 + } 162 108 } 163 109 164 110 /* Remove white space from the end of the StringBuffer */ 165 -static void trimWhiteSpace(StringBuffer *p){ 166 - while( endsInWhiteSpace(p) ){ 167 - p->b.pData[--p->b.nData-1] = '\0'; 111 +static void fts3SnippetTrimWhiteSpace(StringBuffer *p){ 112 + if( p->z ){ 113 + while( p->nUsed && fts3snippetIsspace(p->z[p->nUsed-1]) ){ 114 + p->nUsed--; 115 + } 116 + p->z[p->nUsed] = 0; 168 117 } 169 118 } 170 - 171 119 172 120 /* 173 121 ** Release all memory associated with the Snippet structure passed as 174 122 ** an argument. 175 123 */ 176 124 static void fts3SnippetFree(Snippet *p){ 177 - sqlite3_free(p->aMatch); 178 - sqlite3_free(p->zOffset); 179 - sqlite3_free(p->zSnippet); 180 - sqlite3_free(p); 125 + if( p ){ 126 + sqlite3_free(p->aMatch); 127 + sqlite3_free(p->zOffset); 128 + sqlite3_free(p->zSnippet); 129 + sqlite3_free(p); 130 + } 181 131 } 182 132 183 133 /* 184 134 ** Append a single entry to the p->aMatch[] log. 185 135 */ 186 -static void snippetAppendMatch( 136 +static int snippetAppendMatch( 187 137 Snippet *p, /* Append the entry to this snippet */ 188 138 int iCol, int iTerm, /* The column and query term */ 189 139 int iToken, /* Matching token in document */ 190 140 int iStart, int nByte /* Offset and size of the match */ 191 141 ){ 192 142 int i; 193 143 struct snippetMatch *pMatch; 194 144 if( p->nMatch+1>=p->nAlloc ){ 145 + struct snippetMatch *pNew; 195 146 p->nAlloc = p->nAlloc*2 + 10; 196 - p->aMatch = sqlite3_realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) ); 197 - if( p->aMatch==0 ){ 147 + pNew = sqlite3_realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) ); 148 + if( pNew==0 ){ 149 + p->aMatch = 0; 198 150 p->nMatch = 0; 199 151 p->nAlloc = 0; 200 - return; 152 + return SQLITE_NOMEM; 201 153 } 154 + p->aMatch = pNew; 202 155 } 203 156 i = p->nMatch++; 204 157 pMatch = &p->aMatch[i]; 205 158 pMatch->iCol = iCol; 206 159 pMatch->iTerm = iTerm; 207 160 pMatch->iToken = iToken; 208 161 pMatch->iStart = iStart; 209 162 pMatch->nByte = nByte; 163 + return SQLITE_OK; 210 164 } 211 165 212 166 /* 213 167 ** Sizing information for the circular buffer used in snippetOffsetsOfColumn() 214 168 */ 215 169 #define FTS3_ROTOR_SZ (32) 216 170 #define FTS3_ROTOR_MASK (FTS3_ROTOR_SZ-1) ................................................................................ 276 230 return 0; 277 231 } 278 232 279 233 /* 280 234 ** Add entries to pSnippet->aMatch[] for every match that occurs against 281 235 ** document zDoc[0..nDoc-1] which is stored in column iColumn. 282 236 */ 283 -static void snippetOffsetsOfColumn( 237 +static int snippetOffsetsOfColumn( 284 238 Fts3Cursor *pCur, /* The fulltest search cursor */ 285 239 Snippet *pSnippet, /* The Snippet object to be filled in */ 286 240 int iColumn, /* Index of fulltext table column */ 287 241 const char *zDoc, /* Text of the fulltext table column */ 288 242 int nDoc /* Length of zDoc in bytes */ 289 243 ){ 290 244 const sqlite3_tokenizer_module *pTModule; /* The tokenizer module */ ................................................................................ 306 260 int iRotorLen[FTS3_ROTOR_SZ]; /* Length of token */ 307 261 308 262 pVtab = (Fts3Table *)pCur->base.pVtab; 309 263 nColumn = pVtab->nColumn; 310 264 pTokenizer = pVtab->pTokenizer; 311 265 pTModule = pTokenizer->pModule; 312 266 rc = pTModule->xOpen(pTokenizer, zDoc, nDoc, &pTCursor); 313 - if( rc ) return; 267 + if( rc ) return rc; 314 268 pTCursor->pTokenizer = pTokenizer; 315 269 316 270 prevMatch = 0; 317 - while( !pTModule->xNext(pTCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos) ){ 271 + while( (rc = pTModule->xNext(pTCursor, &zToken, &nToken, 272 + &iBegin, &iEnd, &iPos))==SQLITE_OK ){ 318 273 Fts3Expr *pIter = pCur->pExpr; 319 274 int iIter = -1; 320 275 iRotorBegin[iRotor&FTS3_ROTOR_MASK] = iBegin; 321 276 iRotorLen[iRotor&FTS3_ROTOR_MASK] = iEnd-iBegin; 322 277 match = 0; 323 278 for(i=0; i<(FTS3_ROTOR_SZ-1) && fts3NextExprToken(&pIter, &iIter); i++){ 324 279 int nPhrase; /* Number of tokens in current phrase */ ................................................................................ 335 290 assert( pToken->n<=nToken ); 336 291 if( memcmp(pToken->z, zToken, pToken->n) ) continue; 337 292 if( iIter>0 && (prevMatch & (1<<i))==0 ) continue; 338 293 match |= 1<<i; 339 294 if( i==(FTS3_ROTOR_SZ-2) || nPhrase==iIter+1 ){ 340 295 for(j=nPhrase-1; j>=0; j--){ 341 296 int k = (iRotor-j) & FTS3_ROTOR_MASK; 342 - snippetAppendMatch(pSnippet, iColumn, i-j, iPos-j, 343 - iRotorBegin[k], iRotorLen[k]); 297 + rc = snippetAppendMatch(pSnippet, iColumn, i-j, iPos-j, 298 + iRotorBegin[k], iRotorLen[k]); 299 + if( rc ) goto end_offsets_of_column; 344 300 } 345 301 } 346 302 } 347 303 prevMatch = match<<1; 348 304 iRotor++; 349 305 } 306 +end_offsets_of_column: 350 307 pTModule->xClose(pTCursor); 308 + return rc==SQLITE_DONE ? SQLITE_OK : rc; 351 309 } 352 310 353 311 /* 354 312 ** Remove entries from the pSnippet structure to account for the NEAR 355 313 ** operator. When this is called, pSnippet contains the list of token 356 314 ** offsets produced by treating all NEAR operators as AND operators. 357 315 ** This function removes any entries that should not be present after ................................................................................ 485 443 static int snippetAllOffsets(Fts3Cursor *pCsr, Snippet **ppSnippet){ 486 444 Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; 487 445 int nColumn; 488 446 int iColumn, i; 489 447 int iFirst, iLast; 490 448 int iTerm = 0; 491 449 Snippet *pSnippet; 450 + int rc = SQLITE_OK; 492 451 493 452 if( pCsr->pExpr==0 ){ 494 453 return SQLITE_OK; 495 454 } 496 455 497 456 pSnippet = (Snippet *)sqlite3_malloc(sizeof(Snippet)); 498 457 *ppSnippet = pSnippet; ................................................................................ 508 467 iFirst = 0; 509 468 iLast = nColumn-1; 510 469 }else{ 511 470 /* Look for matches in the iColumn-th column of the index only */ 512 471 iFirst = iColumn; 513 472 iLast = iColumn; 514 473 } 515 - for(i=iFirst; i<=iLast; i++){ 474 + for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){ 516 475 const char *zDoc; 517 476 int nDoc; 518 477 zDoc = (const char*)sqlite3_column_text(pCsr->pStmt, i+1); 519 478 nDoc = sqlite3_column_bytes(pCsr->pStmt, i+1); 520 - snippetOffsetsOfColumn(pCsr, pSnippet, i, zDoc, nDoc); 479 + if( zDoc==0 && sqlite3_column_type(pCsr->pStmt, i+1)!=SQLITE_NULL ){ 480 + rc = SQLITE_NOMEM; 481 + }else{ 482 + rc = snippetOffsetsOfColumn(pCsr, pSnippet, i, zDoc, nDoc); 483 + } 521 484 } 522 485 523 486 while( trimSnippetOffsets(pCsr->pExpr, pSnippet, &iTerm) ){ 524 487 iTerm = 0; 525 488 } 526 489 527 - return SQLITE_OK; 490 + return rc; 528 491 } 529 492 530 493 /* 531 494 ** Convert the information in the aMatch[] array of the snippet 532 495 ** into the string zOffset[0..nOffset-1]. This string is used as 533 496 ** the return of the SQL offsets() function. 534 497 */ 535 498 static void snippetOffsetText(Snippet *p){ 536 499 int i; 537 500 int cnt = 0; 538 501 StringBuffer sb; 539 502 char zBuf[200]; 540 503 if( p->zOffset ) return; 541 - initStringBuffer(&sb); 504 + fts3SnippetSbInit(&sb); 542 505 for(i=0; i<p->nMatch; i++){ 543 506 struct snippetMatch *pMatch = &p->aMatch[i]; 544 507 if( pMatch->iTerm>=0 ){ 545 508 /* If snippetMatch.iTerm is less than 0, then the match was 546 509 ** discarded as part of processing the NEAR operator (see the 547 510 ** trimSnippetOffsetsForNear() function for details). Ignore 548 511 ** it in this case 549 512 */ 550 513 zBuf[0] = ' '; 551 514 sqlite3_snprintf(sizeof(zBuf)-1, &zBuf[cnt>0], "%d %d %d %d", 552 515 pMatch->iCol, pMatch->iTerm, pMatch->iStart, pMatch->nByte); 553 - append(&sb, zBuf); 516 + fts3SnippetAppend(&sb, zBuf, -1); 554 517 cnt++; 555 518 } 556 519 } 557 - p->zOffset = stringBufferData(&sb); 558 - p->nOffset = stringBufferLength(&sb); 520 + p->zOffset = sb.z; 521 + p->nOffset = sb.z ? sb.nUsed : 0; 559 522 } 560 523 561 524 /* 562 525 ** zDoc[0..nDoc-1] is phrase of text. aMatch[0..nMatch-1] are a set 563 526 ** of matching words some of which might be in zDoc. zDoc is column 564 527 ** number iCol. 565 528 ** ................................................................................ 589 552 return aMatch[i].iStart; 590 553 } 591 554 if( i>0 && aMatch[i-1].iStart+aMatch[i-1].nByte>=iBreak ){ 592 555 return aMatch[i-1].iStart; 593 556 } 594 557 } 595 558 for(i=1; i<=10; i++){ 596 - if( safe_isspace(zDoc[iBreak-i]) ){ 559 + if( fts3snippetIsspace(zDoc[iBreak-i]) ){ 597 560 return iBreak - i + 1; 598 561 } 599 - if( safe_isspace(zDoc[iBreak+i]) ){ 562 + if( fts3snippetIsspace(zDoc[iBreak+i]) ){ 600 563 return iBreak + i + 1; 601 564 } 602 565 } 603 566 return iBreak; 604 567 } 605 568 606 569 ................................................................................ 636 599 int iMatch; 637 600 638 601 639 602 sqlite3_free(pSnippet->zSnippet); 640 603 pSnippet->zSnippet = 0; 641 604 aMatch = pSnippet->aMatch; 642 605 nMatch = pSnippet->nMatch; 643 - initStringBuffer(&sb); 606 + fts3SnippetSbInit(&sb); 644 607 645 608 for(i=0; i<nMatch; i++){ 646 609 aMatch[i].snStatus = SNIPPET_IGNORE; 647 610 } 648 611 nDesired = 0; 649 612 for(i=0; i<FTS3_ROTOR_SZ; i++){ 650 613 for(j=0; j<nMatch; j++){ ................................................................................ 670 633 if( iStart<=10 ){ 671 634 iStart = 0; 672 635 } 673 636 if( iCol==tailCol && iStart<=tailOffset+20 ){ 674 637 iStart = tailOffset; 675 638 } 676 639 if( (iCol!=tailCol && tailCol>=0) || iStart!=tailOffset ){ 677 - trimWhiteSpace(&sb); 678 - appendWhiteSpace(&sb); 679 - append(&sb, zEllipsis); 680 - appendWhiteSpace(&sb); 640 + fts3SnippetTrimWhiteSpace(&sb); 641 + fts3SnippetAppendWhiteSpace(&sb); 642 + fts3SnippetAppend(&sb, zEllipsis, -1); 643 + fts3SnippetAppendWhiteSpace(&sb); 681 644 } 682 645 iEnd = aMatch[i].iStart + aMatch[i].nByte + 40; 683 646 iEnd = wordBoundary(iEnd, zDoc, nDoc, aMatch, nMatch, iCol); 684 647 if( iEnd>=nDoc-10 ){ 685 648 iEnd = nDoc; 686 649 tailEllipsis = 0; 687 650 }else{ ................................................................................ 691 654 while( iStart<iEnd ){ 692 655 while( iMatch<nMatch && aMatch[iMatch].iStart<iStart 693 656 && aMatch[iMatch].iCol<=iCol ){ 694 657 iMatch++; 695 658 } 696 659 if( iMatch<nMatch && aMatch[iMatch].iStart<iEnd 697 660 && aMatch[iMatch].iCol==iCol ){ 698 - nappend(&sb, &zDoc[iStart], aMatch[iMatch].iStart - iStart); 661 + fts3SnippetAppend(&sb, &zDoc[iStart], aMatch[iMatch].iStart - iStart); 699 662 iStart = aMatch[iMatch].iStart; 700 - append(&sb, zStartMark); 701 - nappend(&sb, &zDoc[iStart], aMatch[iMatch].nByte); 702 - append(&sb, zEndMark); 663 + fts3SnippetAppend(&sb, zStartMark, -1); 664 + fts3SnippetAppend(&sb, &zDoc[iStart], aMatch[iMatch].nByte); 665 + fts3SnippetAppend(&sb, zEndMark, -1); 703 666 iStart += aMatch[iMatch].nByte; 704 667 for(j=iMatch+1; j<nMatch; j++){ 705 668 if( aMatch[j].iTerm==aMatch[iMatch].iTerm 706 669 && aMatch[j].snStatus==SNIPPET_DESIRED ){ 707 670 nDesired--; 708 671 aMatch[j].snStatus = SNIPPET_IGNORE; 709 672 } 710 673 } 711 674 }else{ 712 - nappend(&sb, &zDoc[iStart], iEnd - iStart); 675 + fts3SnippetAppend(&sb, &zDoc[iStart], iEnd - iStart); 713 676 iStart = iEnd; 714 677 } 715 678 } 716 679 tailCol = iCol; 717 680 tailOffset = iEnd; 718 681 } 719 - trimWhiteSpace(&sb); 682 + fts3SnippetTrimWhiteSpace(&sb); 720 683 if( tailEllipsis ){ 721 - appendWhiteSpace(&sb); 722 - append(&sb, zEllipsis); 684 + fts3SnippetAppendWhiteSpace(&sb); 685 + fts3SnippetAppend(&sb, zEllipsis, -1); 723 686 } 724 - pSnippet->zSnippet = stringBufferData(&sb); 725 - pSnippet->nSnippet = stringBufferLength(&sb); 687 + pSnippet->zSnippet = sb.z; 688 + pSnippet->nSnippet = sb.z ? sb.nUsed : 0; 726 689 } 727 690 728 691 void sqlite3Fts3Offsets( 729 692 sqlite3_context *pCtx, /* SQLite function call context */ 730 693 Fts3Cursor *pCsr /* Cursor object */ 731 694 ){ 732 695 Snippet *p; /* Snippet structure */ 733 696 int rc = snippetAllOffsets(pCsr, &p); 734 - snippetOffsetText(p); 735 - sqlite3_result_text(pCtx, p->zOffset, p->nOffset, SQLITE_TRANSIENT); 697 + if( rc==SQLITE_OK ){ 698 + snippetOffsetText(p); 699 + if( p->zOffset ){ 700 + sqlite3_result_text(pCtx, p->zOffset, p->nOffset, SQLITE_TRANSIENT); 701 + }else{ 702 + sqlite3_result_error_nomem(pCtx); 703 + } 704 + }else{ 705 + sqlite3_result_error_nomem(pCtx); 706 + } 736 707 fts3SnippetFree(p); 737 708 } 738 709 739 710 void sqlite3Fts3Snippet( 740 711 sqlite3_context *pCtx, /* SQLite function call context */ 741 712 Fts3Cursor *pCsr, /* Cursor object */ 742 713 const char *zStart, /* Snippet start text - "<b>" */ 743 714 const char *zEnd, /* Snippet end text - "</b>" */ 744 715 const char *zEllipsis /* Snippet ellipsis text - "<b>...</b>" */ 745 716 ){ 746 717 Snippet *p; /* Snippet structure */ 747 718 int rc = snippetAllOffsets(pCsr, &p); 748 - snippetText(pCsr, p, zStart, zEnd, zEllipsis); 749 - sqlite3_result_text(pCtx, p->zSnippet, p->nSnippet, SQLITE_TRANSIENT); 719 + if( rc==SQLITE_OK ){ 720 + snippetText(pCsr, p, zStart, zEnd, zEllipsis); 721 + if( p->zSnippet ){ 722 + sqlite3_result_text(pCtx, p->zSnippet, p->nSnippet, SQLITE_TRANSIENT); 723 + }else{ 724 + sqlite3_result_error_nomem(pCtx); 725 + } 726 + }else{ 727 + sqlite3_result_error_nomem(pCtx); 728 + } 750 729 fts3SnippetFree(p); 751 730 } 752 731 753 732 #endif
Changes to ext/fts3/fts3_write.c.
642 642 rc = fts3PendingTermsAdd(p, zText, -1); 643 643 if( rc!=SQLITE_OK ){ 644 644 sqlite3_reset(pSelect); 645 645 return rc; 646 646 } 647 647 } 648 648 } 649 + rc = sqlite3_reset(pSelect); 650 + }else{ 651 + sqlite3_reset(pSelect); 649 652 } 650 - 651 - return sqlite3_reset(pSelect); 653 + return rc; 652 654 } 653 655 654 656 /* 655 657 ** Forward declaration to account for the circular dependency between 656 658 ** functions fts3SegmentMerge() and fts3AllocateSegdirIdx(). 657 659 */ 658 660 static int fts3SegmentMerge(Fts3Table *, int);