Skip to content

Commit 9df59b4

Browse files
asg017claude
andcommitted
Temporarily block vector UPDATE for DiskANN and IVF indexes
vec0Update_UpdateVectorColumn writes to flat chunk blobs but does not update DiskANN graph or IVF index structures, silently corrupting KNN results. Now returns a clear error for these index types. Rescore UPDATE is unaffected — it already has a full implementation that updates both quantized chunks and float vectors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 07f56e3 commit 9df59b4

3 files changed

Lines changed: 35 additions & 12 deletions

File tree

‎sqlite-vec.c‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10055,6 +10055,26 @@ int vec0Update_Update(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv) {
1005510055
continue;
1005610056
}
1005710057

10058+
// Block vector UPDATE for index types that don't implement it —
10059+
// the DiskANN graph / IVF lists would become stale.
10060+
{
10061+
enum Vec0IndexType idx_type = p->vector_columns[vector_idx].index_type;
10062+
const char *idx_name = NULL;
10063+
if (idx_type == VEC0_INDEX_TYPE_DISKANN) idx_name = "DiskANN";
10064+
#if SQLITE_VEC_EXPERIMENTAL_IVF_ENABLE
10065+
else if (idx_type == VEC0_INDEX_TYPE_IVF) idx_name = "IVF";
10066+
#endif
10067+
if (idx_name) {
10068+
vtab_set_error(
10069+
&p->base,
10070+
"UPDATE on vector column \"%.*s\" is not supported for %s indexes.",
10071+
p->vector_columns[vector_idx].name_length,
10072+
p->vector_columns[vector_idx].name,
10073+
idx_name);
10074+
return SQLITE_ERROR;
10075+
}
10076+
}
10077+
1005810078
rc = vec0Update_UpdateVectorColumn(p, chunk_id, chunk_offset, vector_idx,
1005910079
valueVector, rowid);
1006010080
if (rc != SQLITE_OK) {

‎tests/test-diskann.py‎

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,7 @@ def test_diskann_delete_preserves_graph_connectivity(db):
891891
# ======================================================================
892892

893893
def test_diskann_update_vector(db):
894-
"""UPDATE a vector on DiskANN table may not be supported; verify it either works or errors cleanly."""
894+
"""UPDATE a vector on DiskANN table should error (will be implemented soon)."""
895895
db.execute("""
896896
CREATE VIRTUAL TABLE t USING vec0(
897897
emb float[8] INDEXED BY diskann(neighbor_quantizer=binary, n_neighbors=8)
@@ -901,17 +901,8 @@ def test_diskann_update_vector(db):
901901
db.execute("INSERT INTO t(rowid, emb) VALUES (2, ?)", [_f32([0, 1, 0, 0, 0, 0, 0, 0])])
902902
db.execute("INSERT INTO t(rowid, emb) VALUES (3, ?)", [_f32([0, 0, 1, 0, 0, 0, 0, 0])])
903903

904-
# UPDATE may not be fully supported for DiskANN yet; verify no crash
905-
result = exec(db, "UPDATE t SET emb = ? WHERE rowid = 1", [_f32([0, 0.9, 0.1, 0, 0, 0, 0, 0])])
906-
if "error" not in result:
907-
# If UPDATE succeeded, verify KNN reflects the new value
908-
rows = db.execute(
909-
"SELECT rowid, distance FROM t WHERE emb MATCH ? AND k=3",
910-
[_f32([0, 1, 0, 0, 0, 0, 0, 0])],
911-
).fetchall()
912-
assert len(rows) == 3
913-
# rowid 2 should still be closest (exact match)
914-
assert rows[0][0] == 2
904+
with pytest.raises(sqlite3.OperationalError, match="UPDATE on vector column.*not supported for DiskANN"):
905+
db.execute("UPDATE t SET emb = ? WHERE rowid = 1", [_f32([0, 0.9, 0.1, 0, 0, 0, 0, 0])])
915906

916907

917908
# ======================================================================

‎tests/test-ivf-mutations.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,3 +573,15 @@ def test_interleaved_ops_correctness(db):
573573
# Verify we get the right count (25 odd + 15 new - 10 deleted new = 30)
574574
expected_alive = set(range(1, 50, 2)) | set(range(50, 60)) | set(range(70, 75))
575575
assert rowids.issubset(expected_alive)
576+
577+
578+
def test_ivf_update_vector_blocked(db):
579+
"""UPDATE on a vector column with IVF index should error (index would become stale)."""
580+
db.execute(
581+
"CREATE VIRTUAL TABLE t USING vec0(emb float[4] indexed by ivf(nlist=2))"
582+
)
583+
db.execute("INSERT INTO t(rowid, emb) VALUES (1, ?)", [_f32([1, 0, 0, 0])])
584+
db.execute("INSERT INTO t(rowid, emb) VALUES (2, ?)", [_f32([0, 1, 0, 0])])
585+
586+
with pytest.raises(sqlite3.OperationalError, match="UPDATE on vector column.*not supported for IVF"):
587+
db.execute("UPDATE t SET emb = ? WHERE rowid = 1", [_f32([0, 0, 1, 0])])

0 commit comments

Comments
 (0)