Try it · Documentation · Status
You write PostgreSQL — queries in .sql files, a schema, migrations,
PL/pgSQL routines — and it stays raw: no ORM, no DSL. pgzest only
seasons it: it rebuilds the catalog from those files, without a
database, checks every statement the way PostgreSQL would, and turns the
result into typed Rust, verified migrations, editor feedback and
traceable routines.
-- queries/restaurants.sql
-- name: restaurant_by_slug
SELECT id, name, opened_on, created_at
FROM restaurants
WHERE slug = $slug;// src/queries.rs
pgzest_macros::queries!("queries");
// slug is UNIQUE: at most one row. opened_on is nullable: Option.
let r: Option<RestaurantBySlugRow> =
queries::restaurants::restaurant_by_slug(&client, RestaurantBySlugParams { slug: "le-bistrot" }).await?;A mistake in the SQL fails the build, at its place in the .sql file:
error: undescribed queries:
…/examples/demo/queries/restaurants.sql:11:18: error[column-not-found]: column "opened_at" does not exist
--> examples/demo/src/queries.rs:5:25
|
5 | pgzest_macros::queries!("queries");
| ^^^^^^^^^
An oracle runs in CI against a real PostgreSQL 18 and compares what PostgreSQL says with what pgzest says:
| What | Result |
|---|---|
| Columns and parameters of 243 queries | 749/751 and 146/146 typed exactly as PostgreSQL prepares them |
| Function and operator overload resolutions | 19,032/19,032 identical |
| Two real projects' migrations (1,740 and 294 statements) | fully parsed; catalogs rebuilt identical to the real databases — DDL run through dynamic SQL aside, reported instead of guessed |
Generated Rust against hand-written tokio-postgres |
145.0 vs 145.7 µs per query |
What pgzest cannot establish, it says (not-analyzed,
query-parameter-untyped…). It never guesses a type or a nullability.
Typed queries in Rust. Named queries in .sql files, sql! in the
code or query! inline become functions over tokio-postgres, with
parameter and row structs, generated enums, Option exactly where a value
can be null, and a return type that follows the number of rows.
COPY … FROM STDIN included. → guide
Analysis as PostgreSQL would. Overloads, implicit casts, nullability
through joins, DEFAULT and generated columns, PL/pgSQL bodies, and each
trigger function checked once per table that fires it. Errors use
PostgreSQL's words, at the faulty expression.
→ guide
Fragments and macros. A column list or an expression written once and
inserted with @name — in the schema, the queries and the Rust strings —
so an index and the query that must use it cannot drift apart.
@function, @procedure and @trigger without the boilerplate, expanded
to plain SQL. → guide
Declarative schema, generated migrations. Edit schema/, then
pgzest migrate create writes the migration and replays it to verify
it reaches the schema; what may lose data or fail on a populated table is
listed at its top. Prisma and Diesel layouts are read as they are.
pgzest db check finds what was changed by hand in a production
database. → guide
Observable routines. Isolated temp tables, snapshots and run logs in
PL/pgSQL routines; the triggers a statement fired, as OpenTelemetry spans
under your request's span; pgzest.max_depth to stop an endless trigger
cascade by name. About a microsecond per run when the request is not
traced. → guide
In the editor. VS Code: errors as you type, hover with types and query descriptors, completion, macro expansion — in SQL files and inside Rust strings. → guide
The catalog of PostgreSQL 18 and of extensions such as citext,
pg_trgm or PostGIS is generated from a real server and versioned in the
repository; your files are replayed on it in order, each statement
checked against the state at that point. Every query gets a descriptor —
parameters, columns, types, nullability, number of rows — that the Rust
generation, the migrations and the editor all read.
→ how it works
Documentation: pgzest.creatiwity.net.
cargo install pgzest --locked # the CLI, language server included (`pgzest lsp`)Prebuilt binaries (macOS arm64, Linux x64/arm64, Windows x64) are on the
releases page. The
pgzest extension is on the
VS Code Marketplace
and Open VSX, its
binary included. For Rust code: pgzest-runtime and pgzest-macros (or
pgzest-build) from crates.io —
typed queries in Rust.
Prerequisites: Rust, Node.js 24 or later (the VS Code extension), Docker (PostgreSQL 18).
make bootstrap # Rust and npm dependencies
make demo # VS Code on examples/demo, with the pgzest extension loaded
make demo-run # the demo's Rust program against PostgreSQL 18 (Docker)examples/demo is a small ordering application written
like a real project — declarative schema, generated migrations, named
queries, Rust code, a traced trigger — and its README is a guided tour.
To start a project of your own:
getting started.
pgzest works on real projects' schemas and is tested against PostgreSQL 18 in CI. 0.1.0 is its first release (changelog):
- before 1.0, a minor version may break;
- PostgreSQL 18 only; the extensions covered are listed in versions and extensions;
- Rust only for now — TypeScript is the next phase, .NET considered after (languages);
- known gaps are listed, not hidden: a
ROWS FROM (…)the parser does not read yet, parenthesized joins not analyzed, DDL in a file classified as queries not applied (PLAN.md, the steering document, has the phases and the open items).
make ci # what CI checks: build, tests, clippy, fmt, typecheck
make oracle # the analysis against PostgreSQL 18 (Docker)
make site-dev # the documentation site, locally (Node.js 24 or later)The documentation lives in site/ (Astro Starlight); its CLI
and diagnostics references are generated from the code
(cargo xtask docs). Code, comments and documentation are in English.
Commit messages follow Conventional Commits:
the changelog is generated from them (releasing).
pgzest is made by Creatiwity. It is licensed under either of Apache License, Version 2.0 or MIT license, at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in pgzest by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.