CQuilite

CQuilite is Cruft's SQLite-compatible database engine behind cruft:sqlite and bun:sqlite. It has its own tokenizer, parser, executor, and function library, covers a broad SQLite dialect with type affinity and collation, and reads and writes the genuine sqlite3 on-disk file format so databases round-trip with the sqlite3 CLI and Bun.

CQuilite, Cruft's SQLite-compatible database engine (the rusty-sqlite crate), is the engine behind cruft:sqlite and bun:sqlite, and one of the two dialects on the SQL stack. The tokenizer, parser, executor, function library, and the on-disk file format are all Cruft's own Rust, over the shared sql-core relational executor. There is no libsqlite and no binding to the SQLite C library.

Alpha. CQuilite (Cruft 0.0.10) stands in for better-sqlite3 / rusqlite / bundled SQLite C. It is SQLite-compatible, matching SQLite's behavior on the surface it implements, not a complete reimplementation of SQLite. It is not production-ready; treat it as a young engine and do not rely on it as your only line of defense against adversarial SQL.

SQL surface

The engine covers a broad SQLite dialect:

  • Data definition. CREATE TABLE (with IF NOT EXISTS, TEMP, per-column and table-level constraints, DEFAULT expressions, CHECK, COLLATE, and GENERATED ALWAYS AS (...) stored and virtual columns), DROP TABLE, ALTER TABLE (ADD COLUMN, RENAME TO, RENAME COLUMN), CREATE INDEX / CREATE UNIQUE INDEX (including partial and expression indexes) and DROP INDEX, CREATE VIEW / DROP VIEW, and CREATE TRIGGER / DROP TRIGGER (BEFORE/AFTER/INSTEAD OF, row bodies, WHEN guards).
  • Queries. SELECT with projection and aliases, WHERE, GROUP BY, HAVING, DISTINCT, ORDER BY (by column, alias, or ordinal), and LIMIT/OFFSET; INNER, LEFT, and CROSS joins with ON and USING, including joins onto derived tables and table-valued functions; scalar subqueries, IN (SELECT ...), and EXISTS; common table expressions including WITH RECURSIVE; the set operations UNION, UNION ALL, INTERSECT, and EXCEPT; and window functions over OVER (PARTITION BY ... ORDER BY ...) with explicit frames.
  • Data modification. INSERT (multi-row VALUES, INSERT OR IGNORE / OR REPLACE, full ON CONFLICT ... DO NOTHING / DO UPDATE upsert, and RETURNING), UPDATE, and DELETE, both with RETURNING.
  • Transactions. BEGIN / COMMIT / ROLLBACK, and nested SAVEPOINT / RELEASE / ROLLBACK TO.
  • Other. ATTACH / DETACH DATABASE, and the common PRAGMA set (table_info, index_list, foreign_key_list, foreign_keys, user_version, journal_mode, integrity_check, and others).

Type affinity and collation

Values are the five SQLite storage classes, Null, Integer, Real, Text, and Blob. Column type affinity follows SQLite's rules exactly: the declared type text is scanned for the usual substrings (INT gives Integer affinity, CHAR / CLOB / TEXT give Text, REAL / FLOA / DOUB give Real, and so on), values are coerced on store accordingly, and text-to-number conversion uses SQLite's leading-numeric-prefix rule. Comparison is three-valued around NULL. Collation is a real axis: BINARY (the default), NOCASE, and RTRIM, settable per column and per expression with COLLATE.

Function library

The built-in functions are broad and match SQLite's behavior:

Scalar
substrreplacetrim/ltrim/rtriminstrroundabshex/unhexquotecharunicodesignnullififnullcoalescelengthupperlowertypeofprintf/formatcastconcatconcat_wsiifzeroblob
Math
powersqrtexplnlog/log10/log2modfloorceiltruncpithe full triginverse-trigand hyperbolic setatan2degreesradians
Date & time
datetimedatetimejuliandayunixepochstrftimetimediffwith the full modifier set (start of monthweekday N±N days/months/yearsunixepochutclocaltime)
JSON
jsonjson_extractjson_objectjson_arrayjson_validjson_typejson_set/insert/replacejson_removejson_patchjson_prettythe -> and ->> operatorsJSONPaththe table-valued json_each / json_treeand the binary jsonb family
Aggregates
countsumtotalavgminmaxgroup_concatjson_group_arrayjson_group_objectwith DISTINCTFILTER (WHERE ...)and ordered aggregates
Window
row_numberrankdense_rankntilefirst_valuelast_valuenth_valuelagleadand any aggregate as a window functionwith frames (ROWS/RANGE/GROUPSPRECEDING/FOLLOWINGEXCLUDE)

