Skip to content

fix(macros): cache macro metadata based on CARGO_MANIFEST_DIR #3815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ time = { version = "0.3.36", features = ["formatting", "parsing", "macros"] }
uuid = "1.1.2"

# Common utility crates
dotenvy = { version = "0.15.0", default-features = false }
dotenvy = { version = "0.15.7", default-features = false }

# Runtimes
[workspace.dependencies.async-std]
Expand Down
4 changes: 2 additions & 2 deletions sqlx-macros-core/src/query/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ impl DynQueryData {
let mut cache = OFFLINE_DATA_CACHE
.lock()
// Just reset the cache on error
.unwrap_or_else(|posion_err| {
let mut guard = posion_err.into_inner();
.unwrap_or_else(|poison_err| {
let mut guard = poison_err.into_inner();
*guard = Default::default();
guard
});
Expand Down
43 changes: 29 additions & 14 deletions sqlx-macros-core/src/query/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::{fs, io};
Expand Down Expand Up @@ -106,27 +107,28 @@ impl Metadata {
}
}

static METADATA: Lazy<Mutex<HashMap<String, Metadata>>> = Lazy::new(Default::default);

// If we are in a workspace, lookup `workspace_root` since `CARGO_MANIFEST_DIR` won't
// reflect the workspace dir: https://github.com/rust-lang/cargo/issues/3946
static METADATA: Lazy<Metadata> = Lazy::new(|| {
let manifest_dir: PathBuf = env("CARGO_MANIFEST_DIR")
.expect("`CARGO_MANIFEST_DIR` must be set")
.into();
fn init_metadata(manifest_dir: &String) -> Metadata {
let manifest_dir: PathBuf = manifest_dir.into();

// If a .env file exists at CARGO_MANIFEST_DIR, load environment variables from this,
// otherwise fallback to default dotenv behaviour.
let env_path = manifest_dir.join(".env");

#[cfg_attr(not(procmacro2_semver_exempt), allow(unused_variables))]
let env_path = if env_path.exists() {
let res = dotenvy::from_path(&env_path);
// Load the new environment variables and override the old ones if necessary.
let res = dotenvy::from_path_override(&env_path);
if let Err(e) = res {
panic!("failed to load environment from {env_path:?}, {e}");
}

Some(env_path)
} else {
dotenvy::dotenv().ok()
dotenvy::dotenv_override().ok()
};

// tell the compiler to watch the `.env` for changes, if applicable
Expand All @@ -147,32 +149,46 @@ static METADATA: Lazy<Metadata> = Lazy::new(|| {
database_url,
workspace_root: Arc::new(Mutex::new(None)),
}
});
}

pub fn expand_input<'a>(
input: QueryMacroInput,
drivers: impl IntoIterator<Item = &'a QueryDriver>,
) -> crate::Result<TokenStream> {
let data_source = match &*METADATA {
let manifest_dir = env("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` must be set");

let mut metadata_lock = METADATA
.lock()
// Just reset the metadata on error
.unwrap_or_else(|poison_err| {
let mut guard = poison_err.into_inner();
*guard = Default::default();
guard
});

let metadata = metadata_lock
.entry(manifest_dir)
.or_insert_with_key(init_metadata);

let data_source = match &metadata {
Metadata {
offline: false,
database_url: Some(db_url),
..
} => QueryDataSource::live(db_url)?,

Metadata { offline, .. } => {
// Try load the cached query metadata file.
let filename = format!("query-{}.json", hash_string(&input.sql));

// Check SQLX_OFFLINE_DIR, then local .sqlx, then workspace .sqlx.
let dirs = [
|| env("SQLX_OFFLINE_DIR").ok().map(PathBuf::from),
|| Some(METADATA.manifest_dir.join(".sqlx")),
|| Some(METADATA.workspace_root().join(".sqlx")),
|_: &Metadata| env("SQLX_OFFLINE_DIR").ok().map(PathBuf::from),
|meta: &Metadata| Some(meta.manifest_dir.join(".sqlx")),
|meta: &Metadata| Some(meta.workspace_root().join(".sqlx")),
];
let Some(data_file_path) = dirs
.iter()
.filter_map(|path| path())
.filter_map(|path| path(metadata))
.map(|path| path.join(&filename))
.find(|path| path.exists())
else {
Expand All @@ -184,7 +200,6 @@ pub fn expand_input<'a>(
}.into()
);
};

QueryDataSource::Cached(DynQueryData::from_data_file(&data_file_path, &input.sql)?)
}
};
Expand Down
Loading