sqlrite 0.1.12__tar.gz → 0.1.14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sqlrite-0.1.12 → sqlrite-0.1.14}/Cargo.lock +5 -5
- {sqlrite-0.1.12 → sqlrite-0.1.14}/Cargo.toml +1 -1
- {sqlrite-0.1.12 → sqlrite-0.1.14}/PKG-INFO +1 -1
- {sqlrite-0.1.12 → sqlrite-0.1.14}/desktop/package.json +1 -1
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/phase-7-plan.md +8 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/roadmap.md +1 -1
- {sqlrite-0.1.12 → sqlrite-0.1.14}/pyproject.toml +1 -1
- {sqlrite-0.1.12 → sqlrite-0.1.14}/sdk/python/Cargo.toml +1 -1
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/db/table.rs +72 -1
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/executor.rs +370 -29
- sqlrite-0.1.14/src/sql/hnsw.rs +790 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/mod.rs +159 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/pager/mod.rs +143 -2
- {sqlrite-0.1.12 → sqlrite-0.1.14}/.github/workflows/ci.yml +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/.github/workflows/release-pr.yml +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/.github/workflows/release.yml +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/.github/workflows/rust.yml +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/.gitignore +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/CODE_OF_CONDUCT.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/LICENSE +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/MAINTAINERS +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/Makefile +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/README.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/desktop/index.html +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/desktop/package-lock.json +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/desktop/src/App.svelte +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/desktop/src/app.css +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/desktop/src/main.ts +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/desktop/src/vite-env.d.ts +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/desktop/svelte.config.js +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/desktop/tsconfig.json +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/desktop/vite.config.ts +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/_index.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/architecture.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/design-decisions.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/desktop.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/embedding.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/file-format.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/getting-started.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/pager.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/release-plan.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/release-secrets.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/smoke-test.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/sql-engine.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/storage-model.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/supported-sql.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/docs/usage.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/examples/README.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/examples/c/Makefile +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/examples/c/hello.c +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/examples/go/go.mod +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/examples/go/hello.go +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/examples/nodejs/hello.mjs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/examples/python/hello.py +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/examples/rust/quickstart.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/examples/wasm/Makefile +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/examples/wasm/index.html +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/images/SQLRite - Desktop.png +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/images/SQLRite Data Structures.png +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/images/SQLRite Simple SQL Execution High Level Diagram.png +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/images/SQLRite Simple SQL INSERT Execution High Level Diagram (Insert Row).png +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/images/SQLRite Simple SQL INSERT Execution High Level Diagram.png +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/images/SQLRite_logo.png +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/images/architecture.png +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/rust-toolchain.toml +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/samples/AST.delete.example +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/samples/AST.insert.exemple +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/samples/AST.select.example +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/samples/AST.update.example +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/samples/CREATE TABLE sqlrite_schema.sql +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/samples/CREATE_TABLE with duplicate.sql +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/samples/CREATE_TABLE.sql +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/samples/INSERT.sql +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/scripts/bump-version.sh +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/sdk/go/README.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/sdk/go/conn.go +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/sdk/go/go.mod +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/sdk/go/rows.go +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/sdk/go/sqlrite.go +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/sdk/go/sqlrite_test.go +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/sdk/go/stmt.go +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/sdk/python/README.md +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/sdk/python/src/lib.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/sdk/python/tests/test_sqlrite.py +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/connection.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/error.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/lib.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/main.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/meta_command/mod.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/repl/mod.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/db/database.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/db/mod.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/db/secondary_index.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/pager/cell.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/pager/file.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/pager/header.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/pager/index_cell.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/pager/interior_page.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/pager/overflow.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/pager/page.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/pager/pager.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/pager/table_page.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/pager/varint.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/pager/wal.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/parser/create.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/parser/insert.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/parser/mod.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/parser/select.rs +0 -0
- {sqlrite-0.1.12 → sqlrite-0.1.14}/src/sql/tokenizer.rs +0 -0
|
@@ -3736,7 +3736,7 @@ dependencies = [
|
|
|
3736
3736
|
|
|
3737
3737
|
[[package]]
|
|
3738
3738
|
name = "sqlrite-desktop"
|
|
3739
|
-
version = "0.1.
|
|
3739
|
+
version = "0.1.14"
|
|
3740
3740
|
dependencies = [
|
|
3741
3741
|
"serde",
|
|
3742
3742
|
"serde_json",
|
|
@@ -3748,7 +3748,7 @@ dependencies = [
|
|
|
3748
3748
|
|
|
3749
3749
|
[[package]]
|
|
3750
3750
|
name = "sqlrite-engine"
|
|
3751
|
-
version = "0.1.
|
|
3751
|
+
version = "0.1.14"
|
|
3752
3752
|
dependencies = [
|
|
3753
3753
|
"clap",
|
|
3754
3754
|
"env_logger",
|
|
@@ -3763,7 +3763,7 @@ dependencies = [
|
|
|
3763
3763
|
|
|
3764
3764
|
[[package]]
|
|
3765
3765
|
name = "sqlrite-ffi"
|
|
3766
|
-
version = "0.1.
|
|
3766
|
+
version = "0.1.14"
|
|
3767
3767
|
dependencies = [
|
|
3768
3768
|
"cbindgen",
|
|
3769
3769
|
"sqlrite-engine",
|
|
@@ -3771,7 +3771,7 @@ dependencies = [
|
|
|
3771
3771
|
|
|
3772
3772
|
[[package]]
|
|
3773
3773
|
name = "sqlrite-nodejs"
|
|
3774
|
-
version = "0.1.
|
|
3774
|
+
version = "0.1.14"
|
|
3775
3775
|
dependencies = [
|
|
3776
3776
|
"napi",
|
|
3777
3777
|
"napi-build",
|
|
@@ -3781,7 +3781,7 @@ dependencies = [
|
|
|
3781
3781
|
|
|
3782
3782
|
[[package]]
|
|
3783
3783
|
name = "sqlrite-python"
|
|
3784
|
-
version = "0.1.
|
|
3784
|
+
version = "0.1.14"
|
|
3785
3785
|
dependencies = [
|
|
3786
3786
|
"pyo3",
|
|
3787
3787
|
"sqlrite-engine",
|
|
@@ -27,7 +27,7 @@ resolver = "3"
|
|
|
27
27
|
# `package =` key so the import name stays `sqlrite` internally:
|
|
28
28
|
# sqlrite = { package = "sqlrite-engine", path = "…" }
|
|
29
29
|
name = "sqlrite-engine"
|
|
30
|
-
version = "0.1.
|
|
30
|
+
version = "0.1.14"
|
|
31
31
|
authors = ["Joao Henrique Machado Silva <joaoh82@gmail.com>"]
|
|
32
32
|
edition = "2024"
|
|
33
33
|
rust-version = "1.85"
|
|
@@ -158,6 +158,14 @@ SELECT id, title FROM docs ORDER BY embedding <-> [0.1, ...] LIMIT 10;
|
|
|
158
158
|
|
|
159
159
|
**LOC estimate:** ~700-900 lines. The big sub-phase.
|
|
160
160
|
|
|
161
|
+
> **Scope correction (2026-04-27, post-7c):** Re-scoping during implementation showed 7d works out to ~1300 LOC across three logical chunks, more than the original ~700-900 estimate and too much for one reviewable PR. Splitting into three:
|
|
162
|
+
>
|
|
163
|
+
> - **✅ 7d.1 — Pure HNSW algorithm** *(~700 LOC, shipped in v0.1.13).* `src/sql/hnsw.rs` standalone module: insert + search + layer assignment + beam search per layer + L2/cosine/dot distance dispatch. No SQL integration yet — vectors are passed in via a `get_vec` closure so the algorithm doesn't depend on table types. Tests verify recall@k ≥ 0.95 vs brute-force on randomly-generated vector sets; deterministic via a fixed RNG seed.
|
|
164
|
+
> - **✅ 7d.2 — SQL integration** *(~500 LOC).* `CREATE INDEX … USING hnsw (col)` parser + engine, INSERT wiring (also calls `hnsw.insert()` incrementally), query optimizer hook (recognizes `ORDER BY vec_distance_l2(col, literal) LIMIT k` and probes the HNSW instead of full-scanning). HNSW lives in memory only at this point; the **CREATE INDEX SQL persists in `sqlrite_master` and reopen rebuilds the graph from current rows** — partial persistence ahead of 7d.3. DELETE/UPDATE on HNSW-indexed tables refused with helpful error pointing at 7d.3.
|
|
165
|
+
> - **7d.3 — Persistence** *(~300 LOC).* Wire HNSW into the cell format: new `KIND_HNSW` cell tag, page-tree storage parallel to secondary indexes, save/reopen round-trip without rebuild. Also adds DELETE/UPDATE support since the persisted form gives us a natural rebuild trigger.
|
|
166
|
+
>
|
|
167
|
+
> Each 7d.x ships as its own PR + release wave. The user-facing value lands at 7d.2; 7d.3 closes the persistence loop. 7d.1 is foundational but ships a tested algorithmic primitive on its own — useful as documentation of the engine's "from scratch" theme.
|
|
168
|
+
|
|
161
169
|
**Tests:** recall@k vs brute-force baseline (should be ≥ 0.95 on standard benchmark vectors); insert performance; delete semantics; persistence roundtrip.
|
|
162
170
|
|
|
163
171
|
---
|
|
@@ -473,7 +473,7 @@ Approved sub-phases (Q1–Q10 resolved):
|
|
|
473
473
|
- **✅ 7a — `VECTOR(N)` column type** *(v0.1.10)* — dense fixed-dimension f32 storage via the existing cell encoding; format bumped to v4. Bracket-array literal syntax `[0.1, 0.2, …]` (Q7).
|
|
474
474
|
- **✅ 7b — Distance functions** *(v0.1.11)* — `vec_distance_l2/cosine/dot`, plus the ORDER BY-expressions parser change so KNN queries work end-to-end. Operators (`<->` `<=>` `<#>`) deferred to **7b.1** — sqlparser doesn't parse them natively, contradicting Q6's "tiny parser change" assumption.
|
|
475
475
|
- **✅ 7c — Brute-force KNN executor optimization** — bounded `BinaryHeap` of size k for `ORDER BY <expr> LIMIT k`. ~1.8× faster than full-sort at N=10k for cheap keys; bigger gains on expensive keys like `vec_distance_l2`.
|
|
476
|
-
- **7d — HNSW ANN index** — `CREATE INDEX … USING hnsw (col)`;
|
|
476
|
+
- **7d — HNSW ANN index** — split into 7d.1 (✅ algorithm), 7d.2 (✅ SQL integration), 7d.3 (persistence). `CREATE INDEX … USING hnsw (col)`; fixed defaults `M=16, ef_construction=200, ef_search=50` (Q2).
|
|
477
477
|
- **7e — JSON column type + path queries** — `JSON` data type stored as bincoded `serde_json::Value` (Q3); `json_extract` / `json_array_length` / `json_object_keys` / `json_type`.
|
|
478
478
|
- **7f — ~~Full-text search with BM25~~** — **deferred to Phase 8** (Q1).
|
|
479
479
|
- **7g — `ask()` API across the product surface** — natural-language → SQL via Anthropic API (Q4), Anthropic-first then OpenAI + Ollama follow-ups. Foundational 7g.1 introduces a new `sqlrite-ask` crate (Q10 — separate crate, not a feature flag). Thin per-product adapters in 7g.2-7g.8 cover REPL, desktop, Python, Node.js, Go, WASM (JS-callback shape per Q9), and the MCP `ask` tool.
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sqlrite"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.14"
|
|
8
8
|
description = "Python bindings for SQLRite — a small, embeddable SQLite clone written in Rust."
|
|
9
9
|
authors = [{ name = "Joao Henrique Machado Silva", email = "joaoh82@gmail.com" }]
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
use crate::error::{Result, SQLRiteError};
|
|
2
2
|
use crate::sql::db::secondary_index::{IndexOrigin, SecondaryIndex};
|
|
3
|
+
use crate::sql::hnsw::HnswIndex;
|
|
3
4
|
use crate::sql::parser::create::CreateQuery;
|
|
4
5
|
use std::collections::{BTreeMap, HashMap};
|
|
5
6
|
use std::fmt;
|
|
@@ -118,12 +119,32 @@ pub struct Table {
|
|
|
118
119
|
/// add more. Looking up an index: iterate by column name, or by index
|
|
119
120
|
/// name via `Table::index_by_name`.
|
|
120
121
|
pub secondary_indexes: Vec<SecondaryIndex>,
|
|
122
|
+
/// HNSW indexes on VECTOR columns (Phase 7d.2). Maintained in lockstep
|
|
123
|
+
/// with row storage on INSERT (incremental); rebuilt on open from the
|
|
124
|
+
/// persisted CREATE INDEX SQL. The graph itself is NOT yet persisted —
|
|
125
|
+
/// see Phase 7d.3 for cell-encoded graph storage.
|
|
126
|
+
pub hnsw_indexes: Vec<HnswIndexEntry>,
|
|
121
127
|
/// ROWID of most recent insert.
|
|
122
128
|
pub last_rowid: i64,
|
|
123
129
|
/// PRIMARY KEY column name, or "-1" if the table has no PRIMARY KEY.
|
|
124
130
|
pub primary_key: String,
|
|
125
131
|
}
|
|
126
132
|
|
|
133
|
+
/// One HNSW index attached to a table. Phase 7d.2 only supports L2
|
|
134
|
+
/// distance; cosine and dot are 7d.x follow-ups (would require either
|
|
135
|
+
/// distinct USING methods like `hnsw_cosine` or a `WITH (metric = …)`
|
|
136
|
+
/// clause — see `docs/phase-7-plan.md` for the deferred decision).
|
|
137
|
+
#[derive(Debug, Clone)]
|
|
138
|
+
pub struct HnswIndexEntry {
|
|
139
|
+
/// User-supplied name from `CREATE INDEX <name> …`. Unique across
|
|
140
|
+
/// both `secondary_indexes` and `hnsw_indexes` on a given table.
|
|
141
|
+
pub name: String,
|
|
142
|
+
/// The VECTOR column this index covers.
|
|
143
|
+
pub column_name: String,
|
|
144
|
+
/// The graph itself.
|
|
145
|
+
pub index: HnswIndex,
|
|
146
|
+
}
|
|
147
|
+
|
|
127
148
|
impl Table {
|
|
128
149
|
pub fn new(create_query: CreateQuery) -> Self {
|
|
129
150
|
let table_name = create_query.table_name;
|
|
@@ -194,6 +215,11 @@ impl Table {
|
|
|
194
215
|
columns: table_cols,
|
|
195
216
|
rows: table_rows,
|
|
196
217
|
secondary_indexes,
|
|
218
|
+
// HNSW indexes only land via explicit CREATE INDEX … USING hnsw
|
|
219
|
+
// statements (Phase 7d.2); never auto-created at CREATE TABLE
|
|
220
|
+
// time, because there's no UNIQUE-style constraint that
|
|
221
|
+
// implies a vector index.
|
|
222
|
+
hnsw_indexes: Vec::new(),
|
|
197
223
|
last_rowid: 0,
|
|
198
224
|
primary_key,
|
|
199
225
|
}
|
|
@@ -217,6 +243,10 @@ impl Table {
|
|
|
217
243
|
columns: self.columns.clone(),
|
|
218
244
|
rows: Arc::new(Mutex::new(cloned_rows)),
|
|
219
245
|
secondary_indexes: self.secondary_indexes.clone(),
|
|
246
|
+
// HnswIndexEntry derives Clone, so the snapshot owns its own
|
|
247
|
+
// graph copy. Phase 4f's snapshot-rollback semantics require
|
|
248
|
+
// the snapshot to be fully decoupled from live state.
|
|
249
|
+
hnsw_indexes: self.hnsw_indexes.clone(),
|
|
220
250
|
last_rowid: self.last_rowid,
|
|
221
251
|
primary_key: self.primary_key.clone(),
|
|
222
252
|
}
|
|
@@ -813,16 +843,57 @@ impl Table {
|
|
|
813
843
|
|
|
814
844
|
// Step 2: maintain the secondary index (if any). insert() is a
|
|
815
845
|
// no-op for Value::Null and cheap for other value kinds.
|
|
816
|
-
if let Some(v) = typed_value {
|
|
846
|
+
if let Some(v) = typed_value.clone() {
|
|
817
847
|
if let Some(idx) = self.index_for_column_mut(key) {
|
|
818
848
|
idx.insert(&v, next_rowid)?;
|
|
819
849
|
}
|
|
820
850
|
}
|
|
851
|
+
|
|
852
|
+
// Step 3 (Phase 7d.2): maintain any HNSW indexes on this column.
|
|
853
|
+
// The HNSW algorithm needs access to other rows' vectors when
|
|
854
|
+
// wiring up neighbor edges, so build a get_vec closure that
|
|
855
|
+
// pulls from the table's row storage (which we *just* updated
|
|
856
|
+
// with the new value).
|
|
857
|
+
if let Some(Value::Vector(new_vec)) = typed_value {
|
|
858
|
+
self.maintain_hnsw_on_insert(key, next_rowid, &new_vec);
|
|
859
|
+
}
|
|
821
860
|
}
|
|
822
861
|
self.last_rowid = next_rowid;
|
|
823
862
|
Ok(())
|
|
824
863
|
}
|
|
825
864
|
|
|
865
|
+
/// After a row insert, push the new (rowid, vector) into every HNSW
|
|
866
|
+
/// index whose column matches `column`. Split out of `insert_row` so
|
|
867
|
+
/// the borrowing dance — we need both `&self.rows` (read other
|
|
868
|
+
/// vectors) and `&mut self.hnsw_indexes` (insert into the graph) —
|
|
869
|
+
/// stays localized.
|
|
870
|
+
fn maintain_hnsw_on_insert(&mut self, column: &str, rowid: i64, new_vec: &[f32]) {
|
|
871
|
+
// Snapshot the current vector storage so the get_vec closure
|
|
872
|
+
// doesn't fight with `&mut self.hnsw_indexes`. For a typical
|
|
873
|
+
// HNSW insert we touch ef_construction × log(N) other vectors,
|
|
874
|
+
// so the snapshot cost is small relative to the graph wiring.
|
|
875
|
+
let mut vec_snapshot: HashMap<i64, Vec<f32>> = HashMap::new();
|
|
876
|
+
{
|
|
877
|
+
let row_data = self.rows.lock().expect("rows mutex poisoned");
|
|
878
|
+
if let Some(Row::Vector(map)) = row_data.get(column) {
|
|
879
|
+
for (id, v) in map.iter() {
|
|
880
|
+
vec_snapshot.insert(*id, v.clone());
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
// The new row was just written into row storage — make sure the
|
|
885
|
+
// snapshot reflects it (it should, but defensive).
|
|
886
|
+
vec_snapshot.insert(rowid, new_vec.to_vec());
|
|
887
|
+
|
|
888
|
+
for entry in &mut self.hnsw_indexes {
|
|
889
|
+
if entry.column_name == column {
|
|
890
|
+
entry.index.insert(rowid, new_vec, |id| {
|
|
891
|
+
vec_snapshot.get(&id).cloned().unwrap_or_default()
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
826
897
|
/// Print the table schema to standard output in a pretty formatted way.
|
|
827
898
|
///
|
|
828
899
|
/// # Example
|