| name | sqlx-code-review |
| description | Reviews sqlx database code for compile-time query checking, connection pool management, migration patterns, and PostgreSQL-specific usage. Use when reviewing Rust code that uses sqlx, database queries, connection pools, or migrations. Covers offline mode, type mapping, and transaction patterns. |
sqlx Code Review
Review Workflow
- Check Cargo.toml — Note sqlx features (
runtime-tokio, tls-rustls/tls-native-tls, postgres/mysql/sqlite, uuid, chrono, json, migrate) and Rust edition (2024 changes RPIT lifetime capture and removes need for async-trait)
- Check query patterns — Compile-time checked (
query!, query_as!) vs runtime (query, query_as)
- Check pool configuration — Connection limits, timeouts, idle settings
- Check migrations — File naming, reversibility, data migration safety
- Check type mappings — Rust types align with SQL column types
Gates (evidence before severity)
Complete in order; do not assign Critical / Major until the gate for that claim is passed.
- Scope — Identify the crate under review (
Cargo.toml path) and the .rs files (or directory) you opened. Pass: At least one concrete path you inspected is named.
- sqlx / compile claims — Before asserting issues about
query! / query_as!, offline mode, sqlx.toml, DATABASE_URL, or Cargo features: open the relevant Cargo.toml and, if applicable, sqlx.toml or documented env. Pass: The finding cites a line or you state that those files were absent / out of scope.
- Finding anchors — Each reported issue includes
[FILE:LINE] per Output Format. Pass: No Critical or Major without a line reference.
- Protocol — Load and complete the review-verification-protocol skill after gates 1–3 and before final severity labels. Pass: Protocol steps satisfied for each retained finding.
Output Format
Report findings as:
[FILE:LINE] ISSUE_TITLE
Severity: Critical | Major | Minor | Informational
Description of the issue and why it matters.
Quick Reference
Review Checklist
Query Patterns
Connection Pool
Transactions
Type Mapping
Edition 2024 Compatibility
Migrations
Severity Calibration
Critical
- String interpolation in SQL queries (SQL injection)
- Missing transaction for multi-statement writes (partial writes on error)
- Connection pool created per-request (connection exhaustion)
- Missing bind parameter escaping
Major
- Runtime queries (
query()) where compile-time (query!()) could verify correctness
- Missing transaction rollback on error paths
- Enum type mismatch between Rust and database
- Unbounded
.fetch_all() on potentially large tables
- Field or variant named
gen without r#gen escape (edition 2024 compile failure)
Minor
- Pool defaults used in production without tuning
- Missing
.fetch_optional() (using .fetch_one() then handling error for "not found")
- Overly broad
SELECT * when only specific columns needed
- Missing indexes for queried columns (flag only if query pattern is clearly slow)
- Edition 2024:
once_cell::sync::Lazy or lazy_static! used where std::sync::LazyLock works
- Using
#[allow(unused)] instead of #[expect(unused)] for query fields (prefer self-cleaning lint suppression)
Informational
- Suggestions to use
query_as! for type-safe result mapping
- Suggestions to add database-level constraints alongside Rust validation
- Migration organization improvements
Valid Patterns (Do NOT Flag)
- Runtime
query() for dynamic queries — Compile-time checking doesn't work with dynamic SQL
sqlx::FromRow derive — Valid alternative to query_as! for reusable row types
TEXT columns for enum storage — Valid with sqlx::Type derive, simpler than custom SQL types
.execute() ignoring row count — Acceptable for idempotent operations (upserts, deletes)
- Shared DB with other languages — e.g., Elixir owns migrations, Rust reads. This is a valid architecture.
r#gen with #[sqlx(rename = "gen")] — Correct edition 2024 workaround for gen columns in database types
+ use<'a> on query helper return types — Precise RPIT lifetime capture (edition 2024)
std::sync::LazyLock for static pool initialization — Replaces once_cell/lazy_static (stable since Rust 1.80)
- Native
async fn in custom FromRow/Type trait impls — async-trait crate no longer needed (stable since Rust 1.75)
Before Submitting Findings
Complete Gates (evidence before severity), then load and follow the review-verification-protocol skill before reporting any issue.