From a394c5f0dc98506dca565fc6e07fa2e43382fc5d Mon Sep 17 00:00:00 2001 From: Alex Garcia Date: Wed, 13 Nov 2024 15:35:22 -0800 Subject: [PATCH] support delete --- TODO | 3 +- sqlite-vec.c | 36 +++++- tests/__snapshots__/test-auxiliary.ambr | 164 ++++++++++++++++++++++++ tests/test-auxiliary.py | 14 +- 4 files changed, 213 insertions(+), 4 deletions(-) diff --git a/TODO b/TODO index c42ab8f..dd3b651 100644 --- a/TODO +++ b/TODO @@ -7,8 +7,7 @@ # auxiliary columns -- in xBestIndex, ensure there are no constraints on any aux column -- DELETE and UPDATE support +- UPDATE support - later: - NOT NULL? - perf: INSERT stmt should be cached on vec0_vtab diff --git a/sqlite-vec.c b/sqlite-vec.c index 1e38bcf..3fda9aa 100644 --- a/sqlite-vec.c +++ b/sqlite-vec.c @@ -6975,6 +6975,34 @@ cleanup: return rc; } +int vec0Update_Delete_DeleteAux(vec0_vtab *p, i64 rowid) { + int rc; + sqlite3_stmt *stmt = NULL; + + char *zSql = + sqlite3_mprintf("DELETE FROM " VEC0_SHADOW_AUXILIARY_NAME " WHERE rowid = ?", + p->schemaName, p->tableName); + if (!zSql) { + return SQLITE_NOMEM; + } + + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + sqlite3_free(zSql); + if (rc != SQLITE_OK) { + goto cleanup; + } + sqlite3_bind_int64(stmt, 1, rowid); + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE) { + goto cleanup; + } + rc = SQLITE_OK; + +cleanup: + sqlite3_finalize(stmt); + return rc; +} + int vec0Update_Delete(sqlite3_vtab *pVTab, sqlite3_value *idValue) { vec0_vtab *p = (vec0_vtab *)pVTab; int rc; @@ -7020,7 +7048,13 @@ int vec0Update_Delete(sqlite3_vtab *pVTab, sqlite3_value *idValue) { return rc; } - // TODO delete any auxiliary rows + // 6. delete any auxiliary rows + if(p->numAuxiliaryColumns > 0) { + rc = vec0Update_Delete_DeleteAux(p, rowid); + if (rc != SQLITE_OK) { + return rc; + } + } return SQLITE_OK; } diff --git a/tests/__snapshots__/test-auxiliary.ambr b/tests/__snapshots__/test-auxiliary.ambr index 33dd836..01ab03b 100644 --- a/tests/__snapshots__/test-auxiliary.ambr +++ b/tests/__snapshots__/test-auxiliary.ambr @@ -5,6 +5,170 @@ 'message': 'vec0 constructor error: More than 16 auxiliary columns were provided', }) # --- +# name: test_deletes + OrderedDict({ + 'sql': 'select rowid, * from v', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'vector': b'\x00\x00\x80?', + 'name': 'alex', + }), + OrderedDict({ + 'rowid': 2, + 'vector': b'\x00\x00\x00@', + 'name': 'brian', + }), + OrderedDict({ + 'rowid': 3, + 'vector': b'\x00\x00@@', + 'name': 'craig', + }), + ]), + }) +# --- +# name: test_deletes.1 + dict({ + 'v_auxiliary': OrderedDict({ + 'sql': 'select * from v_auxiliary', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'value00': 'alex', + }), + OrderedDict({ + 'rowid': 2, + 'value00': 'brian', + }), + OrderedDict({ + 'rowid': 3, + 'value00': 'craig', + }), + ]), + }), + 'v_chunks': OrderedDict({ + 'sql': 'select * from v_chunks', + 'rows': list([ + OrderedDict({ + 'chunk_id': 1, + 'size': 8, + 'validity': b'\x07', + 'rowids': b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + }), + ]), + }), + 'v_rowids': OrderedDict({ + 'sql': 'select * from v_rowids', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'id': None, + 'chunk_id': 1, + 'chunk_offset': 0, + }), + OrderedDict({ + 'rowid': 2, + 'id': None, + 'chunk_id': 1, + 'chunk_offset': 1, + }), + OrderedDict({ + 'rowid': 3, + 'id': None, + 'chunk_id': 1, + 'chunk_offset': 2, + }), + ]), + }), + 'v_vector_chunks00': OrderedDict({ + 'sql': 'select * from v_vector_chunks00', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'vectors': b'\x00\x00\x80?\x00\x00\x00@\x00\x00@@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + }), + ]), + }), + }) +# --- +# name: test_deletes.2 + OrderedDict({ + 'sql': 'delete from v where rowid = 1', + 'rows': list([ + ]), + }) +# --- +# name: test_deletes.3 + OrderedDict({ + 'sql': 'select rowid, * from v', + 'rows': list([ + OrderedDict({ + 'rowid': 2, + 'vector': b'\x00\x00\x00@', + 'name': 'brian', + }), + OrderedDict({ + 'rowid': 3, + 'vector': b'\x00\x00@@', + 'name': 'craig', + }), + ]), + }) +# --- +# name: test_deletes.4 + dict({ + 'v_auxiliary': OrderedDict({ + 'sql': 'select * from v_auxiliary', + 'rows': list([ + OrderedDict({ + 'rowid': 2, + 'value00': 'brian', + }), + OrderedDict({ + 'rowid': 3, + 'value00': 'craig', + }), + ]), + }), + 'v_chunks': OrderedDict({ + 'sql': 'select * from v_chunks', + 'rows': list([ + OrderedDict({ + 'chunk_id': 1, + 'size': 8, + 'validity': b'\x06', + 'rowids': b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + }), + ]), + }), + 'v_rowids': OrderedDict({ + 'sql': 'select * from v_rowids', + 'rows': list([ + OrderedDict({ + 'rowid': 2, + 'id': None, + 'chunk_id': 1, + 'chunk_offset': 1, + }), + OrderedDict({ + 'rowid': 3, + 'id': None, + 'chunk_id': 1, + 'chunk_offset': 2, + }), + ]), + }), + 'v_vector_chunks00': OrderedDict({ + 'sql': 'select * from v_vector_chunks00', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'vectors': b'\x00\x00\x80?\x00\x00\x00@\x00\x00@@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + }), + ]), + }), + }) +# --- # name: test_knn OrderedDict({ 'sql': 'select * from v', diff --git a/tests/test-auxiliary.py b/tests/test-auxiliary.py index 71d8a49..e8a51b7 100644 --- a/tests/test-auxiliary.py +++ b/tests/test-auxiliary.py @@ -77,7 +77,19 @@ def test_updates(db, snapshot): def test_deletes(db, snapshot): - pass + db.execute( + "create virtual table v using vec0(vector float[1], +name text, chunk_size=8)" + ) + db.executemany( + "insert into v(vector, name) values (?, ?)", + [("[1]", "alex"), ("[2]", "brian"), ("[3]", "craig")], + ) + assert exec(db, "select rowid, * from v") == snapshot() + assert vec0_shadow_table_contents(db, "v") == snapshot() + + assert exec(db, "delete from v where rowid = 1") == snapshot() + assert exec(db, "select rowid, * from v") == snapshot() + assert vec0_shadow_table_contents(db, "v") == snapshot() def test_knn(db, snapshot):