Skip to content

Commit d2000bb

Browse files
Benoit Aubuchonclaude
andcommitted
fix: handle backslash-escaped quotes in column block and keyword scanners
The single-quote state handlers in the column block walker and `find_top_level_keyword` would exit on any `'` character, including backslash-escaped ones (`\'`). If `)` appeared after a false quote-close inside a SOURCE credential, depth tracking became corrupted, causing incorrect clause boundary detection. Add a `skip_next_single` flag (matching the approach used in `find_closing_quote`) so `\'` is treated as an embedded quote rather than a string terminator. Add tests for both scanners covering escaped-quote + `)` sequences. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b0d3f66 commit d2000bb

1 file changed

Lines changed: 36 additions & 0 deletions

File tree

apps/framework-cli/src/framework/core/infra_reality_checker.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,18 @@ fn dicts_ddl_equivalent(actual_ddl: &str, desired_ddl: &str) -> bool {
220220
let mut in_single = false;
221221
let mut in_double = false;
222222
let mut in_backtick = false;
223+
let mut skip_next_single = false;
223224
let mut end = start;
224225
for (off, ch) in ddl[start..].char_indices() {
225226
if in_single {
227+
if skip_next_single {
228+
skip_next_single = false;
229+
continue;
230+
}
231+
if ch == '\\' {
232+
skip_next_single = true;
233+
continue;
234+
}
226235
if ch == '\'' {
227236
in_single = false;
228237
}
@@ -285,11 +294,20 @@ fn dicts_ddl_equivalent(actual_ddl: &str, desired_ddl: &str) -> bool {
285294
let mut in_single_quote = false;
286295
let mut in_double_quote = false;
287296
let mut in_backtick = false;
297+
let mut skip_next_single = false;
288298

289299
for (i, c) in text[from..].char_indices() {
290300
let abs_pos = from + i;
291301

292302
if in_single_quote {
303+
if skip_next_single {
304+
skip_next_single = false;
305+
continue;
306+
}
307+
if c == '\\' {
308+
skip_next_single = true;
309+
continue;
310+
}
293311
if c == '\'' {
294312
in_single_quote = false;
295313
}
@@ -2439,6 +2457,24 @@ mod tests {
24392457
assert!(dicts_ddl_equivalent(ddl, ddl));
24402458
}
24412459

2460+
#[test]
2461+
fn test_dicts_ddl_equivalent_column_default_escaped_quote_with_paren() {
2462+
// A column DEFAULT whose value contains a backslash-escaped single quote
2463+
// followed by ')' must not prematurely close the column block. Without the
2464+
// backslash-skip fix the scanner exits in_single at the escaped quote and
2465+
// then sees ')' outside any string, corrupting depth tracking.
2466+
let ddl = "CREATE DICTIONARY `db`.`d` (\n `id` UInt64,\n `status` String DEFAULT '\\')' \n)\nPRIMARY KEY `id`\nSOURCE(CLICKHOUSE(TABLE 'src'))\nLAYOUT(HASHED())\nLIFETIME(MIN 0 MAX 300)";
2467+
assert!(dicts_ddl_equivalent(ddl, ddl));
2468+
}
2469+
2470+
#[test]
2471+
fn test_dicts_ddl_equivalent_source_password_escaped_quote_with_paren() {
2472+
// A SOURCE credential containing a backslash-escaped single quote followed
2473+
// by ')' must not corrupt clause boundary detection in find_top_level_keyword.
2474+
let ddl = "CREATE DICTIONARY `db`.`d` (\n `id` UInt64\n)\nPRIMARY KEY `id`\nSOURCE(MYSQL(HOST 'localhost' PORT 3306 USER 'user' PASSWORD 'p@ss\\')word' DB 'mydb' TABLE 'src'))\nLAYOUT(HASHED())\nLIFETIME(MIN 0 MAX 300)";
2475+
assert!(dicts_ddl_equivalent(ddl, ddl));
2476+
}
2477+
24422478
#[test]
24432479
fn test_dicts_ddl_equivalent_column_backtick_identifier_with_paren() {
24442480
// A backtick-quoted column identifier containing ')' must not prematurely

0 commit comments

Comments
 (0)