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 forbetter-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(withIF NOT EXISTS,TEMP, per-column and table-level constraints,DEFAULTexpressions,CHECK,COLLATE, andGENERATED 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) andDROP INDEX,CREATE VIEW/DROP VIEW, andCREATE TRIGGER/DROP TRIGGER(BEFORE/AFTER/INSTEAD OF, row bodies,WHENguards). - Queries.
SELECTwith projection and aliases,WHERE,GROUP BY,HAVING,DISTINCT,ORDER BY(by column, alias, or ordinal), andLIMIT/OFFSET;INNER,LEFT, andCROSSjoins withONandUSING, including joins onto derived tables and table-valued functions; scalar subqueries,IN (SELECT ...), andEXISTS; common table expressions includingWITH RECURSIVE; the set operationsUNION,UNION ALL,INTERSECT, andEXCEPT; and window functions overOVER (PARTITION BY ... ORDER BY ...)with explicit frames. - Data modification.
INSERT(multi-rowVALUES,INSERT OR IGNORE/OR REPLACE, fullON CONFLICT ... DO NOTHING/DO UPDATEupsert, andRETURNING),UPDATE, andDELETE, both withRETURNING. - Transactions.
BEGIN/COMMIT/ROLLBACK, and nestedSAVEPOINT/RELEASE/ROLLBACK TO. - Other.
ATTACH/DETACH DATABASE, and the commonPRAGMAset (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:
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/ROLLBACKplus savepoints), not concurrent-writer MVCC. - SQL not covered.
NATURALjoins, and multi-joinRIGHT/FULL OUTER(a singleRIGHT/FULL OUTERjoin is emulated in the ORM path by rewrite —RIGHT→swappedLEFT,FULL→LEFT UNION ALL LEFT);EXPLAIN;CREATE VIRTUAL TABLE(so no FTS or R-Tree) and loadable extensions; thePERCENT_RANKandCUME_DISTwindow functions; andSTRICTtable mode. - File-format writer. Writing emits UTF-8 rowid tables; it does not yet emit index B-trees,
WITHOUT ROWIDtables, 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, andStatement.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-sqlite3and the bundled C library remain the choice for production and for opening genuinely untrusted database files.