Skip to content

fix: make PushDownLeafProjections work with unnest - #22620

Merged
gabotechs merged 1 commit into
apache:mainfrom
pabadrubio:pabadrubio/fix-unnest-bug
Jun 2, 2026
Merged

gabotechs merged 1 commit into
apache:mainfrom
pabadrubio:pabadrubio/fix-unnest-bug

Conversation

@pabadrubio

@pabadrubio pabadrubio commented May 29, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Closes #22615.

Rationale for this change

PushDownLeafProjections throws an error when it tries to push down an expression through an Unnest node. It also tries to push leaf projections incorrectly for Unnest nodes.

What changes are included in this PR?

This PR makes PushDownLeafProjections never push down leaf expressions through Unnest nodes. This avoids the code in try_push_into_inputs from calling:

let new_node = node.with_new_exprs(node.expressions(), new_inputs)?;

on an Unnest node, which doesn't work.

Changing that line is not enough, as even in that case the code in try_push_into_inputs to decide when to push down a projection is not valid for Unnest nodes, as explained in #22615

In this PR, I only want to make sure that the optimizer works for Unnest cases, but we could probably do better and allow leaf expressions push downs when the expression refer to a column that is not being unnested.

Are these changes tested?

Added unit tests and .slt tests for these cases.

Are there any user-facing changes?

No

@github-actions github-actions Bot added optimizer Optimizer rules sqllogictest SQL Logic Tests (.slt) labels May 29, 2026

@kosiew kosiew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pabadrubio

Thanks for working on this.

Looks 👍 to me

@gabotechs gabotechs left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @pabadrubio for the PR, and @kosiew for the review! I'll pull this one in now.

@gabotechs
gabotechs added this pull request to the merge queue Jun 2, 2026
Merged via the queue into apache:main with commit 7a0d026 Jun 2, 2026
61 of 62 checks passed
pabadrubio added a commit to DataDog/datafusion that referenced this pull request Jun 2, 2026
pabadrubio added a commit to DataDog/datafusion that referenced this pull request Jun 3, 2026
felipecrv pushed a commit to sdf-labs/datafusion that referenced this pull request Oct 9, 2026
Closes apache#22615.

`PushDownLeafProjections` throws an error when it tries to push down an
expression through an `Unnest` node. It also tries to push leaf
projections incorrectly for `Unnest` nodes.

This PR makes PushDownLeafProjections never push down leaf expressions
through Unnest nodes. This avoids the code in `try_push_into_inputs`
from calling:
```
let new_node = node.with_new_exprs(node.expressions(), new_inputs)?;
```
on an Unnest node, which doesn't work.

Changing that line is not enough, as even in that case the code in
`try_push_into_inputs` to decide when to push down a projection is not
valid for Unnest nodes, as explained in
apache#22615

In this PR, I only want to make sure that the optimizer works for Unnest
cases, but we could probably do better and allow leaf expressions push
downs when the expression refer to a column that is not being unnested.

Added unit tests and `.slt` tests for these cases.

No

(cherry picked from commit 7a0d026)
felipecrv pushed a commit to sdf-labs/datafusion that referenced this pull request Oct 12, 2026
Closes apache#22615.

`PushDownLeafProjections` throws an error when it tries to push down an
expression through an `Unnest` node. It also tries to push leaf
projections incorrectly for `Unnest` nodes.

This PR makes PushDownLeafProjections never push down leaf expressions
through Unnest nodes. This avoids the code in `try_push_into_inputs`
from calling:
```
let new_node = node.with_new_exprs(node.expressions(), new_inputs)?;
```
on an Unnest node, which doesn't work.

Changing that line is not enough, as even in that case the code in
`try_push_into_inputs` to decide when to push down a projection is not
valid for Unnest nodes, as explained in
apache#22615

In this PR, I only want to make sure that the optimizer works for Unnest
cases, but we could probably do better and allow leaf expressions push
downs when the expression refer to a column that is not being unnested.

Added unit tests and `.slt` tests for these cases.

No

(cherry picked from commit 7a0d026)
fa-assistant pushed a commit to dbt-labs/dbt that referenced this pull request Oct 15, 2026
* Bump arrow-adbc 0.24, Arrow 58.4, DataFusion 53.1, sqlparser 0.61, toml 1.x

