Skip to content

feat(cli): add cargo sqlx prepare --per-crate - #4382

Draft
macalinao wants to merge 1 commit into
transact-rs:mainfrom
macalinao:prepare-per-crate
Draft

feat(cli): add cargo sqlx prepare --per-crate#4382
macalinao wants to merge 1 commit into
transact-rs:mainfrom
macalinao:prepare-per-crate

Conversation

@macalinao

Copy link
Copy Markdown

Adds cargo sqlx prepare --per-crate, which generates a .sqlx directory next to each workspace crate's Cargo.toml instead of a single one at the workspace root. Editing one crate's queries then only touches that crate's query data.

cargo sqlx prepare --per-crate
cargo sqlx prepare --check --per-crate

How

The read side already supported this. expand_input looks for each query file in $CARGO_MANIFEST_DIR/.sqlx before falling back to the workspace root, and it does so per file, so a crate can use either layout (or both). Only the write side was workspace-wide, because prepare sets one SQLX_OFFLINE_DIR for a single cargo check covering the whole workspace.

The obvious alternative — loop over members running cargo check -p <member> with SQLX_OFFLINE_DIR=<member>/.sqlx — is wrong: when crate A is checked, its workspace-member dependencies compile for the first time in that same invocation, so their query data lands in A/.sqlx. Avoiding that needs a warm-up build plus reverse-topological ordering, and N cargo check invocations.

Instead the macros route their own save directory. sqlx-cli sets SQLX_OFFLINE_PER_CRATE, and each expansion saves to $SQLX_OFFLINE_DIR/$CARGO_PKG_NAME. That is correct regardless of build order and still needs only one cargo check.

The staging tree lives under target/, and the CLI moves each crate's files into its .sqlx afterwards. --check reuses the same layout to compare against the checked-in data without touching it, and attributes failures to the crate they came from:

error: prepare check failed: .sqlx in crate-a is missing one or more queries; you should re-run sqlx prepare

Notes

  • A query used by two crates is stored once per crate. That is inherent to the layout.
  • Only query-*.json files are written or deleted, so anything else a user keeps in .sqlx survives. A .sqlx directory emptied of query data is removed if nothing else is in it.
  • --per-crate implies --workspace and rejects --all, since crates outside the workspace have no crate directory to write to.
  • After a successful run, query data left at the workspace root by a previous --workspace run is reported as no longer generated or checked, since the macros still fall back to it.
  • save_in now creates its target directory, so a dependency outside the workspace that expands a query mid-prepare doesn't fail the build.

Testing

  • 14 unit tests in sqlx-cli/src/prepare.rs covering the crate/directory mapping, staging reset, install (stale replacement, preserving non-query files, creating and removing directories), the check comparison, and the stale-workspace-data detection.
  • 4 unit tests in sqlx-macros-core/src/query/metadata.rs covering save-directory resolution.
  • 2 integration tests in sqlx-cli/tests/prepare.rs for the flag validation.
  • Manually verified against a two-crate SQLite workspace: --per-crate writes each crate's queries to its own .sqlx with a shared query duplicated in both; SQLX_OFFLINE=true cargo check builds against them; --check passes when current and fails naming the right crate after a query changes; the stale-workspace warning fires; and --workspace still produces a single root .sqlx and passes --check.

https://claude.ai/code/session_011CmjiD9jHNqmdTptXkMu4j

Generates a `.sqlx` directory next to each workspace crate's `Cargo.toml`
instead of a single one at the workspace root, so editing one crate's
queries only touches that crate's query data.

The read side already supported this: `expand_input` looks for each query
file in `$CARGO_MANIFEST_DIR/.sqlx` before falling back to the workspace
root, per file. Only the write side was workspace-wide, because
`prepare` sets one `SQLX_OFFLINE_DIR` for a single `cargo check` over the
whole workspace.

Rather than run `cargo check` once per crate — which writes a crate's
query data into whichever sibling happened to trigger its first compile —
the macros now route their own save directory. `sqlx-cli` sets
`SQLX_OFFLINE_PER_CRATE`, and each expansion saves to
`$SQLX_OFFLINE_DIR/$CARGO_PKG_NAME`, which is correct regardless of build
order and still only needs one `cargo check`.

That staging tree lives under `target/`, and the CLI moves each crate's
files into its `.sqlx` afterwards. `--check` reuses the same layout to
compare against the checked-in data without touching it, and reports
failures per crate. Only `query-*.json` files are ever written or
deleted, so anything else in a `.sqlx` directory is left alone.

`--per-crate` implies `--workspace` and rejects `--all`, since crates
outside the workspace have no crate directory to write to. After a
successful run, query data left at the workspace root by a previous
`--workspace` run is reported as no longer generated or checked.

Claude-Session: https://claude.ai/code/session_011CmjiD9jHNqmdTptXkMu4j
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant