fix(persist): unnamed FTS/HNSW indexes survive restart (nameless replay DDL)
This commit is contained in:
@@ -1321,7 +1321,13 @@ proc executeQueryImpl(ctx: ExecutionContext, astNode: Node, params: seq[WireValu
|
|||||||
ctx.ftsIndexes[colKey] = ftsIdx
|
ctx.ftsIndexes[colKey] = ftsIdx
|
||||||
# Persist reconstructed DDL so restoreEngines can rebuild the index
|
# Persist reconstructed DDL so restoreEngines can rebuild the index
|
||||||
# from table data after a restart (replay re-writes the same key).
|
# from table data after a restart (replay re-writes the same key).
|
||||||
let ftsDdl = "CREATE INDEX " & idxName & " ON " & stmt.ciTarget & " (" & stmt.ciColumns.join(", ") & ") USING FTS"
|
# Unnamed indexes: the colKey fallback name is dotted ("docs.content")
|
||||||
|
# and unparseable, so persist the nameless form — replay regenerates
|
||||||
|
# the same colKey default.
|
||||||
|
let ftsDdl = if stmt.ciName.len > 0:
|
||||||
|
"CREATE INDEX " & idxName & " ON " & stmt.ciTarget & " (" & stmt.ciColumns.join(", ") & ") USING FTS"
|
||||||
|
else:
|
||||||
|
"CREATE INDEX ON " & stmt.ciTarget & " (" & stmt.ciColumns.join(", ") & ") USING FTS"
|
||||||
ctx.db.put(SchemaFtsIndexPrefix & colKey, cast[seq[byte]](ftsDdl))
|
ctx.db.put(SchemaFtsIndexPrefix & colKey, cast[seq[byte]](ftsDdl))
|
||||||
return okResult(msg="CREATE INDEX " & idxName & " on " & stmt.ciTarget & " USING FTS")
|
return okResult(msg="CREATE INDEX " & idxName & " on " & stmt.ciTarget & " USING FTS")
|
||||||
|
|
||||||
@@ -1359,7 +1365,11 @@ proc executeQueryImpl(ctx: ExecutionContext, astNode: Node, params: seq[WireValu
|
|||||||
ctx.vectorIndexes[colKey] = hnswIdx
|
ctx.vectorIndexes[colKey] = hnswIdx
|
||||||
# Persist reconstructed DDL so restoreEngines can rebuild the index
|
# Persist reconstructed DDL so restoreEngines can rebuild the index
|
||||||
# from table data after a restart (replay re-writes the same key).
|
# from table data after a restart (replay re-writes the same key).
|
||||||
let vecDdl = "CREATE INDEX " & idxName & " ON " & stmt.ciTarget & " (" & stmt.ciColumns.join(", ") & ") USING HNSW"
|
# Unnamed indexes: persist the nameless form (see FTS branch above).
|
||||||
|
let vecDdl = if stmt.ciName.len > 0:
|
||||||
|
"CREATE INDEX " & idxName & " ON " & stmt.ciTarget & " (" & stmt.ciColumns.join(", ") & ") USING HNSW"
|
||||||
|
else:
|
||||||
|
"CREATE INDEX ON " & stmt.ciTarget & " (" & stmt.ciColumns.join(", ") & ") USING HNSW"
|
||||||
ctx.db.put(SchemaVecIndexPrefix & colKey, cast[seq[byte]](vecDdl))
|
ctx.db.put(SchemaVecIndexPrefix & colKey, cast[seq[byte]](vecDdl))
|
||||||
return okResult(msg="CREATE INDEX " & idxName & " on " & stmt.ciTarget & " USING HNSW")
|
return okResult(msg="CREATE INDEX " & idxName & " on " & stmt.ciTarget & " USING HNSW")
|
||||||
|
|
||||||
|
|||||||
@@ -168,6 +168,73 @@ suite "Schema persistence":
|
|||||||
db2.close()
|
db2.close()
|
||||||
removeDir(dir)
|
removeDir(dir)
|
||||||
|
|
||||||
|
test "Unnamed FTS index survives reopen":
|
||||||
|
let dir = "/tmp/baradb_schema_persist_fts_noname"
|
||||||
|
removeDir(dir)
|
||||||
|
block:
|
||||||
|
var db = newLSMTree(dir)
|
||||||
|
var ctx = newExecutionContext(db)
|
||||||
|
check execSql(ctx, "CREATE TABLE docs (id INTEGER PRIMARY KEY, content TEXT)").success
|
||||||
|
check execSql(ctx, "INSERT INTO docs (id, content) VALUES (1, 'quick brown fox')").success
|
||||||
|
# No index name — idxName defaults to colKey (docs.content) at execution
|
||||||
|
check execSql(ctx, "CREATE INDEX ON docs (content) USING FTS").success
|
||||||
|
check ctx.ftsIndexes.hasKey("docs.content")
|
||||||
|
db.close()
|
||||||
|
# Reopen fresh context (simulates process restart)
|
||||||
|
block:
|
||||||
|
var db2 = newLSMTree(dir)
|
||||||
|
var ctx2 = newExecutionContext(db2)
|
||||||
|
# Persisted DDL must be replayable — the dotted fallback name is not.
|
||||||
|
check ctx2.ftsIndexes.hasKey("docs.content")
|
||||||
|
if ctx2.ftsIndexes.hasKey("docs.content"):
|
||||||
|
check ctx2.ftsIndexes["docs.content"].search("quick", limit = 10).len >= 1
|
||||||
|
# Nameless persisted DDL must not confuse DROP INDEX name matching
|
||||||
|
let d = execSql(ctx2, "DROP INDEX content")
|
||||||
|
check d.success
|
||||||
|
check "docs.content" notin ctx2.ftsIndexes
|
||||||
|
let (found, _) = db2.get(SchemaFtsIndexPrefix & "docs.content")
|
||||||
|
check not found
|
||||||
|
db2.close()
|
||||||
|
block:
|
||||||
|
var db3 = newLSMTree(dir)
|
||||||
|
var ctx3 = newExecutionContext(db3)
|
||||||
|
check "docs.content" notin ctx3.ftsIndexes # no ghost rebuild
|
||||||
|
db3.close()
|
||||||
|
removeDir(dir)
|
||||||
|
|
||||||
|
test "Unnamed HNSW index survives reopen":
|
||||||
|
let dir = "/tmp/baradb_schema_persist_vec_noname"
|
||||||
|
removeDir(dir)
|
||||||
|
block:
|
||||||
|
var db = newLSMTree(dir)
|
||||||
|
var ctx = newExecutionContext(db)
|
||||||
|
check execSql(ctx, "CREATE TABLE vecs (id INTEGER PRIMARY KEY, embedding TEXT)").success
|
||||||
|
check execSql(ctx, "INSERT INTO vecs (id, embedding) VALUES (1, '[1.0, 0.0, 0.0]')").success
|
||||||
|
# No index name — idxName defaults to colKey (vecs.embedding)
|
||||||
|
check execSql(ctx, "CREATE INDEX ON vecs (embedding) USING HNSW").success
|
||||||
|
check ctx.vectorIndexes.hasKey("vecs.embedding")
|
||||||
|
db.close()
|
||||||
|
# Reopen fresh context (simulates process restart)
|
||||||
|
block:
|
||||||
|
var db2 = newLSMTree(dir)
|
||||||
|
var ctx2 = newExecutionContext(db2)
|
||||||
|
check ctx2.vectorIndexes.hasKey("vecs.embedding")
|
||||||
|
if ctx2.vectorIndexes.hasKey("vecs.embedding"):
|
||||||
|
check vengine.search(ctx2.vectorIndexes["vecs.embedding"],
|
||||||
|
@[1.0'f32, 0.0'f32, 0.0'f32], k = 5).len >= 1
|
||||||
|
let d = execSql(ctx2, "DROP INDEX embedding")
|
||||||
|
check d.success
|
||||||
|
check "vecs.embedding" notin ctx2.vectorIndexes
|
||||||
|
let (found, _) = db2.get(SchemaVecIndexPrefix & "vecs.embedding")
|
||||||
|
check not found
|
||||||
|
db2.close()
|
||||||
|
block:
|
||||||
|
var db3 = newLSMTree(dir)
|
||||||
|
var ctx3 = newExecutionContext(db3)
|
||||||
|
check "vecs.embedding" notin ctx3.vectorIndexes # no ghost rebuild
|
||||||
|
db3.close()
|
||||||
|
removeDir(dir)
|
||||||
|
|
||||||
test "Graph survives reopen":
|
test "Graph survives reopen":
|
||||||
let dir = "/tmp/baradb_schema_persist_graph"
|
let dir = "/tmp/baradb_schema_persist_graph"
|
||||||
removeDir(dir)
|
removeDir(dir)
|
||||||
|
|||||||
Reference in New Issue
Block a user