AI AgentRetrieval & Knowledge Systems

SQLite Hides a Virtual Machine: Turso Turned It Into the LLVM of Databases

SQLite Hides a Virtual Machine Inside

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
VDBE bytecode execution model: On the left, a flat linear bytecode loop (addr + opcode + registers + while(1) switch); on the right, classic Volcano model tree-structured operator recursion for comparison.

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.

Why We Need to Compile Postgres to SQLite’s 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.

From Implementation Detail to Public Abstraction

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.

The LLVM of databases: On the left, LLVM compiler (C++/Rust/Swift frontends → LLVM IR → x86/ARM backends); on the right, Turso database (SQLite frontend / Postgres frontend pgmicro → VDBE bytecode → unified storage engine), with two IR middle layers aligned through a public abstraction.

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.

Playing Games Inside a Database VM: A Real Signal of a Universal Compilation Path

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.

The Real Hard Nut to Crack Is the Extension Ecosystem

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.

Turning Internal Details Into Public Infrastructure

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.