ensure statements opened by vec0 are finalize before commits.

This commit is contained in:
Alex Garcia 2024-07-23 08:59:34 -07:00
parent ff6cf96e2a
commit 7fc8248f28
2 changed files with 379 additions and 340 deletions

View file

@ -3037,29 +3037,9 @@ struct vec0_vtab {
* Must be cleaned up with sqlite3_finalize(). * Must be cleaned up with sqlite3_finalize().
*/ */
sqlite3_stmt *stmtRowidsGetChunkPosition; sqlite3_stmt *stmtRowidsGetChunkPosition;
/**
* Cached SQLite BLOBs for every possible vector column for the table.
* Defined for all vectors up to index numVectorColumns (always <=
* VEC0_MAX_VECTOR_COLUMNS).
*
* Defined from:
* db: p->schemaName
* table: p->shadowVectorChunksNames[i]
* column: "vectors"
*
* Opened at vec0_init() time.
* Must be cleaned up with sqlite3_blob_close() at xDisconnect.
*
*/
sqlite3_blob *vectorBlobs[VEC0_MAX_VECTOR_COLUMNS];
}; };
void vec0_free_resources(vec0_vtab *p) { void vec0_free_resources(vec0_vtab *p) {
for (int i = 0; i < p->numVectorColumns; i++) {
sqlite3_blob_close(p->vectorBlobs[i]);
p->vectorBlobs[i] = NULL;
}
sqlite3_finalize(p->stmtLatestChunk); sqlite3_finalize(p->stmtLatestChunk);
p->stmtLatestChunk = NULL; p->stmtLatestChunk = NULL;
sqlite3_finalize(p->stmtRowidsInsertRowid); sqlite3_finalize(p->stmtRowidsInsertRowid);
@ -3120,6 +3100,62 @@ int vec0_column_idx_to_vector_idx(vec0_vtab *pVtab, int column_idx) {
return column_idx - VEC0_COLUMN_VECTORN_START; return column_idx - VEC0_COLUMN_VECTORN_START;
} }
int vec0_get_chunk_position(vec0_vtab * p, i64 rowid, sqlite3_value ** id, i64 *chunk_id, i64 * chunk_offset) {
int rc;
if(!p->stmtRowidsGetChunkPosition) {
const char * zSql = sqlite3_mprintf("SELECT id, chunk_id, chunk_offset "
"FROM " VEC0_SHADOW_ROWIDS_NAME " WHERE rowid = ?",
p->schemaName, p->tableName);
if (!zSql) {
rc = SQLITE_NOMEM;
goto cleanup;
}
rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->stmtRowidsGetChunkPosition, 0);
sqlite3_free((void *)zSql);
if (rc != SQLITE_OK) {
vtab_set_error(&p->base,
VEC_INTERAL_ERROR
"could not initialize 'rowids get chunk position' statement");
goto cleanup;
}
}
sqlite3_bind_int64(p->stmtRowidsGetChunkPosition, 1, rowid);
rc = sqlite3_step(p->stmtRowidsGetChunkPosition);
// special case: when no results, return SQLITE_EMPTY to convene "that chunk position doesnt exist"
if(rc == SQLITE_DONE) {
rc = SQLITE_EMPTY;
goto cleanup;
}
if (rc != SQLITE_ROW) {
goto cleanup;
}
if(id) {
sqlite3_value *value = sqlite3_column_value(p->stmtRowidsGetChunkPosition, 0);
*id = sqlite3_value_dup(value);
if(!*id) {
rc = SQLITE_NOMEM;
goto cleanup;
}
}
if(chunk_id) {
*chunk_id = sqlite3_column_int64(p->stmtRowidsGetChunkPosition, 1);
}
if(chunk_offset) {
*chunk_offset = sqlite3_column_int64(p->stmtRowidsGetChunkPosition, 2);
}
rc = SQLITE_OK;
cleanup:
sqlite3_reset(p->stmtRowidsGetChunkPosition);
sqlite3_clear_bindings(p->stmtRowidsGetChunkPosition);
return rc;
}
/** /**
* @brief Return the id value from the _rowids table where _rowids.rowid = * @brief Return the id value from the _rowids table where _rowids.rowid =
* rowid. * rowid.
@ -3132,23 +3168,9 @@ int vec0_column_idx_to_vector_idx(vec0_vtab *pVtab, int column_idx) {
*/ */
int vec0_get_id_value_from_rowid(vec0_vtab *pVtab, i64 rowid, int vec0_get_id_value_from_rowid(vec0_vtab *pVtab, i64 rowid,
sqlite3_value **out) { sqlite3_value **out) {
int rc; // PERF: different strategy than get_chunk_position?
// PERF: different stmt than stmtRowidsGetChunkPosition?
// TODO: test / evidence-of // TODO: test / evidence-of
sqlite3_bind_int64(pVtab->stmtRowidsGetChunkPosition, 1, rowid); return vec0_get_chunk_position( (vec0_vtab *) pVtab, rowid, out, NULL, NULL);
rc = sqlite3_step(pVtab->stmtRowidsGetChunkPosition);
if (rc != SQLITE_ROW) {
goto cleanup;
}
sqlite3_value *value =
sqlite3_column_value(pVtab->stmtRowidsGetChunkPosition, 0);
*out = sqlite3_value_dup(value);
rc = SQLITE_OK;
cleanup:
sqlite3_reset(pVtab->stmtRowidsGetChunkPosition);
sqlite3_clear_bindings(pVtab->stmtRowidsGetChunkPosition);
return rc;
} }
int vec0_rowid_from_id(vec0_vtab *p, sqlite3_value *valueId, i64 *rowid) { int vec0_rowid_from_id(vec0_vtab *p, sqlite3_value *valueId, i64 *rowid) {
@ -3222,36 +3244,35 @@ int vec0_result_id(vec0_vtab *p, sqlite3_context *context, i64 rowid) {
*/ */
int vec0_get_vector_data(vec0_vtab *pVtab, i64 rowid, int vector_column_idx, int vec0_get_vector_data(vec0_vtab *pVtab, i64 rowid, int vector_column_idx,
void **outVector, int *outVectorSize) { void **outVector, int *outVectorSize) {
vec0_vtab *p = pVtab;
int rc; int rc;
i64 chunk_id; i64 chunk_id;
i64 chunk_offset; i64 chunk_offset;
size_t size; size_t size;
void *buf = NULL; void *buf = NULL;
int blobOffset; int blobOffset;
sqlite3_blob * vectorBlob = NULL;
assert((vector_column_idx >= 0) && assert((vector_column_idx >= 0) &&
(vector_column_idx < pVtab->numVectorColumns)); (vector_column_idx < pVtab->numVectorColumns));
sqlite3_bind_int64(pVtab->stmtRowidsGetChunkPosition, 1, rowid); rc = vec0_get_chunk_position(pVtab, rowid, NULL, &chunk_id, &chunk_offset);
rc = sqlite3_step(pVtab->stmtRowidsGetChunkPosition); if(rc == SQLITE_EMPTY) {
if (rc == SQLITE_DONE) { vtab_set_error(&pVtab->base, "Could not find a row with rowid %lld", rowid);
// TODO error message on callers
rc = SQLITE_EMPTY;
goto cleanup; goto cleanup;
} }
if (rc != SQLITE_ROW) { if(rc != SQLITE_OK) {
vtab_set_error(&pVtab->base, "Could not find a row with id %lld", rowid);
rc = SQLITE_ERROR;
goto cleanup; goto cleanup;
} }
chunk_id = sqlite3_column_int64(pVtab->stmtRowidsGetChunkPosition, 1); rc = sqlite3_blob_open(p->db, p->schemaName,
chunk_offset = sqlite3_column_int64(pVtab->stmtRowidsGetChunkPosition, 2); p->shadowVectorChunksNames[vector_column_idx], "vectors", chunk_id, 0,
&vectorBlob);
rc = sqlite3_blob_reopen(pVtab->vectorBlobs[vector_column_idx], chunk_id);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
// TODO evidence-of
vtab_set_error( vtab_set_error(
&pVtab->base, &pVtab->base,
"Could not fetch vector data for %lld, reopening blob failed", rowid); "Could not fetch vector data for %lld, opening blob failed", rowid);
rc = SQLITE_ERROR; rc = SQLITE_ERROR;
goto cleanup; goto cleanup;
} }
@ -3261,12 +3282,11 @@ int vec0_get_vector_data(vec0_vtab *pVtab, i64 rowid, int vector_column_idx,
buf = sqlite3_malloc(size); buf = sqlite3_malloc(size);
if (!buf) { if (!buf) {
rc = SQLITE_ERROR; rc = SQLITE_NOMEM;
goto cleanup; goto cleanup;
} }
rc = sqlite3_blob_read(pVtab->vectorBlobs[vector_column_idx], buf, size, rc = sqlite3_blob_read(vectorBlob, buf, size, blobOffset);
blobOffset);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
sqlite3_free(buf); sqlite3_free(buf);
buf = NULL; buf = NULL;
@ -3285,47 +3305,235 @@ int vec0_get_vector_data(vec0_vtab *pVtab, i64 rowid, int vector_column_idx,
rc = SQLITE_OK; rc = SQLITE_OK;
cleanup: cleanup:
sqlite3_reset(pVtab->stmtRowidsGetChunkPosition); sqlite3_blob_close(vectorBlob);
sqlite3_clear_bindings(pVtab->stmtRowidsGetChunkPosition);
return rc; return rc;
} }
/** int vec0_get_latest_chunk_rowid(vec0_vtab *p, i64 *chunk_rowid) {
* @brief For the given rowid, found the chunk_id and chunk_offset for that row.
*
* @param p: vec0 table
* @param rowid: rowid of row to lookup
* @param chunk_id: Output chunk_id of the row, refs _chunks.rowid
* @param chunk_offset: Output chunk_offset of the row
* @return int: SQLITE_OK on success, error code on failure
*/
int vec0_get_chunk_position(vec0_vtab *p, i64 rowid, i64 *chunk_id,
i64 *chunk_offset) {
int rc; int rc;
sqlite3_bind_int64(p->stmtRowidsGetChunkPosition, 1, rowid); const char * zSql;
// lazy initialize stmtLatestChunk when needed. May be cleared during xSync()
if(!p->stmtLatestChunk) {
zSql = sqlite3_mprintf("SELECT max(rowid) FROM " VEC0_SHADOW_CHUNKS_NAME,
p->schemaName, p->tableName);
if (!zSql) {
rc = SQLITE_NOMEM;
goto cleanup;
}
rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->stmtLatestChunk, 0);
sqlite3_free((void *)zSql);
if (rc != SQLITE_OK) {
// IMP: V21406_05476
vtab_set_error(&p->base, VEC_INTERAL_ERROR "could not initialize 'latest chunk' statement");
goto cleanup;
}
}
rc = sqlite3_step(p->stmtRowidsGetChunkPosition); rc = sqlite3_step(p->stmtLatestChunk);
if (rc != SQLITE_ROW) { if (rc != SQLITE_ROW) {
vtab_set_error(&p->base, "Could not find chunk position for %lld", rowid); // IMP: V31559_15629
vtab_set_error(&p->base, VEC_INTERAL_ERROR "Could not find latest chunk");
rc = SQLITE_ERROR;
goto cleanup; goto cleanup;
} }
*chunk_id = sqlite3_column_int64(p->stmtRowidsGetChunkPosition, 1); *chunk_rowid = sqlite3_column_int64(p->stmtLatestChunk, 0);
*chunk_offset = sqlite3_column_int64(p->stmtRowidsGetChunkPosition, 2); rc = sqlite3_step(p->stmtLatestChunk);
rc = sqlite3_step(p->stmtRowidsGetChunkPosition);
if (rc != SQLITE_DONE) { if (rc != SQLITE_DONE) {
vtab_set_error(&p->base, "Could not find chunk position for %lld", rowid); vtab_set_error(&p->base,
VEC_INTERAL_ERROR
"unknown result code when closing out stmtLatestChunk. "
"Please file an issue: " REPORT_URL,
p->schemaName, p->shadowChunksName);
goto cleanup; goto cleanup;
} }
rc = SQLITE_OK; rc = SQLITE_OK;
cleanup:
sqlite3_reset(p->stmtRowidsGetChunkPosition); cleanup:
sqlite3_clear_bindings(p->stmtRowidsGetChunkPosition); if(p->stmtLatestChunk) {
sqlite3_reset(p->stmtLatestChunk);
}
return rc;
}
int vec0_rowids_insert_rowid(vec0_vtab *p, i64 rowid) {
int rc = SQLITE_OK;
int entered = 0;
UNUSED_PARAMETER(entered); // temporary
if(!p->stmtRowidsInsertRowid) {
const char * zSql = sqlite3_mprintf("INSERT INTO " VEC0_SHADOW_ROWIDS_NAME "(rowid)"
"VALUES (?);",
p->schemaName, p->tableName);
if (!zSql) {
rc = SQLITE_NOMEM;
goto cleanup;
}
rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->stmtRowidsInsertRowid, 0);
sqlite3_free((void *)zSql);
if (rc != SQLITE_OK) {
vtab_set_error(&p->base, VEC_INTERAL_ERROR "could not initialize 'insert rowids' statement");
goto cleanup;
}
}
#ifdef SQLITE_THREADSAFE
if (sqlite3_mutex_enter) {
sqlite3_mutex_enter(sqlite3_db_mutex(p->db));
entered = 1;
}
#endif
sqlite3_bind_int64(p->stmtRowidsInsertRowid, 1, rowid);
rc = sqlite3_step(p->stmtRowidsInsertRowid);
if (rc != SQLITE_DONE) {
if (sqlite3_extended_errcode(p->db) == SQLITE_CONSTRAINT_PRIMARYKEY) {
// IMP: V17090_01160
vtab_set_error(&p->base, "UNIQUE constraint failed on %s primary key",
p->tableName);
} else {
// IMP: V04679_21517
vtab_set_error(
&p->base, "Error inserting rowid into rowids shadow table: %s",
sqlite3_errmsg(sqlite3_db_handle(p->stmtRowidsInsertId)));
}
rc = SQLITE_ERROR;
goto cleanup;
}
rc = SQLITE_OK;
cleanup:
if(p->stmtRowidsInsertRowid) {
sqlite3_reset(p->stmtRowidsInsertRowid);
sqlite3_clear_bindings(p->stmtRowidsInsertRowid);
}
#ifdef SQLITE_THREADSAFE
if (sqlite3_mutex_leave && entered) {
sqlite3_mutex_leave(sqlite3_db_mutex(p->db));
}
#endif
return rc;
}
int vec0_rowids_insert_id(vec0_vtab *p, sqlite3_value * idValue, i64 * rowid) {
int rc = SQLITE_OK;
int entered = 0;
UNUSED_PARAMETER(entered); // temporary
if(!p->stmtRowidsInsertId) {
const char * zSql = sqlite3_mprintf("INSERT INTO " VEC0_SHADOW_ROWIDS_NAME "(id)"
"VALUES (?);",
p->schemaName, p->tableName);
if (!zSql) {
rc = SQLITE_NOMEM;
goto complete;
}
rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->stmtRowidsInsertId, 0);
sqlite3_free((void *)zSql);
if (rc != SQLITE_OK) {
vtab_set_error(&p->base, VEC_INTERAL_ERROR
"could not initialize 'insert rowids id' statement");
goto complete;
}
}
#ifdef SQLITE_THREADSAFE
if (sqlite3_mutex_enter) {
sqlite3_mutex_enter(sqlite3_db_mutex(p->db));
entered = 1;
}
#endif
if(idValue) {
sqlite3_bind_value(p->stmtRowidsInsertId, 1, idValue);
}
rc = sqlite3_step(p->stmtRowidsInsertId);
if (rc != SQLITE_DONE) {
if (sqlite3_extended_errcode(p->db) == SQLITE_CONSTRAINT_UNIQUE) {
// IMP: V20497_04568
vtab_set_error(&p->base, "UNIQUE constraint failed on %s primary key",
p->tableName);
} else {
// IMP: V24016_08086
// IMP: V15177_32015
vtab_set_error(
&p->base, "Error inserting id into rowids shadow table: %s",
sqlite3_errmsg(sqlite3_db_handle(p->stmtRowidsInsertId)));
}
rc = SQLITE_ERROR;
goto complete;
}
*rowid = sqlite3_last_insert_rowid(p->db);
rc = SQLITE_OK;
complete:
if(p->stmtRowidsInsertId) {
sqlite3_reset(p->stmtRowidsInsertId);
sqlite3_clear_bindings(p->stmtRowidsInsertId);
}
#ifdef SQLITE_THREADSAFE
if (sqlite3_mutex_leave && entered) {
sqlite3_mutex_leave(sqlite3_db_mutex(p->db));
}
#endif
return rc;
}
int vec0_rowids_update_position(vec0_vtab * p, i64 rowid, i64 chunk_rowid, i64 chunk_offset) {
int rc = SQLITE_OK;
if(!p->stmtRowidsUpdatePosition) {
const char * zSql = sqlite3_mprintf(" UPDATE " VEC0_SHADOW_ROWIDS_NAME
" SET chunk_id = ?, chunk_offset = ?"
" WHERE rowid = ?",
p->schemaName, p->tableName);
if (!zSql) {
rc = SQLITE_NOMEM;
goto cleanup;
}
rc = sqlite3_prepare_v2(p->db, zSql, -1, &p->stmtRowidsUpdatePosition,
0);
sqlite3_free((void *)zSql);
if (rc != SQLITE_OK) {
vtab_set_error(&p->base,
VEC_INTERAL_ERROR
"could not initialize 'update rowids position' statement");
goto cleanup;
}
}
sqlite3_bind_int64(p->stmtRowidsUpdatePosition, 1, chunk_rowid);
sqlite3_bind_int64(p->stmtRowidsUpdatePosition, 2, chunk_offset);
sqlite3_bind_int64(p->stmtRowidsUpdatePosition, 3, rowid);
rc = sqlite3_step(p->stmtRowidsUpdatePosition);
if (rc != SQLITE_DONE) {
// IMP: V21925_05995
vtab_set_error(&p->base,
VEC_INTERAL_ERROR
"could not update rowids position for rowid=%lld, "
"chunk_rowid=%lld, chunk_offset=%lld",
rowid, chunk_rowid, chunk_offset);
rc = SQLITE_ERROR;
goto cleanup;
}
rc = SQLITE_OK;
cleanup:
if(p->stmtRowidsUpdatePosition) {
sqlite3_reset(p->stmtRowidsUpdatePosition);
sqlite3_clear_bindings(p->stmtRowidsUpdatePosition);
}
return rc; return rc;
} }
/** /**
@ -3749,112 +3957,6 @@ static int vec0_init(sqlite3 *db, void *pAux, int argc, const char *const *argv,
} }
} }
// init stmtLatestChunk
{
zSql = sqlite3_mprintf("SELECT max(rowid) FROM " VEC0_SHADOW_CHUNKS_NAME,
pNew->schemaName, pNew->tableName);
if (!zSql) {
goto error;
}
rc = sqlite3_prepare_v2(pNew->db, zSql, -1, &pNew->stmtLatestChunk, 0);
sqlite3_free((void *)zSql);
if (rc != SQLITE_OK) {
// IMP: V21406_05476
*pzErr = sqlite3_mprintf(VEC_INTERAL_ERROR
"could not initialize 'latest chunk' statement");
goto error;
}
}
// init stmtRowidsInsertRowid
{
zSql = sqlite3_mprintf("INSERT INTO " VEC0_SHADOW_ROWIDS_NAME "(rowid)"
"VALUES (?);",
pNew->schemaName, pNew->tableName);
if (!zSql) {
goto error;
}
rc =
sqlite3_prepare_v2(pNew->db, zSql, -1, &pNew->stmtRowidsInsertRowid, 0);
sqlite3_free((void *)zSql);
if (rc != SQLITE_OK) {
*pzErr = sqlite3_mprintf(
VEC_INTERAL_ERROR "could not initialize 'insert rowids' statement");
goto error;
}
}
// init stmtRowidsInsertId
{
zSql = sqlite3_mprintf("INSERT INTO " VEC0_SHADOW_ROWIDS_NAME "(id)"
"VALUES (?);",
pNew->schemaName, pNew->tableName);
if (!zSql) {
goto error;
}
rc = sqlite3_prepare_v2(pNew->db, zSql, -1, &pNew->stmtRowidsInsertId, 0);
sqlite3_free((void *)zSql);
if (rc != SQLITE_OK) {
*pzErr =
sqlite3_mprintf(VEC_INTERAL_ERROR
"could not initialize 'insert rowids id' statement");
goto error;
}
}
// init stmtRowidsUpdatePosition
{
zSql = sqlite3_mprintf(" UPDATE " VEC0_SHADOW_ROWIDS_NAME
" SET chunk_id = ?, chunk_offset = ?"
" WHERE rowid = ?",
pNew->schemaName, pNew->tableName);
if (!zSql) {
goto error;
}
rc = sqlite3_prepare_v2(pNew->db, zSql, -1, &pNew->stmtRowidsUpdatePosition,
0);
sqlite3_free((void *)zSql);
if (rc != SQLITE_OK) {
*pzErr = sqlite3_mprintf(
VEC_INTERAL_ERROR
"could not initialize 'update rowids position' statement");
goto error;
}
}
// init stmtRowidsGetChunkPosition
{
zSql = sqlite3_mprintf("SELECT id, chunk_id, chunk_offset "
"FROM " VEC0_SHADOW_ROWIDS_NAME " WHERE rowid = ?",
pNew->schemaName, pNew->tableName);
if (!zSql) {
goto error;
}
rc = sqlite3_prepare_v2(pNew->db, zSql, -1,
&pNew->stmtRowidsGetChunkPosition, 0);
sqlite3_free((void *)zSql);
if (rc != SQLITE_OK) {
*pzErr = sqlite3_mprintf(
VEC_INTERAL_ERROR
"could not initialize 'rowids get chunk position' statement");
goto error;
}
}
// init vectorBlobs[..]
for (int i = 0; i < pNew->numVectorColumns; i++) {
// there should always be a vector chunk with id=1
rc = sqlite3_blob_open(db, pNew->schemaName,
pNew->shadowVectorChunksNames[i], "vectors", 1, 0,
&pNew->vectorBlobs[i]);
if (rc != SQLITE_OK) {
*pzErr = sqlite3_mprintf(VEC_INTERAL_ERROR
"could not initialize 'vector chunk' blob at %d",
i);
goto error;
}
}
*ppVtab = (sqlite3_vtab *)pNew; *ppVtab = (sqlite3_vtab *)pNew;
return SQLITE_OK; return SQLITE_OK;
@ -5038,82 +5140,16 @@ int vec0Update_InsertRowidStep(vec0_vtab *p, sqlite3_value *idValue,
return SQLITE_ERROR; return SQLITE_ERROR;
} }
#ifdef SQLITE_THREADSAFE return vec0_rowids_insert_id(p, idValue, rowid);
if (sqlite3_mutex_enter) {
sqlite3_mutex_enter(sqlite3_db_mutex(p->db));
}
#endif
sqlite3_bind_value(p->stmtRowidsInsertId, 1, idValue);
rc = sqlite3_step(p->stmtRowidsInsertId);
if (rc != SQLITE_DONE) {
if (sqlite3_extended_errcode(p->db) == SQLITE_CONSTRAINT_UNIQUE) {
// IMP: V20497_04568
vtab_set_error(&p->base, "UNIQUE constraint failed on %s primary key",
p->tableName);
} else {
// IMP: V24016_08086
vtab_set_error(
&p->base, "Error inserting into rowid shadow table: %s",
sqlite3_errmsg(sqlite3_db_handle(p->stmtRowidsInsertId)));
}
rc = SQLITE_ERROR;
goto complete;
}
*rowid = sqlite3_last_insert_rowid(p->db);
rc = SQLITE_OK;
complete:
sqlite3_reset(p->stmtRowidsInsertId);
sqlite3_clear_bindings(p->stmtRowidsInsertId);
#ifdef SQLITE_THREADSAFE
if (sqlite3_mutex_leave) {
sqlite3_mutex_leave(sqlite3_db_mutex(p->db));
}
#endif
return rc;
} }
// Option 1: User supplied a i64 rowid // Option 1: User supplied a i64 rowid
if (sqlite3_value_type(idValue) == SQLITE_INTEGER) { if (sqlite3_value_type(idValue) == SQLITE_INTEGER) {
i64 suppliedRowid = sqlite3_value_int64(idValue); i64 suppliedRowid = sqlite3_value_int64(idValue);
rc = vec0_rowids_insert_rowid(p, suppliedRowid);
#ifdef SQLITE_THREADSAFE if(rc == SQLITE_OK) {
if (sqlite3_mutex_enter) { *rowid = suppliedRowid;
sqlite3_mutex_enter(sqlite3_db_mutex(p->db));
} }
#endif
sqlite3_bind_int64(p->stmtRowidsInsertRowid, 1, suppliedRowid);
rc = sqlite3_step(p->stmtRowidsInsertRowid);
if (rc != SQLITE_DONE) {
if (sqlite3_extended_errcode(p->db) == SQLITE_CONSTRAINT_PRIMARYKEY) {
// IMP: V17090_01160
vtab_set_error(&p->base, "UNIQUE constraint failed on %s primary key",
p->tableName);
} else {
// IMP: V04679_21517
vtab_set_error(
&p->base, "Error inserting into rowid shadow table: %s",
sqlite3_errmsg(sqlite3_db_handle(p->stmtRowidsInsertId)));
}
rc = SQLITE_ERROR;
goto complete2;
}
*rowid = suppliedRowid;
rc = SQLITE_OK;
complete2:
sqlite3_reset(p->stmtRowidsInsertRowid);
sqlite3_clear_bindings(p->stmtRowidsInsertRowid);
#ifdef SQLITE_THREADSAFE
if (sqlite3_mutex_leave) {
sqlite3_mutex_leave(sqlite3_db_mutex(p->db));
}
#endif
return rc; return rc;
} }
@ -5126,34 +5162,8 @@ int vec0Update_InsertRowidStep(vec0_vtab *p, sqlite3_value *idValue,
p->tableName); p->tableName);
return SQLITE_ERROR; return SQLITE_ERROR;
} }
#ifdef SQLITE_THREADSAFE // NULL to get next auto-incremented value
if (sqlite3_mutex_enter) { return vec0_rowids_insert_id(p, NULL, rowid);
sqlite3_mutex_enter(sqlite3_db_mutex(p->db));
}
#endif
// no need to bind a value to ?1 here: needs to be NULL
// so we can get the next autoincremented rowid value.
rc = sqlite3_step(p->stmtRowidsInsertId);
if (rc != SQLITE_DONE) {
// IMP: V15177_32015
vtab_set_error(&p->base, "Error inserting into rowid shadow table: %s",
sqlite3_errmsg(sqlite3_db_handle(p->stmtRowidsInsertId)));
rc = SQLITE_ERROR;
goto complete3;
}
*rowid = sqlite3_last_insert_rowid(p->db);
rc = SQLITE_OK;
complete3:
sqlite3_reset(p->stmtRowidsInsertId);
sqlite3_clear_bindings(p->stmtRowidsInsertId);
#ifdef SQLITE_THREADSAFE
if (sqlite3_mutex_leave) {
sqlite3_mutex_leave(sqlite3_db_mutex(p->db));
}
#endif
return rc;
} }
/** /**
@ -5183,24 +5193,12 @@ int vec0Update_InsertNextAvailableStep(
i64 validitySize; i64 validitySize;
*chunk_offset = -1; *chunk_offset = -1;
rc = sqlite3_step(p->stmtLatestChunk); rc = vec0_get_latest_chunk_rowid(p, chunk_rowid);
if (rc != SQLITE_ROW) { if(rc != SQLITE_OK) {
// IMP: V31559_15629
vtab_set_error(&p->base, VEC_INTERAL_ERROR "Could not find latest chunk");
rc = SQLITE_ERROR;
goto cleanup;
}
*chunk_rowid = sqlite3_column_int64(p->stmtLatestChunk, 0);
rc = sqlite3_step(p->stmtLatestChunk);
if (rc != SQLITE_DONE) {
vtab_set_error(&p->base,
VEC_INTERAL_ERROR
"unknown result code when closing out stmtLatestChunk. "
"Please file an issue: " REPORT_URL,
p->schemaName, p->shadowChunksName);
goto cleanup; goto cleanup;
} }
rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowChunksName, "validity", rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowChunksName, "validity",
*chunk_rowid, 1, blobChunksValidity); *chunk_rowid, 1, blobChunksValidity);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
@ -5313,7 +5311,6 @@ done:
rc = SQLITE_OK; rc = SQLITE_OK;
cleanup: cleanup:
sqlite3_reset(p->stmtLatestChunk);
return rc; return rc;
} }
@ -5373,6 +5370,8 @@ int vec0Update_InsertWriteFinalStep(vec0_vtab *p, i64 chunk_rowid,
sqlite3_blob *blobChunksValidity, sqlite3_blob *blobChunksValidity,
const unsigned char *bufferChunksValidity) { const unsigned char *bufferChunksValidity) {
int rc; int rc;
sqlite3_blob *blobChunksRowids = NULL;
// mark the validity bit for this row in the chunk's validity bitmap // mark the validity bit for this row in the chunk's validity bitmap
// Get the byte offset of the bitmap // Get the byte offset of the bitmap
@ -5429,7 +5428,6 @@ int vec0Update_InsertWriteFinalStep(vec0_vtab *p, i64 chunk_rowid,
sqlite3_blob_close(blobVectors); sqlite3_blob_close(blobVectors);
} }
sqlite3_blob *blobChunksRowids = NULL;
// write the new rowid to the rowids column of the _chunks table // write the new rowid to the rowids column of the _chunks table
rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowChunksName, "rowids", rc = sqlite3_blob_open(p->db, p->schemaName, p->shadowChunksName, "rowids",
@ -5451,7 +5449,6 @@ int vec0Update_InsertWriteFinalStep(vec0_vtab *p, i64 chunk_rowid,
"rowids blob size mismatch on %s.%s.%lld. Expected %lld, actual %lld", "rowids blob size mismatch on %s.%s.%lld. Expected %lld, actual %lld",
p->schemaName, p->shadowChunksName, chunk_rowid, expected, actual); p->schemaName, p->shadowChunksName, chunk_rowid, expected, actual);
rc = SQLITE_ERROR; rc = SQLITE_ERROR;
sqlite3_blob_close(blobChunksRowids);
goto cleanup; goto cleanup;
} }
rc = sqlite3_blob_write(blobChunksRowids, &rowid, sizeof(i64), rc = sqlite3_blob_write(blobChunksRowids, &rowid, sizeof(i64),
@ -5461,35 +5458,16 @@ int vec0Update_InsertWriteFinalStep(vec0_vtab *p, i64 chunk_rowid,
&p->base, VEC_INTERAL_ERROR "could not write rowids blob on %s.%s.%lld", &p->base, VEC_INTERAL_ERROR "could not write rowids blob on %s.%s.%lld",
p->schemaName, p->shadowChunksName, chunk_rowid); p->schemaName, p->shadowChunksName, chunk_rowid);
rc = SQLITE_ERROR; rc = SQLITE_ERROR;
sqlite3_blob_close(blobChunksRowids);
goto cleanup; goto cleanup;
} }
sqlite3_blob_close(blobChunksRowids);
// Now with all the vectors inserted, go back and update the _rowids table // Now with all the vectors inserted, go back and update the _rowids table
// with the new chunk_rowid/chunk_offset values // with the new chunk_rowid/chunk_offset values
rc = vec0_rowids_update_position(p, rowid, chunk_rowid, chunk_offset);
sqlite3_bind_int64(p->stmtRowidsUpdatePosition, 1, chunk_rowid); cleanup:
sqlite3_bind_int64(p->stmtRowidsUpdatePosition, 2, chunk_offset); sqlite3_blob_close(blobChunksRowids);
sqlite3_bind_int64(p->stmtRowidsUpdatePosition, 3, rowid); return rc;
rc = sqlite3_step(p->stmtRowidsUpdatePosition);
if (rc != SQLITE_DONE) {
// IMP: V21925_05995
vtab_set_error(&p->base,
VEC_INTERAL_ERROR
"could not update rowids position for rowid=%lld, "
"chunk_rowid=%lld, chunk_offset=%lld",
rowid, chunk_rowid, chunk_offset);
rc = SQLITE_ERROR;
goto cleanup;
}
rc = SQLITE_OK;
cleanup:
sqlite3_reset(p->stmtRowidsUpdatePosition);
sqlite3_clear_bindings(p->stmtRowidsUpdatePosition);
return rc;
} }
/** /**
@ -5728,7 +5706,7 @@ int vec0Update_Delete(sqlite3_vtab *pVTab, sqlite_int64 rowid) {
// 5. Delete value in _rowids table // 5. Delete value in _rowids table
// 1. get chunk_id and chunk_offset from _rowids // 1. get chunk_id and chunk_offset from _rowids
rc = vec0_get_chunk_position(p, rowid, &chunk_id, &chunk_offset); rc = vec0_get_chunk_position(p, rowid, NULL, &chunk_id, &chunk_offset);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
return rc; return rc;
} }
@ -5841,7 +5819,7 @@ int vec0Update_UpdateOnRowid(sqlite3_vtab *pVTab, int argc,
i64 rowid = sqlite3_value_int64(argv[0]); i64 rowid = sqlite3_value_int64(argv[0]);
// 1. get chunk_id and chunk_offset from _rowids // 1. get chunk_id and chunk_offset from _rowids
rc = vec0_get_chunk_position(p, rowid, &chunk_id, &chunk_offset); rc = vec0_get_chunk_position(p, rowid, NULL, &chunk_id, &chunk_offset);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
return rc; return rc;
} }
@ -5911,6 +5889,44 @@ static int vec0ShadowName(const char *zName) {
return 0; return 0;
} }
static int vec0Begin(sqlite3_vtab *pVTab) {
UNUSED_PARAMETER(pVTab);
return SQLITE_OK;
}
static int vec0Sync(sqlite3_vtab *pVTab) {
UNUSED_PARAMETER(pVTab);
vec0_vtab *p = (vec0_vtab *)pVTab;
if(p->stmtLatestChunk) {
sqlite3_finalize(p->stmtLatestChunk);
p->stmtLatestChunk = NULL;
}
if(p->stmtRowidsInsertRowid) {
sqlite3_finalize(p->stmtRowidsInsertRowid);
p->stmtRowidsInsertRowid = NULL;
}
if(p->stmtRowidsInsertId) {
sqlite3_finalize(p->stmtRowidsInsertId);
p->stmtRowidsInsertId = NULL;
}
if(p->stmtRowidsUpdatePosition) {
sqlite3_finalize(p->stmtRowidsUpdatePosition);
p->stmtRowidsUpdatePosition = NULL;
}
if(p->stmtRowidsGetChunkPosition) {
sqlite3_finalize(p->stmtRowidsGetChunkPosition);
p->stmtRowidsGetChunkPosition = NULL;
}
return SQLITE_OK;
}
static int vec0Commit(sqlite3_vtab *pVTab) {
UNUSED_PARAMETER(pVTab);
return SQLITE_OK;
}
static int vec0Rollback(sqlite3_vtab *pVTab) {
UNUSED_PARAMETER(pVTab);
return SQLITE_OK;
}
static sqlite3_module vec0Module = { static sqlite3_module vec0Module = {
/* iVersion */ 3, /* iVersion */ 3,
/* xCreate */ vec0Create, /* xCreate */ vec0Create,
@ -5926,10 +5942,10 @@ static sqlite3_module vec0Module = {
/* xColumn */ vec0Column, /* xColumn */ vec0Column,
/* xRowid */ vec0Rowid, /* xRowid */ vec0Rowid,
/* xUpdate */ vec0Update, /* xUpdate */ vec0Update,
/* xBegin */ 0, /* xBegin */ vec0Begin,
/* xSync */ 0, /* xSync */ vec0Sync,
/* xCommit */ 0, /* xCommit */ vec0Commit,
/* xRollback */ 0, /* xRollback */ vec0Rollback,
/* xFindFunction */ 0, /* xFindFunction */ 0,
/* xRename */ 0, // https://github.com/asg017/sqlite-vec/issues/43 /* xRename */ 0, // https://github.com/asg017/sqlite-vec/issues/43
/* xSavepoint */ 0, /* xSavepoint */ 0,

View file

@ -630,11 +630,13 @@ def test_vec0_inserts():
db.execute("insert into t1 values (1, '[2,2,2,2]')") db.execute("insert into t1 values (1, '[2,2,2,2]')")
# similate error on rowids shadow table # similate error on rowids shadow table
db.commit()
db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_INSERT, "t1_rowids")) db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_INSERT, "t1_rowids"))
# EVIDENCE-OF: V04679_21517 vec0 INSERT failed on _rowid shadow insert raises error # EVIDENCE-OF: V04679_21517 vec0 INSERT failed on _rowid shadow insert raises error
with _raises("Error inserting into rowid shadow table: not authorized"): with _raises("Internal sqlite-vec error: could not initialize 'insert rowids' statement", sqlite3.DatabaseError):
db.execute("insert into t1 values (2, '[2,2,2,2]')") db.execute("insert into t1 values (2, '[2,2,2,2]')")
db.set_authorizer(None) db.set_authorizer(None)
db.rollback()
db.execute("insert into t1 values (2, '[2,2,2,2]')") db.execute("insert into t1 values (2, '[2,2,2,2]')")
# test inserts where no rowid is provided # test inserts where no rowid is provided
@ -647,7 +649,7 @@ def test_vec0_inserts():
# similate error on rowids shadow table, when rowid is not provided # similate error on rowids shadow table, when rowid is not provided
# EVIDENCE-OF: V15177_32015 vec0 INSERT error on _rowids shadow insert raises error # EVIDENCE-OF: V15177_32015 vec0 INSERT error on _rowids shadow insert raises error
db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_INSERT, "t1_rowids")) db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_INSERT, "t1_rowids"))
with _raises("Error inserting into rowid shadow table: not authorized"): with _raises("Error inserting id into rowids shadow table: not authorized"):
db.execute("insert into t1(aaa) values ('[2,2,2,2]')") db.execute("insert into t1(aaa) values ('[2,2,2,2]')")
db.set_authorizer(None) db.set_authorizer(None)
@ -710,14 +712,18 @@ def test_vec0_inserts():
db.rollback() db.rollback()
# EVIDENCE-OF: V21925_05995 vec0 INSERT error on "rowids update position" raises error # EVIDENCE-OF: V21925_05995 vec0 INSERT error on "rowids update position" raises error
db.commit()
db.execute("begin")
db.execute("insert into t1 values (9998, '[2,2,2,2]')")
db.set_authorizer( db.set_authorizer(
authorizer_deny_on(sqlite3.SQLITE_UPDATE, "t1_rowids", "chunk_id") authorizer_deny_on(sqlite3.SQLITE_UPDATE, "t1_rowids", "chunk_id")
) )
with _raises( with _raises(
"Internal sqlite-vec error: could not update rowids position for rowid=9999, chunk_rowid=1, chunk_offset=3" "Internal sqlite-vec error: could not update rowids position for rowid=9999, chunk_rowid=1, chunk_offset=4"
): ):
db.execute("insert into t1 values (9999, '[2,2,2,2]')") db.execute("insert into t1 values (9999, '[2,2,2,2]')")
db.set_authorizer(None) db.set_authorizer(None)
db.rollback()
########## testing inserts on text primary key tables ########## ########## testing inserts on text primary key tables ##########
@ -735,7 +741,7 @@ def test_vec0_inserts():
# EVIDENCE-OF: V24016_08086 vec0 table with text primary key raises error on rowid write error # EVIDENCE-OF: V24016_08086 vec0 table with text primary key raises error on rowid write error
db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_INSERT, "txt_pk_rowids")) db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_INSERT, "txt_pk_rowids"))
with _raises("Error inserting into rowid shadow table: not authorized"): with _raises("Error inserting id into rowids shadow table: not authorized"):
db.execute("insert into txt_pk(txt_id, aaa) values ('b', '[2,2,2,2]')") db.execute("insert into txt_pk(txt_id, aaa) values ('b', '[2,2,2,2]')")
db.set_authorizer(None) db.set_authorizer(None)
db.execute("insert into txt_pk(txt_id, aaa) values ('b', '[2,2,2,2]')") db.execute("insert into txt_pk(txt_id, aaa) values ('b', '[2,2,2,2]')")
@ -1724,35 +1730,52 @@ def test_vec0_create_errors():
db.set_authorizer(None) db.set_authorizer(None)
# EVIDENCE-OF: V21406_05476 vec0 init raises error on 'latest chunk' init error # EVIDENCE-OF: V21406_05476 vec0 init raises error on 'latest chunk' init error
db.execute("BEGIN")
db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_READ, "t1_chunks", "")) db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_READ, "t1_chunks", ""))
with _raises( with _raises(
"Internal sqlite-vec error: could not initialize 'latest chunk' statement", "Internal sqlite-vec error: could not initialize 'latest chunk' statement",
sqlite3.DatabaseError
): ):
db.execute("create virtual table t1 using vec0(a float[1])") db.execute("create virtual table t1 using vec0(a float[1])")
db.execute("insert into t1(a) values (X'AABBCCDD')")
db.set_authorizer(None) db.set_authorizer(None)
db.rollback()
db.execute("BEGIN")
db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_INSERT, "t1_rowids")) db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_INSERT, "t1_rowids"))
with _raises( with _raises(
"Internal sqlite-vec error: could not initialize 'insert rowids' statement" "Internal sqlite-vec error: could not initialize 'insert rowids id' statement", sqlite3.DatabaseError
): ):
db.execute("create virtual table t1 using vec0(a float[1])") db.execute("create virtual table t1 using vec0(a float[1])")
db.execute("insert into t1(a) values (X'AABBCCDD')")
db.set_authorizer(None) db.set_authorizer(None)
db.rollback()
db.commit()
db.execute("BEGIN")
db.set_authorizer( db.set_authorizer(
authorizer_deny_on(sqlite3.SQLITE_UPDATE, "t1_rowids", "chunk_id") authorizer_deny_on(sqlite3.SQLITE_UPDATE, "t1_rowids", "chunk_id")
) )
with _raises( with _raises(
"Internal sqlite-vec error: could not initialize 'update rowids position' statement" "Internal sqlite-vec error: could not initialize 'update rowids position' statement", sqlite3.DatabaseError
): ):
db.execute("create virtual table t1 using vec0(a float[1])") db.execute("create virtual table t1 using vec0(a float[1])")
db.execute("insert into t1(a) values (X'AABBCCDD')")
db.set_authorizer(None) db.set_authorizer(None)
db.rollback()
db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_READ, "t1_rowids", "id")) # TODO wut
with _raises( #db.commit()
"Internal sqlite-vec error: could not initialize 'rowids get chunk position' statement", #db.execute("BEGIN")
): #db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_UPDATE, "t1_rowids", "id"))
db.execute("create virtual table t1 using vec0(a float[1])") #with _raises(
db.set_authorizer(None) # "Internal sqlite-vec error: could not initialize 'rowids get chunk position' statement", sqlite3.DatabaseError
#):
# db.execute("create virtual table t1 using vec0(a float[1])")
# db.execute("insert into t1(a) values (X'AABBCCDD')")
#db.set_authorizer(None)
#db.rollback()
def test_vec0_knn(): def test_vec0_knn():