Building a query execution engine
From evaluating one expression against one row to a planner that reads statistics and compiles a query into bytecode you can pause, save, and resume.
A SQL query is a tree. Something has to turn that tree into rows. This series builds that something from scratch, in small steps, in both Go and Clojure. It's at the level of sqlglot's Python SQL engine: small enough to read in an afternoon, real enough that the ideas carry over to SQLite, Postgres, and DuckDB.
Every part has interactive figures. Step through them; they're the point.
Practice
Reading about it isn't the same as building it. A few things help it stick:
- Predict, then reveal. Figures ask for your guess before they show the answer. Being wrong first is the point.
- Build it. Every lesson's key functions with the bodies removed and the tests left in.
- In the wild. The same mechanisms in the source of real implementations, verbatim and linked to the exact lines. Open each post's section after its exercises.
- Review. A few spaced-repetition cards a day from the parts you've read. Each post opens with a warm-up from the one before.
The code
Each part has a standalone lesson in clojure/src/lessonNN and go/lessonNN with a
runnable demo; the capstone puts everything together. Or skip the install: the terminal runs the Go programs in your browser, compiled to WebAssembly.
The capstone is the integrated engine in clojure/src/engine and go/engine:
SQL text in, a parser, a planner that reads statistics, and two executors (iterators or bytecode)
that must agree.
cd clojure && clojure -M -m engine.main
cd go && go run ./cmd/engine