toydb is a small, read-only SQLite file reader written in Rust. It is a
learning project for understanding how SQLite stores schemas, records, pages,
cells, and B-trees on disk.
ToyDB reads an existing SQLite database directly; it does not use SQLite as its query engine.
The current implementation supports a narrow, educational query path:
- SQLite database-header and
sqlite_schemaparsing - Table and index B-tree traversal
- Interior and leaf B-tree pages
- Overflow-page chains for large cell payloads
- SQLite record serial types, including integer, text, and real values
- Basic table scans and projections
- Simple indexed equality lookups for supported indexes
- Interactive
.dbinfoand.tablescommands .quitand.exitfrom the interactive prompt
The indexing path currently works best for simple equality lookups on existing rowid tables, especially text values, for example:
select * from test where name='Ava Martinez';ToyDB expects an existing SQLite database file:
cargo run --release -- test.dbThe interactive prompt uses $:
$ .dbinfo
$ .tables
$ select * from test;
$ select * from test where name='Ava Martinez';
$ .quit
The repository also contains SQLite fixtures under testdb/ for exploring
different page, record, overflow, and schema layouts.
For a supported indexed lookup, ToyDB follows this path:
- Read the schema and locate the table and its index.
- Search the index B-tree for the indexed value.
- Use the resulting rowid to search the table B-tree.
- Decode and print the matching table row.
The CLI output below is an example of the indexed lookup path working:
$ select * from test where name='Ava Martinez';
name | id | rollno | age | balance |
Ava Martinez | | 20260001 | 28 | 987.35
Total rows: 0
Total execution time: 1.351552ms
The displayed row confirms the lookup, although the current executor does not
increment Total rows for the indexed SearchByID path. That count is a known
small correctness issue.
The parser recognizes SELECT and CREATE statements, but the executor
currently executes only SELECT. CREATE is used when reading schema
definitions; ToyDB does not create or modify database files.
The practical query shape is currently close to:
select * from table_name;
select * from table_name where indexed_column='value';Current limitations include:
INSERT,UPDATE, andDELETEare not supported.- Only one simple equality
WHEREpredicate is supported. FROMparsing is case-sensitive and currently expects lowercasefrom.- Joins, expressions, aggregates, ordering, grouping, limits, offsets, and subqueries are not implemented.
- WITHOUT ROWID tables, views, triggers, and some automatically generated indexes are not supported by the query path.
- Malformed or unsupported database layouts may produce an error or panic; this is an experimental reader rather than a production database engine.
src/
├── btree.rs B-tree search helpers and public exports
├── btree/
│ ├── child.rs Table/index child-page traversal
│ ├── common.rs Shared B-tree payload types
│ └── root.rs Database header and schema-root handling
├── cell.rs SQLite cell parsing
├── commands.rs `.dbinfo` and `.tables`
├── custom_error.rs Page/parser error types
├── file.rs Database-file reads and initialization
├── file/
│ └── enums.rs File and encoding enums
├── page.rs Page headers, cells, and overflow chains
├── query.rs Query module exports
├── query/
│ ├── common.rs Query data structures and enums
│ ├── executor.rs Query execution and row retrieval
│ └── parser.rs SQL parsing
├── record_type.rs SQLite record values and conversions
├── schema.rs `sqlite_schema` parsing
├── utils.rs Byte parsing and input helpers
└── main.rs CLI entry point
- SQLite file format
- SQLite B-tree pages
- SQLite Internals: B-trees
- Exploring SQLite Internals
- SQLite representation visualizer
- SQLite internals visual guide
- SQLite limits
This project is for educational purposes.