Skip to content

Commit 3a5aaaf

Browse files
Benoit Aubuchonclaude
andcommitted
fix: make DDL depth trackers quote-aware to handle parens in string literals
Column block walker: a ')' inside DEFAULT ')' no longer prematurely ends the column block — added single/double-quote state tracking to skip paren chars inside quoted values. find_top_level_keyword: quote tracking previously only activated at depth==0, so a ')' inside SOURCE credentials like PASSWORD 'p@ss)word' would corrupt the depth counter. Quote tracking now applies at all depths. Adds regression tests for both cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 11525b6 commit 3a5aaaf

1 file changed

Lines changed: 36 additions & 2 deletions

File tree

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

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,28 @@ fn dicts_ddl_equivalent(actual_ddl: &str, desired_ddl: &str) -> bool {
152152

153153
// Walk the DDL from the opening `(` tracking depth to find the
154154
// matching `)` that closes the column list.
155+
// Quote-aware: a `)` inside a DEFAULT value like `DEFAULT ')'` must not
156+
// prematurely end the column block.
155157
let mut depth = 0usize;
158+
let mut in_single = false;
159+
let mut in_double = false;
156160
let mut end = start;
157161
for (off, ch) in ddl[start..].char_indices() {
162+
if in_single {
163+
if ch == '\'' {
164+
in_single = false;
165+
}
166+
continue;
167+
}
168+
if in_double {
169+
if ch == '"' {
170+
in_double = false;
171+
}
172+
continue;
173+
}
158174
match ch {
175+
'\'' => in_single = true,
176+
'"' => in_double = true,
159177
'(' => depth += 1,
160178
')' => {
161179
depth -= 1;
@@ -215,8 +233,10 @@ fn dicts_ddl_equivalent(actual_ddl: &str, desired_ddl: &str) -> bool {
215233
match c {
216234
'(' => depth += 1,
217235
')' => depth = depth.saturating_sub(1),
218-
'\'' if depth == 0 => in_single_quote = true,
219-
'"' if depth == 0 => in_double_quote = true,
236+
// Track quotes at any depth so a ')' inside SOURCE(MYSQL(PASSWORD 'p@ss)word'))
237+
// does not corrupt the depth counter.
238+
'\'' => in_single_quote = true,
239+
'"' => in_double_quote = true,
220240
_ if depth == 0 => {
221241
for &kw in keywords {
222242
if text[abs_pos..].starts_with(kw) {
@@ -2320,6 +2340,20 @@ mod tests {
23202340
assert!(!dicts_ddl_equivalent(actual, desired));
23212341
}
23222342

2343+
#[test]
2344+
fn test_dicts_ddl_equivalent_column_default_with_paren() {
2345+
// A column DEFAULT containing ')' must not prematurely close the column block.
2346+
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)";
2347+
assert!(dicts_ddl_equivalent(ddl, ddl));
2348+
}
2349+
2350+
#[test]
2351+
fn test_dicts_ddl_equivalent_source_password_with_paren() {
2352+
// A SOURCE credential containing ')' must not corrupt clause boundary detection.
2353+
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)";
2354+
assert!(dicts_ddl_equivalent(ddl, ddl));
2355+
}
2356+
23232357
// ─── Structural mismatch detection tests ───────────────────────────────
23242358

23252359
#[tokio::test]

0 commit comments

Comments
 (0)