mirror of
https://github.com/asg017/sqlite-vec.git
synced 2026-04-25 00:36:56 +02:00
Merge branch 'main' into pr/rescore
This commit is contained in:
commit
45d1375602
8 changed files with 122 additions and 27 deletions
2
Makefile
2
Makefile
|
|
@ -43,10 +43,12 @@ ifndef OMIT_SIMD
|
|||
CFLAGS += -mcpu=apple-m1 -DSQLITE_VEC_ENABLE_NEON
|
||||
endif
|
||||
ifeq ($(shell uname -s),Linux)
|
||||
ifeq ($(findstring android,$(CC)),)
|
||||
ifneq ($(filter avx,$(shell grep -o 'avx[^ ]*' /proc/cpuinfo 2>/dev/null | head -1)),)
|
||||
CFLAGS += -mavx -DSQLITE_VEC_ENABLE_AVX
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef USE_BREW_SQLITE
|
||||
|
|
|
|||
73
TODO.md
Normal file
73
TODO.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# TODO: `ann` base branch + consolidated benchmarks
|
||||
|
||||
## 1. Create `ann` branch with shared code
|
||||
|
||||
### 1.1 Branch setup
|
||||
- [x] `git checkout -B ann origin/main`
|
||||
- [x] Cherry-pick `624f998` (vec0_distance_full shared distance dispatch)
|
||||
- [x] Cherry-pick stdint.h fix for test header
|
||||
- [ ] Pull NEON cosine optimization from ivf-yolo3 into shared code
|
||||
- Currently only in ivf branch but is general-purpose (benefits all distance calcs)
|
||||
- Lives in `distance_cosine_float()` — ~57 lines of ARM NEON vectorized cosine
|
||||
|
||||
### 1.2 Benchmark infrastructure (`benchmarks-ann/`)
|
||||
- [x] Seed data pipeline (`seed/Makefile`, `seed/build_base_db.py`)
|
||||
- [x] Ground truth generator (`ground_truth.py`)
|
||||
- [x] Results schema (`schema.sql`)
|
||||
- [x] Benchmark runner with `INDEX_REGISTRY` extension point (`bench.py`)
|
||||
- Baseline configs (float, int8-rescore, bit-rescore) implemented
|
||||
- Index branches register their types via `INDEX_REGISTRY` dict
|
||||
- [x] Makefile with baseline targets
|
||||
- [x] README
|
||||
|
||||
### 1.3 Rebase feature branches onto `ann`
|
||||
- [x] Rebase `diskann-yolo2` onto `ann` (1 commit: DiskANN implementation)
|
||||
- [x] Rebase `ivf-yolo3` onto `ann` (1 commit: IVF implementation)
|
||||
- [x] Rebase `annoy-yolo2` onto `ann` (2 commits: Annoy implementation + schema fix)
|
||||
- [x] Verify each branch has only its index-specific commits remaining
|
||||
- [ ] Force-push all 4 branches to origin
|
||||
|
||||
---
|
||||
|
||||
## 2. Per-branch: register index type in benchmarks
|
||||
|
||||
Each index branch should add to `benchmarks-ann/` when rebased onto `ann`:
|
||||
|
||||
### 2.1 Register in `bench.py`
|
||||
|
||||
Add an `INDEX_REGISTRY` entry. Each entry provides:
|
||||
- `defaults` — default param values
|
||||
- `create_table_sql(params)` — CREATE VIRTUAL TABLE with INDEXED BY clause
|
||||
- `insert_sql(params)` — custom insert SQL, or None for default
|
||||
- `post_insert_hook(conn, params)` — training/building step, returns time
|
||||
- `run_query(conn, params, query, k)` — custom query, or None for default MATCH
|
||||
- `describe(params)` — one-line description for report output
|
||||
|
||||
### 2.2 Add configs to `Makefile`
|
||||
|
||||
Append index-specific config variables and targets. Example pattern:
|
||||
|
||||
```makefile
|
||||
DISKANN_CONFIGS = \
|
||||
"diskann-R48-binary:type=diskann,R=48,L=128,quantizer=binary" \
|
||||
...
|
||||
|
||||
ALL_CONFIGS += $(DISKANN_CONFIGS)
|
||||
|
||||
bench-diskann: seed
|
||||
$(BENCH) --subset-size 10000 -k 10 -o runs/diskann $(BASELINES) $(DISKANN_CONFIGS)
|
||||
...
|
||||
```
|
||||
|
||||
### 2.3 Migrate existing benchmark results/docs
|
||||
|
||||
- Move useful results docs (RESULTS.md, etc.) into `benchmarks-ann/results/`
|
||||
- Delete redundant per-branch benchmark directories once consolidated infra is proven
|
||||
|
||||
---
|
||||
|
||||
## 3. Future improvements
|
||||
|
||||
- [ ] Reporting script (`report.py`) — query results.db, produce markdown comparison tables
|
||||
- [ ] Profiling targets in Makefile (lift from ivf-yolo3's Instruments/perf wrappers)
|
||||
- [ ] Pre-computed ground truth integration (use GT DB files instead of on-the-fly brute-force)
|
||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.1.8
|
||||
0.1.9
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ bench-rescore: seed
|
|||
$(BENCH) --subset-size 10000 -k 10 -o runs/rescore \
|
||||
$(RESCORE_CONFIGS)
|
||||
|
||||
|
||||
# --- Standard sizes ---
|
||||
bench-10k: seed
|
||||
$(BENCH) --subset-size 10000 -k 10 -o runs/10k $(ALL_CONFIGS)
|
||||
|
|
|
|||
|
|
@ -2553,6 +2553,7 @@ struct Vec0RescoreConfig {
|
|||
};
|
||||
#endif
|
||||
|
||||
|
||||
struct VectorColumnDefinition {
|
||||
char *name;
|
||||
int name_length;
|
||||
|
|
@ -8899,11 +8900,17 @@ int vec0Update_Delete_ClearMetadata(vec0_vtab *p, int metadata_idx, i64 rowid, i
|
|||
}
|
||||
sqlite3_bind_int64(stmt, 1, rowid);
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
if(rc != SQLITE_DONE) {
|
||||
rc = SQLITE_ERROR;
|
||||
goto done;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
// Fix for https://github.com/asg017/sqlite-vec/issues/274
|
||||
// sqlite3_step returns SQLITE_DONE (101) on DML success, but the
|
||||
// `done:` epilogue treats anything other than SQLITE_OK as an error.
|
||||
// Without this, SQLITE_DONE propagates up to vec0Update_Delete,
|
||||
// which aborts the DELETE scan and silently drops remaining rows.
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@
|
|||
OrderedDict({
|
||||
'chunk_id': 1,
|
||||
'size': 8,
|
||||
'validity': b'\x06',
|
||||
'rowids': b'\x00\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',
|
||||
'validity': b'\x02',
|
||||
'rowids': b'\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
'rows': list([
|
||||
OrderedDict({
|
||||
'rowid': 1,
|
||||
'data': b'\x06',
|
||||
'data': b'\x02',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
'rows': list([
|
||||
OrderedDict({
|
||||
'rowid': 1,
|
||||
'data': b'\x00\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',
|
||||
'data': b'\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
'rows': list([
|
||||
OrderedDict({
|
||||
'rowid': 1,
|
||||
'data': b'\x00\x00\x00\x00\x00\x00\x00\x00\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',
|
||||
'data': b'\x00\x00\x00\x00\x00\x00\x00\x00\x9a\x99\x99\x99\x99\x99\x01@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
|
|
@ -64,17 +64,13 @@
|
|||
'rows': list([
|
||||
OrderedDict({
|
||||
'rowid': 1,
|
||||
'data': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\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',
|
||||
'data': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00test2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\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({
|
||||
|
|
@ -86,12 +82,6 @@
|
|||
'chunk_id': 1,
|
||||
'chunk_offset': 1,
|
||||
}),
|
||||
OrderedDict({
|
||||
'rowid': 3,
|
||||
'id': None,
|
||||
'chunk_id': 1,
|
||||
'chunk_offset': 2,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
'v_vector_chunks00': OrderedDict({
|
||||
|
|
@ -99,7 +89,7 @@
|
|||
'rows': list([
|
||||
OrderedDict({
|
||||
'rowid': 1,
|
||||
'vectors': b'\x00\x00\x00\x00""""3333\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
'vectors': b'\x00\x00\x00\x00""""\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
|
|
@ -370,14 +360,6 @@
|
|||
'f': 2.2,
|
||||
't': 'test2',
|
||||
}),
|
||||
OrderedDict({
|
||||
'rowid': 3,
|
||||
'vector': b'3333',
|
||||
'b': 1,
|
||||
'n': 3,
|
||||
'f': 3.3,
|
||||
't': '1234567890123',
|
||||
}),
|
||||
]),
|
||||
})
|
||||
# ---
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ struct Vec0RescoreConfig {
|
|||
};
|
||||
#endif
|
||||
|
||||
|
||||
struct VectorColumnDefinition {
|
||||
char *name;
|
||||
int name_length;
|
||||
|
|
|
|||
|
|
@ -265,6 +265,35 @@ def test_deletes(db, snapshot):
|
|||
assert vec0_shadow_table_contents(db, "v") == snapshot()
|
||||
|
||||
|
||||
def test_delete_by_metadata_with_long_text(db):
|
||||
"""Regression for https://github.com/asg017/sqlite-vec/issues/274.
|
||||
|
||||
ClearMetadata left rc=SQLITE_DONE after the long-text DELETE, which
|
||||
propagated as an error and silently aborted the DELETE scan.
|
||||
"""
|
||||
db.execute(
|
||||
"create virtual table v using vec0("
|
||||
" tag text, embedding float[4], chunk_size=8"
|
||||
")"
|
||||
)
|
||||
for i in range(6):
|
||||
db.execute(
|
||||
"insert into v(tag, embedding) values (?, zeroblob(16))",
|
||||
[f"long_text_value_{i}"],
|
||||
)
|
||||
for i in range(4):
|
||||
db.execute(
|
||||
"insert into v(tag, embedding) values (?, zeroblob(16))",
|
||||
[f"long_text_value_0"],
|
||||
)
|
||||
assert db.execute("select count(*) from v").fetchone()[0] == 10
|
||||
|
||||
# DELETE by metadata WHERE — the pattern from the issue
|
||||
db.execute("delete from v where tag = 'long_text_value_0'")
|
||||
assert db.execute("select count(*) from v where tag = 'long_text_value_0'").fetchone()[0] == 0
|
||||
assert db.execute("select count(*) from v").fetchone()[0] == 5
|
||||
|
||||
|
||||
def test_knn(db, snapshot):
|
||||
db.execute(
|
||||
"create virtual table v using vec0(vector float[1], name text, chunk_size=8)"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue