ozzo-dbx adds struct population and a query builder to database/sql
A Go (golang) package that enhances the standard database/sql package by providing powerful data retrieval methods as well as DB-agnostic query building capabilities.
At a glance
- What is it?
- ozzo-dbx is a Go package that sits on top of the standard database/sql package, adding struct and map population, named parameter binding, DB-agnostic query building and CRUD helpers. It suits a Go developer who wants SQL without a full ORM, and it is a poor fit for a project that needs a schema migration tool or automatic model mapping.
- Who is it for?
- ozzo-dbx is a good fit for a Go service that wants the readability of a query builder and the predictability of hand written SQL, especially with the same code running against SQLite for tests and PostgreSQL or MySQL in production. It is the wrong choice if you want an ORM with migrations, associations and automatic schema handling, because the readme says plainly that this is not an ORM and the feature list stops at struct and map population.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 50 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A data access helper, not an ORM, and the readme says so first
ozzo-dbx enhances the standard database/sql package with data retrieval methods and DB-agnostic query building, and the description rules out a category straight away. It is not an ORM. The feature list is deliberately short: populating data into structs and NullString maps, named parameter binding, DB-agnostic query building covering SELECT, data manipulation and schema manipulation queries, inserting, updating and deleting model structs, powerful query condition building, an open architecture for adding new database support or customising existing support, logging of executed SQL statements, and support for major relational databases. What is absent is as informative as what is present. There is no mapper between structs and tables, no association handling, no lazy loading, no identity map, no change tracking. You write the SQL or you build it, and the library handles the plumbing of getting rows into Go values. For a team that has been burned by an ORM's hidden queries, that restraint is the feature. For a team that wanted a mapper, it is the reason to look elsewhere.
Opening a connection the same way, with one important difference
The entry point mirrors the standard library so there is nothing new to learn. You call dbx.Open() the way you would call the Open method in database/sql:
db, err := dbx.Open("mysql", "user:pass@hostname/db_name")The returned value is a dbx.DB instance used to create and execute queries, and the driver key is the same string you would pass to sql.Open. One behaviour is worth knowing, because it can look like a bug. The method does not really establish a connection until a query is made using the returned instance, and it does not check the correctness of the data source name either. The reason is that database/sql opens connections lazily, and the wrapper inherits that. If you want the data source validated at startup rather than at first query, the documentation points you at dbx.MustOpen() instead, which panics on failure in the way the standard library's MustOpen does. The database driver itself is a separate dependency. The project links the Go wiki list of SQL drivers, and the readme's example installs github.com/go-sql-driver/mysql and imports it with a blank import:
import _ "github.com/go-sql-driver/mysql"The blank import is the standard way to register a driver, and ozzo-dbx never sees the driver itself, it only sees the key.
Four ways to get rows out, and one to send a statement
Query execution splits into two categories. If the statement does not retrieve data, you build a Query with DB.NewQuery() and call Query.Execute(), which returns a result, for example for an UPDATE. If the statement does retrieve data, one of the population methods is called instead. Query.All() populates all rows of the result into a slice of structs or NullString maps, and pointer slices of structs are also supported. Query.One() populates the first row into a struct or a NullString map. Query.Column() populates the first column into a slice. Query.Row() populates the first row into a list of variables, one for each returning column. Query.Rows() returns a dbx.Rows instance so you can retrieve data row by row, and the rows object offers ScanStruct, ScanMap and Scan variants. The set of options is what makes the library pleasant to use across different shapes of result. The NullString map is the escape hatch for a query whose columns are not known at compile time, so a report query with a variable column set can still be read without writing a struct per shape. The full example in the readme shows all of these in one function, from All into a slice of anonymous structs, through One into a single struct, through One into a NullStringMap, to a Rows loop using ScanStruct.
The query builder produces SQL you can read in the source
The builder is the part of the library that carries the most weight, and its output style is worth judging on its own terms. A SELECT is assembled from method calls, and the readme prints the resulting statement as a comment above the code so you can see exactly what will run:
// build a SELECT query
// SELECT `id`, `name` FROM `users` WHERE `name` LIKE '%Charles%' ORDER BY `id`
q := db.Select("id", "name").
From("users").
Where(dbx.Like("name", "Charles")).
OrderBy("id")The comment shows that identifiers are quoted, and the Like condition is expressed as a named helper rather than a raw string, so the value is bound as a parameter instead of being interpolated into the statement. The same style applies to writes, with an Insert call that takes the table name and a dbx.Params map:
// build an INSERT query
// INSERT INTO `users` (`name`) VALUES ('James')
err = db.Insert("users", dbx.Params{
"name": "James",
}).Execute()The builder covers three families of query, as the table of contents shows: SELECT queries, query conditions, data manipulation queries and schema manipulation queries, with CRUD operations layered on top for create, read, update and delete, plus null handling and quoting of table and column names. The readme also documents cancelable queries and transactions.
Supported databases, and the caveat for everything else
The supported list is short and specific, and the readme is honest about what happens outside it. SQLite, MySQL, PostgreSQL, MS SQL Server 2012 or above, and Oracle are fully supported out of the box. SQLite is covered by two driver keys, sqlite3 for mattn/go-sqlite3 and sqlite for modernc.org/sqlite, which the readme identifies as the CGo-free driver. SQLite schema operations DropColumn, RenameColumn and RenameTable are supported through standard ALTER TABLE syntax, with a version floor stated for each, since rename requires SQLite 3.25.0 or newer and dropping a column requires 3.35.0 or newer. For any other database, the readme says the query building feature may not work as expected and that you can create a custom builder to solve the problem. That is the boundary a reader has to check. The open architecture is real, and the repository layout shows it, with a separate builder file per database, builder_mysql.go, builder_pgsql.go, builder_mssql.go, builder_oci.go and builder_sqlite.go, plus builder_standard.go as the generic case, and the tests mirror that structure. So extending the library means writing one builder file and registering it, not patching the core.
Maintenance, the release gap and what a new adopter inherits
The repository is small and tidy, with the core files at the top level, builder.go, db.go, expression.go, model_query.go, query.go, query_builder.go, rows.go, select.go, struct.go and tx.go, plus an integration directory and a testdata directory. The go.mod declares the module as github.com/go-ozzo/ozzo-dbx, requires Go 1.22, and its only direct dependency is github.com/stretchr/testify v1.12.1 for tests, with a YAML library as an indirect dependency. A library with a single test-only dependency and no runtime dependencies is genuinely easy to adopt and to vendor, and it is also a signal that the project does not take on much of the ecosystem. The release history is the part to look at carefully. The current release is v1.6.0, named the Revival Release, published on 2026-09-03, and the last push was on 2026-09-03 as well. Before that, v1.5.0 dates from 2020-01-07 and v1.4.0 from 2019-12-23. So the project was dormant for most of the last six years and has just come back to life, which is a genuine revival rather than a steady maintenance record. The licence is MIT, and the repository carries the usual set of governance files including a SECURITY.md, a CODE_OF_CONDUCT.md and a ROADMAP.md, plus a README in Russian alongside the English one.
Editorial conclusion
ozzo-dbx is a good fit for a Go service that wants the readability of a query builder and the predictability of hand written SQL, especially with the same code running against SQLite for tests and PostgreSQL or MySQL in production. It is the wrong choice if you want an ORM with migrations, associations and automatic schema handling, because the readme says plainly that this is not an ORM and the feature list stops at struct and map population. Verify first that the database you use is on the supported list, since the readme warns that for other databases the query building may not work as expected and a custom builder is needed, and that your Go toolchain is 1.22 or newer. The last push was on 2026-09-03 and the current release is v1.6.0 from the same day.
Frequently asked questions
Is ozzo-dbx an ORM?
No. The readme says explicitly that it is not an ORM. It enhances database/sql with data retrieval into structs and NullString maps, named parameter binding, query building and CRUD helpers, without an object mapper or schema migration.
How do I install ozzo-dbx?
Run go get github.com/go-ozzo/ozzo-dbx, and install the driver package for your database separately, for example go get github.com/go-sql-driver/mysql, then import it with a blank import in your main code.
Which databases does ozzo-dbx support?
SQLite, MySQL, PostgreSQL, MS SQL Server from 2012, and Oracle are fully supported out of the box. For other databases the readme says query building may not work as expected and you need a custom builder.
What Go version does ozzo-dbx need?
Go 1.22 or above, which matches the go.mod declaration of go 1.22.
How do I read rows into a struct with ozzo-dbx?
Call All to populate a slice of structs, One for a single row, Column for the first column, Row for a list of variables, or Rows to scan row by row with ScanStruct, ScanMap or Scan. A NullStringMap works where the column set is not known at compile time.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/go-ozzo-ozzo-dbx)Was this useful?