The date and time functions run on the engine's own calendar arithmetic, and the JSON functions include a JSONPath engine and both the text json1 surface and the binary jsonb surface. The engine reports its SQLite version as 3.45.0.

The real on-disk file format

The engine reads and writes the genuine sqlite3 on-disk format. A database it writes begins with the real SQLite format 3\0 header, and files round-trip against the sqlite3 CLI, Bun, and other SQLite tools in both directions: a file a Cruft program writes opens in DB Browser or the sqlite3 shell, and a file those tools write opens in Cruft.

On the read side it handles the full format that matters in practice: table B-trees and index B-trees (index B-trees being the storage for WITHOUT ROWID tables), overflow-page chains with cycle detection, all the record serial types, INTEGER PRIMARY KEY aliased to the rowid, the UTF-8, UTF-16LE, and UTF-16BE text encodings, and WAL-mode databases through a -wal sidecar overlay, so an un-checkpointed database whose contents live in the write-ahead log still reads correctly. On the write side it emits real SQLite images for UTF-8 rowid tables, building the header, sqlite_master, records, and a multi-page B-tree with overflow.

Constraints

NOT NULL, UNIQUE (column, table-level, and unique-index), PRIMARY KEY, CHECK, and DEFAULT are enforced at execution with SQLite-matching error text, and generated columns are computed on write. FOREIGN KEY constraints are enforced when PRAGMA foreign_keys = ON (SQLite's default-off is honored), with ON DELETE actions modeled. Constraint failures also drive the INSERT OR ... and ON CONFLICT resolution paths.

Parameters and prepared statements

All five SQLite parameter forms are supported, ?, ?N, :name, $name, and @name. prepare() returns a reusable statement, and the engine tracks changes(), last_insert_rowid(), and total changes. Integer values round-trip losslessly as JavaScript BigInt under safeIntegers, so values beyond 2^53 are preserved.

Reaching it from JavaScript

Two module surfaces drive the same engine, plus the ORM.

// bun:sqlite — the Bun-compatible API
import { Database } from "bun:sqlite";
const db = new Database("app.db");             // or ":memory:"
db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
db.query("SELECT * FROM users WHERE id = ?").get(1);    // one row
db.query("SELECT name FROM users ORDER BY id").all();   // all rows
db.prepare("INSERT INTO users (name) VALUES (?)").run("kay");
// { changes: 1, lastInsertRowid: 3 }
db.close();

The bun:sqlite surface covers Database (query, prepare, run, exec, transaction with .deferred/.immediate/.exclusive, serialize, safeIntegers, close, inTransaction, deserialize) and Statement (all, get, values, run, iterate, finalize, safeIntegers, and as(Class) for reparenting rows onto a class). The canonical cruft:sqlite surface is smaller: open(path?) returns a handle with query, run, and close. The ORM's openSqlite reaches the same engine and adds the row-soundness boundary.

Safety

The engine, the file-format reader, and the function library contain no unsafe. The parser caps expression nesting depth (both during recursion and with a pre-parse scan), so a deeply nested WHERE ((((...)))) is rejected with a catchable error rather than overflowing the stack. The file-format reader bounds-checks every read, detects cycles in overflow chains, caps B-tree depth, and returns errors on truncated or malformed pages rather than panicking. Parameter binding is fully separated from SQL text, and the ORM path additionally validates identifiers against the live catalog.

Limitations

  • Engine model. The executor walks a tree over fully materialized in-memory tables, with an equality-index fast path for point lookups but no cost-based query planner and no join reordering. There is no general row cap or cartesian-join memory bound, so a large cross join is limited only by available memory. Transactions are single-connection (BEGIN/COMMIT/ROLLBACK plus savepoints), not concurrent-writer MVCC.
  • SQL not covered. NATURAL joins, and multi-join RIGHT / FULL OUTER (a single RIGHT / FULL OUTER join is emulated in the ORM path by rewrite — RIGHT→swapped LEFT, FULLLEFT UNION ALL LEFT); EXPLAIN; CREATE VIRTUAL TABLE (so no FTS or R-Tree) and loadable extensions; the PERCENT_RANK and CUME_DIST window functions; and STRICT table mode.
  • File-format writer. Writing emits UTF-8 rowid tables; it does not yet emit index B-trees, WITHOUT ROWID tables, UTF-16 text, or WAL images. serialize() uses the engine's own compact format rather than the sqlite3 image writer.
  • JavaScript surface. There is no node:sqlite (DatabaseSync) surface yet, and Statement.iterate() returns a materialized iterable rather than a lazy cursor.
  • Conformance. It matches SQLite on the surface above, and where a behavior is not listed here, assume it may differ from SQLite. better-sqlite3 and the bundled C library remain the choice for production and for opening genuinely untrusted database files.