{"record":{"id":"61c4533a49a213e6","repo":"dbt-labs/dbt-core","slug":"projection-order-drift-should-be-ignored","errorCode":null,"errorMessage":"projection order drift should be ignored","messagePattern":"projection order drift should be ignored","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbt-adapter/src/sql/diff.rs","lineNumber":3559,"sourceCode":"        let expected_sql = format!(\n            \"select {} from t\",\n            expected_alias_order\n                .iter()\n                .map(|a| projection_for(a))\n                .collect::<Vec<_>>()\n                .join(\", \")\n        );\n        let actual_sql = format!(\n            \"select {} from t\",\n            actual_alias_order\n                .iter()\n                .map(|a| projection_for(a))\n                .collect::<Vec<_>>()\n                .join(\", \")\n        );\n\n        compare_sql(&actual_sql, &expected_sql, AdapterType::Bigquery)\n            .expect(\"projection order drift should be ignored\");\n    }\n\n    #[test]\n    fn test_bigquery_struct_projection_order_drift_with_comment_apostrophe_should_be_ignorable() {\n        // Minimal repro for the *ordering-only* Snowplow SQL mismatch observed in replay:\n        //\n        // Some Jinja/adapter code paths build a list of `STRUCT(...) AS <context_alias>` projections\n        // by iterating a map. Different runners/recorders may emit identical projections in\n        // different orders (e.g. insertion-order vs key-sorted iteration). We treat this drift as\n        // ignorable for replay via canonicalization.\n        //\n        // This variant includes a `--` comment containing an apostrophe (e.g. \"hasn't\"), which\n        // appears in the real project SQL. Replay should still treat projection ordering drift as\n        // ignorable.\n        let expected_alias_order = [\n            \"service_configuration_context\",\n            \"modal_entity\",\n            \"experiment_entity\",","sourceCodeStart":3541,"sourceCodeEnd":3577,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-adapter/src/sql/diff.rs#L3541-L3577","documentation":"Test panic from .expect at crates/dbt-adapter/src/sql/diff.rs:3559: compare_sql(actual_sql, expected_sql, AdapterType::Bigquery) returned Err even though the two BigQuery queries differ only in the order of projected columns. The differ's order-independent projection matching is expected to absorb this drift so generated and recorded SQL compare as equal.","triggerScenarios":"cargo test -p dbt-adapter with test at diff.rs:3559: queries built from the same alias list via projection_for(a).collect::<Vec<_>>().join(\", \") in two different orders compare unequal under AdapterType::Bigquery, panicking the expect.","commonSituations":"BigQuery model compilation changes SELECT item order; the canonicalizer's projection reordering path is BigQuery-gated and was broken or not applied; replay comparisons between Mantle-generated SQL and recorded Fusion SQL start failing on pure ordering.","solutions":["Read the Err message from compare_sql to identify the first diverging canonical projection","Ensure the alias-based projection ordering normalization runs for AdapterType::Bigquery (it may only be wired for Snowflake)","Fix alias extraction so reordered projections with identical alias->expression maps canonicalize identically","If order is semantically required (e.g. SELECT * replacement), correct the SQL generator instead"],"exampleFix":"// before\ncompare_sql(&actual_sql, &expected_sql, AdapterType::Bigquery)\n    .expect(\"projection order drift should be ignored\");\n// after (library-side: canonicalize projections as alias-keyed maps)\nlet norm = |s: &str| sort_projections_by_alias(canonicalize(s, AdapterType::Bigquery));\nassert_eq!(norm(&actual_sql), norm(&expected_sql));","handlingStrategy":"validation","validationCode":"let aliases_a: BTreeSet<&str> = extract_aliases(actual_sql).collect();\nlet aliases_b: BTreeSet<&str> = extract_aliases(expected_sql).collect();\nif aliases_a != aliases_b { panic!(\"aliases differ; diff will legitimately fail\"); }","typeGuard":null,"tryCatchPattern":"match compare_sql(&actual_sql, &expected_sql, AdapterType::Bigquery) {\n    Ok(()) => {},\n    Err(e) => eprintln!(\"projection drift is real: {e}\"),\n}","preventionTips":["Confirm the order-insensitive projection path is enabled per adapter (Bigquery gating)","Generate SELECT lists from a canonical (e.g. sorted-by-alias) source order when possible","Unit-test the alias extractor against reordered projections","Compare both orders (a,b) and (b,a) in regression tests"],"tags":["rust","sql-diff","test-panic","bigquery","projection-order"],"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-14T16:17:12.679Z"}