Skip to content

[GLUTEN-12538][VL] Fall back to row-based Delta stats when the stats plan cannot be offloaded#12539

Draft
felipepessoto wants to merge 1 commit into
apache:mainfrom
felipepessoto:delta-stats-fallback
Draft

[GLUTEN-12538][VL] Fall back to row-based Delta stats when the stats plan cannot be offloaded#12539
felipepessoto wants to merge 1 commit into
apache:mainfrom
felipepessoto:delta-stats-fallback

Conversation

@felipepessoto

@felipepessoto felipepessoto commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

What changes are proposed in this pull request?

Fixes #12538.

GlutenDeltaJobStatsTracker collects Delta write statistics by building a local Velox aggregation plan and assuming it is always offloaded to a WholeStageTransformer. When the statistics plan references a type or expression Velox cannot offload — for example an aggregate over a TIMESTAMP_NTZ column — the offload rules reject it and leave a vanilla ProjectExec, so the unconditional cast threw:

java.lang.ClassCastException: class org.apache.spark.sql.execution.ProjectExec cannot be cast to
class org.apache.gluten.execution.WholeStageTransformer
  at ...GlutenDeltaJobStatsTracker$GlutenDeltaTaskStatsTracker.<init>(GlutenDeltaJobStatsTracker.scala:174)
  at ...GlutenDeltaJobStatsTracker.newTaskInstance(GlutenDeltaJobStatsTracker.scala:80)

failing the whole write.

This PR makes the Delta stats path fall back to row-based statistics collection when the plan cannot be fully offloaded, as suggested by rui-mo and zhztheplayer in 12388:

  • The decision is made on the executor in newTaskInstance(), where the native plan is actually built.
  • tryBuildOffloadedStatsPlan builds the aggregation plan, runs the offload rules, and returns the offloaded TransformSupport only when the whole plan was offloaded — every node except the StatisticsInputNode leaf is a TransformSupport. A root WholeStageTransformer alone is not sufficient, because ColumnarCollapseTransformStages can wrap an offloaded parent above a vanilla child.
  • Otherwise it returns None and statistics are collected the row-based way through the existing GlutenDeltaJobStatsFallbackTracker. Native resources (the single-thread executor and NativePlanEvaluator) are allocated only on the accepted path.

Both the Delta 3.3 (src-delta33) and Delta 4.0+ (src-delta40) variants are updated.

How was this patch tested?

Added DeltaTimestampNtzStatsWriteSuite (both src-delta33 and src-delta40), which writes a Delta table with a top-level and a struct-nested TIMESTAMP_NTZ column through the native write path with statistics collection enabled. Without the fix the write throws the ClassCastException above; with it the write succeeds, the data reads back correctly, and Delta minValues/maxValues statistics are still produced for the TIMESTAMP_NTZ column via the fallback.

Compiles for -Pspark-4.1 (delta40) and -Pspark-3.5 (delta33).

Was this patch authored or co-authored using generative AI tooling?

Generated-by: GitHub Copilot CLI (Claude Opus 4.8)

…plan cannot be offloaded

GlutenDeltaJobStatsTracker collects Delta write statistics by building a local
Velox aggregation plan and assuming it is always offloaded to a
WholeStageTransformer. When the stats plan references a type/expression Velox
cannot offload (for example an aggregate over TIMESTAMP_NTZ), the offload rules
reject it and leave a vanilla ProjectExec, so the unconditional cast threw
`ClassCastException: ProjectExec cannot be cast to WholeStageTransformer` and
failed the whole write.

The decision is now made on the executor in newTaskInstance(), where the native
plan is actually built. tryBuildOffloadedStatsPlan builds the aggregation plan,
runs the offload rules, and returns the offloaded TransformSupport only when the
WHOLE plan was offloaded -- every node except the StatisticsInputNode leaf is a
TransformSupport (a root WholeStageTransformer is not sufficient, since
ColumnarCollapseTransformStages can wrap an offloaded parent above a vanilla
child). Otherwise it returns None and statistics are collected the row-based way
through the existing GlutenDeltaJobStatsFallbackTracker. Native resources are
allocated only on the accepted path.

Add DeltaTimestampNtzStatsWriteSuite covering top-level and struct-nested
TIMESTAMP_NTZ columns: the native write succeeds and Delta min/max statistics
are still produced via the fallback.

Generated-by: GitHub Copilot CLI (Claude Opus 4.8)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 66a9e40f-3ac8-45be-8fee-a606a22fa098
@felipepessoto
felipepessoto force-pushed the delta-stats-fallback branch from cbcb8b4 to 8106751 Compare July 17, 2026 23:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[VL][Delta] Gluten Delta stats tracker should fall back when the stats plan can't be offloaded (ClassCastException on TIMESTAMP_NTZ)

1 participant