{"record":{"id":"fcbe137abb11dd38","repo":"dbt-labs/dbt-core","slug":"forward-fill-projection-column-order-drift-should","errorCode":null,"errorMessage":"Forward-fill projection column order drift should be ignored","messagePattern":"Forward-fill projection column order drift should be ignored","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbt-adapter/src/sql/diff.rs","lineNumber":3760,"sourceCode":"  billing_group_id,\n  __as_of,\n  last_value(b ignore nulls) over (\n    partition by billing_group_id\n    order by __as_of\n    rows between unbounded preceding and current row\n  ) as b,\n  last_value(a ignore nulls) over (\n    partition by billing_group_id\n    order by __as_of\n    rows between unbounded preceding and current row\n  ) as a,\n  rn\nfrom filled_data\nqualify row_number() over (partition by billing_group_id, __as_of order by rn desc) = 1\n\"#;\n\n        compare_sql(sql_fusion, sql_recorded, AdapterType::Snowflake)\n            .expect(\"Forward-fill projection column order drift should be ignored\");\n    }\n\n    #[test]\n    fn test_split_union_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      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\"]);","sourceCodeStart":3742,"sourceCodeEnd":3778,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-adapter/src/sql/diff.rs#L3742-L3778","documentation":"Test panic from .expect at crates/dbt-adapter/src/sql/diff.rs:3760: compare_sql between two Snowflake forward-fill queries (CTE pipeline ending in `qualify row_number() over (partition by billing_group_id, __as_of order by rn desc) = 1`) returned Err because the projection column order of `filled_data`/final SELECT differs. The differ should treat reordered projection columns as equivalent so recorded and Fusion-generated SQL match.","triggerScenarios":"cargo test -p dbt-adapter with test at diff.rs:3760: sql_fusion and sql_recorded differ only in the column order of the forward-fill projection, and compare_sql(sql_fusion, sql_recorded, AdapterType::Snowflake) yields Err, panicking the expect.","commonSituations":"The forward-fill macro/generator emits SELECT columns in a different order than the recorded snapshot; canonicalizer's projection reordering fails when columns feed a QUALIFY/window clause; replay comparisons of incremental forward-fill models flag ordering drift.","solutions":["Read the Err detail to find which projection column pair failed to canonicalize to the same form","Ensure alias-based projection reordering is applied to the final SELECT feeding the QUALIFY clause, not just plain SELECTs","Check whether window-function references (rn in QUALIFY) bypassed the projection normalization","If the drift is real (different expression per column), fix the generator"],"exampleFix":"// before\ncompare_sql(sql_fusion, sql_recorded, AdapterType::Snowflake)\n    .expect(\"Forward-fill projection column order drift should be ignored\");\n// after (library-side: reorder projections by alias before compare, incl. QUALIFY-fed selects)\nlet norm = |s: &str| sort_projections_by_alias(canonicalize(s, AdapterType::Snowflake));\nassert_eq!(norm(sql_fusion), norm(sql_recorded));","handlingStrategy":"validation","validationCode":"let cols_a = projection_for_all(sql_fusion);\nlet cols_b = projection_for_all(sql_recorded);\nif BTreeSet::from_iter(cols_a) != BTreeSet::from_iter(cols_b) {\n    eprintln!(\"projection sets differ; order-insensitive compare will fail\");\n}","typeGuard":null,"tryCatchPattern":"compare_sql(sql_fusion, sql_recorded, AdapterType::Snowflake)\n    .unwrap_or_else(|e| eprintln!(\"forward-fill drift: {e}\"));","preventionTips":["Apply alias-based projection reordering to SELECTs feeding QUALIFY/window clauses too","Pin generated column order in forward-fill macros, or normalize before diffing","Test CTE pipelines ending in qualify/row_number patterns","Diff the canonical forms when the test fails to locate the true divergence"],"tags":["rust","sql-diff","test-panic","snowflake","qualify","window-function"],"backgroundTag":"sql-diff-mismatch","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"}