11#pragma once
22
33#include < sqlite3.h>
4+ #include < cstdint>
45#include < cstring>
56#include < memory>
67#include < sstream>
78#include < string>
89#include < vector>
910#include " ../utils/error.hpp"
11+ #include " parsers.hpp"
12+ #include " value.hpp"
1013
1114namespace sqlite_vec_cpp ::sqlite {
1215
@@ -84,7 +87,11 @@ inline bool parse_vec0_schema(int argc, const char* const* argv, std::string& em
8487
8588// Helper: Create shadow tables for vec0 storage
8689inline int create_shadow_tables (sqlite3* db, const char * schema, const char * table,
87- const char * embedding_col, size_t dims, char ** pzErr) {
90+ const char * embedding_col, [[maybe_unused]] size_t dims,
91+ char ** pzErr) {
92+ // Note: dims parameter is reserved for future use (validation, typed vectors)
93+ (void )dims;
94+
8895 // Shadow table for metadata
8996 std::ostringstream meta_sql;
9097 meta_sql << " CREATE TABLE IF NOT EXISTS \" " << schema << " \" .\" " << table << " _metadata\" ("
@@ -124,6 +131,9 @@ inline int vec0Create(sqlite3* db, void* pAux, int argc, const char* const* argv
124131 sqlite3_vtab** ppVTab, char ** pzErr) {
125132 (void )pAux;
126133
134+ // Enable shadow table writes through xUpdate
135+ sqlite3_vtab_config (db, SQLITE_VTAB_DIRECTONLY);
136+
127137 if (argc < 3 ) {
128138 *pzErr = sqlite3_mprintf (" vec0: insufficient arguments" );
129139 return SQLITE_ERROR;
@@ -342,12 +352,33 @@ inline int vec0Update(sqlite3_vtab* pVTab, int argc, sqlite3_value** argv, sqlit
342352 bool is_insert = (sqlite3_value_type (argv[0 ]) == SQLITE_NULL);
343353 int64_t rowid = is_insert ? 0 : sqlite3_value_int64 (argv[0 ]);
344354
345- // argv[2] = embedding column value
346- const void * blob = sqlite3_value_blob (argv[2 ]);
347- int bytes = sqlite3_value_bytes (argv[2 ]);
355+ // argv layout per SQLite vtab spec:
356+ // - argv[0]: old rowid (or NULL)
357+ // - argv[1]: new rowid (or NULL)
358+ // - argv[2..]: column values in declared order
359+ if (argc < 4 ) {
360+ return SQLITE_MISUSE;
361+ }
362+
363+ // Our declared schema is: CREATE TABLE x(rowid INTEGER PRIMARY KEY, "embedding")
364+ // So the embedding column value is argv[3] (argv[2] corresponds to rowid column value).
365+ sqlite3_value* embedding_val = argv[3 ];
366+ const void * blob = sqlite3_value_blob (embedding_val);
367+ int bytes = sqlite3_value_bytes (embedding_val);
368+
369+ // If xUpdate doesn't materialize function-returned blobs, parse JSON/text and bind as blob
370+ std::vector<float > parsed_vec;
371+ if (sqlite3_value_type (embedding_val) == SQLITE_TEXT) {
372+ Value value (embedding_val);
373+ auto parsed = parse_vector_from_value<float >(value);
374+ if (parsed) {
375+ parsed_vec = std::move (parsed.value ());
376+ blob = parsed_vec.data ();
377+ bytes = static_cast <int >(parsed_vec.size () * sizeof (float ));
378+ }
379+ }
348380
349381 if (is_insert) {
350- // INSERT
351382 std::ostringstream sql;
352383 sql << " INSERT INTO \" " << table->schema_name << " \" .\" " << table->table_name
353384 << " _vectors\" (\" " << table->embedding_column << " \" ) VALUES (?)" ;
@@ -357,9 +388,13 @@ inline int vec0Update(sqlite3_vtab* pVTab, int argc, sqlite3_value** argv, sqlit
357388 if (rc != SQLITE_OK)
358389 return rc;
359390
360- sqlite3_bind_blob (stmt, 1 , blob, bytes, SQLITE_TRANSIENT);
361- rc = sqlite3_step (stmt);
391+ if (blob && bytes > 0 ) {
392+ sqlite3_bind_blob (stmt, 1 , blob, bytes, SQLITE_TRANSIENT);
393+ } else {
394+ sqlite3_bind_null (stmt, 1 );
395+ }
362396
397+ rc = sqlite3_step (stmt);
363398 if (rc == SQLITE_DONE) {
364399 *pRowid = sqlite3_last_insert_rowid (table->db );
365400 rc = SQLITE_OK;
@@ -368,7 +403,6 @@ inline int vec0Update(sqlite3_vtab* pVTab, int argc, sqlite3_value** argv, sqlit
368403 sqlite3_finalize (stmt);
369404 return rc;
370405 } else {
371- // UPDATE
372406 std::ostringstream sql;
373407 sql << " UPDATE \" " << table->schema_name << " \" .\" " << table->table_name
374408 << " _vectors\" SET \" " << table->embedding_column << " \" =? WHERE rowid=" << rowid;
@@ -378,9 +412,13 @@ inline int vec0Update(sqlite3_vtab* pVTab, int argc, sqlite3_value** argv, sqlit
378412 if (rc != SQLITE_OK)
379413 return rc;
380414
381- sqlite3_bind_blob (stmt, 1 , blob, bytes, SQLITE_TRANSIENT);
382- rc = sqlite3_step (stmt);
415+ if (blob && bytes > 0 ) {
416+ sqlite3_bind_blob (stmt, 1 , blob, bytes, SQLITE_TRANSIENT);
417+ } else {
418+ sqlite3_bind_null (stmt, 1 );
419+ }
383420
421+ rc = sqlite3_step (stmt);
384422 if (rc == SQLITE_DONE) {
385423 *pRowid = rowid;
386424 rc = SQLITE_OK;
@@ -419,7 +457,8 @@ static sqlite3_module vec0_module = {
419457 /* xSavepoint */ nullptr ,
420458 /* xRelease */ nullptr ,
421459 /* xRollbackTo */ nullptr ,
422- /* xShadowName */ nullptr };
460+ /* xShadowName */ nullptr ,
461+ /* xIntegrity */ nullptr };
423462
424463inline Result<void > register_vec0_module (sqlite3* db) {
425464 if (!db) {
0 commit comments