Skip to content

feat(rust): port vectorlite to Rust (vtab in Rust; FFI only to hnswlib + ops) - #56

Open
1yefuwang1 wants to merge 4 commits into
mainfrom
rust-port-vtab
Open

feat(rust): port vectorlite to Rust (vtab in Rust; FFI only to hnswlib + ops)#56
1yefuwang1 wants to merge 4 commits into
mainfrom
rust-port-vtab

Conversation

@1yefuwang1

@1yefuwang1 1yefuwang1 commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a Rust port of the vectorlite SQLite extension under rust/. All virtual-table logic lives in Rust; C++ is reached only via FFI, and only for two things: hnswlib and the SIMD ops (Google Highway). The port is a drop-in replacement for the C++ extension and passes the existing Python integration suite (112 tests) unchanged.

What is in Rust vs C++

Rust (this crate) — the entire virtual table + all policy:

  • SQLite glue: xCreate/xConnect/xBestIndex/xFilter/xUpdate/xColumn/xRowid/xRename/xFindFunction/...
  • Scalar functions: vector_distance, vector_from_json, vector_to_json, knn_search, knn_param, vectorlite_info
  • Constraint handling and rowid IN (...) pushdown (sqlite3_vtab_in)
  • Per-connection index registry (survives reparse/vacuum/rename)
  • Vector-space and index-option parsing, JSON/blob serde
  • All numeric policy: which distance/space to use, quantization + normalization for float32/bfloat16/float16, the rowid filter predicate, per-query ef save/restore, the load data-size mismatch check, and save/load orchestration

C++ (reached only through the C ABI) — nothing but hnswlib + ops:

  • cpp/core_shim.cpp is generic, policy-free glue: a SpaceInterface adapter around a Rust distance callback, a BaseFilterFunctor adapter around a Rust predicate, thin HierarchicalNSW wrappers, and forwarders to ops.
  • vectorlite/ops/ops.cpp — the existing, un-ported Highway SIMD kernels.
  • Only these two .cpp files are compiled; no C++ virtual-table sources are used.

The distance function hnswlib calls is a Rust extern "C" callback that forwards to ops; the rowid filter is a Rust predicate invoked via a trampoline. So the only C++ is hnswlib itself, the ops kernels, and the minimal adapters required to expose those through a C ABI.

Highlights

  • Faithful behavior — mirrors the C++ paths: knn with ef/rowid filters, knn_param pointer passing, persistence with the data-size check, quantization/normalization across all element types and l2/ip/cosine, and the registry that keeps the index alive across schema reparses.
  • No libclang for normal builds — SQLite extension-API bindings are pre-generated and committed in a vendored vectorlite-sqlite-sys crate (the libsqlite3-sys pattern). Refresh with cargo build -p vectorlite-sqlite-sys --features regenerate.
  • Static SQLite — the full SQLite amalgamation is statically embedded (~3.2 MB), mirroring the C++ build; embedded symbols aren't exported, so they can't interpose the host's SQLite.
  • Cross-platformbuild.rs discovers the vcpkg triplet by scanning build/*/vcpkg_installed/*/, picks the right static-archive names (.a/.lib), system libs, and per-linker flags, so it builds on Linux, macOS and Windows. Only hwy + sqlite3 are linked.

Testing

sh build.sh                 # repo root: build C++/vcpkg deps once
sh rust/build.sh            # build + deploy the Rust extension
PYTHONPATH=bindings/python python -m pytest bindings/python/vectorlite_py/test
# 112 passed

Notes

  • This PR only adds the rust/ tree; no existing C++ sources or build files are modified.
  • Cargo.lock is committed for reproducible builds; target/, .libclang/ are git-ignored.

1yefuwang1 and others added 2 commits June 24, 2026 09:15
Add a Rust port of vectorlite's SQLite virtual-table glue and scalar
functions under rust/. The numeric core (hnswlib + Highway `ops` +
quantization) is not ported: it is wrapped behind a small C ABI
(cpp/core_shim) and linked as a static library, keeping unsafe confined to
the FFI boundary.

- Ports vtab (BestIndex/Filter/Update/Column/Rename/FindFunction/...),
  scalar functions, constraint handling, per-connection index registry,
  vector-space/index-option parsing and JSON/blob serde to safe Rust.
- SQLite extension-API bindings are pre-generated and committed in a vendored
  vectorlite-sqlite-sys crate, so normal builds need no libclang.
