/ Check-in [7e24645b]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Handle updating the only row of an FTS table correctly. Fix for [9fd058691].
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7e24645be2fe0ffe092212e7bcfa5b4500305811
User & Date: dan 2011-10-13 17:16:45
References
2011-10-13
17:34 Ticket [9fd05869] Problem with updating an FTS table that contains a single row status still Closed with 1 other change artifact: 6796c36a user: dan
Context
2011-10-13
18:00
Simplifications to the upper() and lower() SQL functions. Updates to documentation on sqlite3_bind_text() and sqlite3_result_text() to make it clear that users should not try to create strings with embedded NULs and that if they do the result of expression on those strings is undefined. Ticket [57c971fc74524a] check-in: 9984cc20 user: drh tags: trunk
17:16
Handle updating the only row of an FTS table correctly. Fix for [9fd058691]. check-in: 7e24645b user: dan tags: trunk
17:09
An improved fix for the page_count and quick_check problem previously patched at [150592b4b4d8637] check-in: c3cb7f4f user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts3/fts3.c.

  1334   1334       }else{
  1335   1335         int rc = sqlite3_reset(pCsr->pStmt);
  1336   1336         if( rc==SQLITE_OK ){
  1337   1337           /* If no row was found and no error has occured, then the %_content
  1338   1338           ** table is missing a row that is present in the full-text index.
  1339   1339           ** The data structures are corrupt.
  1340   1340           */
  1341         -        rc = SQLITE_CORRUPT_VTAB;
         1341  +        rc = FTS_CORRUPT_VTAB;
  1342   1342         }
  1343   1343         pCsr->isEof = 1;
  1344   1344         if( pContext ){
  1345   1345           sqlite3_result_error_code(pContext, rc);
  1346   1346         }
  1347   1347         return rc;
  1348   1348       }
