diff --git a/sqlite-vec.c b/sqlite-vec.c index 9209725..d22dba2 100644 --- a/sqlite-vec.c +++ b/sqlite-vec.c @@ -3054,9 +3054,12 @@ int vec0_new_chunk(vec0_vtab *p, i64 *chunk_rowid) { sqlite3_finalize(stmt); return rc; } + +#ifdef SQLITE_THREADSAFE if (sqlite3_mutex_enter) { sqlite3_mutex_enter(sqlite3_db_mutex(p->db)); } +#endif sqlite3_bind_int64(stmt, 1, p->chunk_size); // size sqlite3_bind_zeroblob(stmt, 2, p->chunk_size / CHAR_BIT); // validity bitmap @@ -3065,9 +3068,11 @@ int vec0_new_chunk(vec0_vtab *p, i64 *chunk_rowid) { rc = sqlite3_step(stmt); int failed = rc != SQLITE_DONE; rowid = sqlite3_last_insert_rowid(p->db); +#ifdef SQLITE_THREADSAFE if (sqlite3_mutex_leave) { sqlite3_mutex_leave(sqlite3_db_mutex(p->db)); } +#endif sqlite3_finalize(stmt); if (failed) { return SQLITE_ERROR; @@ -3263,8 +3268,9 @@ static int vec0_init(sqlite3 *db, void *pAux, int argc, const char *const *argv, int keyLength, valueLength; rc = vec0_parse_table_option(argv[i], strlen(argv[i]), &key, &keyLength, &value, &valueLength); - if(rc == SQLITE_ERROR) { - *pzErr = sqlite3_mprintf( VEC_CONSTRUCTOR_ERROR "could not parse table option '%s'", argv[i]); + if (rc == SQLITE_ERROR) { + *pzErr = sqlite3_mprintf( + VEC_CONSTRUCTOR_ERROR "could not parse table option '%s'", argv[i]); goto error; } if (rc == SQLITE_OK) { @@ -3272,23 +3278,28 @@ static int vec0_init(sqlite3 *db, void *pAux, int argc, const char *const *argv, chunk_size = atoi(value); if (chunk_size <= 0) { // IMP: V01931_18769 - *pzErr = sqlite3_mprintf( VEC_CONSTRUCTOR_ERROR "chunk_size must be a non-zero positive integer"); + *pzErr = + sqlite3_mprintf(VEC_CONSTRUCTOR_ERROR + "chunk_size must be a non-zero positive integer"); goto error; } if ((chunk_size % 8) != 0) { // IMP: V14110_30948 - *pzErr = sqlite3_mprintf( VEC_CONSTRUCTOR_ERROR "chunk_size must be divisible by 8"); + *pzErr = sqlite3_mprintf(VEC_CONSTRUCTOR_ERROR + "chunk_size must be divisible by 8"); goto error; } } else { // IMP: V27642_11712 - *pzErr = sqlite3_mprintf(VEC_CONSTRUCTOR_ERROR "Unknown table option: %.*s", keyLength, key); + *pzErr = sqlite3_mprintf( + VEC_CONSTRUCTOR_ERROR "Unknown table option: %.*s", keyLength, key); goto error; } continue; } - *pzErr = sqlite3_mprintf(VEC_CONSTRUCTOR_ERROR "Could not parse '%s'", argv[i]); - goto error; + *pzErr = + sqlite3_mprintf(VEC_CONSTRUCTOR_ERROR "Could not parse '%s'", argv[i]); + goto error; } if (chunk_size < 0) { @@ -4355,9 +4366,11 @@ int vec0Update_InsertRowidStep(vec0_vtab *p, sqlite3_value *idValue, return SQLITE_ERROR; } +#ifdef SQLITE_THREADSAFE 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); @@ -4383,9 +4396,11 @@ int vec0Update_InsertRowidStep(vec0_vtab *p, sqlite3_value *idValue, 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; } @@ -4393,9 +4408,11 @@ int vec0Update_InsertRowidStep(vec0_vtab *p, sqlite3_value *idValue, if (sqlite3_value_type(idValue) == SQLITE_INTEGER) { i64 suppliedRowid = sqlite3_value_int64(idValue); +#ifdef SQLITE_THREADSAFE if (sqlite3_mutex_enter) { sqlite3_mutex_enter(sqlite3_db_mutex(p->db)); } +#endif sqlite3_bind_int64(p->stmtRowidsInsertRowid, 1, suppliedRowid); rc = sqlite3_step(p->stmtRowidsInsertRowid); @@ -4420,9 +4437,11 @@ int vec0Update_InsertRowidStep(vec0_vtab *p, sqlite3_value *idValue, 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; } @@ -4435,10 +4454,11 @@ int vec0Update_InsertRowidStep(vec0_vtab *p, sqlite3_value *idValue, p->tableName); return SQLITE_ERROR; } - +#ifdef SQLITE_THREADSAFE if (sqlite3_mutex_enter) { 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. @@ -4456,9 +4476,11 @@ int vec0Update_InsertRowidStep(vec0_vtab *p, sqlite3_value *idValue, 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; } diff --git a/tests/test-loadable.py b/tests/test-loadable.py index 3d02353..7a2d18c 100644 --- a/tests/test-loadable.py +++ b/tests/test-loadable.py @@ -15,6 +15,7 @@ from math import isclose EXT_PATH = "./dist/vec0" SUPPORTS_SUBTYPE = sqlite3.version_info[1] > 38 +SUPPORTS_DROP_COLUMN = sqlite3.version_info[1] >= 35 def bitmap_full(n: int) -> bytearray: @@ -604,14 +605,15 @@ def test_vec0_updates(): db.set_authorizer(None) # EVIDENCE-OF: V22053_06123 vec0 INSERT error on reading validity blob - db.commit() - db.execute("begin") - db.execute("ALTER TABLE t1_chunks DROP COLUMN validity") - with _raises( - "Internal sqlite-vec error: could not open validity blob on main.t1_chunks.1" - ): - db.execute("insert into t1 values (9999, '[2,2,2,2]')") - db.rollback() + if SUPPORTS_DROP_COLUMN: + db.commit() + db.execute("begin") + db.execute("ALTER TABLE t1_chunks DROP COLUMN validity") + with _raises( + "Internal sqlite-vec error: could not open validity blob on main.t1_chunks.1" + ): + db.execute("insert into t1 values (9999, '[2,2,2,2]')") + db.rollback() # EVIDENCE-OF: V29362_13432 vec0 INSERT validity blob size mismatch with chunk_size db.commit() @@ -634,14 +636,15 @@ def test_vec0_updates(): db.rollback() # EVIDENCE-OF: V09221_26060 vec0 INSERT rowids blob open error - db.commit() - db.execute("begin") - db.execute("ALTER TABLE t1_chunks DROP COLUMN rowids") - with _raises( - "Internal sqlite-vec error: could not open rowids blob on main.t1_chunks.1" - ): - db.execute("insert into t1 values (9999, '[2,2,2,2]')") - db.rollback() + if SUPPORTS_DROP_COLUMN: + db.commit() + db.execute("begin") + db.execute("ALTER TABLE t1_chunks DROP COLUMN rowids") + with _raises( + "Internal sqlite-vec error: could not open rowids blob on main.t1_chunks.1" + ): + db.execute("insert into t1 values (9999, '[2,2,2,2]')") + db.rollback() # EVIDENCE-OF: V12779_29618 vec0 INSERT rowids blob validates size db.commit() @@ -964,14 +967,14 @@ def test_vec0_constructor(): ): db.execute("create virtual table v using vec0(chunk_size=7)") - table_option_errors = ['chunk_size=', 'chunk_size=8 x'] + table_option_errors = ["chunk_size=", "chunk_size=8 x"] for x in table_option_errors: - with _raises( - f"vec0 constructor error: could not parse table option '{x}'", - sqlite3.DatabaseError, - ): - db.execute(f"create virtual table v using vec0({x})") + with _raises( + f"vec0 constructor error: could not parse table option '{x}'", + sqlite3.DatabaseError, + ): + db.execute(f"create virtual table v using vec0({x})") with _raises( "vec0 constructor error: Could not parse '4'", @@ -979,6 +982,7 @@ def test_vec0_constructor(): ): db.execute("create virtual table v using vec0(4)") + def test_vec0_create_errors(): # EVIDENCE-OF: V17740_01811 vec0 create _chunks error handling db.set_authorizer(authorizer_deny_on(sqlite3.SQLITE_CREATE_TABLE, "t1_chunks"))