Add test code to drop an fts5 table with corrupt records in its shadow tables.

FossilOrigin-Name: ca21c942c30a3dbff0e7d118e105b847d80b5388c74d19c2eeea71581f8f40b8
This commit is contained in:
dan
2024-08-27 18:25:04 +00:00
parent dac0d0b310
commit 3c39f1f5d6
4 changed files with 208 additions and 23 deletions
+96 -15
View File
@@ -96,14 +96,14 @@ static int SQLITE_TCLAPI f5tDbAndApi(
rc = sqlite3_prepare_v2(db, "SELECT fts5(?1)", -1, &pStmt, 0);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (char*)0);
return TCL_ERROR;
}
sqlite3_bind_pointer(pStmt, 1, (void*)&pApi, "fts5_api_ptr", 0);
sqlite3_step(pStmt);
if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (char*)0);
return TCL_ERROR;
}
@@ -392,7 +392,7 @@ static int SQLITE_TCLAPI xF5tApi(
CASE(12, "xSetAuxdata") {
F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc(sizeof(F5tAuxData));
if( pData==0 ){
Tcl_AppendResult(interp, "out of memory", 0);
Tcl_AppendResult(interp, "out of memory", (char*)0);
return TCL_ERROR;
}
pData->pObj = objv[2];
@@ -452,7 +452,7 @@ static int SQLITE_TCLAPI xF5tApi(
rc = p->pApi->xPhraseFirst(p->pFts, iPhrase, &iter, &iCol, &iOff);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
Tcl_AppendResult(interp, sqlite3ErrName(rc), (char*)0);
return TCL_ERROR;
}
for( ;iCol>=0; p->pApi->xPhraseNext(p->pFts, &iter, &iCol, &iOff) ){
@@ -683,7 +683,7 @@ static int SQLITE_TCLAPI f5tCreateFunction(
pApi, zName, (void*)pCtx, xF5tFunction, xF5tDestroy
);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (char*)0);
return TCL_ERROR;
}
@@ -750,7 +750,7 @@ static int SQLITE_TCLAPI f5tTokenize(
if( objc==5 ){
char *zOpt = Tcl_GetString(objv[1]);
if( strcmp("-subst", zOpt) ){
Tcl_AppendResult(interp, "unrecognized option: ", zOpt, 0);
Tcl_AppendResult(interp, "unrecognized option: ", zOpt, (char*)0);
return TCL_ERROR;
}
}
@@ -759,7 +759,7 @@ static int SQLITE_TCLAPI f5tTokenize(
return TCL_ERROR;
}
if( nArg==0 ){
Tcl_AppendResult(interp, "no such tokenizer: ", 0);
Tcl_AppendResult(interp, "no such tokenizer: ", (char*)0);
Tcl_Free((void*)azArg);
return TCL_ERROR;
}
@@ -767,13 +767,13 @@ static int SQLITE_TCLAPI f5tTokenize(
rc = pApi->xFindTokenizer(pApi, azArg[0], &pUserdata, &tokenizer);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "no such tokenizer: ", azArg[0], 0);
Tcl_AppendResult(interp, "no such tokenizer: ", azArg[0], (char*)0);
return TCL_ERROR;
}
rc = tokenizer.xCreate(pUserdata, &azArg[1], (int)(nArg-1), &pTok);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "error in tokenizer.xCreate()", 0);
Tcl_AppendResult(interp, "error in tokenizer.xCreate()", (char*)0);
return TCL_ERROR;
}
@@ -787,7 +787,7 @@ static int SQLITE_TCLAPI f5tTokenize(
);
tokenizer.xDelete(pTok);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "error in tokenizer.xTokenize()", 0);
Tcl_AppendResult(interp, "error in tokenizer.xTokenize()", (char*)0);
Tcl_DecrRefCount(pRet);
return TCL_ERROR;
}
@@ -1049,7 +1049,7 @@ static int SQLITE_TCLAPI f5tTokenizerLocale(
if( p->xToken==0 ){
Tcl_AppendResult(interp,
"sqlite3_fts5_locale may only be used by tokenizer callback", 0
"sqlite3_fts5_locale may only be used by tokenizer callback", (char*)0
);
return TCL_ERROR;
}
@@ -1098,7 +1098,7 @@ static int SQLITE_TCLAPI f5tTokenizerReturn(
if( p->xToken==0 ){
Tcl_AppendResult(interp,
"sqlite3_fts5_token may only be used by tokenizer callback", 0
"sqlite3_fts5_token may only be used by tokenizer callback", (char*)0
);
return TCL_ERROR;
}
@@ -1250,7 +1250,7 @@ static int SQLITE_TCLAPI f5tCreateTokenizer(
Tcl_AppendResult(interp, (
bV2 ? "error in fts5_api.xCreateTokenizer_v2()"
: "error in fts5_api.xCreateTokenizer()"
), 0);
), (char*)0);
return TCL_ERROR;
}
@@ -1540,12 +1540,92 @@ static int SQLITE_TCLAPI f5tRegisterOriginText(
Tcl_ResetResult(interp);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (void*)0);
return TCL_ERROR;
}
return TCL_OK;
}
/*
** This function is used to DROP an fts5 table. It works even if the data
** structures fts5 stores within the database are corrupt, which sometimes
** prevents a straight "DROP TABLE" command from succeeding.
**
** The first parameter is the database handle to use for the DROP TABLE
** operation. The second is the name of the database to drop the fts5 table
** from (i.e. "main", "temp" or the name of an attached database). The
** third parameter is the name of the fts5 table to drop.
**
** SQLITE_OK is returned if the table is successfully dropped. Or, if an
** error occurs, an SQLite error code.
*/
static int sqlite3_fts5_drop_corrupt_table(
sqlite3 *db, /* Database handle */
const char *zDb, /* Database name ("main", "temp" etc.) */
const char *zTab /* Name of fts5 table to drop */
){
int rc = SQLITE_OK;
int bDef = 0;
rc = sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, -1, &bDef);
if( rc==SQLITE_OK ){
char *zScript = sqlite3_mprintf(
"DELETE FROM %Q.'%q_data';"
"DELETE FROM %Q.'%q_config';"
"INSERT INTO %Q.'%q_data' VALUES(10, X'0000000000');"
"INSERT INTO %Q.'%q_config' VALUES('version', 4);"
"DROP TABLE %Q.'%q';",
zDb, zTab, zDb, zTab, zDb, zTab, zDb, zTab, zDb, zTab
);
if( zScript==0 ){
rc = SQLITE_NOMEM;
}else{
if( bDef ) sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0);
rc = sqlite3_exec(db, zScript, 0, 0, 0);
if( bDef ) sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, 0);
sqlite3_free(zScript);
}
}
return rc;
}
/*
** sqlite3_fts5_drop_corrupt_table DB DATABASE TABLE
**
** Description...
*/
static int SQLITE_TCLAPI f5tDropCorruptTable(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3 *db = 0;
const char *zDb = 0;
const char *zTab = 0;
int rc = SQLITE_OK;
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB DATABASE TABLE");
return TCL_ERROR;
}
if( f5tDbPointer(interp, objv[1], &db) ){
return TCL_ERROR;
}
zDb = Tcl_GetString(objv[2]);
zTab = Tcl_GetString(objv[3]);
rc = sqlite3_fts5_drop_corrupt_table(db, zDb, zTab);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), (void*)0);
return TCL_ERROR;
}
return TCL_OK;
}
/*
** Entry point.
*/
@@ -1564,7 +1644,8 @@ int Fts5tcl_Init(Tcl_Interp *interp){
{ "sqlite3_fts5_token_hash", f5tTokenHash, 0 },
{ "sqlite3_fts5_register_matchinfo", f5tRegisterMatchinfo, 0 },
{ "sqlite3_fts5_register_fts5tokenize", f5tRegisterTok, 0 },
{ "sqlite3_fts5_register_origintext",f5tRegisterOriginText, 0 }
{ "sqlite3_fts5_register_origintext",f5tRegisterOriginText, 0 },
{ "sqlite3_fts5_drop_corrupt_table", f5tDropCorruptTable, 0 }
};
int i;
F5tTokenizerContext *pContext;
+103
View File
@@ -0,0 +1,103 @@
# 2024 Aug 28
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5corrupt8
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
ifcapable !fts5 {
finish_test
return
}
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
}
do_execsql_test 1.1 {
UPDATE t1_data SET block='hello world' WHERE id=10
}
db close
sqlite3 db test.db
do_catchsql_test 1.2 {
SELECT * FROM t1
} {1 {vtable constructor failed: t1}}
do_catchsql_test 1.3 {
DROP TABLE t1
} {1 {vtable constructor failed: t1}}
do_test 1.4 {
sqlite3_db_config db DEFENSIVE 0
} {0}
do_test 1.5 {
sqlite3_fts5_drop_corrupt_table db main t1
} {}
do_test 1.6 {
sqlite3_db_config db DEFENSIVE -1
} {0}
do_execsql_test 1.7 {
SELECT * FROM sqlite_schema
}
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
}
do_execsql_test 2.1 {
UPDATE t1_config SET v=555 WHERE k='version'
}
db close
sqlite3 db test.db
do_catchsql_test 2.2 {
SELECT * FROM t1
} {1 {invalid fts5 file format (found 555, expected 4 or 5) - run 'rebuild'}}
do_catchsql_test 2.3 {
DROP TABLE t1
} {1 {invalid fts5 file format (found 555, expected 4 or 5) - run 'rebuild'}}
do_test 2.4 {
sqlite3_fts5_drop_corrupt_table db main t1
} {}
do_execsql_test 2.5 {
SELECT * FROM sqlite_schema
}
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE t1 USING fts5(x);
}
do_execsql_test 3.1 {
DELETE FROM t1_config;
}
db close
sqlite3 db test.db
do_catchsql_test 3.2 {
SELECT * FROM t1
} {1 {invalid fts5 file format (found 0, expected 4 or 5) - run 'rebuild'}}
do_catchsql_test 3.3 {
DROP TABLE t1
} {1 {invalid fts5 file format (found 0, expected 4 or 5) - run 'rebuild'}}
do_test 3.4 {
sqlite3_db_config db DEFENSIVE 1
} {1}
do_test 3.5 {
sqlite3_fts5_drop_corrupt_table db main t1
} {}
do_test 3.6 {
sqlite3_db_config db DEFENSIVE -1
} {1}
do_execsql_test 3.7 {
SELECT * FROM sqlite_schema
}
finish_test
+8 -7
View File
@@ -1,5 +1,5 @@
C Updates\sto\stestrunner:\s\s(1)\sOmit\sall\stesting\sof\sUser-Auth.\n(2)\sAutomatically\sadd\sthe\s"*"\swildcard\sbefore\sand\safter\sall\spattern\narguments.\s\s(3)\sBuild\sthe\ssqlite3\sCLI\sfor\srelease\stests.
D 2024-08-27T17:38:26.497
C Add\stest\scode\sto\sdrop\san\sfts5\stable\swith\scorrupt\srecords\sin\sits\sshadow\stables.
D 2024-08-27T18:25:04.935
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -102,7 +102,7 @@ F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a
F ext/fts5/fts5_index.c eb9a0dda3bc6ef969a6be8d2746af56856e67251810ddba08622b45be8477abe
F ext/fts5/fts5_main.c 3d8b778f65fe5be218f6a8e4019048f092c0c09621b035ce3e8804093036b578
F ext/fts5/fts5_storage.c 9a9b880be12901f1962ae2a5a7e1b74348b3099a1e728764e419f75d98e3e612
F ext/fts5/fts5_tcl.c 1dcf08028141c40a32634bdcf2d5601622ce4edc48f82ac4ce0cbe0a92a6961d
F ext/fts5/fts5_tcl.c 4db9258a7882c5eac0da4433042132aaf15b87dd1e1636c7a6ca203abd2c8bfe
F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee
F ext/fts5/fts5_test_tok.c 3cb0a9b508b30d17ef025ccddd26ae3dc8ddffbe76c057616e59a9aa85d36f3b
F ext/fts5/fts5_tokenize.c ae9c4fa93174ef06ffc138bd4280a1c37f7e13624d3d2706aad4b80573f23c41
@@ -153,6 +153,7 @@ F ext/fts5/test/fts5corrupt4.test dc08d19f5b8943e95a7778a7d8da592042504faf18dd93
F ext/fts5/test/fts5corrupt5.test 11b47126f5772cc37b67e3e8b2ed05895c4d07c05338bc07e4eea225bfe32c76
F ext/fts5/test/fts5corrupt6.test 2d72db743db7b5d9c9a6d0cfef24d799ed1aa5e8192b66c40e871a37ed9eed06
F ext/fts5/test/fts5corrupt7.test 4e830875c33b9ea3c4cf1ba71e692b63893cbb4faae8c69b1071889dc26e211c
F ext/fts5/test/fts5corrupt8.test f78b6de9e4327e16ba7a7958b1ce576f9b5d886b4b97690f90b7f3cf654da458
F ext/fts5/test/fts5delete.test 619295b20dbc1d840b403ee07c878f52378849c3c02e44f2ee143b3e978a0aa7
F ext/fts5/test/fts5detail.test 54015e9c43ec4ba542cfb93268abdf280e0300f350efd08ee411284b03595cc4
F ext/fts5/test/fts5determin.test 1b77879b2ae818b5b71c859e534ee334dac088b7cf3ff3bf76a2c82b1c788d11
@@ -2210,8 +2211,8 @@ F vsixtest/vsixtest.tcl 6195aba1f12a5e10efc2b8c0009532167be5e301abe5b31385638080
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P e9b03b082dcf141695140a6c2c50bf0dc577c3d64ab76f81dd22f0084eca26d6
R 4a4a4d707423caa7361c65011daf0aec
U drh
Z 9d66ab840eaa6125582be7511985f53f
P d03d35eebaf82709414c87cfa6abc9d2baf8d7e64c2627bad0fd5bbda3e78d60
R eae9b17364855941c00e92c86d398a4e
U dan
Z d07b6efd95ad4c9798aced1dad6099da
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
d03d35eebaf82709414c87cfa6abc9d2baf8d7e64c2627bad0fd5bbda3e78d60
ca21c942c30a3dbff0e7d118e105b847d80b5388c74d19c2eeea71581f8f40b8