Experimental · Rust · no_std core

SQL, vectors and full-text search
in a single file

InlaySQL keeps SQLite's model — one file, no server, embed it anywhere — and adds concurrent writers, native vector and BM25 retrieval, and a MySQL wire protocol your ORM already speaks.

Still in development, and under heavy testing right now. The on-disk format is pre-1.0 (recreate, not migrate) and the MySQL server's wire parser is young — it now refuses a bind that would expose the database by accident, but its fuzzing campaigns have only just been written and have not yet run. See What this is not below before you rely on it for anything real. If the idea here is one you'd use, tell us in Issues — what you'd need first is exactly what decides what gets built next.

2,577,421 point reads per second 2.10× SQLite WAL, ~7.6× SQLite journal (durable)
~17× SQLite on concurrent writes 1,555 commits/s at 8 writers, 0% aborted
167 µs hybrid search latency vector + BM25 fused in the engine
1307 SQL Logic Tests passing 100%, against SQLite's own corpus

Retrieval is not a bolt-on

A vector column and a full-text index live in the same file, in the same transaction, behind the same SQL. Fusing them is one statement — not two queries and a client-side merge.

-- A vector column is a column. An index is an index.
CREATE TABLE docs (
  id        INTEGER PRIMARY KEY,
  body      TEXT,
  embedding VECTOR(384)
);
CREATE INDEX docs_body      ON docs (body);       -- BM25
CREATE INDEX docs_embedding ON docs (embedding);  -- HNSW

-- Hybrid search, ranked by reciprocal rank fusion, in the engine.
SELECT id, body,
       fuse(vector_score(embedding, ?), bm25_score(body, ?)) AS score
FROM docs
ORDER BY score DESC
LIMIT 10;

Run it here

The engine below is compiled to WebAssembly and running in this tab — no server, no network round trip. It seeds a small corpus, indexes it for BM25 and vector search, and answers with the same code the native build runs.

loading the module…

    

The database is one file, in the same format the CLI reads. Saving hands those bytes to the origin-private file system; downloading gives you a file you can open with inlaysql serve --mcp.


    

Measured, including where we lose

Every number regenerates from a script in the repository. A table that contains only wins is advertising, so the losses are here too — and the rest of them are in the list below.

WorkloadInlaySQLCompared withResult
Point read by primary key 2,577,421 ops/s SQLite, journal (durable) / SQLite, WAL ~7.6× faster durable; 2.10× vs WAL
Concurrent writes, 8 writers 1,555 commits/s SQLite, journal ~17× faster
Concurrent writes, 32 writers 3,529 commits/s SQLite, journal ~40× faster
Vector search, recall@10 1.000 58.04 µs sqlite-vec ~11× faster
Hybrid search (BM25 + vector) 167.0 µs DuckDB, pgvector ~70-90× faster
Secondary-index inner join, 20k × 160k rows 3.38 ms SQLite ~8× faster
PK inner join, LIMIT 10 3.29 µs p50 SQLite, journal (3.46 µs p50) faster in all three runs on p50; a wash on throughput, inside an 18% spread, so not claimed as a win there
Indexed range scan, 50 rows 7.13 µs SQLite, journal (6.63 µs) ~1.1× slower
Batch insert, 100 rows per statement, containerised like the servers 88,456 rows/s MySQL 8.4 (52,935 rows/s), PostgreSQL 17 (100,305 rows/s) 1.64× MySQL, 0.88× PostgreSQL (tie inside a 21% band)
Server writes over MySQL wire, 8 connections 1,456.5 ops/s MySQL 8.4 (4,837.8 ops/s) ~2-3× slower

One developer machine, one sitting. These multiples are rounded to the precision the measured run-to-run spread supports, so a bare "3.3×" would claim more than the measurement has. Reproduce it; do not trust it — ./bench/run.sh and ./bench/compare.sh are in the repository, and BENCHMARK.md records the machine, the durability settings, the provenance of every table and the asymmetries that remain — SCOREBOARD.md is the win/loss matrix with its fairness audit.

What this is not, yet

Scheduled work rather than oversights — one line each, with the detail behind it. A page that lists only wins is advertising.

Get started

# Use it like SQLite: download the library, one small loader, SQL.
# Prebuilt for macOS (Apple silicon) and Linux — from the releases page:
#   https://github.com/inlaySQL/inlaysql/releases

# PHP (FFI is built in; Python and Ruby are the same shape)
$db = new InlaySQL('app.inlay');
$db->run('INSERT INTO users (name) VALUES (?)', ['Ada']);
$rows = $db->run('SELECT id, name FROM users');
// → {"columns":["id","name"],"rows":[[1,"Ada"]]}

# From source — the CLI, and hybrid retrieval end to end
git clone https://github.com/inlaySQL/inlaysql && cd inlaysql
cargo run --example hybrid_search

# Or serve a database over the MySQL wire protocol,
# then point your ORM at 127.0.0.1:3306
cargo run -p inlaysql-mcp --bin inlaysql -- serve --mysql app.inlay

Per-language loaders (PHP, Python, Ruby, C#, Java) are in the client-languages guide. Reading order for the repository: README for the pitch and the architecture, TESTING.md for what is covered and what is not, and docs/PLAN.md for what is being built next and why.