Rust
sqltgen generates async Rust code using sqlx.
Model structs derive sqlx::FromRow. Query functions are async fn returning
Result<…, sqlx::Error>, generic over sqlx::Executor so they accept a pool or a
transaction.
Configuration
"rust": {
"out": "src/db",
"package": ""
}
| Field | Description |
|---|---|
out | Output directory (relative to the project root). |
package | Unused for Rust — set to "". |
list_params | "native" (default) or "dynamic". |
What is generated
src/db/
mod.rs — re-exports all modules
author.rs — Author struct with sqlx::FromRow
book.rs — Book struct
_sqltgen.rs — shared helper trait (SqlxAdapter)
queries.rs — async query functions (single-file form)
users.rs — per-group query files (grouped form)
Model structs
#![allow(unused)] fn main() { // src/db/author.rs #[derive(Debug, sqlx::FromRow)] pub struct Author { pub id: i64, pub name: String, pub bio: Option<String>, pub birth_year: Option<i32>, } }
- Non-null columns → bare type (
i64,String,bool, …). - Nullable columns →
Option<T>. snake_caseSQL names →snake_caseRust field names (unchanged).
Query functions
Each function is generic over sqlx::Executor, so the same function runs against a
connection pool, a pooled connection, or a transaction. The Database bound is fixed
to the configured engine (sqlx::Postgres / sqlx::Sqlite / sqlx::MySql).
#![allow(unused)] fn main() { // src/db/queries.rs use super::author::Author; pub async fn get_author<'e, E>(executor: E, id: i64) -> Result<Option<Author>, sqlx::Error> where E: sqlx::Executor<'e, Database = sqlx::Postgres>, { sqlx::query_as::<_, Author>(SQL_GET_AUTHOR) .bind(id) .fetch_optional(executor) .await } pub async fn list_authors<'e, E>(executor: E) -> Result<Vec<Author>, sqlx::Error> where E: sqlx::Executor<'e, Database = sqlx::Postgres>, { sqlx::query_as::<_, Author>(SQL_LIST_AUTHORS) .fetch_all(executor) .await } pub async fn delete_author<'e, E>(executor: E, id: i64) -> Result<(), sqlx::Error> where E: sqlx::Executor<'e, Database = sqlx::Postgres>, { sqlx::query(SQL_DELETE_AUTHOR) .bind(id) .execute(executor) .await .map(|_| ()) } }
_sqltgen.rs helper
The generated _sqltgen.rs file provides a shared SqlxAdapter trait that
abstracts over the three pool types. This is an implementation detail — you do
not need to call it directly.
mod.rs
#![allow(unused)] fn main() { pub mod author; pub mod book; pub mod queries; mod _sqltgen; }
Lints and formatting
Clippy — the generated crate root carries crate-wide allows that propagate to the
submodules, so the generated module passes cargo clippy -- -D warnings with no action
on your part:
#![allow(unused)] #![allow(dead_code)] #![allow(clippy::too_many_arguments)] #![allow(clippy::module_inception)] fn main() { }
rustfmt — a formatter's output depends on your project's rustfmt.toml, which the
generator cannot know, so sqltgen does not shape the generated code to any particular
profile. Keep it out of cargo fmt --check by applying #[rustfmt::skip] to the mod
declaration where you include the generated module:
#![allow(unused)] fn main() { #[rustfmt::skip] pub mod db; // wherever you pull in the generated output directory }
#[rustfmt::skip] on the mod declaration exempts the whole generated tree (the module
file and all its submodules) from cargo fmt on stable Rust, regardless of your rustfmt
configuration. (The ignore option in rustfmt.toml is nightly-only, so this attribute
is the portable way to do it.)
Wiring up
Cargo.toml
[dependencies]
sqlx = { version = "0.8", features = [
"runtime-tokio",
"postgres", # or "sqlite" / "mysql"
"time", # for Date, Time, Timestamp, TimestampTz
"uuid", # for UUID
"rust_decimal", # for NUMERIC / DECIMAL
] }
tokio = { version = "1", features = ["full"] }
Using the pool directly
mod db; use db::queries; #[tokio::main] async fn main() -> Result<(), sqlx::Error> { let pool = sqlx::PgPool::connect("postgres://user:pass@localhost/mydb").await?; let author = queries::get_author(&pool, 1).await?; let all = queries::list_authors(&pool).await?; Ok(()) }
&pool satisfies the Executor bound, so the call sites are unchanged from a
concrete-pool API.
Inside a transaction
Because the functions are generic over Executor, pass &mut *tx to run several
generated operations in one transaction and commit them atomically:
#![allow(unused)] fn main() { let mut tx = pool.begin().await?; queries::create_sale(&mut *tx, customer_id).await?; queries::add_sale_item(&mut *tx, book_id, qty, price).await?; tx.commit().await?; // both statements commit together, or neither }
Database type per dialect
The Database bound on each function's Executor is fixed to the configured engine:
| Dialect | Pool type | Database bound |
|---|---|---|
| PostgreSQL | sqlx::PgPool | sqlx::Postgres |
| SQLite | sqlx::SqlitePool | sqlx::Sqlite |
| MySQL | sqlx::MySqlPool | sqlx::MySql |
Inline row types
JOIN queries or partial RETURNING queries produce additional structs in the query file:
#![allow(unused)] fn main() { #[derive(Debug, sqlx::FromRow)] pub struct ListBooksWithAuthorRow { pub id: i64, pub title: String, pub genre: String, pub price: rust_decimal::Decimal, pub author_name: String, pub author_bio: Option<String>, } pub async fn list_books_with_author<'e, E>(executor: E) -> Result<Vec<ListBooksWithAuthorRow>, sqlx::Error> where E: sqlx::Executor<'e, Database = sqlx::Postgres>, { … } }
List parameters
For PostgreSQL, list parameters use = ANY($1) with a slice bind:
#![allow(unused)] fn main() { // generated pub async fn get_books_by_ids<'e, E>(executor: E, ids: &[i64]) -> Result<Vec<Book>, sqlx::Error> where E: sqlx::Executor<'e, Database = sqlx::Postgres>, { sqlx::query_as::<_, Book>(SQL_GET_BOOKS_BY_IDS) .bind(ids) .fetch_all(executor) .await } }
For SQLite and MySQL, the native strategy uses json_each / JSON_TABLE
with a JSON-serialized string:
#![allow(unused)] fn main() { pub async fn get_books_by_ids<'e, E>(executor: E, ids: &[i64]) -> Result<Vec<Book>, sqlx::Error> where E: sqlx::Executor<'e, Database = sqlx::Sqlite>, { let ids_json = serde_json::to_string(ids).unwrap(); sqlx::query_as::<_, Book>(SQL_GET_BOOKS_BY_IDS) .bind(ids_json) .fetch_all(executor) .await } }
Naming conventions
| SQL | Rust |
|---|---|
GetAuthor | get_author |
ListBooksWithAuthor | list_books_with_author |
birth_year column | birth_year field |
Author table | Author struct |