Skip to content

Commit 772b90b

Browse files
authored
feat: don't subst env vars for system (#223)
1 parent 2a451cc commit 772b90b

File tree

4 files changed

+51
-27
lines changed

4 files changed

+51
-27
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
**Breaking changes**:
1111
* runner: `RecordOutput` is now returned by `Runner::run` (or `Runner::run_async`). This allows users to access the output of each record, or check whether the record is skipped.
12-
* substitution: add a special variable `__NOW__` which will be replaced with the current Unix timestamp in nanoseconds.
12+
* runner(substitution): add a special variable `__NOW__` which will be replaced with the current Unix timestamp in nanoseconds.
13+
* runner(substitution): for `system` commands, we do not substitute environment variables any more, because the shell can do that. It's necessary to escape like `\\` any more. `$__TEST_DIR__`, and are still supported.
1314

1415
## [0.20.6] - 2024-06-21
1516

sqllogictest/src/runner.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ impl<D: AsyncDB, M: MakeConnection<Conn = D>> Runner<D, M> {
576576
expected: _,
577577
loc: _,
578578
} => {
579-
let sql = match self.may_substitute(sql) {
579+
let sql = match self.may_substitute(sql, true) {
580580
Ok(sql) => sql,
581581
Err(error) => {
582582
return RecordOutput::Statement {
@@ -627,7 +627,7 @@ impl<D: AsyncDB, M: MakeConnection<Conn = D>> Runner<D, M> {
627627
return RecordOutput::Nothing;
628628
}
629629

630-
let mut command = match self.may_substitute(command) {
630+
let mut command = match self.may_substitute(command, false) {
631631
Ok(command) => command,
632632
Err(error) => {
633633
return RecordOutput::System {
@@ -723,7 +723,7 @@ impl<D: AsyncDB, M: MakeConnection<Conn = D>> Runner<D, M> {
723723
expected,
724724
loc: _,
725725
} => {
726-
let sql = match self.may_substitute(sql) {
726+
let sql = match self.may_substitute(sql, true) {
727727
Ok(sql) => sql,
728728
Err(error) => {
729729
return RecordOutput::Query {
@@ -1196,12 +1196,17 @@ impl<D: AsyncDB, M: MakeConnection<Conn = D>> Runner<D, M> {
11961196

11971197
/// Substitute the input SQL or command with [`Substitution`], if enabled by `control
11981198
/// substitution`.
1199-
fn may_substitute(&self, input: String) -> Result<String, AnyError> {
1200-
#[derive(thiserror::Error, Debug)]
1201-
#[error("substitution failed: {0}")]
1202-
struct SubstError(subst::Error);
1199+
///
1200+
/// If `subst_env_vars`, we will use the `subst` crate to support extensive substitutions, incl. `$NAME`, `${NAME}`, `${NAME:default}`.
1201+
/// The cost is that we will have to use escape characters, e.g., `\$` & `\\`.
1202+
///
1203+
/// Otherwise, we just do simple string substitution for `__TEST_DIR__` and `__NOW__`.
1204+
/// This is useful for `system` commands: The shell can do the environment variables, and we can write strings like `\n` without escaping.
1205+
fn may_substitute(&self, input: String, subst_env_vars: bool) -> Result<String, AnyError> {
12031206
if let Some(substitution) = &self.substitution {
1204-
subst::substitute(&input, substitution).map_err(|e| Arc::new(SubstError(e)) as AnyError)
1207+
substitution
1208+
.substitute(&input, subst_env_vars)
1209+
.map_err(|e| Arc::new(e) as AnyError)
12051210
} else {
12061211
Ok(input)
12071212
}

sqllogictest/src/substitution.rs

+33-14
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,44 @@ pub(crate) struct Substitution {
1111
test_dir: Arc<OnceLock<TempDir>>,
1212
}
1313

14+
#[derive(thiserror::Error, Debug)]
15+
#[error("substitution failed: {0}")]
16+
pub(crate) struct SubstError(subst::Error);
17+
18+
impl Substitution {
19+
pub fn substitute(&self, input: &str, subst_env_vars: bool) -> Result<String, SubstError> {
20+
if !subst_env_vars {
21+
Ok(input
22+
.replace("$__TEST_DIR__", &self.test_dir())
23+
.replace("$__NOW__", &self.now()))
24+
} else {
25+
subst::substitute(input, self).map_err(SubstError)
26+
}
27+
}
28+
29+
fn test_dir(&self) -> String {
30+
let test_dir = self
31+
.test_dir
32+
.get_or_init(|| tempdir().expect("failed to create testdir"));
33+
test_dir.path().to_string_lossy().into_owned()
34+
}
35+
36+
fn now(&self) -> String {
37+
std::time::SystemTime::now()
38+
.duration_since(std::time::UNIX_EPOCH)
39+
.expect("failed to get current time")
40+
.as_nanos()
41+
.to_string()
42+
}
43+
}
44+
1445
impl<'a> subst::VariableMap<'a> for Substitution {
1546
type Value = String;
1647

1748
fn get(&'a self, key: &str) -> Option<Self::Value> {
1849
match key {
19-
"__TEST_DIR__" => {
20-
let test_dir = self
21-
.test_dir
22-
.get_or_init(|| tempdir().expect("failed to create testdir"));
23-
test_dir.path().to_string_lossy().into_owned().into()
24-
}
25-
26-
"__NOW__" => std::time::SystemTime::now()
27-
.duration_since(std::time::UNIX_EPOCH)
28-
.expect("failed to get current time")
29-
.as_nanos()
30-
.to_string()
31-
.into(),
32-
50+
"__TEST_DIR__" => self.test_dir().into(),
51+
"__NOW__" => self.now().into(),
3352
key => Env.get(key),
3453
}
3554
}

tests/system_command/system_command.slt

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
control substitution on
22

33
system ok
4-
echo 114514 > ${__TEST_DIR__}/test.txt
4+
echo 114514 > $__TEST_DIR__/test.txt
55

66
query T
77
select read("${__TEST_DIR__}/test.txt")
88
----
99
114514
1010

1111
system ok
12-
echo 1919810 > ${__TEST_DIR__}/test.txt
12+
echo 1919810 > $__TEST_DIR__/test.txt
1313

1414
query T
1515
select read("${__TEST_DIR__}/test.txt")
@@ -23,10 +23,9 @@ echo "114514"
2323
114514
2424

2525

26-
# Note: when substitution is on, we need to escape the backslash
2726
# Note: 1 blank line in the middle is ok, but not 2
2827
system ok
29-
echo "114\\n\\n514"
28+
echo "114\n\n514"
3029
----
3130
114
3231

0 commit comments

Comments
 (0)