................................................................................
  1394   1394     ** contents, or two zero bytes. Or, if the node is read from the %_segments
  1395   1395     ** table, then there are always 20 bytes of zeroed padding following the
  1396   1396     ** nNode bytes of content (see sqlite3Fts3ReadBlock() for details).
  1397   1397     */
  1398   1398     zCsr += sqlite3Fts3GetVarint(zCsr, &iChild);
  1399   1399     zCsr += sqlite3Fts3GetVarint(zCsr, &iChild);
  1400   1400     if( zCsr>zEnd ){
  1401         -    return SQLITE_CORRUPT_VTAB;
         1401  +    return FTS_CORRUPT_VTAB;
  1402   1402     }
  1403   1403     
  1404   1404     while( zCsr<zEnd && (piFirst || piLast) ){
  1405   1405       int cmp;                      /* memcmp() result */
  1406   1406       int nSuffix;                  /* Size of term suffix */
  1407   1407       int nPrefix = 0;              /* Size of term prefix */
  1408   1408       int nBuffer;                  /* Total term size */
................................................................................
  1412   1412       if( !isFirstTerm ){
  1413   1413         zCsr += sqlite3Fts3GetVarint32(zCsr, &nPrefix);
  1414   1414       }
  1415   1415       isFirstTerm = 0;
  1416   1416       zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix);
  1417   1417       
  1418   1418       if( nPrefix<0 || nSuffix<0 || &zCsr[nSuffix]>zEnd ){
  1419         -      rc = SQLITE_CORRUPT_VTAB;
         1419  +      rc = FTS_CORRUPT_VTAB;
  1420   1420         goto finish_scan;
  1421   1421       }
  1422   1422       if( nPrefix+nSuffix>nAlloc ){
  1423   1423         char *zNew;
  1424   1424         nAlloc = (nPrefix+nSuffix) * 2;
  1425   1425         zNew = (char *)sqlite3_realloc(zBuffer, nAlloc);
  1426   1426         if( !zNew ){
................................................................................
  3855   3855       pEnd = &a[sqlite3_column_bytes(pStmt, 0)];
  3856   3856       a += sqlite3Fts3GetVarint(a, &nDoc);
  3857   3857       while( a<pEnd ){
  3858   3858         a += sqlite3Fts3GetVarint(a, &nByte);
  3859   3859       }
  3860   3860       if( nDoc==0 || nByte==0 ){
  3861   3861         sqlite3_reset(pStmt);
  3862         -      return SQLITE_CORRUPT_VTAB;
         3862  +      return FTS_CORRUPT_VTAB;
  3863   3863       }
  3864   3864   
  3865   3865       pCsr->nDoc = nDoc;
  3866   3866       pCsr->nRowAvg = (int)(((nByte / nDoc) + p->nPgsz) / p->nPgsz);
  3867   3867       assert( pCsr->nRowAvg>0 ); 
  3868   3868       rc = sqlite3_reset(pStmt);
  3869   3869       if( rc!=SQLITE_OK ) return rc;
................................................................................
  4822   4822       for(i=0; i<pPhrase->nToken; i++){
  4823   4823         fts3SegReaderCursorFree(pPhrase->aToken[i].pSegcsr);
  4824   4824         pPhrase->aToken[i].pSegcsr = 0;
  4825   4825       }
  4826   4826     }
  4827   4827   }
  4828   4828   
         4829  +/*
         4830  +** Return SQLITE_CORRUPT_VTAB.
         4831  +*/
         4832  +#ifdef SQLITE_DEBUG
         4833  +int sqlite3Fts3Corrupt(){
         4834  +  return SQLITE_CORRUPT_VTAB;
         4835  +}
         4836  +#endif
         4837  +
  4829   4838   #if !SQLITE_CORE
  4830   4839   /*
  4831   4840   ** Initialize API pointer table, if required.
  4832   4841   */
  4833   4842   int sqlite3_extension_init(
  4834   4843     sqlite3 *db, 
  4835   4844     char **pzErrMsg,

Changes to ext/fts3/fts3Int.h.

   152    152   #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
   153    153   # define TESTONLY(X)  X
   154    154   #else
   155    155   # define TESTONLY(X)
   156    156   #endif
   157    157   
   158    158   #endif /* SQLITE_AMALGAMATION */
          159  +
          160  +#ifdef SQLITE_DEBUG
          161  +int sqlite3Fts3Corrupt(void);
          162  +# define FTS_CORRUPT_VTAB sqlite3Fts3Corrupt()
          163  +#else
          164  +# define FTS_CORRUPT_VTAB SQLITE_CORRUPT_VTAB
          165  +#endif
   159    166   
   160    167   typedef struct Fts3Table Fts3Table;
   161    168   typedef struct Fts3Cursor Fts3Cursor;
   162    169   typedef struct Fts3Expr Fts3Expr;
   163    170   typedef struct Fts3Phrase Fts3Phrase;
   164    171   typedef struct Fts3PhraseToken Fts3PhraseToken;
   165    172   

Changes to ext/fts3/fts3_snippet.c.

   844    844       if( rc!=SQLITE_OK ) return rc;
   845    845     }
   846    846     pStmt = *ppStmt;
   847    847     assert( sqlite3_data_count(pStmt)==1 );
   848    848   
   849    849     a = sqlite3_column_blob(pStmt, 0);
   850    850     a += sqlite3Fts3GetVarint(a, &nDoc);
   851         -  if( nDoc==0 ) return SQLITE_CORRUPT_VTAB;
          851  +  if( nDoc==0 ) return FTS_CORRUPT_VTAB;
   852    852     *pnDoc = (u32)nDoc;
   853    853   
   854    854     if( paLen ) *paLen = a;
   855    855     return SQLITE_OK;
   856    856   }
   857    857   
   858    858   /*
................................................................................
  1423   1423           if( rc==SQLITE_OK ){
  1424   1424             char aBuffer[64];
  1425   1425             sqlite3_snprintf(sizeof(aBuffer), aBuffer, 
  1426   1426                 "%d %d %d %d ", iCol, pTerm-sCtx.aTerm, iStart, iEnd-iStart
  1427   1427             );
  1428   1428             rc = fts3StringAppend(&res, aBuffer, -1);
  1429   1429           }else if( rc==SQLITE_DONE ){
  1430         -          rc = SQLITE_CORRUPT_VTAB;
         1430  +          rc = FTS_CORRUPT_VTAB;
  1431   1431           }
  1432   1432         }
  1433   1433       }
  1434   1434       if( rc==SQLITE_DONE ){
  1435   1435         rc = SQLITE_OK;
  1436   1436       }
  1437   1437   

Changes to ext/fts3/fts3_write.c.

   337    337     if( rc==SQLITE_OK ){
   338    338       if( eStmt==SQL_SELECT_DOCSIZE ){
   339    339         sqlite3_bind_int64(pStmt, 1, iDocid);
   340    340       }
   341    341       rc = sqlite3_step(pStmt);
   342    342       if( rc!=SQLITE_ROW || sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){
   343    343         rc = sqlite3_reset(pStmt);
   344         -      if( rc==SQLITE_OK ) rc = SQLITE_CORRUPT_VTAB;
          344  +      if( rc==SQLITE_OK ) rc = FTS_CORRUPT_VTAB;
   345    345         pStmt = 0;
   346    346       }else{
   347    347         rc = SQLITE_OK;
   348    348       }
   349    349     }
   350    350   
   351    351     *ppStmt = pStmt;
................................................................................
  1141   1141     /* Because of the FTS3_NODE_PADDING bytes of padding, the following is 
  1142   1142     ** safe (no risk of overread) even if the node data is corrupted. */
  1143   1143     pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix);
  1144   1144     pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix);
  1145   1145     if( nPrefix<0 || nSuffix<=0 
  1146   1146      || &pNext[nSuffix]>&pReader->aNode[pReader->nNode] 
  1147   1147     ){
  1148         -    return SQLITE_CORRUPT_VTAB;
         1148  +    return FTS_CORRUPT_VTAB;
  1149   1149     }
  1150   1150   
  1151   1151     if( nPrefix+nSuffix>pReader->nTermAlloc ){
  1152   1152       int nNew = (nPrefix+nSuffix)*2;
  1153   1153       char *zNew = sqlite3_realloc(pReader->zTerm, nNew);
  1154   1154       if( !zNew ){
  1155   1155         return SQLITE_NOMEM;
................................................................................
  1171   1171     /* Check that the doclist does not appear to extend past the end of the
  1172   1172     ** b-tree node. And that the final byte of the doclist is 0x00. If either 
  1173   1173     ** of these statements is untrue, then the data structure is corrupt.
  1174   1174     */
  1175   1175     if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode] 
  1176   1176      || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
  1177   1177     ){
  1178         -    return SQLITE_CORRUPT_VTAB;
         1178  +    return FTS_CORRUPT_VTAB;
  1179   1179     }
  1180   1180     return SQLITE_OK;
  1181   1181   }
  1182   1182   
  1183   1183   /*
  1184   1184   ** Set the SegReader to point to the first docid in the doclist associated
  1185   1185   ** with the current term.
................................................................................
  3125   3125     int nArg,                       /* Size of argument array */
  3126   3126     sqlite3_value **apVal,          /* Array of arguments */
  3127   3127     sqlite_int64 *pRowid            /* OUT: The affected (or effected) rowid */
  3128   3128   ){
  3129   3129     Fts3Table *p = (Fts3Table *)pVtab;
  3130   3130     int rc = SQLITE_OK;             /* Return Code */
  3131   3131     int isRemove = 0;               /* True for an UPDATE or DELETE */
  3132         -  sqlite3_int64 iRemove = 0;      /* Rowid removed by UPDATE or DELETE */
  3133   3132     u32 *aSzIns = 0;                /* Sizes of inserted documents */
  3134   3133     u32 *aSzDel;                    /* Sizes of deleted documents */
  3135   3134     int nChng = 0;                  /* Net change in number of documents */
  3136   3135     int bInsertDone = 0;
  3137   3136   
  3138   3137     assert( p->pSegments==0 );
  3139   3138   
................................................................................
  3208   3207     }
  3209   3208   
  3210   3209     /* If this is a DELETE or UPDATE operation, remove the old record. */
  3211   3210     if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
  3212   3211       assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER );
  3213   3212       rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel);
  3214   3213       isRemove = 1;
  3215         -    iRemove = sqlite3_value_int64(apVal[0]);
  3216   3214     }
  3217   3215     
  3218   3216     /* If this is an INSERT or UPDATE operation, insert the new record. */
  3219   3217     if( nArg>1 && rc==SQLITE_OK ){
  3220   3218       if( bInsertDone==0 ){
  3221   3219         rc = fts3InsertData(p, apVal, pRowid);
  3222         -      if( rc==SQLITE_CONSTRAINT ) rc = SQLITE_CORRUPT_VTAB;
         3220  +      if( rc==SQLITE_CONSTRAINT ) rc = FTS_CORRUPT_VTAB;
  3223   3221       }
  3224         -    if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){
         3222  +    if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){
  3225   3223         rc = fts3PendingTermsDocid(p, *pRowid);
  3226   3224       }
  3227   3225       if( rc==SQLITE_OK ){
         3226  +      assert( p->iPrevDocid==*pRowid );
  3228   3227         rc = fts3InsertTerms(p, apVal, aSzIns);
  3229   3228       }
  3230   3229       if( p->bHasDocsize ){
  3231   3230         fts3InsertDocsize(&rc, p, aSzIns);
  3232   3231       }
  3233   3232       nChng++;
  3234   3233     }

