sqlx-queries
Skill96 starsChanged 30 days agoApache-2.0View on GitHub
Use when writing or reviewing control-layer SQLx queries, repository methods, QueryBuilder SQL, SELECT or RETURNING projections, pool routing, or cached-plan result-type errors.
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 0 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 8
Keep database access in dwctl/src/db/handlers/ repositories.
- Very specific: line 18
When replacing a wildcard, preserve the existing result descriptor.
- Very specific: line 20
Prefer query! / query_as! for static SQL.
- Very specific: line 78
Use #[sqlx::test] and repository/API tests, including relevant dynamic filters, empty results, nulls, and read-after-write workflows.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
SQLx queries in control-layer
Keep database access in dwctl/src/db/handlers/ repositories. Follow the nearby
repository's connection/transaction and error conventions; handlers remain
generic over PoolProvider. Paths below are repository-root-relative.
Write queries with stable results
- Name result columns explicitly in
SELECTand in INSERT/UPDATE/DELETERETURNING. Do not use table-resultSELECT *,alias.*, orRETURNING *, even when the Rust struct currently matches the whole table. - Match the intended Rust row fields, types, nullability, and SQLx aliases.
When replacing a wildcard, preserve the existing result descriptor; review
regenerated
.sqlxmetadata for unintended changes. - Prefer
query!/query_as!for static SQL. Bind values with parameters; use.bind()for runtime queries andQueryBuilder::push_bind()for dynamic values. Only append trusted SQL fragments or allowlisted identifiers withpush(); never interpolate request input into SQL. - Runtime
query_asandQueryBuilderSQL needs execution tests: SQLx prepare does not compile-check those query strings.
Example from the groups repository, inside its transaction:
let group = sqlx::query_as!(
Group,
r#"
INSERT INTO groups (name, description, created_by, source)
VALUES ($1, $2, $3, 'native')
RETURNING id, name, description, created_by, created_at, updated_at, source
"#,
request.name,
request.description,
request.created_by
)
.fetch_one(&mut *self.db)
.await?;COUNT(*) does not expand a row. Fixed CTE/typed UNNEST projections need review
of the complete query before an exemption: the final client-visible result must
remain stable. Do not exempt a table wildcard merely to pass the guard.
Choose the connection by semantics
| Operation | Connection |
| Mutation or atomic multi-step operation | Primary .write() transaction; scope repository borrows, then commit |
| Read immediately after a write, including POST then GET across endpoints | Primary .write() |
| Read that tolerates replication lag | .read() |
| LISTEN, session advisory locks, migrations/maintenance | Existing dedicated direct connection/provider, never a transaction-pooled query connection |
Preserve the existing schema-routing helpers, including Fusillade's
transaction-local schema selection where configured. Session SET state cannot
be assumed to survive transaction pooling.
Migration compatibility
SQLx compile checks validate one schema snapshot. PgBouncer can retain prepared
statements after all application clients restart. Adding a column can therefore
break wildcard results with cached plan must not change result type.
Explicit columns protect additive changes, not in-place changes to selected
column types. Use expand → backfill → switch readers/writers → contract after
old clients drain. Do not blindly replay writes to recover from cached-plan
errors or treat SQLx persistent(false) as invalidation of an existing statement.
See the migration explainer and use sqlx-index-migrations for index DDL.
Validate changes
Use #[sqlx::test] and repository/API tests, including relevant dynamic filters,
empty results, nulls, and read-after-write workflows. Refresh .sqlx metadata with
cargo sqlx prepare --workspace against correctly migrated local databases;
preserve per-crate database configuration. Run just lint rust and
just test rust before pushing Rust changes.
Lint runs scripts/check_query_projections.py; reviewed exceptions live in
.github/fixtures/stable-query-projections.json. This source guard is not a SQL
parser. For migration-sensitive queries, extend and run both shared/scoped
pooled E2Es: warm queries, apply DDL,
then exercise existing and replacement clients while PgBouncer stays alive.
In this repository5 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- rust-test-efficiencySkill
- sqlx-index-migrationsSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

