Skip to content

Commit cd3bd5f

Browse files
Benoit Aubuchonclaude
andcommitted
fix: track backtick-quoted identifiers in find_top_level_keyword
A column named after a reserved keyword (e.g. \`SOURCE\`) in a PRIMARY KEY clause would pass both word-boundary checks since backtick satisfies !p.is_alphanumeric() && p != '_', causing a false keyword split and spurious drift detection. Add in_backtick state alongside in_single_quote/in_double_quote so keyword scanning is suppressed inside backtick-quoted identifiers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 3a5aaaf commit cd3bd5f

1 file changed

Lines changed: 20 additions & 2 deletions

File tree

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ fn dicts_ddl_equivalent(actual_ddl: &str, desired_ddl: &str) -> bool {
214214
let mut depth: usize = 0;
215215
let mut in_single_quote = false;
216216
let mut in_double_quote = false;
217+
let mut in_backtick = false;
217218

218219
for (i, c) in text[from..].char_indices() {
219220
let abs_pos = from + i;
@@ -230,13 +231,21 @@ fn dicts_ddl_equivalent(actual_ddl: &str, desired_ddl: &str) -> bool {
230231
}
231232
continue;
232233
}
234+
if in_backtick {
235+
if c == '`' {
236+
in_backtick = false;
237+
}
238+
continue;
239+
}
233240
match c {
234241
'(' => depth += 1,
235242
')' => depth = depth.saturating_sub(1),
236-
// Track quotes at any depth so a ')' inside SOURCE(MYSQL(PASSWORD 'p@ss)word'))
237-
// does not corrupt the depth counter.
243+
// Track quotes/backticks at any depth so special chars inside
244+
// SOURCE credentials or backtick-quoted identifiers don't
245+
// corrupt depth tracking or trigger false keyword matches.
238246
'\'' => in_single_quote = true,
239247
'"' => in_double_quote = true,
248+
'`' => in_backtick = true,
240249
_ if depth == 0 => {
241250
for &kw in keywords {
242251
if text[abs_pos..].starts_with(kw) {
@@ -2354,6 +2363,15 @@ mod tests {
23542363
assert!(dicts_ddl_equivalent(ddl, ddl));
23552364
}
23562365

2366+
#[test]
2367+
fn test_dicts_ddl_equivalent_backtick_keyword_identifier() {
2368+
// A column named `SOURCE` (a reserved keyword) in PRIMARY KEY must not be
2369+
// mistaken for the SOURCE clause — backtick-quoted identifiers must be skipped.
2370+
let actual = "CREATE DICTIONARY `db`.`d` (\n `SOURCE` UInt64\n)\nPRIMARY KEY `SOURCE`\nSOURCE(CLICKHOUSE(TABLE 'src'))\nLAYOUT(HASHED())\nLIFETIME(MIN 0 MAX 300)";
2371+
let desired = "CREATE DICTIONARY IF NOT EXISTS `db`.`d` (\n `SOURCE` UInt64\n)\nPRIMARY KEY `SOURCE`\nSOURCE(CLICKHOUSE(TABLE 'src'))\nLAYOUT(HASHED())\nLIFETIME(MIN 0 MAX 300)";
2372+
assert!(dicts_ddl_equivalent(actual, desired));
2373+
}
2374+
23572375
// ─── Structural mismatch detection tests ───────────────────────────────
23582376

23592377
#[tokio::test]

0 commit comments

Comments
 (0)