From c1055ab3f81206736cd9badf24ca77a23f2fe50d Mon Sep 17 00:00:00 2001 From: Alex Garcia Date: Thu, 14 Nov 2024 11:32:48 -0800 Subject: [PATCH] UPDATE support --- TODO | 2 +- sqlite-vec.c | 287 +++++----- tests/__snapshots__/test-metadata.ambr | 711 +++++++++++++++++++++++++ tests/test-metadata.py | 48 +- 4 files changed, 924 insertions(+), 124 deletions(-) diff --git a/TODO b/TODO index 505e0b3..131ab11 100644 --- a/TODO +++ b/TODO @@ -14,7 +14,7 @@ # metadata filtering -- UPDATE support +- null! - date/datetime - later - `v in (...)` handling diff --git a/sqlite-vec.c b/sqlite-vec.c index db06d47..a33a321 100644 --- a/sqlite-vec.c +++ b/sqlite-vec.c @@ -7341,144 +7341,160 @@ cleanup: return rc; } -/** - * @brief Insert any provided metadata values - * - * @param p vec0_vtab - * @param argc num args from xFilter() - * @param argv sqlite3_value args from xFilter() - * @param chunk_id chunk_id to insert row into - * @param chunk_offset offset the row/metadata value is assigned to - * @return int - */ -int vec0_insert_metadata_values(vec0_vtab *p, int argc, sqlite3_value ** argv, i64 chunk_id, i64 chunk_offset, i64 rowid) { +int vec0_write_metadata_value(vec0_vtab *p, int metadata_column_idx, i64 rowid, i64 chunk_id, i64 chunk_offset, sqlite3_value * v, int isupdate) { int rc; - for(int i = 0; i < vec0_num_defined_user_columns(p); i++) { - if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_METADATA) { - continue; - } - int metadata_idx = p->user_column_idxs[i]; - vec0_metadata_column_kind kind = p->metadata_columns[metadata_idx].kind; - sqlite3_value *v = argv[2 + VEC0_COLUMN_USERN_START + i]; + struct Vec0MetadataColumnDefinition * metadata_column = &p->metadata_columns[metadata_column_idx]; + vec0_metadata_column_kind kind = metadata_column->kind; - // verify input value matches column type - switch(kind) { - case VEC0_METADATA_COLUMN_KIND_BOOLEAN: { - if(sqlite3_value_type(v) != SQLITE_INTEGER || ((sqlite3_value_int(v) != 0) && (sqlite3_value_int(v) != 1))) { - rc = SQLITE_ERROR; - vtab_set_error(&p->base, "Expected 0 or 1 for BOOLEAN metadata column %.*s", p->metadata_columns[metadata_idx].name_length, p->metadata_columns[metadata_idx].name); - goto done; - } - break; - } - case VEC0_METADATA_COLUMN_KIND_INTEGER: { - if(sqlite3_value_type(v) != SQLITE_INTEGER) { - rc = SQLITE_ERROR; - vtab_set_error(&p->base, "Expected integer for INTEGER metadata column %.*s, received %s", p->metadata_columns[metadata_idx].name_length, p->metadata_columns[metadata_idx].name, type_name(sqlite3_value_type(v))); - goto done; - } - break; - } - case VEC0_METADATA_COLUMN_KIND_FLOAT: { - if(sqlite3_value_type(v) != SQLITE_FLOAT) { - rc = SQLITE_ERROR; - vtab_set_error(&p->base, "Expected float for FLOAT metadata column %.*s, received %s", p->metadata_columns[metadata_idx].name_length, p->metadata_columns[metadata_idx].name, type_name(sqlite3_value_type(v))); - goto done; - } - break; - } - case VEC0_METADATA_COLUMN_KIND_TEXT: { - if(sqlite3_value_type(v) != SQLITE_TEXT) { - rc = SQLITE_ERROR; - vtab_set_error(&p->base, "Expected text for TEXT metadata column %.*s, received %s", p->metadata_columns[metadata_idx].name_length, p->metadata_columns[metadata_idx].name, type_name(sqlite3_value_type(v))); - goto done; - } - break; + // verify input value matches column type + switch(kind) { + case VEC0_METADATA_COLUMN_KIND_BOOLEAN: { + if(sqlite3_value_type(v) != SQLITE_INTEGER || ((sqlite3_value_int(v) != 0) && (sqlite3_value_int(v) != 1))) { + rc = SQLITE_ERROR; + vtab_set_error(&p->base, "Expected 0 or 1 for BOOLEAN metadata column %.*s", metadata_column->name_length, metadata_column->name); + goto done; } + break; } + case VEC0_METADATA_COLUMN_KIND_INTEGER: { + if(sqlite3_value_type(v) != SQLITE_INTEGER) { + rc = SQLITE_ERROR; + vtab_set_error(&p->base, "Expected integer for INTEGER metadata column %.*s, received %s", metadata_column->name_length, metadata_column->name, type_name(sqlite3_value_type(v))); + goto done; + } + break; + } + case VEC0_METADATA_COLUMN_KIND_FLOAT: { + if(sqlite3_value_type(v) != SQLITE_FLOAT) { + rc = SQLITE_ERROR; + vtab_set_error(&p->base, "Expected float for FLOAT metadata column %.*s, received %s", metadata_column->name_length, metadata_column->name, type_name(sqlite3_value_type(v))); + goto done; + } + break; + } + case VEC0_METADATA_COLUMN_KIND_TEXT: { + if(sqlite3_value_type(v) != SQLITE_TEXT) { + rc = SQLITE_ERROR; + vtab_set_error(&p->base, "Expected text for TEXT metadata column %.*s, received %s", metadata_column->name_length, metadata_column->name, type_name(sqlite3_value_type(v))); + goto done; + } + break; + } + } - sqlite3_blob * blobValue; - rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowMetadataChunksNames[metadata_idx], "data", chunk_id, 1, &blobValue); - if(rc != SQLITE_OK) { - // TODO - goto done; + sqlite3_blob * blobValue = NULL; + rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowMetadataChunksNames[metadata_column_idx], "data", chunk_id, 1, &blobValue); + if(rc != SQLITE_OK) { + goto done; + } + + switch(kind) { + case VEC0_METADATA_COLUMN_KIND_BOOLEAN: { + u8 block; + int value = sqlite3_value_int(v); + rc = sqlite3_blob_read(blobValue, &block, sizeof(u8), (int) (chunk_offset / CHAR_BIT)); + if(rc != SQLITE_OK) { + goto done; + } + + if (value) { + block |= 1 << (chunk_offset % CHAR_BIT); + } else { + block &= ~(1 << (chunk_offset % CHAR_BIT)); + } + + rc = sqlite3_blob_write(blobValue, &block, sizeof(u8), chunk_offset / CHAR_BIT); + break; } - switch(kind) { - case VEC0_METADATA_COLUMN_KIND_BOOLEAN: { - u8 block; - int value = sqlite3_value_int(v); - rc = sqlite3_blob_read(blobValue, &block, sizeof(u8), (int) (chunk_offset / CHAR_BIT)); + case VEC0_METADATA_COLUMN_KIND_INTEGER: { + i64 value = sqlite3_value_int64(v); + rc = sqlite3_blob_write(blobValue, &value, sizeof(value), chunk_offset * sizeof(i64)); + break; + } + case VEC0_METADATA_COLUMN_KIND_FLOAT: { + double value = sqlite3_value_double(v); + rc = sqlite3_blob_write(blobValue, &value, sizeof(value), chunk_offset * sizeof(double)); + break; + } + case VEC0_METADATA_COLUMN_KIND_TEXT: { + int prev_n; + rc = sqlite3_blob_read(blobValue, &prev_n, sizeof(int), chunk_offset * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH); + if(rc != SQLITE_OK) { + goto done; + } + + char * s = sqlite3_value_text(v); + int n = sqlite3_value_bytes(v); + u8 view[VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH]; + memset(view, 0, VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH); + memcpy(view, &n, sizeof(int)); + memcpy(view+4, s, min(n, VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH-4)); + + rc = sqlite3_blob_write(blobValue, &view, VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH, chunk_offset * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH); + if(n > 12) { + const char * zSql; + + if(isupdate && (prev_n > 12)) { + zSql = sqlite3_mprintf("UPDATE " VEC0_SHADOW_METADATA_TEXT_DATA_NAME " SET data = ?2 WHERE rowid = ?1", p->schemaName, p->tableName, metadata_column_idx); + }else { + zSql = sqlite3_mprintf("INSERT INTO " VEC0_SHADOW_METADATA_TEXT_DATA_NAME " (rowid, data) VALUES (?1, ?2)", p->schemaName, p->tableName, metadata_column_idx); + } + if(!zSql) { + rc = SQLITE_NOMEM; + goto done; + } + sqlite3_stmt * stmt; + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); if(rc != SQLITE_OK) { - // TODO goto done; } + sqlite3_bind_int64(stmt, 1, rowid); + sqlite3_bind_text(stmt, 2, s, n, SQLITE_STATIC); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); - if (value) { - block |= 1 << (chunk_offset % CHAR_BIT); - } else { - block &= ~(1 << (chunk_offset % CHAR_BIT)); + if(rc != SQLITE_DONE) { + rc = SQLITE_ERROR; + goto done; } - - rc = sqlite3_blob_write(blobValue, &block, sizeof(u8), chunk_offset / CHAR_BIT); - break; } - case VEC0_METADATA_COLUMN_KIND_INTEGER: { - i64 value = sqlite3_value_int64(v); - rc = sqlite3_blob_write(blobValue, &value, sizeof(value), chunk_offset * sizeof(i64)); - break; - } - case VEC0_METADATA_COLUMN_KIND_FLOAT: { - double value = sqlite3_value_double(v); - rc = sqlite3_blob_write(blobValue, &value, sizeof(value), chunk_offset * sizeof(double)); - break; - } - case VEC0_METADATA_COLUMN_KIND_TEXT: { - char * s = sqlite3_value_text(v); - int n = sqlite3_value_bytes(v); - u8 view[VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH]; - memset(view, 0, VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH); - memcpy(view, &n, sizeof(int)); - memcpy(view+4, s, min(n, VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH-4)); - - rc = sqlite3_blob_write(blobValue, &view, VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH, chunk_offset * VEC0_METADATA_TEXT_VIEW_BUFFER_LENGTH); - if(n > 12) { - const char * zSql = sqlite3_mprintf("INSERT INTO " VEC0_SHADOW_METADATA_TEXT_DATA_NAME " (rowid, data) VALUES (?, ?)", p->schemaName, p->tableName, metadata_idx); - if(!zSql) { - abort(); - } - sqlite3_stmt * stmt; - rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); - if(rc != SQLITE_OK) { - abort(); - } - sqlite3_bind_int64(stmt, 1, rowid); - sqlite3_bind_text(stmt, 2, s, n, SQLITE_STATIC); - rc = sqlite3_step(stmt); - if(rc != SQLITE_DONE) { - abort(); - } - sqlite3_finalize(stmt); + else if(prev_n > 12) { + const char * zSql = sqlite3_mprintf("DELETE FROM " VEC0_SHADOW_METADATA_TEXT_DATA_NAME " WHERE rowid = ?", p->schemaName, p->tableName, metadata_column_idx); + if(!zSql) { + rc = SQLITE_NOMEM; + goto done; + } + sqlite3_stmt * stmt; + rc = sqlite3_prepare_v2(p->db, zSql, -1, &stmt, NULL); + if(rc != SQLITE_OK) { + goto done; + } + sqlite3_bind_int64(stmt, 1, rowid); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + + if(rc != SQLITE_DONE) { + rc = SQLITE_ERROR; + goto done; } - break; } + break; } + } - if(rc != SQLITE_OK) { - - } - rc = sqlite3_blob_close(blobValue); - if(rc != SQLITE_OK) { - goto done; - } - + if(rc != SQLITE_OK) { } + rc = sqlite3_blob_close(blobValue); + if(rc != SQLITE_OK) { + goto done; + } done: return rc; - } + /** * @brief Handles INSERT INTO operations on a vec0 table. * @@ -7690,9 +7706,17 @@ int vec0Update_Insert(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, goto cleanup; } - rc = vec0_insert_metadata_values(p, argc, argv, chunk_rowid, chunk_offset, rowid); - if(rc != SQLITE_OK) { - goto cleanup; + + for(int i = 0; i < vec0_num_defined_user_columns(p); i++) { + if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_METADATA) { + continue; + } + int metadata_idx = p->user_column_idxs[i]; + sqlite3_value *v = argv[2 + VEC0_COLUMN_USERN_START + i]; + rc = vec0_write_metadata_value(p, metadata_idx, rowid, chunk_rowid, chunk_offset, v, 0); + if(rc != SQLITE_OK) { + goto cleanup; + } } *pRowid = rowid; @@ -7900,8 +7924,9 @@ int vec0Update_Delete_ClearMetadata(vec0_vtab *p, int metadata_idx, i64 rowid, i break; } } + int rc2; done: - int rc2 = sqlite3_blob_close(blobValue); + rc2 = sqlite3_blob_close(blobValue); if(rc == SQLITE_OK) { return rc2; } @@ -7991,6 +8016,12 @@ int vec0Update_UpdateAuxColumn(vec0_vtab *p, int auxiliary_column_idx, sqlite3_v return SQLITE_OK; } +int vec0Update_UpdateMetadataColumn(vec0_vtab *p, int metadata_column_idx, sqlite3_value * value, i64 rowid) { + int rc; + + return rc; +} + int vec0Update_UpdateVectorColumn(vec0_vtab *p, i64 chunk_id, i64 chunk_offset, int i, sqlite3_value *valueVector) { int rc; @@ -8131,9 +8162,23 @@ int vec0Update_Update(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv) { } } - // TODO handle metadata updates + // 4) handle metadata column updates + for (int i = 0; i < vec0_num_defined_user_columns(p); i++) { + if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_METADATA) { + continue; + } + int metadata_column_idx = p->user_column_idxs[i]; + sqlite3_value * value = argv[2+VEC0_COLUMN_USERN_START + i]; + if(sqlite3_value_nochange(value)) { + continue; + } + rc = vec0_write_metadata_value(p, metadata_column_idx, rowid, chunk_id, chunk_offset, value, 1); + if(rc != SQLITE_OK) { + return rc; + } + } - // 4) iterate over all new vectors, update the vectors + // 5) iterate over all new vectors, update the vectors for (int i = 0; i < vec0_num_defined_user_columns(p); i++) { if(p->user_column_kinds[i] != SQLITE_VEC0_USER_COLUMN_KIND_VECTOR) { continue; diff --git a/tests/__snapshots__/test-metadata.ambr b/tests/__snapshots__/test-metadata.ambr index 41a3800..0ded21f 100644 --- a/tests/__snapshots__/test-metadata.ambr +++ b/tests/__snapshots__/test-metadata.ambr @@ -1698,3 +1698,714 @@ ]), }) # --- +# name: test_updates[1-init] + 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_metadata_chunks00': OrderedDict({ + 'sql': 'select * from v_metadata_chunks00', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x07', + }), + ]), + }), + 'v_metadata_chunks01': OrderedDict({ + 'sql': 'select * from v_metadata_chunks01', + '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_metadata_chunks02': OrderedDict({ + 'sql': 'select * from v_metadata_chunks02', + '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_metadata_chunks03': OrderedDict({ + 'sql': 'select * from v_metadata_chunks03', + '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_metadata_text_data_03': OrderedDict({ + 'sql': 'select * from v_metadata_text_data_03', + '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_updates[general-update-contents] + OrderedDict({ + 'sql': 'select * from v', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'vector': b'\x11\x11\x11\x11', + 'b': 0, + 'n': 11, + 'f': 11.11, + 't': 'newtest1', + }), + 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_updates[general-update-shaodnw] + 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_metadata_chunks00': OrderedDict({ + 'sql': 'select * from v_metadata_chunks00', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x06', + }), + ]), + }), + 'v_metadata_chunks01': OrderedDict({ + 'sql': 'select * from v_metadata_chunks01', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x0b\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_metadata_chunks02': OrderedDict({ + 'sql': 'select * from v_metadata_chunks02', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\xb8\x1e\x85\xebQ8&@\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_metadata_chunks03': OrderedDict({ + 'sql': 'select * from v_metadata_chunks03', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x08\x00\x00\x00newtest1\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_metadata_text_data_03': OrderedDict({ + 'sql': 'select * from v_metadata_text_data_03', + '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_updates[string-update-1-contents] + OrderedDict({ + 'sql': 'select * from v', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'vector': b'\x11\x11\x11\x11', + 'b': 0, + 'n': 11, + 'f': 11.11, + 't': 'newtest1', + }), + 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-updated', + }), + ]), + }) +# --- +# name: test_updates[string-update-1-shadow] + 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_metadata_chunks00': OrderedDict({ + 'sql': 'select * from v_metadata_chunks00', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x06', + }), + ]), + }), + 'v_metadata_chunks01': OrderedDict({ + 'sql': 'select * from v_metadata_chunks01', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x0b\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_metadata_chunks02': OrderedDict({ + 'sql': 'select * from v_metadata_chunks02', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\xb8\x1e\x85\xebQ8&@\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_metadata_chunks03': OrderedDict({ + 'sql': 'select * from v_metadata_chunks03', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x08\x00\x00\x00newtest1\x00\x00\x00\x00\x05\x00\x00\x00test2\x00\x00\x00\x00\x00\x00\x00\x15\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_metadata_text_data_03': OrderedDict({ + 'sql': 'select * from v_metadata_text_data_03', + 'rows': list([ + OrderedDict({ + 'rowid': 3, + 'data': '1234567890123-updated', + }), + ]), + }), + '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_updates[string-update-2-contents] + OrderedDict({ + 'sql': 'select * from v', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'vector': b'\x11\x11\x11\x11', + 'b': 0, + 'n': 11, + 'f': 11.11, + 't': 'newtest1', + }), + OrderedDict({ + 'rowid': 2, + 'vector': b'""""', + 'b': 1, + 'n': 2, + 'f': 2.2, + 't': 'test2-short', + }), + OrderedDict({ + 'rowid': 3, + 'vector': b'3333', + 'b': 1, + 'n': 3, + 'f': 3.3, + 't': '1234567890123-updated', + }), + ]), + }) +# --- +# name: test_updates[string-update-2-shadow] + 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_metadata_chunks00': OrderedDict({ + 'sql': 'select * from v_metadata_chunks00', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x06', + }), + ]), + }), + 'v_metadata_chunks01': OrderedDict({ + 'sql': 'select * from v_metadata_chunks01', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x0b\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_metadata_chunks02': OrderedDict({ + 'sql': 'select * from v_metadata_chunks02', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\xb8\x1e\x85\xebQ8&@\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_metadata_chunks03': OrderedDict({ + 'sql': 'select * from v_metadata_chunks03', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x08\x00\x00\x00newtest1\x00\x00\x00\x00\x0b\x00\x00\x00test2-short\x00\x15\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_metadata_text_data_03': OrderedDict({ + 'sql': 'select * from v_metadata_text_data_03', + 'rows': list([ + OrderedDict({ + 'rowid': 3, + 'data': '1234567890123-updated', + }), + ]), + }), + '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_updates[string-update-3-contents] + OrderedDict({ + 'sql': 'select * from v', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'vector': b'\x11\x11\x11\x11', + 'b': 0, + 'n': 11, + 'f': 11.11, + 't': 'newtest1', + }), + OrderedDict({ + 'rowid': 2, + 'vector': b'""""', + 'b': 1, + 'n': 2, + 'f': 2.2, + 't': 'test2-long-long-long', + }), + OrderedDict({ + 'rowid': 3, + 'vector': b'3333', + 'b': 1, + 'n': 3, + 'f': 3.3, + 't': '1234567890123-updated', + }), + ]), + }) +# --- +# name: test_updates[string-update-3-shadow] + 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_metadata_chunks00': OrderedDict({ + 'sql': 'select * from v_metadata_chunks00', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x06', + }), + ]), + }), + 'v_metadata_chunks01': OrderedDict({ + 'sql': 'select * from v_metadata_chunks01', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x0b\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_metadata_chunks02': OrderedDict({ + 'sql': 'select * from v_metadata_chunks02', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\xb8\x1e\x85\xebQ8&@\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_metadata_chunks03': OrderedDict({ + 'sql': 'select * from v_metadata_chunks03', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x08\x00\x00\x00newtest1\x00\x00\x00\x00\x14\x00\x00\x00test2-long-l\x15\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_metadata_text_data_03': OrderedDict({ + 'sql': 'select * from v_metadata_text_data_03', + 'rows': list([ + OrderedDict({ + 'rowid': 3, + 'data': '1234567890123-updated', + }), + OrderedDict({ + 'rowid': 2, + 'data': 'test2-long-long-long', + }), + ]), + }), + '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_updates[string-update-4-contents] + OrderedDict({ + 'sql': 'select * from v', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'vector': b'\x11\x11\x11\x11', + 'b': 0, + 'n': 11, + 'f': 11.11, + 't': 'newtest1', + }), + OrderedDict({ + 'rowid': 2, + 'vector': b'""""', + 'b': 1, + 'n': 2, + 'f': 2.2, + 't': 'test2-shortx', + }), + OrderedDict({ + 'rowid': 3, + 'vector': b'3333', + 'b': 1, + 'n': 3, + 'f': 3.3, + 't': '1234567890123-updated', + }), + ]), + }) +# --- +# name: test_updates[string-update-4-shadow] + 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_metadata_chunks00': OrderedDict({ + 'sql': 'select * from v_metadata_chunks00', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x06', + }), + ]), + }), + 'v_metadata_chunks01': OrderedDict({ + 'sql': 'select * from v_metadata_chunks01', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x0b\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_metadata_chunks02': OrderedDict({ + 'sql': 'select * from v_metadata_chunks02', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\xb8\x1e\x85\xebQ8&@\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_metadata_chunks03': OrderedDict({ + 'sql': 'select * from v_metadata_chunks03', + 'rows': list([ + OrderedDict({ + 'rowid': 1, + 'data': b'\x08\x00\x00\x00newtest1\x00\x00\x00\x00\x0c\x00\x00\x00test2-shortx\x15\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_metadata_text_data_03': OrderedDict({ + 'sql': 'select * from v_metadata_text_data_03', + 'rows': list([ + OrderedDict({ + 'rowid': 3, + 'data': '1234567890123-updated', + }), + ]), + }), + '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', + }), + ]), + }), + }) +# --- diff --git a/tests/test-metadata.py b/tests/test-metadata.py index afd1623..1466b3f 100644 --- a/tests/test-metadata.py +++ b/tests/test-metadata.py @@ -3,7 +3,6 @@ from collections import OrderedDict def test_constructor_limit(db, snapshot): - pass assert exec( db, f""" @@ -82,7 +81,52 @@ def test_types(db, snapshot): def test_updates(db, snapshot): - pass + 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 (?, ?, ?, ?, ?, ?)" + + exec(db, INSERT, [1, b"\x11\x11\x11\x11", 1, 1, 1.1, "test1"]) + exec(db, INSERT, [2, b"\x22\x22\x22\x22", 1, 2, 2.2, "test2"]) + exec(db, INSERT, [3, b"\x33\x33\x33\x33", 1, 3, 3.3, "1234567890123"]) + assert exec(db, "select * from v") == snapshot(name="1-init") + assert vec0_shadow_table_contents(db, "v") == snapshot(name="1-init") + + assert exec( + db, "UPDATE v SET b = 0, n = 11, f = 11.11, t = 'newtest1' where rowid = 1" + ) + assert exec(db, "select * from v") == snapshot(name="general-update-contents") + assert vec0_shadow_table_contents(db, "v") == snapshot( + name="general-update-shaodnw" + ) + + # string update #1: long string updated to long string + exec(db, "UPDATE v SET t = '1234567890123-updated' where rowid = 3") + assert exec(db, "select * from v") == snapshot(name="string-update-1-contents") + assert vec0_shadow_table_contents(db, "v") == snapshot( + name="string-update-1-shadow" + ) + + # string update #2: short string updated to short string + exec(db, "UPDATE v SET t = 'test2-short' where rowid = 2") + assert exec(db, "select * from v") == snapshot(name="string-update-2-contents") + assert vec0_shadow_table_contents(db, "v") == snapshot( + name="string-update-2-shadow" + ) + + # string update #3: short string updated to long string + exec(db, "UPDATE v SET t = 'test2-long-long-long' where rowid = 2") + assert exec(db, "select * from v") == snapshot(name="string-update-3-contents") + assert vec0_shadow_table_contents(db, "v") == snapshot( + name="string-update-3-shadow" + ) + + # string update #4: long string updated to short string + exec(db, "UPDATE v SET t = 'test2-shortx' where rowid = 2") + assert exec(db, "select * from v") == snapshot(name="string-update-4-contents") + assert vec0_shadow_table_contents(db, "v") == snapshot( + name="string-update-4-shadow" + ) def test_deletes(db, snapshot):