From 98649c54e344eeaa02f27c955a0b06d705f50130 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 18 Sep 2026 13:23:35 +0100 Subject: [PATCH] fix: distinguish Snowflake START WITH from CTEs --- .../src/sql_lint/_helpers/engine.rs | 12 ++++++++---- .../src/sql_lint/tests/test_sql_lint.rs | 6 ++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/sqlbuild-rules-native/src/sql_lint/_helpers/engine.rs b/crates/sqlbuild-rules-native/src/sql_lint/_helpers/engine.rs index 68ae046abc..4a692e1352 100644 --- a/crates/sqlbuild-rules-native/src/sql_lint/_helpers/engine.rs +++ b/crates/sqlbuild-rules-native/src/sql_lint/_helpers/engine.rs @@ -515,12 +515,16 @@ fn collect_rule_migration_facts( facts.cross_joins.push(tokens[index].span); } if text.eq_ignore_ascii_case("with") { - if depths[index] > 0 { + let starts_hierarchy = position.checked_sub(1).is_some_and(|previous| { + tokens[significant[previous]].token_type == TokenType::Start + }); + if depths[index] > 0 && !starts_hierarchy { facts.nested_ctes.push(tokens[index].span); } - if significant - .get(position + 1) - .is_some_and(|&next| tokens[next].text.eq_ignore_ascii_case("recursive")) + if !starts_hierarchy + && significant + .get(position + 1) + .is_some_and(|&next| tokens[next].text.eq_ignore_ascii_case("recursive")) { facts.recursive_ctes.push(tokens[index].span); } diff --git a/crates/sqlbuild-rules-native/src/sql_lint/tests/test_sql_lint.rs b/crates/sqlbuild-rules-native/src/sql_lint/tests/test_sql_lint.rs index c344a2dceb..5e4c0d0ed0 100644 --- a/crates/sqlbuild-rules-native/src/sql_lint/tests/test_sql_lint.rs +++ b/crates/sqlbuild-rules-native/src/sql_lint/tests/test_sql_lint.rs @@ -443,6 +443,12 @@ fn given_plain_sql_rules_when_linting_then_project_context_is_not_required() -> rule: "SQBRSQL036", expected_count: 1, }, + test_types::PlainSqlLintTestCase { + description: "Snowflake hierarchy START WITH is not a nested CTE", + sql: "WITH entities AS (SELECT id, parent_id FROM source_rows), hierarchy AS (SELECT id FROM entities START WITH parent_id IS NULL CONNECT BY parent_id = PRIOR id) SELECT id FROM hierarchy", + rule: "SQBRSQL036", + expected_count: 0, + }, test_types::PlainSqlLintTestCase { description: "recursive CTE", sql: "WITH RECURSIVE numbers AS (SELECT 1 UNION ALL SELECT 2) SELECT * FROM numbers",