SQLite

Check-in [9b1afd4acb]
Login

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

Overview
Comment:Improve the error message returned when fts5 finds a row is missing from its content table.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 9b1afd4acb8cf9738154e921dcb074d895bbcc4742bf1016ef9f045a0da78bc8
User & Date: dan 2024-07-08 16:01:49.563
Context
2024-07-08
17:39
Fix a use-after-free that could occur following an OOM introduced by [d8cedbe0]. (check-in: 4fabfacfcf user: dan tags: trunk)
16:01
Improve the error message returned when fts5 finds a row is missing from its content table. (check-in: 9b1afd4acb user: dan tags: trunk)
2024-07-05
13:55
Use a mini Bloom filter to help reduce the number of pointless searches for prior SubrtnSig objects when generating code for IN operators with subqueries as their right operand. (check-in: d8cedbe055 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/fts5/fts5_main.c.
1212
1213
1214
1215
1216
1217
1218












1219
1220
1221
1222
1223
1224
1225
    int eType = sqlite3_value_numeric_type(pVal);
    if( eType==SQLITE_INTEGER ){
      return sqlite3_value_int64(pVal);
    }
  }
  return iDefault;
}













/*
** This is the xFilter interface for the virtual table.  See
** the virtual table xFilter method documentation for additional
** information.
** 
** There are three possible query strategies:







>
>
>
>
>
>
>
>
>
>
>
>







1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
    int eType = sqlite3_value_numeric_type(pVal);
    if( eType==SQLITE_INTEGER ){
      return sqlite3_value_int64(pVal);
    }
  }
  return iDefault;
}

/*
** Set the error message on the virtual table passed as the first argument.
*/
static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){
  va_list ap;                     /* ... printf arguments */
  va_start(ap, zFormat);
  sqlite3_free(p->p.base.zErrMsg);
  p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap);
  va_end(ap);
}


/*
** This is the xFilter interface for the virtual table.  See
** the virtual table xFilter method documentation for additional
** information.
** 
** There are three possible query strategies:
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
        rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
      }else{
        pCsr->ePlan = FTS5_PLAN_MATCH;
        rc = fts5CursorFirst(pTab, pCsr, bDesc);
      }
    }
  }else if( pConfig->zContent==0 ){
    *pConfig->pzErrmsg = sqlite3_mprintf(
        "%s: table does not support scanning", pConfig->zName
    );
    rc = SQLITE_ERROR;
  }else{
    /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup
    ** by rowid (ePlan==FTS5_PLAN_ROWID).  */
    pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID : FTS5_PLAN_SCAN);
    rc = sqlite3Fts5StorageStmt(
        pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg







<
|
<







1400
1401
1402
1403
1404
1405
1406

1407

1408
1409
1410
1411
1412
1413
1414
        rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
      }else{
        pCsr->ePlan = FTS5_PLAN_MATCH;
        rc = fts5CursorFirst(pTab, pCsr, bDesc);
      }
    }
  }else if( pConfig->zContent==0 ){

    fts5SetVtabError(pTab,"%s: table does not support scanning",pConfig->zName);

    rc = SQLITE_ERROR;
  }else{
    /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup
    ** by rowid (ePlan==FTS5_PLAN_ROWID).  */
    pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID : FTS5_PLAN_SCAN);
    rc = sqlite3Fts5StorageStmt(
        pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg
1464
1465
1466
1467
1468
1469
1470

1471
1472
1473
1474
1475
1476
1477
    *pRowid = 0;
  }else{
    *pRowid = fts5CursorRowid(pCsr);
  }

  return SQLITE_OK;
}


