{"record":{"id":"b87239626d56245f","repo":"dbt-labs/dbt-core","slug":"should-split-on-the-top-level-union-all","errorCode":null,"errorMessage":"should split on the top-level UNION ALL","messagePattern":"should split on the top-level UNION ALL","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbt-adapter/src/sql/diff.rs","lineNumber":3785,"sourceCode":"        let sql = \"select 1 as a /* “unicode” */ UNION      select 2 as b\";\n        let parts = split_union_top_level(sql).expect(\"should split on top-level UNION\");\n        assert_eq!(parts, vec![\"select 1 as a\", \"select 2 as b\"]);\n    }\n\n    #[test]\n    fn test_split_union_all_top_level_splits_and_handles_unicode() {\n        // Regression test: previously this could panic if the scan index landed in the middle\n        // of a multi-byte UTF-8 char (e.g. “).\n        let sql = \"select 1 as a /* “unicode” */ UNION   ALL   select 2 as b\";\n        let parts = split_union_all_top_level(sql).expect(\"should split on top-level UNION ALL\");\n        assert_eq!(parts, vec![\"select 1 as a\", \"select 2 as b\"]);\n    }\n\n    #[test]\n    fn test_split_union_all_top_level_does_not_split_inside_parentheses() {\n        let sql = \"select 1 as a union all select (select 2 as b union all select 3 as c)\";\n        let parts =\n            split_union_all_top_level(sql).expect(\"should split on the top-level UNION ALL\");\n        assert_eq!(\n            parts,\n            vec![\n                \"select 1 as a\",\n                \"select (select 2 as b union all select 3 as c)\"\n            ]\n        );\n    }\n\n    #[test]\n    fn test_empty_sql_comparison() {\n        let result1 = compare_sql(\"\", \"\", AdapterType::Snowflake);\n        assert!(result1.is_ok(), \"Empty SQL should match empty SQL\");\n\n        let result2 = compare_sql(\"SELECT 1\", \"\", AdapterType::Snowflake);\n        assert!(result2.is_err(), \"Non-empty SQL should not match empty SQL\");\n\n        let result3 = compare_sql(\"\", \"SELECT 1\", AdapterType::Snowflake);","sourceCodeStart":3767,"sourceCodeEnd":3803,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-adapter/src/sql/diff.rs#L3767-L3803","documentation":"Test panic from Option::expect at crates/dbt-adapter/src/sql/diff.rs:3785: split_union_all_top_level(sql) returned None for input whose only top-level UNION ALL sits between two top-level SELECTs while a nested UNION ALL lives inside parentheses. The splitter must split on the outer operator only, returning [\"select 1 as a\", \"select (select 2 as b union all select 3 as c)\"]; returning None (or splitting at the wrong depth) means paren-depth tracking failed.","triggerScenarios":"cargo test -p dbt-adapter with test_split_union_all_top_level_does_not_split_inside_parentheses: input \"select 1 as a union all select (select 2 as b union all select 3 as c)\" and split_union_all_top_level returns None, panicking \"should split on the top-level UNION ALL\".","commonSituations":"A depth-tracking fix (e.g. switching to char_indices) accidentally started depth at the wrong value or never decremented, so even the top-level UNION ALL was treated as nested; scalar subqueries in the second branch of a UNION ALL are common in generated SQL and replay comparisons.","solutions":["Verify parenthesis depth tracking increments on '(' and decrements on ')' starting at 0 for the whole string","Ensure UNION ALL is only a split candidate when depth == 0 and not inside a string or comment","Check that the scanner still scans after the nested subquery (does not stop at the first parenthesis)","Add nested-subquery + smart-quote combined cases to the splitter unit tests"],"exampleFix":"// before: splits or bails at any UNION ALL\ndepth = 0; ... if kw == \"UNION ALL\" { split_here(); }\n// after: depth-gated\nif kw == \"UNION ALL\" && depth == 0 && !in_string && !in_comment { split_here(); }","handlingStrategy":"validation","validationCode":"// depth-aware pre-scan: verify exactly one top-level UNION ALL\nlet depth = std::cell::Cell::new(0i32);\nlet top_level = count_keyword_at_depth(sql, \"UNION ALL\", &depth); // depth==0 only\nif top_level != 1 { eprintln!(\"expected exactly one top-level UNION ALL, got {top_level}\"); }","typeGuard":null,"tryCatchPattern":"match split_union_all_top_level(sql) {\n    Some(parts) => assert_eq!(parts.len(), 2),\n    None => eprintln!(\"splitter failed to find top-level UNION ALL\"),\n}","preventionTips":["Track parenthesis depth across the whole string; only split at depth 0","Don't stop scanning at the first '(' — nested subqueries must be consumed","Unit-test scalar subqueries containing their own UNION ALL","Combine nested-paren and unicode-comment cases in splitter tests"],"tags":["rust","sql-parsing","test-panic","parentheses","union-all"],"backgroundTag":"sql-parse-failed","analyzedSha":"0267ce9170576975b76b64ce856b2e5848e96617","analyzedAt":"2026-09-07T21:53:39.732Z","contentChangedAt":"2026-09-07T21:53:39.732Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}