Skip to content

Commit

Permalink
CBL-6371: Unnest Query run after delete index produces obtuse error m…
Browse files Browse the repository at this point in the history
…essage (#2178)

We enhanced the error by adding to the original message, "This table is referenced by an array index, which may have been deleted."

* We also enhanced the error message of FTS index in the similar situation.
  • Loading branch information
jianminzhao authored Jan 20, 2025
1 parent c5ec167 commit 2948b7b
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 14 deletions.
33 changes: 20 additions & 13 deletions C/tests/c4ArrayIndexTest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -358,26 +358,30 @@ TEST_CASE_METHOD(ArrayIndexTest, "CRUD Array Index Shared Path", "[C][ArrayIndex
bool deleted = c4coll_deleteIndex(coll, "phones"_sl, ERROR_INFO());
REQUIRE(deleted);

// We must re-create the Query objects because we deleted the index - which means the unnest tables may be missing.
cityQuery = c4query_new2(
db, kC4N1QLQuery,
R"(SELECT p.pid, c.address.city, c.address.state FROM profiles AS p UNNEST p.contacts AS c WHERE c.address.state = "CA")"_sl,
nullptr, ERROR_INFO());
REQUIRE(cityQuery);
// cityQuery is not affected by the deletion of index "phones"
queryenum = REQUIRED(c4query_run(cityQuery, nullslice, nullptr));
validateQuery(queryenum, {
R"(["p-0001", "San Pedro", "CA"])",
R"(["p-0001", "San Pedro", "CA"])",
});

// phoneQuery is affected by the deletion of index "phones"
// Following error will be logged,
// 2024-10-29T21:14:28.226339 DB ERROR SQLite error (code 1): no such table: 152b9815998e188eb99eb1612aafbb3ee6031535 in "SELECT fl_result(fl_value(prof.body, 'pid')), fl_result(fl_unnested_value(c.body, 'address.city')), fl_result(fl_unnested_value(c.body, 'address.state')), fl_result(fl_unnested_value(p.body, 'type')), fl_result(fl_unnested_value(p.body, 'numbers')) FROM "kv_.profiles" AS prof JOIN bc89db8a20fe759bf161b84adf2294d9bfe0c88d AS c ON c.docid=prof.rowid JOIN "152b9815998e188eb99eb1612aafbb3ee6031535" AS p ON p.docid=c.rowid WHERE fl_unnested_value(p.body, 'type') = 'mobile'". This table is referenced by an array index, which may have been deleted.
C4Error error;
queryenum = c4query_run(phoneQuery, nullslice, &error);
CHECK(!queryenum); // This query relies on the index that has been deleted.
CHECK((error.domain == SQLiteDomain && error.code == 1));

// Recompile the query
phoneQuery = c4query_new2(
db, kC4N1QLQuery,
R"(SELECT prof.pid, c.address.city, c.address.state, p.type, p.numbers FROM profiles AS prof UNNEST prof.contacts AS c UNNEST c.phones AS p WHERE p.type = "mobile")"_sl,
nullptr, ERROR_INFO());
REQUIRE(phoneQuery);
queryenum = c4query_run(phoneQuery, nullslice, &error);
CHECK(queryenum);


queryenum = REQUIRED(c4query_run(cityQuery, nullslice, nullptr));
validateQuery(queryenum, {
R"(["p-0001", "San Pedro", "CA"])",
R"(["p-0001", "San Pedro", "CA"])",
});
queryenum = REQUIRED(c4query_run(phoneQuery, nullslice, nullptr));
validateQuery(queryenum, {
R"(["p-0001", "San Pedro", "CA", "mobile", ["310-9601308"]])",
R"(["p-0001", "San Pedro", "CA", "mobile", ["310-4833623"]])",
Expand Down Expand Up @@ -654,6 +658,9 @@ TEST_CASE_METHOD(ArrayIndexTest, "Unnest Without Alias", "[C][Unnest]") {

// 7. TestUnnestArrayLiteralNotSupport
TEST_CASE_METHOD(ArrayIndexTest, "Unnest Array Literal Not Supported", "[C][Unnest]") {
C4Collection* coll = createCollection(db, {"profiles"_sl, "_default"_sl});
importTestData(coll);

C4Error err{};
c4::ref query = c4query_new2(
db, kC4N1QLQuery,
Expand Down
9 changes: 9 additions & 0 deletions C/tests/c4QueryTest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,15 @@ TEST_CASE_METHOD(CollectionTest, "C4Query FTS Multiple collections", "[Query][C]

CHECK(run().size() == 50);
CHECK(runFTS().size() == 50);

auto deleted = c4coll_deleteIndex(names, C4STR("byStreet"), nullptr);
CHECK(deleted);
// The query won't run after the index is deleted. We should see following error in the log,
// 2024-10-29T20:57:00.896439 DB ERROR SQLite error (code 1): no such table: kv_.namedscope.names::by\Street in "SELECT "namedscope.names".rowid, offsets(fts1."kv_.namedscope.names::by\Street"), offsets(fts2."kv_.wiki::by\Text"), fl_result("namedscope.names".key), fl_result(wiki.key) FROM "kv_.namedscope.names" AS "namedscope.names" INNER JOIN "kv_.wiki" AS wiki ON (fl_value("namedscope.names".body, 'birthday') != fl_value(wiki.body, 'title')) JOIN "kv_.namedscope.names::by\Street" AS fts1 ON fts1.docid = "namedscope.names".rowid JOIN "kv_.wiki::by\Text" AS fts2 ON fts2.docid = wiki.rowid WHERE fts1."kv_.namedscope.names::by\Street" MATCH 'Hwy' AND fts2. This table is referenced by an FTS index, which may have been deleted.
C4Error error;
auto qenum = c4query_run(query, c4str(nullptr), &error);
CHECK(!qenum);
CHECK((error.domain == SQLiteDomain && error.code == 1));
}

#pragma mark - OBSERVERS:
Expand Down
29 changes: 28 additions & 1 deletion LiteCore/Storage/SQLiteDataFile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <sqlite3.h>
#include <sstream>
#include <mutex>
#include <regex>
#include <thread>
#include <cinttypes>
#ifdef _WIN32
Expand Down Expand Up @@ -114,6 +115,28 @@ namespace litecore {

void LogStatement(const SQLite::Statement& st) { LogTo(SQL, "... %s", st.getQuery().c_str()); }

static std::pair<bool, std::string> enhanceSQLiteErrorLog(int errCode, const char* msg) {
std::regex noTableRegx{"no such table: (\\S+) in "};
std::cmatch match;
const char* extra = nullptr;
if ( std::regex_search(msg, match, noTableRegx) ) {
if ( std::regex_search(match.suffix().str(), std::regex{"fl_unnested_value"})
&& std::regex_match(match[1].str(), std::regex{"[0-9a-z]{40}"}) ) {
extra = "This table is referenced by an array index, which may have been deleted.";
} else if ( std::regex_match(match[1].str(), std::regex{"kv_\\..+::.+"}) ) {
// example target: match[1].str() = "kv_.namedscope.names::by\\Street"
// where "::" = KeyStore::kIndexSeparator
extra = "This table is referenced by an FTS index, which may have been deleted.";
}
}
std::string enhanced;
if ( extra ) {
enhanced = msg;
enhanced += ". "s + extra;
}
return std::make_pair(extra, enhanced);
}

static void sqlite3_log_callback(C4UNUSED void* pArg, int errCode, const char* msg) {
switch ( errCode & 0xFF ) {
case SQLITE_OK:
Expand All @@ -132,7 +155,11 @@ namespace litecore {
LogWarn(DBLog, "SQLite warning: %s", msg);
break;
default:
LogError(DBLog, "SQLite error (code %d): %s", errCode, msg);
{
auto [enhanced, enhancedMsg] = enhanceSQLiteErrorLog(errCode, msg);
if ( enhanced ) msg = enhancedMsg.c_str();
LogError(DBLog, "SQLite error (code %d): %s", errCode, msg);
}
break;
}
}
Expand Down

0 comments on commit 2948b7b

Please sign in to comment.