- Bump toml to 1.x (reduce dupe deps like winnow)
- Bump arrow-adbc to 0.24, Arrow to 58.4, DataFusion to 53.1 and sqlparser to 0.61
  - Bump Arrow ADBC Rust driver manager to version 0.24
  - Bump Arrow Rust version to version 58.4.0
  - flock-service: Bump tonic and prost to 0.14
    arrow-flight 58 is built on tonic/prost 0.14, so our FlightService impl
    must use the same versions. tonic 0.14 moved its prost integration out
    of the `prost` feature and prost renamed `prost-derive` to `derive`.
  - Bump Datafusion to 53.1 (Arrow 58.x)
  - Enable the sql flag because sqlparser is now a feature flag from Datafusion
  - Bump sqlparser to be in sync with the version Datafusion uses
  - Update workspace-hack Cargo.toml
  - Cargo.lock: Update all Cargo.lock files in the repo
  - Bump datafusion to include the commit that bumps sqlparser internally
  - Update Cargo.lock
  - cargo hakari generate
  - flock-service: Use the sdf-labs datafusion and sqlparser forks
    Patch datafusion-common, datafusion-expr and sqlparser to the same
    forks and revs as the root workspace, so the root crates flock-service
    builds by path get the same DataFusion as in the root build.
  - codex-rs: Use the sdf-labs datafusion and sqlparser forks
    Patch datafusion-common, datafusion-expr and sqlparser to the same
    forks and revs as the root workspace, so the root crates codex-rs
    builds by path get the same DataFusion as in the root build.
  - Bump DataFusion to sdf/53_1@6952f981
    Picks up two fixes on the fork's sdf/53_1 branch:

    - "fix: make PushDownLeafProjections work with unnest" (backport of
      apache/datafusion#22620), which broke every LATERAL FLATTEN query;
    - "fix: fall back to Arrow's cast for non-struct sources in
      cast_column", which broke casts between TIMESTAMP_TZ precisions.

    Also refreshes the Windows-only entries of workspace-hack.
  - cargo hakari generate
    Regenerate the workspace-hack after rebasing on origin/main.
  - Add changelog entry for the Arrow/DataFusion/adbc bump

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fs/sa: Adapt dbt-adbc, dbt-agate, dbt-datafusion-errors and dbt-df-providers

- dbt-adbc: Make the API adaptations (good changes from upstream)
- dbt-agate: Adapt validate_decimal_precision() call to the latest version
- dbt-datafusion-errors: Make the DataFusionError conversion exhaustive
- dbt-df-providers: NdJsonReadOptions -> JsonReadOptions
- dbt-df-providers: Leave literal casts that fail to simplify for execution
  Since DataFusion 53 (apache/datafusion#18332), simplify_expressions
  fails planning on a CAST of a literal that Arrow cannot parse (e.g.
  '1970/03/15' or 'infinity' as a timestamp), even though the target
  warehouse may accept it. Replace the rule with
  LenientSimplifyExpressions, which leaves a plan node whose expressions
  fail to simplify as is and lets execution report any error.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* sdf-sql-functions, sdf-functions-sdk: Adapt to DataFusion 53

- sdf-sql-functions: A trait from Datafusion became a struct
- sdf-sql-functions: Use fields_with_udf in the sysdate test
  data_types_with_scalar_udf is deprecated since DataFusion 52 and is a
  thin wrapper around fields_with_udf.
- sdf-functions-sdk: make_value -> from_naive_datetime
  ArrowTimestampType::make_value is deprecated in Arrow 58.
  from_naive_datetime without a time zone interprets the value as UTC,
  as make_value did.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* sdf-frontend: Adapt to DataFusion 53 and Arrow 58

- sdf-frontend: Expr variants carry a FieldRef instead of a DataType
  DataFusion 53 changed Expr::ScalarVariable, Expr::OuterReferenceColumn
  and expr::Placeholder to hold a FieldRef. Read the data type from the
  field, and build nullable fields where we create these expressions,
  matching DataFusion 50 which always reported them as nullable.
- sdf-frontend: Use DataFusion's own NullTreatment
  DataFusion 53 defines expr::NullTreatment instead of using sqlparser's,
  so map our NULL treatment onto that type.
- sdf-frontend: Set Join::null_aware
  DataFusion 53 added Join::null_aware, which only DataFusion's NOT IN
  decorrelation sets. Our join builders leave it off.
- sdf-frontend: Set CreateExternalTable::or_replace
  DataFusion 53 added CreateExternalTable::or_replace. OR REPLACE is
  rejected before the plan is built, so this is always false today.
- sdf-frontend: Forward AccumulatorArgs::expr_fields in Snowflake array_agg
  DataFusion 53 added AccumulatorArgs::expr_fields. Pass it through to
  the delegated DataFusion array_agg accumulator like exprs.
- sdf-frontend: is_ordered_set_aggregate -> supports_within_group_clause
  DataFusion 53 renamed AggregateUDFImpl::is_ordered_set_aggregate to
  supports_within_group_clause.
- sdf-frontend: Coerce function arguments with fields_with_udf
  type_coercion::functions::data_types and data_types_with_scalar_udf
  are deprecated since DataFusion 52. Use fields_with_udf for both the
  window and the DataFusion scalar UDF paths.

  For the scalar path this is what the deprecated wrapper did. For the
  window path, user-defined signatures now go through the UDF's
  coerce_types instead of failing with an internal error.
- sdf-frontend: Update goldens for Arrow 58 type formatting
  Arrow 58 omits default attributes from Field's Debug output and prints
  FixedSizeList as `FixedSizeList(1 x Binary, field: 'variant')`.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* sdf-lp-decompiler: Adapt to sqlparser 0.61 and DataFusion 53

- sdf-lp-decompiler: Set Expr::Cast::array
  sqlparser 0.61 added Expr::Cast::array for MySQL's
  `CAST(... AS type ARRAY)`. We never emit that form.
- sdf-lp-decompiler: Set TableAlias::explicit
  sqlparser 0.61 moved the `AS` before a table alias into TableAlias
  (printed when `explicit` is set); table factors now print ` {alias}`
  instead of ` AS {alias}`. Keep the previous SQL:

  - table factors: explicit, as in DataFusion's unparser;
  - CTEs (`name AS (...)`) and the fork's TableFactor::Values, which
    print their own `AS`: not explicit.
- sdf-lp-decompiler: Statement::CreateView wraps a CreateView struct
  sqlparser 0.61 moved CREATE VIEW's fields into ast::CreateView and
  added `secure` (Snowflake) and `name_before_not_exists`, which we
  don't emit.
- sdf-lp-decompiler: Convert DataFusion's NullTreatment to sqlparser's
  DataFusion 53 has its own expr::NullTreatment and only converts from
  sqlparser's. Add ast_build::null_treatment for the other direction and
  use it wherever IGNORE/RESPECT NULLS reaches the AST.
- sdf-lp-decompiler: Update the Select builder for sqlparser 0.61
  sqlparser 0.61 added Select::optimizer_hint and select_modifiers, and
  connect_by became a Vec. Build them as DataFusion's unparser does.
- sdf-lp-decompiler: Set TableFactor::Derived::sample
  sqlparser 0.61 added a TABLESAMPLE modifier to derived tables. We never
  emit one, as in DataFusion's unparser.
- sdf-lp-decompiler: JoinOperator::CrossJoin takes a constraint
  sqlparser 0.61 gave JoinOperator::CrossJoin a JoinConstraint. Pass
  JoinConstraint::None, which prints the same `CROSS JOIN <relation>`.
- sdf-lp-decompiler: Set Insert::insert_token and optimizer_hint
  New in sqlparser 0.61; neither changes the printed INSERT.
- sdf-lp-decompiler: Set the new CreateTable fields
  sqlparser 0.61 added Snowflake dynamic table and Postgres partition
  options to CreateTable. We emit none of them.
- sdf-lp-decompiler: Adapt to sqlparser 0.61 DataType changes
  - DataType::Float takes an ExactNumberInfo instead of an Option;
  - DataType::Interval is a struct variant with optional fields and
    precision;
  - ExactNumberInfo::PrecisionAndScale's scale is an i64.

  The SQL printed for these types is unchanged.
- sdf-lp-decompiler: Unparse Operator::Colon
  DataFusion 53 added Operator::Colon. Print it as `:`, like DataFusion's
  unparser.
- sdf-lp-decompiler: Unparse Expr::SetComparison
  DataFusion 53 added Expr::SetComparison. Emit it as
  `<expr> <op> ANY|ALL (<subquery>)`, like DataFusion's unparser.
- sdf-lp-decompiler: Unparse the new ScalarValue variants
  DataFusion 53 added ScalarValue::Decimal32, Decimal64 and
  RunEndEncoded. Unparse them like DataFusion's unparser: decimals as
  numbers, run-end encoded values as their inner value.
- sdf-lp-decompiler: Update snapshots for Arrow 58 Field formatting
  Arrow 58 omits default attributes from Field's Debug output.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* sdf-slt: Adapt to the DataFusion 53 physical plan API

- sdf-slt: ExecutionPlan::properties returns &Arc<PlanProperties>
  DataFusion 53 changed the return type of ExecutionPlan::properties.
  Our nodes delegate to their child, so only the signatures change.
- sdf-slt: ExecutionPlan::statistics -> partition_statistics
  DataFusion 53 removed the deprecated ExecutionPlan::statistics. Our
  symbolic nodes now forward partition_statistics to their child; the old
  whole-plan statistics are partition_statistics(None).
- sdf-slt: Adapt to Arc<[usize]> projections and FilterExecBuilder
  In DataFusion 53, FilterExec and NestedLoopJoinExec expose their
  projection as Option<Arc<[usize]>>, and FilterExec::with_projection is
  deprecated in favour of FilterExecBuilder.

  Rebuild filters with FilterExecBuilder::from(original), which keeps the
  projection and the filter's other settings (including the new fetch
  limit) while swapping the predicate and input.
- sdf-slt: Pass has_grouping_set to PhysicalGroupBy::new
  DataFusion 53 added PhysicalGroupBy::new's has_grouping_set argument.
  Carry it over from the group-by being rebuilt.
- sdf-slt: Pass null_aware to HashJoinExec::try_new
  DataFusion 53 added HashJoinExec::try_new's null_aware argument (and
  the projection it exposes is an Arc<[usize]>). Carry both over from the
  join being rebuilt.
- sdf-slt: Rebuild InListExpr with with_new_children
  InListExpr::new is private in DataFusion 53. Rebuild the expression
  through PhysicalExpr::with_new_children instead, which also keeps its
  static filter (resolving the old TODO).
- sdf-slt: Drop CoalesceBatchesExec handling
  CoalesceBatchesExec is deprecated in DataFusion 53, which no longer
  inserts it into physical plans (batches are coalesced with arrow's
  BatchCoalescer instead), so these checks could no longer match.
- sdf-slt: Use the lenient expression simplifier
  Build the SLT sessions with dbt-df-providers' optimizer_rules(), so
  that a CAST of a literal Arrow cannot parse doesn't fail planning.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Adapt to DataFusion 53 API changes across crates

- Handle execution.time_zone becoming an Option and default it to UTC
  - dbt-df-providers: Deal with default tz changes in datafusion
  - dbt-compute-backend: execution.time_zone is now an Option
    DataFusion 53 changed `execution.time_zone` from String to
    Option<String>, so pass it through instead of wrapping it in Some.
  - dbt-compute-backend: Default the execution time zone to UTC
    DataFusion 53 leaves `execution.time_zone` unset by default (it used to
    be "+00:00"), and the binder fails with "Execution time zone not set"
    without one. Fall back to UTC in SessionComputeBackend for sessions not
    built from get_default_session_config, sharing the default with it via
    dbt_df_providers::context::DEFAULT_EXECUTION_TIME_ZONE.
  - dbt-metric-inference: execution.time_zone is now an Option
    DataFusion 53 changed `execution.time_zone` from String to
    Option<String>, so pass it through instead of wrapping it in Some.
  - dbt-postbinding-analyses: execution.time_zone is now an Option in tests
    DataFusion 53 changed `execution.time_zone` from String to
    Option<String>; the test helpers return it as is.
  - sdf-slt: execution.time_zone is now an Option
    DataFusion 53 changed `execution.time_zone` from String to
    Option<String>. Set and read it as such.
- dbt-classification, dbt-lineage: Handle Statement::ResetVariable
  - dbt-classification: Handle Statement::ResetVariable
    DataFusion 53 added Statement::ResetVariable. Like SetVariable, it
    propagates no classification.
  - dbt-lineage: Handle Statement::ResetVariable
    DataFusion 53 added Statement::ResetVariable. Like the other
    non-query statements, its lineage is not implemented.
- Handle Expr::SetComparison
  - dbt-postbinding-analyses: Handle Expr::SetComparison
    DataFusion 53 added Expr::SetComparison (`x <op> ANY/ALL (subquery)`).
    Treat it like InSubquery in the accepted_values, nullability and
    uniqueness analyses.
  - sdf-frontend: Handle Expr::SetComparison
    DataFusion 53 added Expr::SetComparison (`x <op> ANY/ALL (subquery)`).
    Type it as Boolean and take its nullability from the compared
    expression, like InSubquery.
  - dbt-classification: Handle Expr::SetComparison
    DataFusion 53 added Expr::SetComparison (`x <op> ANY/ALL (subquery)`).
    Propagate through it like InSubquery.
  - dbt-lineage: Handle Expr::SetComparison
    DataFusion 53 added Expr::SetComparison (`x <op> ANY/ALL (subquery)`).
    Compute its lineage like InSubquery.
- Get the arrow Schema from DFSchema directly
  - sdf-frontend: Get the arrow Schema from DFSchema directly
    DataFusion 53 removed `From<&DFSchema> for Schema`. Use
    DFSchema::inner() / as_arrow() instead.
  - dbt-frontend-backend: Get the arrow Schema from DFSchema directly
    DataFusion 53 removed `From<&DFSchema> for Schema`. Use
    DFSchema::as_arrow() instead.
  - sdf-slt: Get the arrow Schema from DFSchema directly
    DataFusion 53 removed `From<DFSchema> for Schema`. Share the DFSchema's
    inner arrow SchemaRef instead of copying it.
- Placeholder::new -> Placeholder::new_with_field
  - sdf-frontend: Placeholder::new -> Placeholder::new_with_field
    Placeholder::new is deprecated in DataFusion 53. Build the nullable,
    unnamed field it used to create ourselves.
  - dbt-tasks: Placeholder::new -> Placeholder::new_with_field
    Placeholder::new is deprecated in DataFusion 53. The test helper passes
    no type, so this is a direct replacement.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* SetComparison and InSubquery are nullable if their subquery is

- dbt-postbinding-analyses: SetComparison may be NULL
  `x <op> ANY/ALL (subquery)` is NULL when `x` is NULL or when no row
  matches and the subquery yields a NULL. Join the nullability of the
  left side and of the subquery's column instead of claiming NonNull.
- dbt-postbinding-analyses: InSubquery may be NULL
  `x IN (subquery)` is NULL when `x` is NULL or when no row matches and
  the subquery yields a NULL, so it is not always NonNull. Join the
  nullability of the left side and of the subquery's column, as for
  SetComparison.
- sdf-frontend: SetComparison is nullable if its subquery is
  `x <op> ANY/ALL (subquery)` can be NULL even when `x` is not: when no
  row matches and the subquery yields a NULL. Also consider the
  nullability of the subquery's column.
- sdf-frontend: InSubquery is nullable if its subquery is
  `x IN (subquery)` can be NULL even when `x` is not: when no row matches
  and the subquery yields a NULL. Also consider the nullability of the
  subquery's column, as for SetComparison.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Rewrite SQLSTATEs in goldens and recordings to adbc 0.24's format

- dbt-cli: Re-record goldens for adbc 0.24 SQLSTATE formatting
  adbc 0.24 prints an error's SQLSTATE as a string (`00000`) instead of
  the raw byte array (`[0, 0, 0, 0, 0]`).
- Rewrite SQLSTATEs in goldens and recordings to adbc 0.24's format
  adbc 0.24 formats an error's SQLSTATE as its characters (`00000`,
  `42S02`) instead of the raw byte array (`[0, 0, 0, 0, 0]`,
  `[52, 50, 83, 48, 50]`). Apply the same conversion as adbc's Display
  (NUL -> '0', printable ASCII -> itself) to every golden, `.slt`,
  `.err` and recordings.yaml, and to the `error` column of the replay
  recordings.db files, so replayed errors print like freshly recorded
  ones.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* sdf-compiler-tests: Update goldens for Arrow 58 and DataFusion 53

Almost all changes are Arrow 58's new type formatting, e.g.
`Timestamp(µs, "UTC")` instead of `Timestamp(Microsecond, Some("UTC"))`,
`FixedSizeList(1 x Binary, field: 'variant')` and `non-null` fields.

trino/round_decimal: DataFusion 53's `round` takes its decimal places
as Int32 (it was Int64), so it now casts our Int64 argument again.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* sdf-frontend: Type and coerce function calls through their DataFusion 53 signatures

- sdf-frontend: Type aggregates via AggregateUDF::return_field
  In DataFusion 53 some aggregates (first_value, last_value) only
  implement return_field and fail return_type with "Not called because
  the return_field_from_args is implemented" (SDF209). Resolve aggregate
  return types through return_field, which works for every aggregate.
- sdf-frontend: Coerce aggregate arguments to their signatures
  Some DataFusion 53 aggregates no longer cast their input themselves and
  declare exact signatures instead (variance and var_pop take Float64
  only), so decimal arguments reached accumulators that expect Float64:
  "could not cast array of type Decimal128(3, 2) to ... Float64Type".

  Coerce native aggregate (and aggregate window) arguments with
  fields_with_udf, as for DataFusion scalar functions, and keep them as is
  when DataFusion cannot coerce them.

  The DuckDB SQL for VARIANCE_POP now casts its input to DOUBLE.
- sdf-frontend: Coerce arguments of _$datafusion$ scalar calls to their signatures
  DataFusion 53's decode takes Binary and relies on its signature to
  coerce strings, but the arguments of functions called through
  `_$datafusion$` macros were passed through uncoerced, so TO_BINARY on a
  VARCHAR failed with "Unexpected value for decode". Coerce them with
  fields_with_udf, as for registered DataFusion scalar functions, and keep
  them as is when DataFusion cannot coerce them.

  Invalid hex strings now fail with an execution error instead of an
  internal one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* sdf-sql-functions: dateadd returns the TIMESTAMP_TZ it produces

The dateadd(string, number, timestamp_tz) implementation collects
TIMESTAMP_TZ values but declared a TIMESTAMP_NTZ return type.
DataFusion 53 asserts that a UDF's result matches the type promised at
planning time, which failed every dateadd and add_months call. Declare
the type it actually returns.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* sdf-sql-functions: Reject a zero step in GENERATE_DATE_ARRAY on DuckDB

A zero interval used to unparse as `INTERVAL ''`, which DuckDB failed
to parse, so the zero-step records errored by accident. DataFusion 53
unparses it as `INTERVAL '0 SECS'`: GENERATE_TIMESTAMP_ARRAY's macro
now raises its own zero-step error, and GENERATE_DATE_ARRAY returned an
empty array. Raise the same error BigQuery does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* sdf-slt: Fix coverage tracking under DataFusion 53

- sdf-slt: Make coverage wrappers expose the expression they wrap as their child
  The coverage wrappers reported the wrapped expression's children as
  their own. DataFusion 53's CaseExpr evaluates each branch on a batch
  projected to the columns its children reference, so a WHEN or THEN that
  was a wrapped column saw a batch with no columns and failed, and every
  coverage counter stayed at 0 (execution errors and panics are swallowed
  in coverage mode). Make the wrapped expression the only child, replace it
  in with_new_children, and delegate fmt_sql instead of panicking.
- sdf-slt: Count CASE ELSE coverage from this thread's WHEN matches
  CaseElseTracker derived the rows that fell through to ELSE by diffing
  the shared WHEN metrics around its own evaluation, so partitions
  evaluating concurrently counted each other's matches and ELSE counts
  varied from run to run. Also record WHEN matches in a thread-local map
  and diff that instead: a batch is evaluated on one thread.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Update dbt-cli, adapter, repl and integration test expectations for Arrow 58 and DataFusion 53

