{"id":13559615,"url":"https://github.com/joaoh82/rust_sqlite","last_synced_at":"2026-05-31T01:01:11.348Z","repository":{"id":39569100,"uuid":"330041868","full_name":"joaoh82/rust_sqlite","owner":"joaoh82","description":"SQLRite - Simple embedded database modeled off SQLite in Rust","archived":false,"fork":false,"pushed_at":"2026-05-25T08:39:53.000Z","size":2521,"stargazers_count":1073,"open_issues_count":0,"forks_count":56,"subscribers_count":31,"default_branch":"main","last_synced_at":"2026-05-25T10:31:09.316Z","etag":null,"topics":["computer-science","database-design","databases","rust-lang","sqlite-architecture"],"latest_commit_sha":null,"homepage":"https://www.sqlritedb.com/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joaoh82.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":"docs/supported-sql.md","governance":null,"roadmap":"docs/roadmap.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":"MAINTAINERS","copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2021-01-15T23:08:17.000Z","updated_at":"2026-05-25T08:39:57.000Z","dependencies_parsed_at":"2022-07-18T03:00:35.900Z","dependency_job_id":"139b7693-2312-4f63-8f78-cf894517f966","html_url":"https://github.com/joaoh82/rust_sqlite","commit_stats":null,"previous_names":[],"tags_count":306,"template":false,"template_full_name":null,"purl":"pkg:github/joaoh82/rust_sqlite","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joaoh82%2Frust_sqlite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joaoh82%2Frust_sqlite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joaoh82%2Frust_sqlite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joaoh82%2Frust_sqlite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joaoh82","download_url":"https://codeload.github.com/joaoh82/rust_sqlite/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joaoh82%2Frust_sqlite/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33715211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["computer-science","database-design","databases","rust-lang","sqlite-architecture"],"created_at":"2024-08-01T13:00:29.952Z","updated_at":"2026-05-31T01:01:11.335Z","avatar_url":"https://github.com/joaoh82.png","language":"Rust","funding_links":["https://github.com/sponsors/joaoh82"],"categories":["Rust","Dev-Utilities"],"sub_categories":[],"readme":"Rust-SQLite (SQLRite)\n===\n[![Build Status](https://github.com/joaoh82/rust_sqlite/workflows/Rust/badge.svg)](https://github.com/joaoh82/rust_sqlite/actions)\n[![dependency status](https://deps.rs/repo/github/joaoh82/rust_sqlite/status.svg)](https://deps.rs/repo/github/joaoh82/rust_sqlite)\n[![Coverage Status](https://coveralls.io/repos/github/joaoh82/rust_sqlite/badge.svg?branch=main)](https://coveralls.io/github/joaoh82/rust_sqlite?branch=main)\n[![Maintenance](https://img.shields.io/badge/maintenance-actively%20maintained-brightgreen.svg)](https://deps.rs/repo/github/joaoh82/rust_sqlite)\n[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)\n\n`Rust-SQLite`, aka `SQLRite` , is a simple embedded database modeled off `SQLite`, but developed with `Rust`. The goal is get a better understanding of database internals by building one.\n\n\u003e What I cannot create, I do not understand. \n\u003e — Richard Feynman\n\n📖 **Project website + docs:** the marketing + getting-started site is in [`web/`](web/) (Next.js 15 + Tailwind v4). Run `cd web \u0026\u0026 npm install \u0026\u0026 npm run dev` to preview locally; deploys to Vercel out of the box.\n\n\n\u003ctable style=\"width:100%\"\u003e\n\u003ctr\u003e\n  \u003ctd\u003e\n    \u003ctable style=\"width:100%\"\u003e\n      \u003ctr\u003e\n        \u003ctd\u003e key \u003c/td\u003e\n        \u003ctd\u003e value \u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n        \u003ctd\u003e\u003ca href=\"https://github.com/sqlrite/design\"\u003eDesign and discussions about direction\u003cbr\u003eof the project going on over here.\u003c/a\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n        \u003ctd\u003e\u003ca href=\"https://github.com/sponsors/joaoh82\"\u003eShow us your support by buying us a coffee, \u003cbr\u003eso we can keep building cool stuff. (coming soon)\u003c/a\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003ca href=\"https://github.com/sponsors/joaoh82\"\u003e\u003cimg src=\"https://img.shields.io/opencollective/backers/sqlrite\"\u003e\u003c/a\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n        \u003ctd\u003e\u003ca href=\"https://docs.rs/sqlrite-engine\"\u003eRust API docs on docs.rs\u003c/a\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003ca href=\"https://docs.rs/sqlrite-engine\"\u003e\u003cimg src=\"https://docs.rs/sqlrite-engine/badge.svg\"\u003e\u003c/a\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n        \u003ctd\u003e\u003ca href=\"https://discord.gg/dHPmw89zAE\"\u003eCome and Chat about databases with us\u003c/a\u003e\u003c/td\u003e\n        \u003ctd\u003e\u003ca href=\"https://discord.gg/dHPmw89zAE\"\u003e\n        \u003cimg src=\"https://discordapp.com/api/guilds/853931853219758091/widget.png?style=shield\" alt=\"sqlritedb discord server\"/\u003e\u003c/a\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n     \u003c/table\u003e\n  \u003c/td\u003e\n  \u003ctd\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/SQLRite_logo.png\" width=\"50%\" height=\"auto\" /\u003e \n  \u003c/p\u003e\n  \u003c/td\u003e\n \u003c/tr\u003e\n\u003c/table\u003e\n\n### Read the series of posts about it:\n##### What would SQLite look like if written in Rust?\n* [Part 0 - Overview](https://medium.com/the-polyglot-programmer/what-would-sqlite-would-look-like-if-written-in-rust-part-0-4fc192368984)\n* [Part 1 - Understanding SQLite and Setting up CLI Application and REPL](https://medium.com/the-polyglot-programmer/what-would-sqlite-look-like-if-written-in-rust-part-1-4a84196c217d)\n* [Part 2 - SQL Statement and Meta Commands Parser + Error Handling](https://medium.com/the-polyglot-programmer/what-would-sqlite-look-like-if-written-in-rust-part-2-55b30824de0c)\n* [Part 3 - Understanding the B-Tree and its role on database design](https://medium.com/the-polyglot-programmer/what-would-sqlite-look-like-if-written-in-rust-part-3-edd2eefda473)\n\n![The SQLite Architecture](images/architecture.png \"The SQLite Architecture\")\n\n### CREATE TABLE and INSERT Statements\n[![asciicast](https://asciinema.org/a/406447.svg)](https://asciinema.org/a/406447)\n\n### Desktop app\n\nA cross-platform Tauri 2.0 + Svelte 5 desktop GUI ships alongside the REPL (see [`desktop/`](desktop/) and [docs/desktop.md](docs/desktop.md) for details).\n\n![SQLRite Desktop](\u003cimages/SQLRite - Desktop.png\u003e \"The SQLRite desktop app\")\n\n**Prebuilt installers** — download from the [latest desktop release](https://github.com/joaoh82/rust_sqlite/releases/latest):\n\n| Platform | Files |\n|---|---|\n| Linux x86_64 | `.AppImage`, `.deb` (Debian/Ubuntu), `.rpm` (Fedora/RHEL) |\n| macOS Apple Silicon | `.dmg`, raw `.app.tar.gz` *(Intel Macs not supported yet — universal dmg is a follow-up)* |\n| Windows x86_64 | `.msi`, `.exe` (NSIS) |\n\n\u003e ⚠️ **Installers are unsigned** until Phase 6.1 wires up Apple Developer ID + Windows code-signing certs. First-launch friction to expect:\n\u003e - **macOS**: \"SQLRite is damaged\" or \"unidentified developer\" → run `xattr -cr /Applications/SQLRite.app` once to strip the quarantine attribute, then it opens normally. The app is fine; Tauri ad-hoc signs every macOS binary (Apple Silicon requires a signature), but quarantined ad-hoc signatures trip a stricter Gatekeeper path with the scary \"damaged\" wording.\n\u003e - **Windows**: SmartScreen → click \"More info\" → \"Run anyway\".\n\u003e - **Linux AppImage**: `chmod +x SQLRite_*.AppImage` before launching.\n\n**From source** — `cd desktop \u0026\u0026 npm install \u0026\u0026 npm run tauri dev`. The header's New… / Open… / Save As… buttons cover the file lifecycle; the query editor has a live line-number gutter, `⌘/` (Ctrl+/) SQL comment toggle, and selection-aware Run (highlight a statement to run just that one).\n\n### MCP server (drive SQLRite from an LLM agent)\n\n`sqlrite-mcp` exposes a SQLRite database as a [Model Context Protocol](https://modelcontextprotocol.io/) stdio server. Spawn it from any MCP client (Claude Code, Cursor, `mcp-inspector`, …) and the agent gets seven tools — `list_tables`, `describe_table`, `query`, `execute`, `schema_dump`, `vector_search`, plus `ask` for natural-language → SQL — without any custom integration code.\n\n```sh\ncargo install sqlrite-mcp\n```\n\nOr grab a per-platform pre-built binary from the [latest release](https://github.com/joaoh82/rust_sqlite/releases/latest) (Linux x86_64/aarch64, macOS aarch64, Windows x86_64).\n\nWire it into Claude Code (`~/.claude.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"sqlrite\": {\n      \"command\": \"sqlrite-mcp\",\n      \"args\": [\"/absolute/path/to/your.sqlrite\"],\n      \"env\": { \"SQLRITE_LLM_API_KEY\": \"sk-ant-…\" }\n    }\n  }\n}\n```\n\n`--read-only` opens the DB with a shared lock and hides the `execute` tool. Full docs + the other six tools' references in [`docs/mcp.md`](docs/mcp.md).\n\n### End-to-end example apps\n\nBeyond the per-language quickstarts in [`examples/`](examples/), the SQLR-38 umbrella tracks longer, opinionated example apps that exercise SQLRite in real-world shapes:\n\n| App | SDK | What it shows |\n|---|---|---|\n| [Python LLM agent with persistent memory](examples/python-agent/) | Python | Vector + lexical recall, fact extraction, summaries — all in one `.sqlrite` file |\n| [Chat-with-your-notes via Claude Desktop MCP](examples/nodejs-notes/) | Node.js | Markdown → hybrid HNSW + BM25 index → `sqlrite-mcp --read-only` → Claude Desktop |\n| [Local-first journaling desktop app](examples/desktop-journal/) | Tauri 2 + Svelte 5 | Markdown daily-notes, BM25 full-text search with hit highlighting, \"ask my journal\" panel — entire data layer is one `.sqlrite` file |\n\n### Developer guide\n\nIn-depth documentation lives under [`docs/`](docs/). Start at [`docs/_index.md`](docs/_index.md) — it navigates to:\n\n- [Getting started](docs/getting-started.md), [Using SQLRite](docs/usage.md), [Architecture](docs/architecture.md)\n- [Design decisions](docs/design-decisions.md), [Roadmap](docs/roadmap.md)\n- Internals: [File format](docs/file-format.md), [Pager](docs/pager.md), [Storage model](docs/storage-model.md), [SQL engine](docs/sql-engine.md)\n\n### Benchmarks\n\nSQLRite ships with a [curated benchmark suite](docs/benchmarks.md) that pits the engine against `SQLite` (and optionally `DuckDB`) on twelve OLTP, SQL-feature-scaling, and AI-era workloads (vector top-10, BM25, hybrid retrieval). Run locally with `make bench` (lean, ~5 min) or `make bench-duckdb` (with the analytical comparator on Group B, ~30 min). Pinned-host JSON envelopes get committed under [`benchmarks/results/`](benchmarks/results/) so trends can be diffed mechanically; `benchmarks/scripts/compare.py` renders any two runs as a Markdown diff.\n\nThe [canonical reference](docs/benchmarks.md) carries headline numbers, methodology notes (Q3 SQLite tuning, parser-tax framing), and the engineering debts (SQLR-18 / 19 / 20 / 21) the suite surfaced honestly.\n\n### Requirements\nBefore you begin, ensure you have met the following requirements:\n* Rust (latest stable) – [How to install Rust](https://www.rust-lang.org/en-US/install.html)\n\n### Usage\n\nBuild and launch the REPL:\n\n```shell\ncargo run\n```\n\nYou'll drop into a REPL connected to a transient in-memory database. Use `.open \u003cpath\u003e` to switch to a file-backed database (auto-saves on every statement; see Meta commands).\n\n```\nSQLRite\nEnter .exit to quit.\nEnter .help for usage hints.\nConnected to a transient in-memory database.\nUse '.open FILENAME' to reopen on a persistent database.\nsqlrite\u003e CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, age INTEGER);\nsqlrite\u003e INSERT INTO users (name, age) VALUES ('alice', 30);\nsqlrite\u003e INSERT INTO users (name, age) VALUES ('bob', 25);\nsqlrite\u003e SELECT name FROM users WHERE age \u003e 25 ORDER BY age DESC LIMIT 5;\n+-------+\n| name  |\n+-------+\n| alice |\n+-------+\nSELECT Statement executed. 1 row returned.\nsqlrite\u003e UPDATE users SET age = age + 1 WHERE name = 'bob';\nsqlrite\u003e DELETE FROM users WHERE age \u003c 30;\n```\n\n#### Supported SQL\n\n**See [docs/supported-sql.md](docs/supported-sql.md) for the full reference** — semantics, error behavior, NULL rules, type coercion, case-sensitivity, read-only mode, and the complete list of what's *not* supported yet. The table below is a quick-reference summary.\n\n| Statement | Features |\n|---|---|\n| `CREATE TABLE` | `PRIMARY KEY`, `UNIQUE`, `NOT NULL`; duplicate-column detection; types `INTEGER`/`INT`/`BIGINT`/`SMALLINT`, `TEXT`/`VARCHAR`, `REAL`/`FLOAT`/`DOUBLE`/`DECIMAL`, `BOOLEAN`. Auto-creates `sqlrite_autoindex_\u003ctable\u003e_\u003ccol\u003e` for every PK + UNIQUE column |\n| `CREATE [UNIQUE] INDEX` | Single-column, named indexes; `IF NOT EXISTS`; persists as a dedicated cell-based B-Tree. INTEGER + TEXT columns only |\n| `INSERT INTO` | Explicit column list required; auto-ROWID for `INTEGER PRIMARY KEY`; multi-row `VALUES (…), (…)`; UNIQUE enforcement; clean type errors (no panics); NULL padding for omitted columns |\n| `SELECT` | `*` or column list with optional `AS alias`; `WHERE`; `DISTINCT`; `GROUP BY col[, col …]`; aggregate projections `COUNT(*)` / `COUNT([DISTINCT] col)` / `SUM` / `AVG` / `MIN` / `MAX`; `[INNER\\|LEFT OUTER\\|RIGHT OUTER\\|FULL OUTER] JOIN ... ON ...` with table aliases and qualified `t.col` references; single-column `ORDER BY [ASC\\|DESC]` (also resolves alias and aggregate display names); `LIMIT n`. `WHERE col = literal` probes an index when one exists |\n| `UPDATE` | Multi-column `SET`; `WHERE`; UNIQUE + type enforcement; arithmetic in assignments (`SET age = age + 1`) |\n| `DELETE` | `WHERE` predicate or full-table delete |\n| `BEGIN` / `COMMIT` / `ROLLBACK` | Real transactions, snapshot-based; WAL-backed commit; single-level (no savepoints); auto-rollback if `COMMIT`'s disk write fails |\n| `PRAGMA auto_vacuum` | Read (`PRAGMA auto_vacuum;`) returns the trigger threshold as a single-row result set; set (`PRAGMA auto_vacuum = 0.5;` / `= OFF;` / `= NONE;`) tunes or disables auto-VACUUM at the SQL layer for SDK / FFI / MCP consumers |\n\nExpressions in `WHERE` and `UPDATE`'s `SET` RHS:\n\n- Comparisons — `=`, `\u003c\u003e`, `\u003c`, `\u003c=`, `\u003e`, `\u003e=`\n- Null tests — `IS NULL`, `IS NOT NULL`\n- Pattern matching — `LIKE`, `NOT LIKE`, `ILIKE` (`%` and `_` wildcards, `\\`-escaped literals; case-insensitive ASCII to match SQLite's default)\n- Set membership — `IN (list)`, `NOT IN (list)` (literal lists only; subquery form is not supported yet)\n- Logical — `AND`, `OR`, `NOT` (SQL three-valued logic; NULL-as-false in `WHERE`)\n- Arithmetic — `+`, `-`, `*`, `/`, `%` (integer ops stay integer; any `REAL` promotes to `f64`; divide/modulo by zero is a clean error)\n- String concat — `||`\n- Literals — integer + real numbers, `'single-quoted strings'`, `TRUE` / `FALSE`, `NULL`; parentheses for grouping\n\n**Not yet supported** (common ones): subqueries, CTEs, `HAVING`, `LIKE … ESCAPE '\u003cchar\u003e'`, `IN (subquery)`, `DISTINCT` on `SUM`/`AVG`/`MIN`/`MAX`, GROUP BY on expressions, expressions in the projection list, `OFFSET`, multi-column `ORDER BY`, savepoints, `JOIN ... USING`, `NATURAL JOIN`, `CROSS JOIN`, comma joins, aggregates / DISTINCT / GROUP BY *over* JOIN results. The [full list with context](docs/supported-sql.md#not-yet-supported) lives in the reference.\n\n#### Meta commands\n\n| Command | Status |\n|---|---|\n| `.help` | working |\n| `.exit` | working |\n| `.open FILENAME` | working — opens an existing `.sqlrite` file or creates a fresh one; **auto-save is enabled from this point on** |\n| `.save FILENAME` | working — explicit flush (rarely needed once `.open` is in play) |\n| `.tables` | working |\n| `.ask \u003cQUESTION\u003e` | working — natural-language → SQL via the configured LLM (Anthropic by default). Prints generated SQL + rationale, prompts `Run? [Y/n]`, runs through the same pipeline as a typed statement on confirm. Requires `SQLRITE_LLM_API_KEY` in the environment. *Phase 7g.2.* |\n| `.read FILENAME` | later |\n| `.ast QUERY` | later |\n\n#### Natural-language → SQL (`sqlrite-ask`)\n\n*Phase 7g.1 / 7g.2.* The companion crate [`sqlrite-ask`](sqlrite-ask/) — pure-Rust over `\u0026str` schemas + questions — provides the LLM-talking machinery. The engine integrates it via the `ask` feature (default-on), exposing `Connection::ask` for ergonomic library use:\n\n```toml\n[dependencies]\nsqlrite-engine = \"0.1\"\nsqlrite-ask    = \"0.1\"\n```\n\n```rust\nuse sqlrite::{Connection, ConnectionAskExt};\nuse sqlrite_ask::AskConfig;\n\nlet conn = Connection::open(\"foo.sqlrite\")?;\nlet cfg  = AskConfig::from_env()?;          // SQLRITE_LLM_API_KEY etc.\nlet resp = conn.ask(\"How many users are over 30?\", \u0026cfg)?;\nprintln!(\"Generated SQL: {}\", resp.sql);\nprintln!(\"Why: {}\",          resp.explanation);\n// Caller decides whether to run resp.sql — the library deliberately doesn't.\n```\n\n**Defaults:** `claude-sonnet-4-6`, `max_tokens: 1024`, schema dump cached for 5 minutes via Anthropic prompt caching (configurable to 1h or off via `AskConfig::cache_ttl`). Bring your own API key — set `SQLRITE_LLM_API_KEY` or pass it on `AskConfig`.\n\nIn the REPL: `.ask \u003cquestion\u003e`. From an open `Connection` (this section). Per-product wrappers shipped across **7g.3 – 7g.8**: the desktop \"Ask…\" composer, `conn.ask()` / `db.ask()` in the Python / Node / Go SDKs, the WASM SDK's split `db.askPrompt()` / `db.askParse()` shape (so the API key never enters the browser), and the MCP `ask` tool exposed by [`sqlrite-mcp`](docs/mcp.md) (so any LLM agent over MCP can call `ask` against your database directly). See [`docs/phase-7-plan.md`](docs/phase-7-plan.md) §7g for the full surface plan.\n\nFor the canonical Ask reference covering every surface (REPL, desktop, Rust library, Python / Node / Go / WASM), env vars, defaults, prompt caching, and the security model — read [`docs/ask.md`](docs/ask.md). For copy-paste backend proxy templates the WASM SDK needs (Cloudflare Workers, Vercel Edge, Deno Deploy, Firebase, AWS Lambda, Express), see [`docs/ask-backend-examples.md`](docs/ask-backend-examples.md).\n\n### Roadmap\n\nThe project is staged in phases, each independently shippable. A finished phase is committed to `main` before the next one starts.\n\n**Phase 0 — Modernization** *(done)*\n- [x] Rust edition 2024, resolver 3, stable toolchain pinned via `rust-toolchain.toml`\n- [x] Upgrade every dependency to current majors: `rustyline` 18, `clap` 4, `sqlparser` 0.61, `thiserror` 2, `env_logger` 0.11, `prettytable-rs` 0.10, `serde` / `log` latest\n\n**Phase 1 — SQL execution surface** *(done)*\n- [x] CLI + rustyline REPL with history, syntax highlighting, bracket matching, line validation\n- [x] Parsing via `sqlparser` (SQLite dialect); typed `SQLRiteError` via `thiserror`\n- [x] `CREATE TABLE` with `PRIMARY KEY`, `UNIQUE`, `NOT NULL`; duplicate-column detection; in-memory `BTreeMap` indexes on PK/UNIQUE columns\n- [x] `INSERT` with auto-ROWID for `INTEGER PRIMARY KEY`, UNIQUE enforcement, NULL padding for missing columns\n- [x] `SELECT` — projection, `WHERE`, `ORDER BY`, `LIMIT`\n- [x] `JOIN` — `INNER`, `LEFT OUTER`, `RIGHT OUTER`, `FULL OUTER` with `ON` (SQLR-5)\n- [x] `UPDATE ... SET ... WHERE ...` with type + UNIQUE enforcement at write time\n- [x] `DELETE ... WHERE ...`\n- [x] Expression evaluator: `=`/`\u003c\u003e`/`\u003c`/`\u003c=`/`\u003e`/`\u003e=`, `AND`/`OR`/`NOT`, arithmetic `+`/`-`/`*`/`/`/`%`, string concat `||`, NULL-as-false in `WHERE`\n- [x] Replaced every `.unwrap()` panic on malformed input with typed errors\n\n**Phase 2 — On-disk persistence** *(done)*\n- [x] Single-file database format — one `.sqlrite` file per database\n- [x] Fixed 4 KiB pages; page 0 carries a header (magic `SQLRiteFormat\\0\\0\\0`, format version, page size, page count, schema-root page)\n- [x] Typed payload pages (schema-root / table-data / overflow) chained via `next`-page pointers; payloads up to 4089 bytes before spilling into overflow\n- [x] Schema catalog + per-table state serialized via `bincode` 2.0\n- [x] `.open FILENAME` — create-or-load a database file\n- [x] `.save FILENAME` — explicit flush of the in-memory DB to disk (auto-save arrives with Phase 3's pager)\n- [x] `.tables` — list tables in the current database\n- [x] Header written last during save, so a mid-save crash leaves the file recognizably unopenable\n\n**Phase 3 — On-disk B-Tree + auto-save pager** *(done)*\n- [x] **3a — Auto-save**: every committing SQL statement (`CREATE` / `INSERT` / `UPDATE` / `DELETE`) against a file-backed DB auto-flushes; `.save` is now a rare manual flush\n- [x] **3b — Pager abstraction**: long-lived `Pager` holding a byte snapshot of every page on disk plus a staging area for the next commit; `commit` diffs staged vs. snapshot and writes only pages whose bytes actually changed; file truncates when the page count shrinks\n- [x] **3c — Cell-based pages** *(format v2)*: rows stored as length-prefixed cells (tag-then-value encoding with null bitmap) in `TableLeaf` pages carrying a SQLite-style slot directory; oversized cells spill into an overflow page chain; the schema catalog itself is now a real table named `sqlrite_master` stored in the same cell format\n- [x] **3d — B-Tree**: `InteriorNode` pages above the existing leaves; save rebuilds the tree bottom-up from the in-memory sorted rows; open descends to the leftmost leaf and scans forward via the sibling `next_page` chain. Interior cells share the `cell_length | kind_tag | body` prefix with local/overflow cells so binary search over slot directories works uniformly. Cursor / lazy-load reads deferred to Phase 5.\n- [x] **3e — Secondary indexes** *(format v3)*: UNIQUE/PRIMARY KEY columns get an auto-index named `sqlrite_autoindex_\u003ctable\u003e_\u003ccol\u003e` at CREATE TABLE time; `CREATE [UNIQUE] INDEX name ON table (col)` adds explicit single-column indexes. `sqlrite_master` gains a `type` column distinguishing `'table'` rows from `'index'` rows. Each index persists as its own cell-based B-Tree using `KIND_INDEX` cells `(rowid, value)`. Executor optimizer probes indexes for `WHERE col = literal` (and `literal = col`) instead of full-scanning.\n\n**Phase 2.5 — Tauri 2.0 desktop app** *(done)*\n- [x] **Engine split into lib + bin** (pulled forward from Phase 5): `sqlrite` is now both a library and a binary. The Tauri app and the eventual WASM / FFI targets all import the engine as a regular Rust dependency.\n- [x] **Thread-safe engine**: `Table`'s row storage switched from `Rc\u003cRefCell\u003c_\u003e\u003e` to `Arc\u003cMutex\u003c_\u003e\u003e` so `Database` is `Send + Sync` and can live inside Tauri's shared state. The serde derives on storage types (dead since 3c.5) dropped at the same time.\n- [x] **Workspace**: root `Cargo.toml` is now a Cargo workspace; `desktop/src-tauri/` is the second member.\n- [x] **Tauri 2.0 backend**: four commands (`open_database`, `list_tables`, `table_rows`, `execute_sql`) wrap the engine; results are tagged enums shipped to the UI via the JSON IPC bridge.\n- [x] **Svelte 5 frontend**: dark-themed three-pane layout — header with \"Open…\" file picker, sidebar with table list + schema, query editor with Cmd/Ctrl+Enter to run, result grid with sticky header.\n\n**Phase 4 — Durability and concurrency** *(done)*\n- [x] **4a — Exclusive file lock**: `Pager::open` / `::create` takes an OS advisory lock (`fs2::try_lock_exclusive`); a second process on the same file gets a clean \"already in use\" error. Lock releases automatically when the Pager drops.\n- [x] **4b — Write-Ahead Log (`\u003cdb\u003e.sqlrite-wal`) file format + frame codec**: 32-byte WAL header (magic / version / page size / salt / checkpoint seq), 4112-byte frames carrying `(page_num, commit_page_count, salt, checksum, body)`. Rolling-sum checksum. Torn-write recovery: corrupt or partial trailing frames are silently truncated at the boundary. Standalone module; not wired yet.\n- [x] **4c — WAL-aware Pager**: `Pager::open` / `::create` now own both the main file and its `-wal` sidecar. Reads resolve `staged → wal_cache → on_disk` with a page-count bounds check; commits append a WAL frame per dirty page plus a final commit frame carrying the new page 0 (encoded header). The main file stays frozen between checkpoints — reopening replays the WAL and the decoded page-0 frame overrides the (stale) main-file header.\n- [x] **4d — Checkpointer**: `Pager::checkpoint()` folds WAL-resident pages into the main file, rewrites the header, truncates the tail, fsyncs, then `Wal::truncate`s the sidecar (rolling the salt). Auto-fires from `commit` past a 100-frame threshold; also callable explicitly. Crash-safe and idempotent — a crash mid-checkpoint leaves the WAL as the source of truth, so reads stay correct and a retry rewrites the same bytes.\n- [x] **4e — Multi-reader / single-writer**: new `AccessMode { ReadWrite, ReadOnly }` drives lock mode. `Pager::open_read_only` takes a shared lock (`flock(LOCK_SH)`) on both the main file and the WAL; `open` / `create` stay exclusive. Multiple RO openers coexist; any writer excludes all readers (POSIX flock semantics — \"multiple readers OR one writer\", not both). Read-only Pagers reject writes with a typed error. REPL gained a `--readonly` flag; library exposes `sqlrite::open_database_read_only`. Read marks aren't needed under flock — a writer can't coexist with readers, so the checkpointer never pulls frames out from under them.\n- [x] **4f — Transactions (`BEGIN` / `COMMIT` / `ROLLBACK`)**: `BEGIN` snapshots the in-memory tables (`Table::deep_clone`) and suppresses auto-save; every subsequent mutation stays in memory. `COMMIT` flushes accumulated changes in one `save_database` call (one WAL commit frame for the whole transaction). `ROLLBACK` restores the pre-BEGIN snapshot. Nested begins, orphan commits/rollbacks, and BEGIN on read-only DBs all return typed errors. Errors mid-transaction keep the transaction open so the caller can explicitly recover.\n\n**Phase 5 — Embedding surface: public API + language SDKs**\n- [x] **5a — Public Rust API**: `Connection` / `Statement` / `Rows` / `Row` / `OwnedRow` / `FromValue` / `Value` at the crate root; structured row return from the executor; `examples/rust/quickstart.rs` runnable via `cargo run --example quickstart`. **SQLR-23 — parameter binding + plan cache:** `Connection::prepare_cached` (default 16-entry LRU), `Statement::query_with_params(\u0026[Value])`, `Statement::execute_with_params(\u0026[Value])`. The cached AST skips re-running sqlparser per execute; `?` placeholders bind via positional `\u0026[Value]`. `Value::Vector(Vec\u003cf32\u003e)` is a first-class bind type so HNSW-eligible KNN queries skip per-iter lexing of the 4 KB query vector — and the HNSW optimizer hook still recognizes the bound vector. Cursor abstraction still deferred to 5a.2.\n- [x] **5b — C FFI shim**: new `sqlrite-ffi/` workspace crate ships `libsqlrite_c.{so,dylib,dll}` + a cbindgen-generated `sqlrite.h`. Opaque-pointer types, thread-local last-error, split `sqlrite_execute` (DDL/DML/transactions) vs `sqlrite_query`/`sqlrite_step` (SELECT iteration). Runnable `examples/c/hello.c` + `Makefile` (`cd examples/c \u0026\u0026 make run`).\n- [x] **5c — Python SDK**: new `sdk/python/` workspace crate via PyO3 (`abi3-py38`) + maturin. DB-API 2.0-inspired — `sqlrite.connect(path)` → `Cursor.execute` / `fetchall` / iteration, context-manager support (commit-on-clean-exit / rollback-on-exception), read-only connections, 16-test pytest suite. `examples/python/hello.py` runs after `maturin develop`. PyPI publish landed in Phase 6f as `sqlrite`.\n- [x] **5d — Node.js SDK**: new `sdk/nodejs/` workspace crate via napi-rs (N-API v9, Node 18+). Prebuilt `.node` binaries — no `node-gyp` install step. `better-sqlite3`-style sync API (`new Database(path)`, `stmt.all() / get() / iterate()` returning row objects), auto-generated TypeScript defs, 11 `node:test` integration tests. `examples/nodejs/hello.mjs` runs after `npm install \u0026\u0026 npm run build`. npm publish landed in Phase 6g as `@joaoh82/sqlrite` (scoped — npm rejected the unscoped `sqlrite` name as too similar to `sqlite`).\n- [x] **5e — Go SDK**: new `sdk/go/` module at `github.com/joaoh82/rust_sqlite/sdk/go`; cgo-wired against `libsqlrite_c` from Phase 5b. Implements the full `database/sql/driver` surface so users get the standard-library experience (`sql.Open(\"sqlrite\", path)`, `db.Query/Exec/Begin`, `rows.Scan(\u0026id, \u0026name)`). 9-test `go test` integration suite. `examples/go/hello.go` runs after `cargo build --release -p sqlrite-ffi`. Module publish landed in Phase 6i — `go get github.com/joaoh82/rust_sqlite/sdk/go@vX.Y.Z` resolves directly via VCS tag.\n- [ ] **5f — Rust crate polish** *(deferred — Phase 6c companion)*: crate metadata, docs.rs config, prep for `cargo publish`. Deferred to land alongside the actual publish workflow.\n- [x] **5g — WASM** build: new `sdk/wasm/` crate via `wasm-bindgen`; engine runs entirely in a browser tab. Feature-gated root crate (`cli` + `file-locks` optional, both default-on) so WASM disables fs2 / rustyline / clap / env_logger cleanly. `Database` class with `exec/query/columns/inTransaction`; rows as plain JS objects in projection order. ~1.8 MB wasm / ~500 KB gzipped. Three `wasm-pack` targets (web/bundler/nodejs). `examples/wasm/` ships a self-contained HTML SQL console.\n- [x] Code examples for every language under `examples/{rust,python,nodejs,go,wasm,c}/`\n\n**Phase 6 — Release engineering + CI/CD**\nLockstep versioning — one dispatch bumps every product to the same `vX.Y.Z`. Two-workflow design: `release-pr.yml` opens a Release PR with the version bumps (human reviews + merges), then `release.yml` fires on merge to tag + publish everything. Trusted-publishing via OIDC for PyPI + npm (no long-lived tokens). Full plan: [`docs/release-plan.md`](docs/release-plan.md).\n\n- [x] **6a — Bump script**: `scripts/bump-version.sh` rewrites the version string in ten manifests (7 TOML, 3 JSON) in a single pass; semver-validated, idempotent, cross-platform (BSD + GNU sed). Runnable locally for rehearsing a release: `./scripts/bump-version.sh 0.2.0 \u0026\u0026 cargo build \u0026\u0026 git diff`.\n- [x] **6b — CI**: `.github/workflows/ci.yml` runs on every PR + push to main. Seven parallel jobs: `rust-build-and-test` (Linux/macOS/Windows × cargo build + test), `rust-lint` (fmt + clippy + doc), `python-sdk` (Linux/macOS/Windows × maturin develop + pytest in a venv), `nodejs-sdk` (Linux/macOS/Windows × napi build + node --test), `go-sdk` (Linux/macOS × cargo build sqlrite-ffi + go test), `wasm-build` (wasm-pack + size report), `desktop-build` (npm ci + Tauri Rust compile). Cargo / npm / pip caching for fast PR turnaround.\n- [x] **6c — Trusted publisher setup + branch protection runbook**: [`docs/release-secrets.md`](docs/release-secrets.md) captures the one-time web-UI setup — crates.io token in the `release` environment, OIDC trusted publishers on PyPI (`sqlrite`) and npm (`@joaoh82/sqlrite` + `@joaoh82/sqlrite-wasm` — both scoped because npm's similarity check rejects the unscoped names against `sqlite`/`sqlite3`/`sqlite-wasm`), GitHub `release` environment with required reviewer, branch protection on `main` requiring 14 CI jobs + 1 review. No code changes — executable as-is, ready to run through in the GitHub + registry UIs.\n- [x] **6d — Release PR + skeleton publish**: two workflows under `.github/workflows/`. `release-pr.yml` (manual dispatch with version input → bump-version.sh → PR), `release.yml` (fires on `release: v\u003csemver\u003e` merge commit → `tag-all` + `publish-crate` + `publish-ffi` matrix [linux x86_64/aarch64, macOS aarch64, windows x86_64] + umbrella release). Idempotent tag creation so \"Re-run failed jobs\" works after partial failures. `cargo publish` gated by the `release` environment's required-reviewer rule. First canary: `v0.1.1`.\n- [x] **6e — Desktop publish**: `publish-desktop` matrix in `release.yml` — `tauri-action@v0` on ubuntu-22.04 (AppImage + deb + rpm), macos-latest (dmg aarch64 + .app tarball), windows-latest (msi + NSIS exe). Seven installer formats per release. Unsigned for now (Phase 6.1 wires up signing). Pre-generated icons committed to `desktop/src-tauri/icons/` keep CI deterministic.\n- [x] **6f — Python SDK publish**: 3-job design (`build-python-wheels` matrix → `build-python-sdist` → `publish-python` aggregator). `maturin-action` builds abi3-py38 wheels for Linux x86_64/aarch64 (manylinux2014), macOS aarch64, Windows x86_64 + an sdist. Atomic OIDC upload via `pypa/gh-action-pypi-publish` to `sqlrite` on PyPI. PEP 740 publish attestations attached automatically.\n- [x] **6g — Node.js SDK publish**: 2-job design (`build-nodejs-binaries` matrix → `publish-nodejs` aggregator). `@napi-rs/cli` builds `.node` binaries per platform; `index.js` dispatcher selects the right one at require time. OIDC publish to npm as `@joaoh82/sqlrite` with sigstore-signed provenance. Painful three-iteration debug to land the OIDC dance — see [docs/release-secrets.md](docs/release-secrets.md) §3 for the playbook the next person should use.\n- [x] **6h — WASM publish**: `wasm-pack build --target bundler --scope joaoh82` + `npm publish --provenance` (OIDC) → `@joaoh82/sqlrite-wasm` on npm. Single job, no matrix (WebAssembly is universal). `.wasm` also attached to the `sqlrite-wasm-v\u003cV\u003e` GitHub Release.\n- [x] **6i — Go SDK publish**: no registry — `sdk/go/v\u003cV\u003e` git tag (Go modules pull straight from VCS), GitHub Release at that tag with the FFI tarballs from `publish-ffi` re-attached so Go users have one page with `go get` + cgo deps. `go get github.com/joaoh82/rust_sqlite/sdk/go@vX.Y.Z` resolves through proxy.golang.org as soon as the tag is pushed.\n\n**Phase 6.1 — Code signing** *(follow-up)*\n- [ ] macOS Apple Developer ID cert → `codesign` + `notarytool` in `tauri-action`\n- [ ] Windows code-signing cert → `signtool` in `tauri-action`\n\n**Phase 7 — AI-era extensions** *(7a–7h shipped; FTS deferred to Phase 8 — full plan in [`docs/phase-7-plan.md`](docs/phase-7-plan.md))*\n- [x] **7a — `VECTOR(N)` column type** *(v0.1.10)*: dense f32 vectors with bracket-array literal syntax (`[0.1, 0.2, ...]`); file format bumped to v4\n- [x] **7b — Distance functions** *(v0.1.11)*: `vec_distance_l2/cosine/dot` + `ORDER BY \u003cexpr\u003e LIMIT k` so KNN queries work end-to-end\n- [x] **7c — Bounded-heap top-k optimization** *(v0.1.12)*\n- [x] **7d — HNSW ANN index** *(v0.1.13–15, +SQLR-28)*: `CREATE INDEX … USING hnsw (col) [WITH (metric = '\u003cl2|cosine|dot\u003e')]`; recall@10 ≥ 0.95 at default `M=16, ef_construction=200, ef_search=50`; persisted as a `KIND_HNSW` cell tree, with the metric round-tripping through the synthesized `sqlrite_master` SQL\n- [x] **7e — JSON column type + path queries** *(v0.1.16)*: `JSON` / `JSONB` columns stored as canonical text; `json_extract` / `json_type` / `json_array_length` / `json_object_keys`; `$.key`, `[N]`, chained JSONPath subset\n- [x] **7g.1 — `sqlrite-ask` crate** *(v0.1.18)*: foundational natural-language → SQL via the [Anthropic API](https://docs.anthropic.com/) (Sonnet 4.6 by default), prompt-cached schema dump, sync `ureq` HTTP.\n- [x] **7g.2 — REPL `.ask` + dep-direction flip** *(v0.1.19)*: `.ask \u003cquestion\u003e` meta-command with `Run? [Y/n]` confirmation. The wiring required dropping the engine dep from `sqlrite-ask` (cargo cycle) — `sqlrite-ask` is now pure over `\u0026str` schemas; the `Connection`/`Database` integration moved to the engine's new `ask` feature. Public surface for callers: `use sqlrite::{Connection, ConnectionAskExt}`.\n- [x] **7g.3 — Desktop \"Ask…\" button** *(this wave)*: composer panel above the editor textarea; click → type a natural-language question → submit → generated SQL drops into the editor for review before Run. Schema introspection + LLM HTTP call run in the Tauri Rust backend, so the API key never crosses into the webview.\n- [x] **7g.4 — Python SDK `conn.ask` / `ask_run` / `AskConfig`** *(v0.1.20)*: PyO3 wrappers; three-layer config precedence (per-call \u003e per-connection \u003e env \u003e defaults); `__repr__` omits the API key.\n- [x] **7g.5 — Node.js SDK `db.ask` / `askRun` / `AskConfig`** *(v0.1.21)*: napi-rs wrappers with idiomatic camelCase option-object; same three-layer precedence; mock HTTP test runs in `worker_thread` to avoid event-loop deadlock.\n- [x] **7g.6 — Go SDK `sqlrite.Ask` / `AskRun` / `AskConfig`** *(v0.1.22)*: cgo wrapper plus `AskContext` / `AskRunContext`; new C FFI function takes `AskConfig` as a JSON string for ABI extensibility.\n- [x] **7g.7 — WASM SDK `db.askPrompt` / `db.askParse` (Q9 split shape)** *(v0.1.23)*: browser builds the prompt + parses the response; user's backend holds the API key. Plus `docs/ask.md` (canonical Ask reference) and `docs/ask-backend-examples.md` (Cloudflare Workers / Vercel Edge / Deno Deploy / Firebase / AWS Lambda / Express templates).\n- [x] **7h — MCP server adapter (`sqlrite-mcp` binary)** *(this wave)*: hand-rolled JSON-RPC 2.0 over stdio; seven tools — `list_tables`, `describe_table`, `query`, `execute`, `schema_dump`, `vector_search`, plus `ask` as Phase **7g.8** behind a default-on cargo feature. Read-only mode hides `execute`. Stdout-redirect dance keeps engine prettytable noise off the protocol channel. See [`docs/mcp.md`](docs/mcp.md) for wiring into Claude Code / Cursor / `mcp-inspector`.\n- [ ] *(deferred to Phase 8)* Full-text search with BM25 + hybrid retrieval\n\n**Possible extras** *(no committed phase)*\n- Joins (`INNER`, `LEFT OUTER`, `CROSS` — SQLite does not support `RIGHT`/`FULL OUTER`)\n- `HAVING`, `IN (subquery)`, `BETWEEN`, `GLOB` / `REGEXP`, `GROUP_CONCAT`, window functions\n- Composite and expression indexes (with cost analysis)\n- Alternate storage engines — LSM/SSTable for write-heavy workloads alongside the B-Tree\n- Benchmarks against SQLite\n\n### Contributing\n**Pull requests are warmly welcome!!!**\n\nFor major changes, please [open an issue](https://github.com/joaoh82/rust_sqlite/issues/new) first and let's talk about it. We are all ears!\n\nIf you'd like to contribute, please fork the repository and make changes as you'd like and shoot a Pull Request our way!\n\n**Please make sure to update tests as appropriate.**\n\nIf you feel like you need it go check the GitHub documentation on [creating a pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request).\n\n### Code of Conduct\n\nContribution to the project is organized under the terms of the\nContributor Covenant, the maintainer of SQLRite, [@joaoh82](https://github.com/joaoh82), promises to\nintervene to uphold that code of conduct.\n\n### Contact\n\nIf you want to contact me you can reach me at \u003cjoaoh82@gmail.com\u003e.\n\n##### Inspiration\n* https://cstack.github.io/db_tutorial/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoaoh82%2Frust_sqlite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoaoh82%2Frust_sqlite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoaoh82%2Frust_sqlite/lists"}