Skip to content

Commit 4eecda0

Browse files
committed
feat(cli): add cargo sqlx prepare --per-crate
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
1 parent 1d674f5 commit 4eecda0

10 files changed

Lines changed: 755 additions & 35 deletions

File tree

sqlx-cli/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,19 @@ to generate a single `.sqlx` directory at the root of the workspace.
133133
cargo sqlx prepare --workspace
134134
```
135135

136+
Alternatively, pass `--per-crate` to give every crate in the workspace its own `.sqlx`
137+
directory next to its `Cargo.toml`, so that changing one crate's queries only touches that
138+
crate's query data. Note that a query used by two crates is stored once per crate.
139+
140+
```bash
141+
cargo sqlx prepare --per-crate
142+
```
143+
144+
The macros look for a query in the crate's own `.sqlx` first and fall back to the one at the
145+
workspace root, so switching between the two layouts doesn't require any other changes.
146+
`--per-crate` cannot be combined with `--all`, since crates outside the workspace have no
147+
crate directory to write to.
148+
136149
Check this directory into version control and an active database connection will
137150
no longer be needed to build your project.
138151

@@ -142,6 +155,8 @@ no longer be needed to build your project.
142155
cargo sqlx prepare --check
143156
# OR
144157
cargo sqlx prepare --check --workspace
158+
# OR
159+
cargo sqlx prepare --check --per-crate
145160
```
146161

147162
Exits with a nonzero exit status if the data in `.sqlx` is out of date with the current

sqlx-cli/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,13 +220,23 @@ async fn do_run(opt: Opt) -> anyhow::Result<()> {
220220
check,
221221
all,
222222
workspace,
223+
per_crate,
223224
mut connect_opts,
224225
args,
225226
config,
226227
} => {
227228
let config = config.load_config().await?;
228229
connect_opts.populate_db_url(&config)?;
229-
prepare::run(&config, check, all, workspace, connect_opts, args).await?
230+
prepare::run(
231+
&config,
232+
check,
233+
all,
234+
workspace,
235+
per_crate,
236+
connect_opts,
237+
args,
238+
)
239+
.await?
230240
}
231241

232242
#[cfg(feature = "completions")]

sqlx-cli/src/metadata.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ use cargo_metadata::{
1515
/// The minimal amount of package information we care about
1616
///
1717
/// The package's `name` is used to `cargo clean -p` specific crates while the `src_paths` are
18-
/// are used to trigger recompiles of packages within the workspace
18+
/// are used to trigger recompiles of packages within the workspace. The `manifest_dir` is where
19+
/// a per-crate `.sqlx` directory belongs.
1920
#[derive(Debug)]
2021
pub struct Package {
2122
name: MetadataPackageName,
23+
manifest_dir: PathBuf,
2224
src_paths: Vec<PathBuf>,
2325
}
2426

@@ -27,6 +29,11 @@ impl Package {
2729
self.name.as_str()
2830
}
2931

32+
/// The directory containing this package's `Cargo.toml`.
33+
pub fn manifest_dir(&self) -> &Path {
34+
&self.manifest_dir
35+
}
36+
3037
pub fn src_paths(&self) -> &[PathBuf] {
3138
&self.src_paths
3239
}
@@ -35,13 +42,21 @@ impl Package {
3542
impl From<&MetadataPackage> for Package {
3643
fn from(package: &MetadataPackage) -> Self {
3744
let name = package.name.clone();
45+
let manifest_dir = package
46+
.manifest_path
47+
.parent()
48+
.map_or_else(PathBuf::new, |dir| dir.as_std_path().to_path_buf());
3849
let src_paths = package
3950
.targets
4051
.iter()
4152
.map(|target| target.src_path.clone().into_std_path_buf())
4253
.collect();
4354

44-
Self { name, src_paths }
55+
Self {
56+
name,
57+
manifest_dir,
58+
src_paths,
59+
}
4560
}
4661
}
4762

sqlx-cli/src/opt.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ pub enum Command {
3939
/// Generate query metadata to support offline compile-time verification.
4040
///
4141
/// Saves metadata for all invocations of `query!` and related macros to a `.sqlx` directory
42-
/// in the current directory (or workspace root with `--workspace`), overwriting if needed.
42+
/// in the current directory (or workspace root with `--workspace`, or each workspace crate
43+
/// with `--per-crate`), overwriting if needed.
4344
///
4445
/// During project compilation, the absence of the `DATABASE_URL` environment variable or
4546
/// the presence of `SQLX_OFFLINE` (with a value of `true` or `1`) will constrain the
@@ -62,6 +63,17 @@ pub enum Command {
6263
#[clap(long)]
6364
workspace: bool,
6465

66+
/// Generate a `.sqlx` folder next to each workspace crate's `Cargo.toml`, instead of a
67+
/// single one at the workspace root.
68+
///
69+
/// Each crate then owns the query data for its own queries, so editing one crate doesn't
70+
/// churn a directory shared by the whole workspace. Note that a query used by two crates
71+
/// is stored once per crate.
72+
///
73+
/// Implies `--workspace`, and cannot be combined with `--all`.
74+
#[clap(long)]
75+
per_crate: bool,
76+
6577
/// Arguments to be passed to `cargo rustc ...`.
6678
#[clap(last = true)]
6779
args: Vec<String>,

0 commit comments

Comments
 (0)