- Statically embeds the full SQLite amalgamation, mirroring the C++ build.
- build.rs is cross-platform (Linux/macOS/Windows): triplet-agnostic vcpkg
  discovery, platform-correct lib names, system libs and linker flags.
- Passes the existing Python integration suite (112 tests).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously core_shim.cpp carried virtual-table policy (space/metric
selection, quantization + normalization dispatch, the rowid filter
predicate, ef handling, save/load orchestration and the load data-size
check). Move all of that into Rust so the C++/FFI surface exposes ONLY
hnswlib and ops.

- cpp/core_shim.{h,cpp}: now a generic, policy-free C ABI — a SpaceInterface
  adapter around a Rust distance callback, a BaseFilterFunctor adapter around
  a Rust predicate, thin HierarchicalNSW wrappers, and forwarders to ops.
- src/ops.rs: safe ops FFI wrappers + the six hnswlib distance callbacks
  (l2/ip x f32/bf16/f16) that read dim from the space param and call ops.
- src/hnsw.rs: safe hnswlib FFI wrappers + a Rust rowid-filter trampoline.
- src/core.rs: owns quantization/normalization, per-query ef save/restore,
  the rowid filter, the load data-size mismatch check and save/load
  orchestration, plus the scalar distance — all in Rust.
- Only vectorlite/ops/ops.cpp and core_shim.cpp are compiled; no C++ vtab
  sources are used.

All 112 Python integration tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@1yefuwang1 1yefuwang1 changed the title feat(rust): port virtual table and scalar functions to Rust feat(rust): port vectorlite to Rust (vtab in Rust; FFI only to hnswlib + ops) Jul 1, 2026
1yefuwang1 and others added 2 commits July 1, 2026 13:20
- Fix clippy::mut_from_ref: drop the `&self -> &mut Registry` helper and
  deref the raw registry pointer directly at the rename call site.
- Inline variables into format! strings (clippy::uninlined_format_args, 42x).
- Data-drive scalar-function registration in sqlite3_extension_init instead
  of six near-identical blocks.
- Use `len().is_multiple_of(..)` for the blob-size check.
- Remove now-dead code (NamedVectorSpace::normalize, IndexEntry's unused
  allow_replace_deleted field, a stale allow(dead_code) on element_size).
- Silence clippy on the machine-generated sqlite bindings.
- Apply rustfmt across the crate.

cargo clippy and cargo fmt --check are clean; all 112 Python integration
tests still pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address the code-quality review of the Rust port:

Safety / soundness
- Set panic="abort" (dev+release) so a Rust panic can never unwind across
  an extern "C" boundary into SQLite/hnswlib (UB). Replace the .expect/
  assert! on live callback paths with graceful SQLITE_ERROR returns
  (VTab::entry now returns Option; Space::new returns Result).
- Guard the C shim: wrap vl_hnsw_space_create and vl_hnsw_contains in
  try/catch so no C++ exception crosses the C ABI.
- Replace `static mut API` with an AtomicPtr (Acquire/Release).
- Fetch value_blob/value_text before value_bytes, per SQLite's ordering.

Correctness / DoS
- Cap the requested k at the current element count before allocating
  result buffers (new vl_hnsw_current_count), preventing an
  attacker-controlled k (e.g. knn_param(v, 2e9)) from forcing a multi-GB
  allocation/abort. Read k/ef via value_int64 to avoid 32-bit truncation.
- vector_to_json now errors on non-finite values instead of emitting
  `null` (which no longer round-trips).
- Decouple idxStr from idxNum: x_filter reads idxStr via CStr.

Build / packaging
- Stop force-embedding SQLite. A loadable extension has no undefined
  SQLite symbols (all calls go through sqlite3_api_routines), so drop the
  whole-archive link plus /OPT:NOREF and --no-gc-sections. The artifact is
  now ~0.6 MB and identical across Linux/macOS/Windows.

Tooling / tests
- Add rust-version, fix error-string typos, remove is_multiple_of.
- Add unit tests for vector, vector_space and index_options.
- Add a matrix (ubuntu + windows) rust_port CI job running
  fmt/clippy/tests, the build, and the Python integration suite against
  the Rust extension.

Verified on macOS: cargo fmt/clippy clean, 19 unit tests and 112 Python
integration tests pass.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 38b9cda2-a92d-4682-a15c-aaa8fe6cf5fb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant