dbt-labs/dbt-core · error
projection order drift should be ignored
Error message
projection order drift should be ignored
What it means
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.
Source
Thrown at crates/dbt-adapter/src/sql/diff.rs:3559
let expected_sql = format!(
"select {} from t",
expected_alias_order
.iter()
.map(|a| projection_for(a))
.collect::<Vec<_>>()
.join(", ")
);
let actual_sql = format!(
"select {} from t",
actual_alias_order
.iter()
.map(|a| projection_for(a))
.collect::<Vec<_>>()
.join(", ")
);
compare_sql(&actual_sql, &expected_sql, AdapterType::Bigquery)
.expect("projection order drift should be ignored");
}
#[test]
fn test_bigquery_struct_projection_order_drift_with_comment_apostrophe_should_be_ignorable() {
// Minimal repro for the *ordering-only* Snowplow SQL mismatch observed in replay:
//
// Some Jinja/adapter code paths build a list of `STRUCT(...) AS <context_alias>` projections
// by iterating a map. Different runners/recorders may emit identical projections in
// different orders (e.g. insertion-order vs key-sorted iteration). We treat this drift as
// ignorable for replay via canonicalization.
//
// This variant includes a `--` comment containing an apostrophe (e.g. "hasn't"), which
// appears in the real project SQL. Replay should still treat projection ordering drift as
// ignorable.
let expected_alias_order = [
"service_configuration_context",
"modal_entity",
"experiment_entity",View on GitHub (pinned to 0267ce9170)
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
Example fix
// before
compare_sql(&actual_sql, &expected_sql, AdapterType::Bigquery)
.expect("projection order drift should be ignored");
// after (library-side: canonicalize projections as alias-keyed maps)
let norm = |s: &str| sort_projections_by_alias(canonicalize(s, AdapterType::Bigquery));
assert_eq!(norm(&actual_sql), norm(&expected_sql)); Defensive patterns
Strategy: validation
Validate before calling
let aliases_a: BTreeSet<&str> = extract_aliases(actual_sql).collect();
let aliases_b: BTreeSet<&str> = extract_aliases(expected_sql).collect();
if aliases_a != aliases_b { panic!("aliases differ; diff will legitimately fail"); } Try / catch
match compare_sql(&actual_sql, &expected_sql, AdapterType::Bigquery) {
Ok(()) => {},
Err(e) => eprintln!("projection drift is real: {e}"),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- projection order drift should be ignored even with apostroph
- bare vs wrapped CREATE VIEW body should compare as equal (or
- STRUCT field order drift should be ignored
- Forward-fill projection column order drift should be ignored
- should treat persisted view vs temp view as equivalent
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/61c4533a49a213e6.
Report an issue: GitHub.