From a03d3e24b059f8795e555af34c98b38040f830aa Mon Sep 17 00:00:00 2001 From: Wilbert Harriman Date: Mon, 24 Feb 2025 13:53:31 +0800 Subject: [PATCH] ALTER TABLE RENAME support --- sqlite-vec.c | 107 +++++++++++++++++- tests/__snapshots__/test-auxiliary.ambr | 86 ++++++++++++++ tests/__snapshots__/test-metadata.ambr | 144 ++++++++++++++++++++++++ tests/test-auxiliary.py | 18 +++ tests/test-metadata.py | 23 ++++ 5 files changed, 377 insertions(+), 1 deletion(-) diff --git a/sqlite-vec.c b/sqlite-vec.c index 3cc802f0..772c59a0 100644 --- a/sqlite-vec.c +++ b/sqlite-vec.c @@ -8915,6 +8915,111 @@ static int vec0Rollback(sqlite3_vtab *pVTab) { return SQLITE_OK; } +static int vec0Rename(sqlite3_vtab *pVTab, const char *zName) { + vec0_vtab *p = (vec0_vtab *)pVTab; + sqlite3_stmt *stmt; + int rc; + const char *zSql; + + vec0_free_resources(p); + + zSql = sqlite3_mprintf("ALTER TABLE " VEC0_SHADOW_CHUNKS_NAME " RENAME TO \"%w_chunks\"", + p->schemaName, p->tableName, zName); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + vtab_set_error(pVTab, "could not rename chunks shadow table"); + goto done; + } + sqlite3_finalize(stmt); + + zSql = sqlite3_mprintf("ALTER TABLE " VEC0_SHADOW_INFO_NAME " RENAME TO \"%w_info\"", p->schemaName, + p->tableName, zName); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + vtab_set_error(pVTab, "could not rename info shadow table"); + goto done; + } + sqlite3_finalize(stmt); + + zSql = sqlite3_mprintf("ALTER TABLE " VEC0_SHADOW_ROWIDS_NAME " RENAME TO \"%w_rowids\"", p->schemaName, + p->tableName, zName); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + vtab_set_error(pVTab, "could not rename rowids shadow table"); + goto done; + } + sqlite3_finalize(stmt); + + for (int i = 0; i < p->numVectorColumns; i++) { + char *newShadowVectorChunksName = sqlite3_mprintf("%s_vector_chunks%02d", zName, i); + if (!newShadowVectorChunksName) { + return SQLITE_NOMEM; + } + zSql = sqlite3_mprintf("ALTER TABLE \"%w\".\"%w\" RENAME TO \"%w\"", p->schemaName, + p->shadowVectorChunksNames[i], newShadowVectorChunksName); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + vtab_set_error(pVTab, "could not rename vector_chunks shadow table"); + goto done; + } + sqlite3_finalize(stmt); + } + + if(p->numAuxiliaryColumns > 0) { + zSql = sqlite3_mprintf("ALTER TABLE " VEC0_SHADOW_AUXILIARY_NAME " RENAME TO \"%w_auxiliary\"", + p->schemaName, p->tableName, zName); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + vtab_set_error(pVTab, "could not rename auxiliary shadow table"); + goto done; + } + sqlite3_finalize(stmt); + } + + for (int i = 0; i < p->numMetadataColumns; i++) { + zSql = sqlite3_mprintf("ALTER TABLE " VEC0_SHADOW_METADATA_N_NAME " RENAME TO \"%w_metadatachunks%02d\"", + p->schemaName, p->tableName, i, zName, i); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + vtab_set_error(pVTab, "could not rename metadatachunks shadow table"); + goto done; + } + sqlite3_finalize(stmt); + + if(p->metadata_columns[i].kind == VEC0_METADATA_COLUMN_KIND_TEXT) { + zSql = sqlite3_mprintf("ALTER TABLE " VEC0_SHADOW_METADATA_TEXT_DATA_NAME " RENAME TO \"%w_metadatatext%02d\"", + p->schemaName, p->tableName, i, zName, i); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, 0); + sqlite3_free((void *)zSql); + if ((rc != SQLITE_OK) || (sqlite3_step(stmt) != SQLITE_DONE)) { + rc = SQLITE_ERROR; + vtab_set_error(pVTab, "could not rename metadatatext shadow table"); + goto done; + } + sqlite3_finalize(stmt); + } + } + + stmt = NULL; + rc = SQLITE_OK; + +done: + sqlite3_finalize(stmt); + return rc; +} + static sqlite3_module vec0Module = { /* iVersion */ 3, /* xCreate */ vec0Create, @@ -8935,7 +9040,7 @@ static sqlite3_module vec0Module = { /* xCommit */ vec0Commit, /* xRollback */ vec0Rollback, /* xFindFunction */ 0, - /* xRename */ 0, // https://github.com/asg017/sqlite-vec/issues/43 + /* xRename */ vec0Rename, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, diff --git a/tests/__snapshots__/test-auxiliary.ambr b/tests/__snapshots__/test-auxiliary.ambr index bfe3d2c9..e393b435 100644 --- a/tests/__snapshots__/test-auxiliary.ambr +++ b/tests/__snapshots__/test-auxiliary.ambr @@ -392,6 +392,92 @@ ]), }) # --- +# name: test_renames + 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_renames.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_types OrderedDict({ 'sql': 'select * from v', diff --git a/tests/__snapshots__/test-metadata.ambr b/tests/__snapshots__/test-metadata.ambr index 12212ff0..5a7e78a9 100644 --- a/tests/__snapshots__/test-metadata.ambr +++ b/tests/__snapshots__/test-metadata.ambr @@ -1925,6 +1925,150 @@ ]), }) # --- +# name: test_renames + OrderedDict({ + 'sql': 'insert into v(rowid, vector, b, n, f, t) values (?, ?, ?, ?, ?, ?)', + 'rows': list([ + ]), + }) +# --- +# name: test_renames.1 + OrderedDict({ + 'sql': 'insert into v(rowid, vector, b, n, f, t) values (?, ?, ?, ?, ?, ?)', + 'rows': list([ + ]), + }) +# --- +# name: test_renames.2 + OrderedDict({ + 'sql': 'insert into v(rowid, vector, b, n, f, t) values (?, ?, ?, ?, ?, ?)', + 'rows': list([ + ]), + }) +# --- +# name: test_renames.3 + OrderedDict({ + 'sql': 'select * from v', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'vector': b'\x11\x11\x11\x11', + 'b': 1, + 'n': 1, + 'f': 1.1, + 't': 'test1', + }), + OrderedDict({ + 'rowid': 2, + 'vector': b'""""', + 'b': 1, + 'n': 2, + 'f': 2.2, + 't': 'test2', + }), + OrderedDict({ + 'rowid': 3, + 'vector': b'3333', + 'b': 1, + 'n': 3, + 'f': 3.3, + 't': '1234567890123', + }), + ]), + }) +# --- +# name: test_renames.4 + dict({ + '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_metadatachunks00': OrderedDict({ + 'sql': 'select * from v_metadatachunks00', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x07', + }), + ]), + }), + 'v_metadatachunks01': OrderedDict({ + 'sql': 'select * from v_metadatachunks01', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': 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_metadatachunks02': OrderedDict({ + 'sql': 'select * from v_metadatachunks02', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x9a\x99\x99\x99\x99\x99\xf1?\x9a\x99\x99\x99\x99\x99\x01@ffffff\n@\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_metadatachunks03': OrderedDict({ + 'sql': 'select * from v_metadatachunks03', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x05\x00\x00\x00test1\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00test2\x00\x00\x00\x00\x00\x00\x00\r\x00\x00\x00123456789012\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\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_metadatatext03': OrderedDict({ + 'sql': 'select * from v_metadatatext03', + 'rows': list([ + OrderedDict({ + 'rowid': 3, + 'data': '1234567890123', + }), + ]), + }), + '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'\x11\x11\x11\x11""""3333\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + }), + ]), + }), + }) +# --- # name: test_stress dict({ 'vec_movies_auxiliary': OrderedDict({ diff --git a/tests/test-auxiliary.py b/tests/test-auxiliary.py index d1f5f568..c1c6cacf 100644 --- a/tests/test-auxiliary.py +++ b/tests/test-auxiliary.py @@ -108,6 +108,24 @@ def test_deletes(db, snapshot): assert vec0_shadow_table_contents(db, "v") == snapshot() +def test_renames(db, snapshot): + 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() + + res = exec(db, "select rowid, * from v") + db.execute( + "alter table v rename to v1" + ) + assert exec(db, "select rowid, * from v1")["rows"] == res["rows"] + + def test_knn(db, snapshot): db.execute("create virtual table v using vec0(vector float[1], +name text)") db.executemany( diff --git a/tests/test-metadata.py b/tests/test-metadata.py index 3c2e5423..05d76871 100644 --- a/tests/test-metadata.py +++ b/tests/test-metadata.py @@ -264,6 +264,29 @@ def test_deletes(db, snapshot): assert vec0_shadow_table_contents(db, "v") == snapshot() +def test_renames(db, snapshot): + db.execute( + "create virtual table v using vec0(vector float[1], b boolean, n int, f float, t text, chunk_size=8)" + ) + INSERT = "insert into v(rowid, vector, b, n, f, t) values (?, ?, ?, ?, ?, ?)" + + assert exec(db, INSERT, [1, b"\x11\x11\x11\x11", 1, 1, 1.1, "test1"]) == snapshot() + assert exec(db, INSERT, [2, b"\x22\x22\x22\x22", 1, 2, 2.2, "test2"]) == snapshot() + assert ( + exec(db, INSERT, [3, b"\x33\x33\x33\x33", 1, 3, 3.3, "1234567890123"]) + == snapshot() + ) + + assert exec(db, "select * from v") == snapshot() + assert vec0_shadow_table_contents(db, "v") == snapshot() + + result = exec(db, "select * from v") + db.execute( + "alter table v rename to v1" + ) + assert exec(db, "select * from v1")["rows"] == result["rows"] + + def test_knn(db, snapshot): db.execute( "create virtual table v using vec0(vector float[1], name text, chunk_size=8)"