/*
** If the cursor requires seeking (bSeekRequired flag is set), seek it.
** Return SQLITE_OK if no error occurs, or an SQLite error code otherwise.
**
** If argument bErrormsg is true and an error occurs, an error message may
** be left in sqlite3_vtab.zErrMsg.







>







1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
    *pRowid = 0;
  }else{
    *pRowid = fts5CursorRowid(pCsr);
  }

  return SQLITE_OK;
}


/*
** If the cursor requires seeking (bSeekRequired flag is set), seek it.
** Return SQLITE_OK if no error occurs, or an SQLite error code otherwise.
**
** If argument bErrormsg is true and an error occurs, an error message may
** be left in sqlite3_vtab.zErrMsg.
1501
1502
1503
1504
1505
1506
1507





1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
    if( rc==SQLITE_ROW ){
      rc = SQLITE_OK;
      CsrFlagClear(pCsr, FTS5CSR_REQUIRE_CONTENT);
    }else{
      rc = sqlite3_reset(pCsr->pStmt);
      if( rc==SQLITE_OK ){
        rc = FTS5_CORRUPT;





      }else if( pTab->pConfig->pzErrmsg ){
        *pTab->pConfig->pzErrmsg = sqlite3_mprintf(
            "%s", sqlite3_errmsg(pTab->pConfig->db)
        );
      }
    }
  }
  return rc;
}

static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){
  va_list ap;                     /* ... printf arguments */
  va_start(ap, zFormat);
  assert( p->p.base.zErrMsg==0 );
  p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap);
  va_end(ap);
}