- Re-record dbt-cli, dbt-adapter-tests and dbt-repl goldens for Arrow 58 and DataFusion 53
  - dbt-cli: Re-record schema table goldens for Arrow 58 type formatting
    Arrow 58 prints `FixedSizeList(1 x Timestamp(µs, "UTC"), field: ...)`
    instead of the Debug form of the inner Field, which narrows the
    data_type column of the pretty-printed schema tables.
  - dbt-cli: Update the internal-analytics frontend digest for Arrow 58
    Arrow 58 prints nested and time-unit types differently, e.g.
    `FixedSizeList(1 x Utf8, field: 'sdfany')` and `Timestamp(µs, "UTC")`,
    which changes the `column_type` strings hashed into compile/columns.

    Exactly the 1207 models that have such a column changed, with the same
    row counts; lineage, nodes and schemas digests are unchanged.
  - dbt-adapter-tests: Re-record bigquery_snapshot schema table for Arrow 58
    Arrow 58 prints `Timestamp(µs, "UTC")` instead of
    `Timestamp(Microsecond, Some("UTC"))`.
  - dbt-repl: Re-record EXPLAIN golden for DataFusion 53 optimizer rules
    DataFusion 53 adds the rewrite_set_comparison and optimize_unions
    logical optimizer rules (the latter replacing eliminate_nested_union),
    which shows up in compile_misc_statements_repl's EXPLAIN output.
  - dbt-cli: Update the internal-analytics frontend digest for dateadd's argument typing
    Three models that already failed to bind (their inferred sources lack the
    epoch column passed to DATEADD) now fail while resolving DATEADD
    (dbt0209) instead of in the projection (dbt0301). No model's columns,
    lineage or compiled code changed.
- sdf-integration-tests: Update expectations for Arrow 58 and DataFusion 53
  - sdf-integration-tests: Update goldens for Arrow 58 type formatting
    Arrow 58 omits default Field attributes from Debug, prints
    `FixedSizeList(1 x Binary, field: 'variant')`, and prints struct
    field metadata in DataType's Display. setops also picks up DataFusion
    53's `coercion from Int64` (no brackets) wording.
  - sdf-integration-tests: round's decimal places are Int32 in DataFusion 53
    DataFusion 53's round coerces its decimal places to Int32 (it was
    Int64), so it casts our Int64 argument once more.
  - sdf-integration-tests: Arrow 58 prints out-of-range decimals with their scale
  - sdf-integration-tests: Re-record DuckDB types and function goldens for Arrow 58 and DataFusion 53
    Arrow 58 omits default Field attributes from Debug, and DataFusion 53
    casts function arguments differently (e.g. ceil and floor take
    decimals without a Float64 cast).
    Literal casts DataFusion cannot parse ('1970/03/15'::DATE, BC
    timestamps) still plan, thanks to the lenient expression simplifier.
  - sdf-integration-tests: Match Arrow 58's struct type format in subscript errors
    Arrow 58 prints a struct type as `Struct("ID": Utf8)` instead of
    `Struct(ID Utf8)`. The record and file checksums need a re-stamp from
    a Snowflake and a Databricks update run.
  - sdf-integration-tests: Recursive CTEs now run in Fusion
    DataFusion 53 no longer fails the recursive CTE with a Decimal128
    precision mismatch (#3981), so let Fusion run the Snowflake record and
    drop the TODO record that expected the error. The checksums need a
    re-stamp from a Snowflake update run.
  - sdf-integration-tests: Update flatten and aggs goldens for Arrow 58 and DataFusion 53 messages
    Arrow 58 formats list types as `FixedSizeList(1 x Binary, field: 'variant')`
    and DataFusion 53 prefixes failed internal assertions with "Assertion
    failed:". All affected records were already failing.
  - sdf-integration-tests: Two fewer dbt unit test records fail on DataFusion 53
    test_snowflake_dbt_unit_tests pins the number of failing records; it
    goes from 23 (22 errored) to 21 (18 errored) of 135.
  - sdf-integration-tests: Update coverage expectations for DataFusion 53 plans
    DataFusion 53 rewrites COALESCE into CASE and plans common
    subexpressions differently, so coalesce no longer shows up in function
    coverage and the CASE counters are numbered and split differently.
  - sdf-integration-tests: Re-record edited Snowflake SLT files against Snowflake
    Re-stamp the checksums of structured_object.slt, cte.slt and
    datediff_mixed_date_varchar.slt. Recording replaced the error regex in
    structured_object.slt with Snowflake's message, so compare Fusion's
    message against a golden instead.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* sdf-integration-tests: IFNULL short-circuits its second argument in DataFusion 53

DataFusion 53 evaluates IFNULL/NVL lazily, so `ifnull(a, 10 / b)` no
longer raises a division by zero for rows where `a` is not null.
Snowflake evaluates both arguments and fails; record the divergence.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
GitOrigin-RevId: 4353c41082eaea9b456161804fc8a141e9850967
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

optimizer Optimizer rules sqllogictest SQL Logic Tests (.slt) v55.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PushDownLeafProjections error with Unnest nodes

4 participants