Skip to content

Commit 9ec03bf

Browse files
committed
feat: add __RUN_ID__ substitution variable
DuckDB derives an attached database name from the file basename, ignoring the directory. A test re-run against a shared server (e.g. once per engine) therefore reuses the basename and fails "database already exists" on a plain ATTACH without AS. __RUN_ID__ expands to the per-run temp-dir random suffix (stable within a run, unique across runs), so embedding it in ATTACH file names makes the derived name unique per run.
1 parent d3d5b08 commit 9ec03bf

2 files changed

Lines changed: 32 additions & 4 deletions

File tree

sqllogictest/src/runner.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -670,8 +670,8 @@ pub(crate) struct RunnerLocals {
670670
}
671671

672672
impl RunnerLocals {
673-
pub fn test_dir(&self) -> String {
674-
let test_dir = self.test_dir.get_or_init(|| {
673+
fn test_dir_handle(&self) -> &TempDir {
674+
self.test_dir.get_or_init(|| {
675675
let dir = TempDir::new().expect("failed to create testdir");
676676
// When the runner and the database server run in separate containers,
677677
// server-side ops (COPY TO/FROM, ATTACH) write into __TEST_DIR__ as a
@@ -684,8 +684,30 @@ impl RunnerLocals {
684684
.expect("failed to relax testdir permissions");
685685
}
686686
dir
687-
});
688-
test_dir.path().to_string_lossy().into_owned()
687+
})
688+
}
689+
690+
pub fn test_dir(&self) -> String {
691+
self.test_dir_handle().path().to_string_lossy().into_owned()
692+
}
693+
694+
/// A short alphanumeric token unique to this runner instance (derived from the
695+
/// random temp-dir name), stable for the whole run. DuckDB derives an attached
696+
/// database's name from the file basename only, ignoring the directory, so the
697+
/// same test re-run against a shared server (e.g. once per engine) would reuse
698+
/// the basename and collide ("database already exists"). Embedding `__RUN_ID__`
699+
/// in the file name makes the derived name unique per run.
700+
pub fn run_id(&self) -> String {
701+
self.test_dir_handle()
702+
.path()
703+
.file_name()
704+
.map(|name| {
705+
name.to_string_lossy()
706+
.chars()
707+
.filter(|c| c.is_ascii_alphanumeric())
708+
.collect()
709+
})
710+
.unwrap_or_default()
689711
}
690712

691713
fn set_var(&mut self, key: String, value: String) {

sqllogictest/src/substitution.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::RunnerLocals;
44

55
pub mod well_known {
66
pub const TEST_DIR: &str = "__TEST_DIR__";
7+
pub const RUN_ID: &str = "__RUN_ID__";
78
pub const NOW: &str = "__NOW__";
89
pub const DATABASE: &str = "__DATABASE__";
910
}
@@ -80,6 +81,10 @@ impl Substitution<'_> {
8081
&format!("${}", well_known::TEST_DIR),
8182
&self.runner_locals.test_dir(),
8283
)
84+
.replace(
85+
&format!("${}", well_known::RUN_ID),
86+
&self.runner_locals.run_id(),
87+
)
8388
.replace(&format!("${}", well_known::NOW), &now_string());
8489
for (key, value) in self.runner_locals.vars() {
8590
res = res.replace(&format!("${}", key), value);
@@ -94,6 +99,7 @@ impl<'a> subst::VariableMap<'a> for Substitution<'a> {
9499
fn get(&'a self, key: &str) -> Option<Self::Value> {
95100
match key {
96101
well_known::TEST_DIR => self.runner_locals.test_dir().into(),
102+
well_known::RUN_ID => self.runner_locals.run_id().into(),
97103
well_known::NOW => now_string().into(),
98104
key => self
99105
.runner_locals

0 commit comments

Comments
 (0)