Skip to content

fix: [branch-1.1] don't treat planner-inserted casts as Parquet schema conversions (#7159) - #7264

Merged
andygrove merged 1 commit into
apache:branch-1.1from
snmvaughan:backport-7159-branch-1.1
Oct 27, 2026
Merged

andygrove merged 1 commit into
apache:branch-1.1from
snmvaughan:backport-7159-branch-1.1

Conversation

@snmvaughan

@snmvaughan snmvaughan commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Backport of #7159 (squash commit 01fc37a) to branch-1.1, for #7158.

Rationale for this change

branch-1.1 has the bug #7159 fixes on main. With spark.comet.parquet.rowFilterPushdown.enabled=true on Spark 3.x, a pushed-down filter that contains a cast Comet's planner inserted, such as the Int64 positions of substring(s, 1, 2), fails with ParquetSchemaConvert { physical_type: "INT32", spark_type: "bigint" }. The native scan's expression adapter treats every cast in the predicate as a Parquet schema conversion. See #7158 for the details.

What changes are included in this PR?

A cherry-pick of #7159 with -x, adapted in one test line: the substring scan test builds its proto ScalarFunc without null_short_circuit, which branch-1.1's proto doesn't have. The fix itself is identical to the source commit. replace_with_spark_cast now rewrites only the casts DataFusion's schema adapter builds, which set the explicit-target flag that CastExpr::has_explicit_metadata() returns, and leaves the planner's casts alone.

Backports to other branches: branch-1.2, #7262.

How are these changes tested?

The tests come with the cherry-pick, in native/core/src/parquet/schema_adapter.rs. On this branch all 96 schema_adapter tests pass, and cargo fmt --check and cargo clippy --all-targets --workspace -- -D warnings are clean.

…apache#7159)

* fix: don't treat planner-inserted casts as Parquet schema conversions

The native Parquet scan's expression adapter rewrites every DataFusion
`CastExpr` in a pushed-down predicate as though it were a file-versus-table
schema conversion, and applies Spark's Parquet read rules to it. A predicate
can also contain casts that have nothing to do with the file schema: Comet's
planner builds them with `CastExpr::new` to coerce scalar-function arguments
to the types the function requires. `substring(s, 1, 2)` is one: Spark sends
Int32 literals, `SparkSubstring` takes Int64 positions, so the planner wraps
them in `CAST(1 AS Int64)` and `CAST(2 AS Int64)`.

On Spark 3.x (`allow_type_promotion = false`) the adapter reports that cast as
a disallowed `INT32 -> bigint` Parquet promotion. The check is deferred to
runtime, so it only fires when the predicate is evaluated against a decoded
batch, i.e. with `spark.comet.parquet.rowFilterPushdown.enabled=true`:

    Error evaluating filter predicate: External(ParquetSchemaConvert {
      file_path: "", column: "[]", physical_type: "INT32", spark_type: "bigint" })

The file's schema matches the requested schema exactly. The empty column name
is the tell: the cast is not over a column.

`replace_with_spark_cast` now leaves a cast alone unless it reconciles a file
column with its table-schema type (`is_schema_adaptation_cast`). DataFusion's
default adapter builds those with `CastExpr::new_with_target_field`, passing
the table schema's field, so the target carries the column's name; the
planner's `CastExpr::new` casts have an unnamed target field.

Casts the planner inserted therefore stay DataFusion `CastExpr`s on every
Spark version, instead of being rewritten into Spark `Cast`s on Spark 4.x.

* fix: find schema-adaptation casts by explicit target, not by name

Spark allows a Parquet column named "", so an empty target name does not
mark a planner cast: an INT64 column named "" read as int skipped the
read-conversion check and returned its values where Spark raises
SchemaColumnConvertNotSupportedException.

DataFusion's default adapter builds its schema casts with
CastExpr::new_with_target_field, which sets the explicit-target flag that
CastExpr::has_explicit_metadata() returns, also for an empty name, and
with_new_children keeps it. Comet's planner builds the casts it puts in a
predicate with CastExpr::new, which doesn't set it. replace_with_spark_cast
now checks that flag, and is_schema_adaptation_cast is gone.

Tests: an INT64 column named "" read as int is rejected under both
type-promotion settings, the naming-convention test is replaced by one that
pins the explicit-target flag, and the three substring filter tests are now
one table-driven test on the existing scan_parquet_file helper, which still
fails with the original INT32 -> bigint error without the fix.

Adapted for branch-1.1: the scan test's ScalarFunc drops null_short_circuit,
which branch-1.1's proto doesn't have.

(cherry picked from commit 01fc37a)
@github-actions github-actions Bot added bug Something isn't working area:scan Parquet scan / data reading labels Oct 27, 2026

@andygrove andygrove left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks @snmvaughan

@andygrove
andygrove merged commit 9223f01 into apache:branch-1.1 Oct 27, 2026
215 of 217 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:scan Parquet scan / data reading bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants