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;
}
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];
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) ){
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;
}
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;
}
}
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;
}
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;
}
);
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;
}
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;
}
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;
}
Tcl_AppendResult(interp, (
bV2 ? "error in fts5_api.xCreateTokenizer_v2()"
: "error in fts5_api.xCreateTokenizer()"
- ), 0);
+ ), (char*)0);
return TCL_ERROR;
}
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.
*/
{ "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;
--- /dev/null
+# 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
+
-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
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
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
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.