Chain Builder
A typed, dialect-aware SQL query builder for Rust.
Generic over a Dialect (PostgreSQL / MySQL / SQLite), with typed bind
parameters, automatic identifier escaping, dialect-correct placeholders
($N for Postgres, ? for MySQL/SQLite), and an sqlx handoff for execution.
v2.0 is a ground-up redesign. Upgrading from 1.x? See CHANGELOG — the API is entirely new (typed binds,
Dialect-generic, noserde_json::Valuein the core).
Documentation
- User guide — full documentation site (queries, binds, errors, sqlx, cookbook).
- API reference — rustdoc on docs.rs.
- Changelog — release notes and breaking changes.
Features
- Dialect-generic — one builder, three dialects; mixing them is a compile error.
- Typed binds — pass real Rust values (
i64,&str,Option<T>, …) viaIntoBind. - Injection-safe — identifiers always escaped, values always bound.
- Full query surface — SELECT/INSERT/UPDATE/DELETE, WHERE (+
or/andgroups), JOINs, CTEs (WITH/RECURSIVE),UNION, GROUP BY/HAVING/ORDER BY, LIMIT/OFFSET. - Upsert + RETURNING —
on_conflict_merge/_do_nothing, dialect-correct. - Row locking —
for_update/for_share+skip_locked/no_wait(no-op on SQLite). - Aggregates —
select_count/sum/avg/min/max(+_as) andselect_as. db()qualification — multi-tenant (one connection, many databases).- Typed fetch —
fetch_all::<T>/fetch_one/count/fetch_scalarviasqlx. - Dynamic —
when/when_else,paginate,distinct/distinct_on,ilike, jsonb.
Installation
[]
# pick the driver(s) you need; the builder itself is driver-agnostic
= { = "3", = ["sqlx_postgres"] }
= { = "0.9", = ["postgres", "runtime-tokio-rustls"] }
Driver features: sqlx_mysql (default), sqlx_sqlite, sqlx_postgres — enable any
combination. json enables Value::Json.
Quick Start
use ;
let = table
.db
.select
.where_eq
.where_in
.order_by
.paginate
.to_sql;
// SELECT "id", "name" FROM "mydb"."users"
// WHERE "status" = $1 AND "role" IN ($2, $3)
// ORDER BY "created_at" DESC LIMIT $4 OFFSET $5
// upsert + RETURNING
use ;
let q = table
.insert
.on_conflict_merge
.returning;
// INSERT INTO "users" ("email", "name") VALUES ($1, $2)
// ON CONFLICT ("email") DO UPDATE SET "name" = EXCLUDED."name" RETURNING "id"
// execute with sqlx (feature sqlx_postgres)
// let rows: Vec<UserRow> = q.fetch_all(&pool).await?;
Same builder, different dialect → MySQL backticks + ?:
use ;
table.select.where_eq;
// SELECT `id` FROM `users` WHERE `status` = ?
Security
Two axes of SQL-injection safety:
- Values are always sent as bound parameters (
?/$N), never inlined. - Identifiers (table/column/alias names) are dialect-escaped automatically;
qualified names (
users.id) are escaped segment-by-segment ("users"."id"),*preserved. Pass bare names — do not pre-quote (it would be double-escaped).
The *_raw methods (select_raw, where_raw, group_by_raw, order_by_raw,
on_raw, having_raw) and the having* helpers are verbatim escape hatches —
never pass untrusted input through them.
Feature Flags
sqlx_mysql(default) — MySQL driver +SqlxDialect for MySqlsqlx_sqlite— SQLite driver +SqlxDialect for Sqlitesqlx_postgres— PostgreSQL driver +SqlxDialect for Postgresjson—Value::Json+IntoBind for serde_json::Valueuuid—Value::Uuid+IntoBind for uuid::Uuidchrono— date/timeValuevariants +IntoBindforchronotypesdecimal—Value::Decimal+IntoBind for rust_decimal::Decimal
The query builder (to_sql()) works with no driver feature; a driver feature is
only needed for the sqlx handoff (to_sqlx_query, fetch_*). All three drivers
can be enabled simultaneously.
License
MIT License - see LICENSE for details.