/*
** This function is called to handle an FTS INSERT command. In other words,
** an INSERT statement of the form:
**
**     INSERT INTO fts(fts) VALUES($pCmd)
**     INSERT INTO fts(fts, rank) VALUES($pCmd, $pVal)
**







>
>
>
>
>

|








<
<
<
<
<
<
<
<







1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533








1534
1535
1536
1537
1538
1539
1540
    if( rc==SQLITE_ROW ){
      rc = SQLITE_OK;
      CsrFlagClear(pCsr, FTS5CSR_REQUIRE_CONTENT);
    }else{
      rc = sqlite3_reset(pCsr->pStmt);
      if( rc==SQLITE_OK ){
        rc = FTS5_CORRUPT;
        fts5SetVtabError((Fts5FullTable*)pTab, 
            "fts5: missing row %lld from content table %s",
            fts5CursorRowid(pCsr), 
            pTab->pConfig->zContent
        );
      }else if( pTab->pConfig->pzErrmsg ){
        fts5SetVtabError((Fts5FullTable*)pTab, 
            "%s", sqlite3_errmsg(pTab->pConfig->db)
        );
      }
    }
  }
  return rc;
}









/*
** This function is called to handle an FTS INSERT command. In other words,
** an INSERT statement of the form:
**
**     INSERT INTO fts(fts) VALUES($pCmd)
**     INSERT INTO fts(fts, rank) VALUES($pCmd, $pVal)
**
2492
2493
2494
2495
2496
2497
2498

2499


2500
2501
2502
2503
2504
2505
2506

  pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId);
  if( pCsr==0 || pCsr->ePlan==0 ){
    char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId);
    sqlite3_result_error(context, zErr, -1);
    sqlite3_free(zErr);
  }else{

    fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]);


  }
}


/*
** Given cursor id iId, return a pointer to the corresponding Fts5Table 
** object. Or NULL If the cursor id does not exist.







>

>
>







2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517

  pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId);
  if( pCsr==0 || pCsr->ePlan==0 ){
    char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId);
    sqlite3_result_error(context, zErr, -1);
    sqlite3_free(zErr);
  }else{
    sqlite3_vtab *pTab = pCsr->base.pVtab;
    fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]);
    sqlite3_free(pTab->zErrMsg);
    pTab->zErrMsg = 0;
  }
}


/*
** Given cursor id iId, return a pointer to the corresponding Fts5Table 
** object. Or NULL If the cursor id does not exist.
Changes to ext/fts5/test/fts5content.test.
324
325
326
327
328
329
330




































331

db func text_value text_value

do_execsql_test 8.3.1 { SELECT * FROM t1 } {one two}
do_execsql_test 8.3.2 { INSERT INTO t1(t1) VALUES('rebuild') }
do_execsql_test 8.3.3 { SELECT * FROM t1 WHERE rowid=1 } {one}
do_execsql_test 8.3.4 { SELECT rowid FROM t1('two') } {2}





































finish_test








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
db func text_value text_value

do_execsql_test 8.3.1 { SELECT * FROM t1 } {one two}
do_execsql_test 8.3.2 { INSERT INTO t1(t1) VALUES('rebuild') }
do_execsql_test 8.3.3 { SELECT * FROM t1 WHERE rowid=1 } {one}
do_execsql_test 8.3.4 { SELECT rowid FROM t1('two') } {2}

#-------------------------------------------------------------------------
reset_db
do_execsql_test 9.1 {
  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
  INSERT INTO t1 VALUES(1, 'one two three');
  INSERT INTO t1 VALUES(2, 'one two three');

  CREATE VIRTUAL TABLE ft USING fts5(b, content=t1, content_rowid=a);
  INSERT INTO ft(ft) VALUES('rebuild');
}

do_execsql_test 9.2 {
  SELECT rowid, b FROM ft('two');
} {
  1 {one two three}
  2 {one two three}
}

do_execsql_test 9.3 {
  DELETE FROM t1 WHERE a=2;
} 

do_catchsql_test 9.4 {
  SELECT rowid FROM ft('two');
} {0 {1 2}}

do_catchsql_test 9.5 {
  SELECT * FROM ft('two');
} {1 {fts5: missing row 2 from content table 'main'.'t1'}}

fts5_aux_test_functions db

do_catchsql_test 9.6 {
  SELECT rowid, fts5_columntext(ft, 0) FROM ft('two');
} {1 SQLITE_CORRUPT_VTAB}

finish_test

Changes to ext/fts5/test/fts5corrupt.test.
95
96
97
98
99
100
101
102
103
104
do_execsql_test 3.1 {
  SELECT * FROM t3 WHERE t3 MATCH 'o'
} {{one o} {three o} {five o}}
sqlite3_db_config db DEFENSIVE 0
do_catchsql_test 3.1 {
  DELETE FROM t3_content WHERE rowid = 3;
  SELECT * FROM t3 WHERE t3 MATCH 'o';
} {1 {database disk image is malformed}}

finish_test







|


95
96
97
98
99
100
101
102
103
104
do_execsql_test 3.1 {
  SELECT * FROM t3 WHERE t3 MATCH 'o'
} {{one o} {three o} {five o}}
sqlite3_db_config db DEFENSIVE 0
do_catchsql_test 3.1 {
  DELETE FROM t3_content WHERE rowid = 3;
  SELECT * FROM t3 WHERE t3 MATCH 'o';
} {1 {fts5: missing row 3 from content table 'main'.'t3_content'}}

finish_test
Changes to ext/fts5/test/fts5corrupt3.test.
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
|   4064: 69 74 79 2d 63 68 65 63 6b 09 02 02 1b 72 65 62   ity-check....reb
|   4080: 75 69 6c 64 0a 01 02 1d 6f 70 74 69 6d 69 7a 65   uild....optimize
| end crash-ae135cb10977c7.db
}]} {}

do_catchsql_test 35.1 {
  SELECT * FROM t1 WHERE t1 MATCH 'e*';
} {1 {database disk image is malformed}}

#-------------------------------------------------------------------------
reset_db
do_test 36.0 {
  sqlite3 db {}
  db deserialize [decode_hexdb {
| size 24576 pagesize 4096 filename crash-a6651222df1bd1.db







|







4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
|   4064: 69 74 79 2d 63 68 65 63 6b 09 02 02 1b 72 65 62   ity-check....reb
|   4080: 75 69 6c 64 0a 01 02 1d 6f 70 74 69 6d 69 7a 65   uild....optimize
| end crash-ae135cb10977c7.db
}]} {}

do_catchsql_test 35.1 {
  SELECT * FROM t1 WHERE t1 MATCH 'e*';
} {1 {fts5: missing row 14 from content table 'main'.'t1_content'}}

#-------------------------------------------------------------------------
reset_db
do_test 36.0 {
  sqlite3 db {}
  db deserialize [decode_hexdb {
| size 24576 pagesize 4096 filename crash-a6651222df1bd1.db