Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SQL frontend & executor

Above the storage and transaction layers sits the part users actually touch: SQL. ferrodb parses and executes SQL with no sqlparser crate — a hand-written lexer and a Pratt parser feed an AST that a volcano-style executor runs over the B+-trees.

Lexer

The lexer turns the raw SQL string into a stream of tokens — keywords, identifiers, string and number literals, operators, punctuation. It is a straightforward character scanner; the only subtleties are case-insensitive keywords and distinguishing - the operator from a negative number.

Pratt parser

Expressions are parsed with a Pratt parser (top-down operator precedence). Each operator has a binding power; the parser reads a prefix (a literal, a column, a parenthesized group, a function call) and then, while the next operator binds tighter than the current context, folds it in. This handles a + b * c, NOT x AND y, comparison chains, and qualified column references (t.col) cleanly, without a precedence-climbing table of special cases. Statements (SELECT, INSERT, CREATE, UPDATE, DELETE) are parsed by recursive descent around the expression parser.

The catalog

The database is self-describing: table definitions live in a catalog stored in the same file, in its own B+-tree keyed by table name. A TableSchema records the columns and their types, the root page of the table's data tree, the next row id, and (as of M8) a row_count statistic for the optimizer. CREATE TABLE writes a catalog entry; every query reads the schema back out of it.

Types and three-valued logic

ferrodb has four types — INTEGER, REAL, TEXT, BOOLEAN — and, like SQL, a first-class NULL. Comparisons and boolean operators follow three-valued logic: NULL = NULL is not true but NULL, NULL AND false is false, NULL AND true is NULL. The evaluator implements these tables so WHERE filters and expressions behave as SQL requires rather than treating NULL as a sentinel value.

The volcano executor

Execution is a tree of operators, each of which produces rows. In M2 this is a simple pipeline: scan a table, filter rows by WHERE, sort by ORDER BY, project the selected columns, apply LIMIT/OFFSET. Each operator pulls from its child — the classic volcano model. INSERT encodes a tuple and puts it into the table's B+-tree; UPDATE and DELETE scan, match, and rewrite version chains (Chapter 5).

This clean operator model is what M5 builds on: the optimizer's job is to choose which operators to assemble and in what order, without changing how any single operator works.