diff --git a/sqlx-cli/src/metadata.rs b/sqlx-cli/src/metadata.rs index bb6afc6339..afcc79ac03 100644 --- a/sqlx-cli/src/metadata.rs +++ b/sqlx-cli/src/metadata.rs @@ -56,6 +56,9 @@ pub struct Metadata { /// /// Typically `target` at the workspace root, but can be overridden target_directory: PathBuf, + /// Package metadata for the crate in the current working directory, None if run from + /// a workspace with the `merged` flag. + current_package: Option, } impl Metadata { @@ -75,6 +78,10 @@ impl Metadata { &self.target_directory } + pub fn current_package(&self) -> Option<&Package> { + self.current_package.as_ref() + } + /// Gets all dependents (direct and transitive) of `id` pub fn all_dependents_of(&self, id: &MetadataId) -> BTreeSet<&MetadataId> { let mut dependents = BTreeSet::new(); @@ -101,13 +108,19 @@ impl FromStr for Metadata { type Err = anyhow::Error; fn from_str(s: &str) -> Result { + let cargo_metadata: CargoMetadata = serde_json::from_str(s)?; + + // Extract the package for the current working directory, will be empty if running + // from a workspace root. + let current_package: Option = cargo_metadata.root_package().map(Package::from); + let CargoMetadata { packages: metadata_packages, workspace_members, resolve, target_directory, .. - } = serde_json::from_str(s)?; + } = cargo_metadata; let mut packages = BTreeMap::new(); for metadata_package in metadata_packages { @@ -136,6 +149,7 @@ impl FromStr for Metadata { workspace_members, reverse_deps, target_directory, + current_package, }) } } diff --git a/sqlx-cli/src/prepare.rs b/sqlx-cli/src/prepare.rs index 77d17c8ec3..2bcb4f4bff 100644 --- a/sqlx-cli/src/prepare.rs +++ b/sqlx-cli/src/prepare.rs @@ -180,7 +180,23 @@ hint: This command only works in the manifest directory of a Cargo package."# bail!("`cargo check` failed with status: {}", check_status); } - let pattern = metadata.target_directory().join("sqlx/query-*.json"); + let package_dir = if merge { + // Merge queries from all workspace crates. + "**" + } else { + // Use a separate sub-directory for each crate in a workspace. This avoids a race condition + // where `prepare` can pull in queries from multiple crates if they happen to be generated + // simultaneously (e.g. Rust Analyzer building in the background). + metadata + .current_package() + .map(|pkg| pkg.name()) + .context("Resolving the crate package for the current working directory failed")? + }; + let pattern = metadata + .target_directory() + .join("sqlx") + .join(package_dir) + .join("query-*.json"); let mut data = BTreeMap::new(); diff --git a/sqlx-cli/tests/assets/sample_metadata.json b/sqlx-cli/tests/assets/sample_metadata.json index 7c57b276a2..74752d4a18 100644 --- a/sqlx-cli/tests/assets/sample_metadata.json +++ b/sqlx-cli/tests/assets/sample_metadata.json @@ -30506,7 +30506,7 @@ "features": [] } ], - "root": null + "root": "b_in_workspace_lib 0.1.0 (path+file:///home/user/problematic/workspace/b_in_workspace_lib)" }, "target_directory": "/home/user/problematic/workspace/target", "version": 1, diff --git a/sqlx-macros/src/query/mod.rs b/sqlx-macros/src/query/mod.rs index e036fd72c9..44a540be8e 100644 --- a/sqlx-macros/src/query/mod.rs +++ b/sqlx-macros/src/query/mod.rs @@ -31,6 +31,8 @@ struct Metadata { offline: bool, database_url: Option, #[cfg(feature = "offline")] + package_name: String, + #[cfg(feature = "offline")] target_dir: PathBuf, #[cfg(feature = "offline")] workspace_root: Arc>>, @@ -74,6 +76,11 @@ static METADATA: Lazy = Lazy::new(|| { .expect("`CARGO_MANIFEST_DIR` must be set") .into(); + #[cfg(feature = "offline")] + let package_name: String = env("CARGO_PKG_NAME") + .expect("`CARGO_PKG_NAME` must be set") + .into(); + #[cfg(feature = "offline")] let target_dir = env("CARGO_TARGET_DIR").map_or_else(|_| "target".into(), |dir| dir.into()); @@ -110,6 +117,8 @@ static METADATA: Lazy = Lazy::new(|| { offline, database_url, #[cfg(feature = "offline")] + package_name, + #[cfg(feature = "offline")] target_dir, #[cfg(feature = "offline")] workspace_root: Arc::new(Mutex::new(None)), @@ -402,7 +411,13 @@ where // If the build is offline, the cache is our input so it's pointless to also write data for it. #[cfg(feature = "offline")] if !offline { - let save_dir = METADATA.target_dir.join("sqlx"); + // Use a separate sub-directory for each crate in a workspace. This avoids a race condition + // where `prepare` can pull in queries from multiple crates if they happen to be generated + // simultaneously (e.g. Rust Analyzer building in the background). + let save_dir = METADATA + .target_dir + .join("sqlx") + .join(&METADATA.package_name); std::fs::create_dir_all(&save_dir)?; data.save_in(save_dir, input.src_span)?; }