From bacbbc6f3d417cfa50015f6f1e99fe9186bf4408 Mon Sep 17 00:00:00 2001 From: Dennis Felsing Date: Sun, 10 May 2026 15:12:42 +0000 Subject: [PATCH] testdrive: escape quotes and backslashes in rewrite_result MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Values containing whitespace combined with `"` or `\` were wrapped in quotes without escaping, causing the parser to either fail with "unterminated quote" or silently corrupt expected values (e.g. `\n` inside a path becomes a newline). Introduce a small `escape_for_parser` helper that emits only the escape sequences `parser::split_line` recognises (`\\`, `\"`, `\n`, `\t`, `\r`, `\0`) and use it from both `rewrite_result` and `TestdriveRow::fmt`. This avoids `format!("{:?}", …)`, whose Debug impl also emits `\u{N}` for non-printable Unicode — escapes the parser does not understand and would silently corrupt on the next read. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/testdrive/src/action/sql.rs | 34 ++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/testdrive/src/action/sql.rs b/src/testdrive/src/action/sql.rs index 02b4f2cdf7c54..a68846a5b4c8e 100644 --- a/src/testdrive/src/action/sql.rs +++ b/src/testdrive/src/action/sql.rs @@ -151,6 +151,30 @@ pub async fn run_sql(mut cmd: SqlCommand, state: &mut State) -> Result String { + let mut out = String::with_capacity(value.len() + 2); + out.push('"'); + for c in value.chars() { + match c { + '\\' => out.push_str("\\\\"), + '"' => out.push_str("\\\""), + '\n' => out.push_str("\\n"), + '\t' => out.push_str("\\t"), + '\r' => out.push_str("\\r"), + '\0' => out.push_str("\\0"), + c => out.push(c), + } + } + out.push('"'); + out +} + fn rewrite_result( state: &mut State, columns: Vec<&str>, @@ -162,8 +186,12 @@ fn rewrite_result( for row in content { let mut formatted_row = Vec::::new(); for value in row { - if value.is_empty() || value.contains(|x: char| char::is_ascii_whitespace(&x)) { - formatted_row.push("\"".to_owned() + &value + "\""); + if value.is_empty() + || value.contains(|x: char| char::is_ascii_whitespace(&x)) + || value.contains('"') + || value.contains('\\') + { + formatted_row.push(escape_for_parser(&value)); } else { formatted_row.push(value); } @@ -827,7 +855,7 @@ impl Display for TestdriveRow<'_> { for col_str in &self.0[0..self.0.len()] { if col_str.contains(' ') || col_str.contains('"') || col_str.is_empty() { - cols.push(format!("{:?}", col_str)); + cols.push(escape_for_parser(col_str)); } else { cols.push(col_str.to_string()); }