Summary
Add a SQLx-style query! / query_as! proc macro that verifies SQL at compile time against a real Hyper database, providing compile-time errors for typos, missing columns, type mismatches, and parameter arity bugs. Premium developer experience; no breaking changes — purely additive on top of the existing Connection / Rowset / FromRow API.
Motivation
The original RUST_API_GAP_ANALYSIS.md (predecessor repo) flagged the absence of compile-time query verification:
No #[derive(FromRow)] proc macro; no compile-time query verification (sqlx-style) — may be intentional scope limits.
— docs/RUST_API_GAP_ANALYSIS.md:214-215 in hyper-api-rust-oldrepo
#[derive(FromRow)] is being tracked in #61. This issue covers the second half: compile-time SQL verification.
Today, a typo or schema drift in conn.fetch_one_as("SELECT name FROM userz WHERE id = $1") surfaces only at runtime. SQLx solved this for Postgres/MySQL/SQLite by talking to a live database during cargo build and generating column-typed bindings inline. The same approach is feasible for Hyper because Hyper supports PREPARE / DESCRIBE and we already speak the protocol — the macro only needs to wire those calls into a build-time query path.
Proposed work
A new hyperdb-api-macros proc-macro crate (or feature-gated in hyperdb-api) exposing:
let users: Vec<User> = query_as!(
User,
"SELECT id, name, score FROM users WHERE active = $1",
true
).fetch_all(&conn)?;
At compile time the macro:
- Connects to a Hyper instance (path read from a build-time env var, e.g.
HYPERDB_URL pointing at a .hyper file or running hyperd).
- Calls
PREPARE on the literal SQL string.
- Reads the resulting parameter and result schemas from Hyper.
- Verifies the inferred parameter types match the Rust expressions passed.
- Verifies the result schema matches the target struct's fields (names + types). For
query! (no struct), generates an anonymous record type.
- Emits a
compile_error! with a precise span on mismatch.
Offline mode
SQLx's killer feature: cargo sqlx prepare snapshots the query metadata into a .sqlx/ directory checked into git, so consumers without a live database can still build. We replicate this:
cargo hyperdb prepare (CLI subcommand or workspace bin) connects once, runs every macro invocation, writes JSON metadata to .hyperdb-queries/.
- Compilation in offline mode reads from that directory; no live connection needed.
- CI flag (e.g.
HYPERDB_OFFLINE=true) refuses to fall back to live mode, so PRs with stale metadata fail fast.
Scope (initial)
Non-goals
- Schema migrations (separate concern).
- Query rewriting / planning hints.
- Replacing the runtime
Connection::fetch_* API. The macro is opt-in; runtime queries continue to work for dynamic SQL.
Backwards compatibility
No breaking changes. This is a brand-new crate / feature flag. Existing fetch_one, fetch_all, fetch_one_as, fetch_all_as, Rowset, and FromRow remain exactly as they are. Users opt in by reaching for the new macros.
Performance note
The macro runs at compile time, so runtime cost is identical to a hand-written prepare + execute pair. Build time grows with the number of macro invocations; the offline cache avoids repeated round-trips to Hyper across incremental builds.
Open questions
- Crate layout: dedicated
hyperdb-api-macros crate (mirroring sqlx-macros), or feature-gated inside hyperdb-api?
- How to discover the Hyper endpoint at compile time — env var only, or a
[package.metadata.hyperdb] block in Cargo.toml?
- Should the macro support Arrow-result paths (
execute_query_to_arrow), or restrict v1 to row-based results?
Related
Summary
Add a SQLx-style
query!/query_as!proc macro that verifies SQL at compile time against a real Hyper database, providing compile-time errors for typos, missing columns, type mismatches, and parameter arity bugs. Premium developer experience; no breaking changes — purely additive on top of the existingConnection/Rowset/FromRowAPI.Motivation
The original
RUST_API_GAP_ANALYSIS.md(predecessor repo) flagged the absence of compile-time query verification:#[derive(FromRow)]is being tracked in #61. This issue covers the second half: compile-time SQL verification.Today, a typo or schema drift in
conn.fetch_one_as("SELECT name FROM userz WHERE id = $1")surfaces only at runtime. SQLx solved this for Postgres/MySQL/SQLite by talking to a live database duringcargo buildand generating column-typed bindings inline. The same approach is feasible for Hyper because Hyper supportsPREPARE/DESCRIBEand we already speak the protocol — the macro only needs to wire those calls into a build-time query path.Proposed work
A new
hyperdb-api-macrosproc-macro crate (or feature-gated inhyperdb-api) exposing:At compile time the macro:
HYPERDB_URLpointing at a.hyperfile or runninghyperd).PREPAREon the literal SQL string.query!(no struct), generates an anonymous record type.compile_error!with a precise span on mismatch.Offline mode
SQLx's killer feature:
cargo sqlx preparesnapshots the query metadata into a.sqlx/directory checked into git, so consumers without a live database can still build. We replicate this:cargo hyperdb prepare(CLI subcommand or workspace bin) connects once, runs every macro invocation, writes JSON metadata to.hyperdb-queries/.HYPERDB_OFFLINE=true) refuses to fall back to live mode, so PRs with stale metadata fail fast.Scope (initial)
query!macro — anonymous record results.query_as!macro — typed struct results, integrates withFromRowfrom Add #[derive(FromRow)] and named-column access for cleaner struct mapping #61..hyperdb-queries/) +cargo hyperdb preparesubcommand.hyperdb-api/examples/, integration tests that intentionally fail to compile (usingtrybuild).Non-goals
Connection::fetch_*API. The macro is opt-in; runtime queries continue to work for dynamic SQL.Backwards compatibility
No breaking changes. This is a brand-new crate / feature flag. Existing
fetch_one,fetch_all,fetch_one_as,fetch_all_as,Rowset, andFromRowremain exactly as they are. Users opt in by reaching for the new macros.Performance note
The macro runs at compile time, so runtime cost is identical to a hand-written
prepare+executepair. Build time grows with the number of macro invocations; the offline cache avoids repeated round-trips to Hyper across incremental builds.Open questions
hyperdb-api-macroscrate (mirroringsqlx-macros), or feature-gated insidehyperdb-api?[package.metadata.hyperdb]block inCargo.toml?execute_query_to_arrow), or restrict v1 to row-based results?Related
#[derive(FromRow)]and named-column access. Compile-time verification builds on top of the typed struct mapping landed there.FromRow(orthogonal; this issue does not depend on it).