Added test/fts-9fd058691.test.

            1  +# 2011 October 13
            2  +#
            3  +#    May you do good and not evil.
            4  +#    May you find forgiveness for yourself and forgive others.
            5  +#    May you share freely, never taking more than you give.
            6  +#
            7  +#***********************************************************************
            8  +#
            9  +# This file implements regression tests for the FTS SQLite module.
           10  +#
           11  +# This file implements tests to verify that ticket [9fd058691] has been
           12  +# fixed.  
           13  +#
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +
           18  +# If SQLITE_ENABLE_FTS3 is defined, omit this file.
           19  +ifcapable !fts3 {
           20  +  finish_test
           21  +  return
           22  +}
           23  +
           24  +set ::testprefix fts3-9fd058691
           25  +
           26  +do_execsql_test 1.0 {
           27  +  CREATE VIRTUAL TABLE fts USING fts3( tags TEXT);
           28  +  INSERT INTO fts (tags) VALUES ('tag1');
           29  +  SELECT * FROM fts WHERE tags MATCH 'tag1';
           30  +} {tag1}
           31  +
           32  +do_test 1.1 {
           33  +  db close
           34  +  sqlite3 db test.db
           35  +  execsql {
           36  +    UPDATE fts SET tags = 'tag1' WHERE rowid = 1;
           37  +    SELECT * FROM fts WHERE tags MATCH 'tag1';
           38  +  }
           39  +} {tag1}
           40  +
           41  +db close
           42  +forcedelete test.db
           43  +sqlite3 db test.db
           44  +
           45  +do_execsql_test 2.0 {
           46  +  CREATE VIRTUAL TABLE fts USING fts3(tags TEXT);
           47  +  INSERT INTO fts (docid, tags) VALUES (1, 'tag1');
           48  +  INSERT INTO fts (docid, tags) VALUES (2, NULL);
           49  +  INSERT INTO fts (docid, tags) VALUES (3, 'three');
           50  +} {}
           51  +
           52  +do_test 2.1 {
           53  +  execsql {
           54  +    UPDATE fts SET tags = 'two' WHERE rowid = 2;
           55  +    SELECT * FROM fts WHERE tags MATCH 'two';
           56  +  }
           57  +} {two}
           58  +
           59  +finish_test