A small, idiomatic embedded SQL database for Go.
RovaDB is a Go-first embedded relational database engine built for clarity, portability, and long-term extensibility. It is intended to feel natural to Go developers, stay understandable to contributors, and grow without boxing itself into a dead-end architecture.
CREATE TABLEINSERT INTO ... VALUESSELECT over a single table with projection, WHERE, ORDER BY, and COUNT(*)UPDATEDELETEALTER TABLE ... ADD COLUMN? in Exec, Query, and QueryRowINT, TEXT, BOOL, REAL, and NULLCREATE TABLEALTER TABLE <table> ADD COLUMN <column> <type>INSERT INTO ... VALUES (...)SELECT ...UPDATE ... SET ...DELETE FROM ...SELECT 1 and SELECT TRUEFROMAND / OR evaluation= != < <= > >=ORDER BY and COUNT(*)JOIN, GROUP BY, HAVINGCOUNT(*)CREATE INDEX SQLINT, TEXT, BOOL, and REAL.
The public API supports one-shot positional argument binding on
Exec(query string, args ...any),
Query(query string, args ...any), and
QueryRow(query string, args ...any).
Use ? placeholders only. Binding is positional, left-to-right, and the argument count must match exactly.
db, err := rovadb.Open("app.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
if _, err := db.Exec("CREATE TABLE users (id INT, name TEXT, active BOOL)"); err != nil {
log.Fatal(err)
}
if _, err := db.Exec("INSERT INTO users VALUES (?, ?, ?)", 1, "Alice", true); err != nil {
log.Fatal(err)
}
rows, err := db.Query("SELECT id, name FROM users WHERE active = ? ORDER BY id", true)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
The repository includes a fuller example that covers open, write, close, reopen, and query flow.
RovaDB exists to provide a pure Go embedded relational database with practical SQL support. It is not trying to outcompete mature databases on breadth or raw performance. The goal is to offer a practical embedded SQL engine for Go that is straightforward to understand, realistic to adopt, and structured to grow carefully over time.
RovaDB grows around these major layers:
The intended execution path is:
SQL -> AST -> Bound AST -> Logical Plan -> Physical Plan -> Executor
Even in a deliberately small engine, those boundaries matter. They reduce coupling and make it possible to add features later without tearing the engine apart.
Go developers who want an embedded SQL database with a straightforward mental model and a clean integration story.
Engineers who want to work on a real database engine in Go without needing to wade into an enormous, opaque codebase.