Open SQLite in your terminal and run a simple conditional query
prefixed with EXPLAIN:
sqlite> EXPLAIN SELECT name FROM users WHERE id <= 10;What prints out in the console isn’t a familiar tree-structured parse tree, but a sequence of assembly-like instructions complete with addresses and registers:
addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- ------------- -- -------------
0 Init 0 9 0 0 Start at 9
1 OpenRead 0 2 0 2 0 root=2 iDb=0; users
2 Rewind 0 8 0 0
3 Column 0 0 1 0 r[1]= cursor 0 column 0
4 Gt 2 7 1 BINARY-8 84 if r[1]>r[2] goto 7
5 Column 0 1 3 0 r[3]= cursor 0 column 1
6 ResultRow 3 1 0 0 output=r[3]
7 Next 0 3 0 1
8 Halt 0 0 0 0
9 Transaction 0 0 1 0 1 usesStmtJournal=0
10 Integer 10 2 0 0 r[2]=10
11 Goto 0 1 0 0
Once these lines appear, how SQLite actually works becomes
immediately obvious. Instruction 1 (OpenRead) opens the
B-Tree table with root node 2. Instruction 2 (Rewind)
resets the cursor to the beginning. Instruction 3 (Column)
retrieves id into register r[1]. Instruction 4
(Gt) compares values: if id exceeds 10, it
jumps to instruction 7 (Next) to skip the row. If not, it
fetches the name column into register r[3],
emits the result via ResultRow, and calls Next
to continue the loop.
Anyone who has built databases knows that physical executors in most
SQL engines are structured as operator trees. For the same query, a
classic engine parses the AST and builds a physical execution plan tree:
Project(name) -> Filter(id <= 10) -> SeqScan(users).
At runtime, following the famous Volcano iterator model, it recursively
invokes next() top-down, flowing data upward layer-by-layer
from the bottom scan node.
SQLite takes an entirely different path. Instead of recursively
traversing an operator tree at runtime, it behaves like a modern
compiler, translating SQL statements directly into a linear bytecode
stream. This code contains a program counter (addr),
opcodes (OpenRead, Le, Goto),
registers (r[1]), and conditional branches. At runtime, the
SQLite kernel effectively runs inside a flat loop:
while(1) { switch(opcode) { ... } }.
The EXPLAIN QUERY PLAN command we commonly use only
shows high-level strategies (such as which index was chosen). Using
EXPLAIN directly exposes the underlying bytecode
instructions.
This hidden engine is called the Virtual Database Engine (VDBE). Ever since Richard Hipp began writing SQLite in 2000, it has been silently running inside the kernel. Yet over the past 25 years, Hipp and the community treated it as an unexposed internal implementation detail—never writing a public standard or opening up its ABI. Developers grew accustomed to interacting with it through the text-based SQL interface, gradually forgetting that deep inside its core laid a fully functional virtual machine.
Since SQLite is fundamentally a Turing-complete virtual machine under the hood, what real-world problem can it solve for us today? This directly addresses a dilemma almost every team faces when building AI Agents and Local-first applications.
On the developer experience and ecosystem side, developers naturally lean toward Postgres. It boasts rich SQL syntax, a powerful type system, and out-of-the-box compatibility with nearly every ORM, migration tool, and framework. But on the deployment and operations side, developers crave SQLite’s physical properties: sub-millisecond cold starts, single-file per-tenant isolation, negligible memory footprint, and the ability to compile directly into WebAssembly to run inside the browser—making it trivial to scale to tens of thousands of isolated databases.
In the past, combining the two meant translating SQL dialects at the outermost text level. For instance, when receiving a Postgres SQL string, a middle layer would rewrite it into SQLite SQL via regex or AST manipulation before passing it along. Anyone who has built complex systems knows how painful this approach is. Scalar function names mismatch, built-in type behaviors diverge, and implicit type coercion introduces subtle inconsistencies. String translation is fragile and cumbersome; catering to edge-case syntax adds extra parsing overhead, quickly hitting a wall of semantic impedance mismatch.
However, looking past the outer SQL text directly into the database kernel reveals that Postgres and SQLite perform virtually identical low-level operations in memory and on disk: both ultimately schedule B-Tree nodes, data pages, and index cursors. The tension and conflict reside entirely in surface SQL syntax; the underlying data structures and storage scheduling speak fundamentally the same language.
So why bother with tedious string translation at the outermost layer? If you compile Postgres’s parsed Abstract Syntax Tree (AST) directly into VDBE bytecode instructions at the compiler layer, everything falls into place. Sharing a single backend base at the instruction and B-Tree scheduling level grants the upper layer full Postgres dialect and ecosystem compatibility, while preserving SQLite’s embedded, lightweight, single-file isolation benefits at the lower layer.
Taking the idea of “compiling Postgres into VDBE instructions” one step further leads to the core architectural move in Turso: taking SQLite’s 25-year-old internal implementation detail and formalizing it into an exposed, public middle layer.
In their July 2026 Official Newsletter, Turso explicitly articulated this vision for the first time: they are becoming the LLVM of databases, featuring a modern, reliable core capable of compiling and running various database frontends. SQLite is their first frontend, and initial code rewriting Postgres has already been merged.
Anyone familiar with compiler design will recognize this architecture. What LLVM accomplished was decoupling frontend parsers for C++, Rust, and Swift from low-level target hardware like x86 and ARM. Turso applies the exact same concept to databases by decoupling into two layers: a pluggable syntax frontend responsible for parsing SQL dialects into ASTs, and a unified storage and VM core that accepts compiled VDBE bytecode and schedules the storage engine.
In their repository, a subproject called pgmicro validates this concept. Written in Rust, pgmicro directly parses Postgres SQL expressions and emits corresponding VDBE bytecode sequences, executing Postgres syntax directly on SQLite’s virtual machine kernel. In the official blog post A new, modern version of Postgres in Rust, the team detailed how they rewrote Postgres syntax parsing in Rust to target the underlying VM; meanwhile, Concurrent writes on Turso Cloud complemented this with their cloud-side concurrent writing architecture, expanding the core’s capabilities.
If compiling Postgres to VDBE is merely bridging database dialects, a natural question arises: how general-purpose is the computational power of this nameless virtual machine? Turso previously demonstrated a viral community project that provided a striking answer: running Doom directly inside the database engine kernel, open-sourced at turso-vdbe-doom-example.
At first glance, running a game inside a database looks like a flashy gimmick. But peeling back the underlying compilation toolchain reveals a hard-core, general-purpose computing pipeline:
C 游戏源码 (doomgeneric)
└── clang -O2 -emit-llvm
└── LLVM IR (.ll)
└── vdbecc
└── VDBE 字节码
The crucial point is that the custom compiler vdbecc
doesn’t consume custom Doom syntax—it consumes standard LLVM IR files.
This means any language (C, C++, Rust, Swift) capable of compiling into
LLVM IR can naturally be translated into VDBE bytecode through this
toolchain. Running a game inside a database kernel is essentially using
complex interactive logic to stress-test the VM’s ability to host
arbitrary C code.
Inspecting vdbecc’s implementation details reveals
clever engineering: the memory address space required at runtime is
mapped directly into a ram BLOB field within a single-row
database table named _vdbecc_mem. All pointer offsets and
memory read/writes are translated into VDBE’s BlobRead and
BlobWrite instructions. When rendering a frame, the C
function triggers vdbe_present, emitting a row of
ResultRow frame data and pausing the state machine, while
an outer loop invokes step() to advance to the next
frame.
This cleanly proves one fundamental point: VDBE is a Turing-complete
virtual machine. It is far more than a SQL interpreter handling
SELECT/INSERT; it is a robust runtime capable
of supporting arbitrary general-purpose computations.
Having verified VDBE’s general-purpose computing capabilities through running a game, returning to database core functionality reveals that while compiling Postgres syntax is elegant, taking it to production immediately hits a hard nut to crack: Postgres’s vast extension ecosystem (such as PostGIS and pgvector).
Postgres’s advantage has never been limited to SQL syntax alone—it lies in decades of rich C-based extensions. Two concepts must be distinguished here: Wire Protocol merely governs message framing between client and database, whereas Internal ABI determines whether extensions can actually execute. Native Postgres extensions directly read C kernel struct pointers and memory layouts.
To solve this challenge, Turso proposed compiling Postgres extensions into WebAssembly modules running inside the virtual machine. However, this approach remains in the PoC stage. Crossing WASM sandbox boundaries to access underlying data structures introduces noticeable performance overhead, and pragmatically, not every legacy C extension can be compiled into WebAssembly seamlessly.
Turso’s official product overview What is Turso and their Hacker News discussion offer a pragmatic assessment: the current architecture serves as an infrastructure foundation rather than an all-encompassing finished product, and they do not intend to blindly chase 100% compatibility with every Postgres edge case and legacy artifact. Developing an efficient extension ABI mechanism remains the true hurdle this vision must overcome.
Though the extension ABI hurdle remains, the evolutionary path from SQLite’s hidden VM to Turso bringing it front and center presents a clear trajectory. Looking back over 25 years—from Richard Hipp burying the VDBE deep inside the kernel for extreme simplicity in 2000, to Turso elevating it into the LLVM of databases today—what resonates most is a simple software engineering principle: when an internal implementation detail proves expressively powerful, formalizing and elevating it into a public abstraction often unlocks capabilities far beyond its original scope.
Elevating the hidden VDBE into a public intermediate layer decouples upper SQL dialects from low-level physical storage. Sharing B-Tree scheduling at the bytecode layer untangles the knot between Postgres developer experience and SQLite physical deployment characteristics, pointing toward a new database topology featuring pluggable frontends and general-purpose backend compute. How single-file lightweight databases handle state isolation in multi-agent architectures is explored in Part 2 of this series; how single-file containers support RAG vector retrieval is tested in Part 3 of this series.