Skip to content

[GLUTEN-12273][CORE] Support staged scans for Iceberg data-file rewrites - #12962

Open
infvg wants to merge 1 commit into
apache:mainfrom
infvg:feature/iceberg-staged-scan
Open

[GLUTEN-12273][CORE] Support staged scans for Iceberg data-file rewrites#12962
infvg wants to merge 1 commit into
apache:mainfrom
infvg:feature/iceberg-staged-scan

Conversation

@infvg

@infvg infvg commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

What changed

  • Add native Gluten support for Iceberg SparkStagedScan.
  • Read staged file tasks from the scan task groups.
  • Use the existing Iceberg schema, partition, and file-format logic for staged scans.

Why

Iceberg RewriteDataFiles uses a staged scan to read selected data files.

Gluten previously supported only SparkBatchQueryScan. Gluten rejected the staged scan, and Spark used the standard Spark execution path.

This change lets Gluten use the native Iceberg scan path for staged scans.

Resolves GLUTEN-12273

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

Run Gluten Clickhouse CI on x86

@infvg infvg changed the title [GLUTEN-12273][CORE] Support Iceberg staged scans [GLUTEN-12273][CORE] Support staged scans for Iceberg data-file rewrites Sep 3, 2026
@infvg
infvg force-pushed the feature/iceberg-staged-scan branch from 6b123d9 to c00a321 Compare September 3, 2026 10:05
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

Run Gluten Clickhouse CI on x86

@infvg
infvg force-pushed the feature/iceberg-staged-scan branch from c00a321 to d7e40b6 Compare September 3, 2026 11:54
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

Run Gluten Clickhouse CI on x86

@infvg
infvg marked this pull request as ready for review September 3, 2026 15:33

@zhouyuan zhouyuan 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!


def getClassOfSparkBatchQueryScan(): Class[SparkBatchQueryScan] = {
classOf[SparkBatchQueryScan]
def supportsScan(sparkScan: Scan): Boolean = sparkScan match {

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.

maybe a clear func name: isSupportedScan

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

renamed

case _: SparkBatchQueryScan => true
case scan: SparkStagedScan =>
val tasks = getScanTasks(scan)
tasks.nonEmpty &&

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.

do we need to exclude empty scan here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

removed

case scan: SparkStagedScan =>
val tasks = getScanTasks(scan)
tasks.nonEmpty &&
(tasks.forall(_.isFileScanTask) || tasks.forall(_.isInstanceOf[CombinedScanTask]))

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.

here it also excluded some of scan tasks, is this necessary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

removed

def supportsScan(sparkScan: Scan): Boolean = sparkScan match {
case _: SparkBatchQueryScan => true
case scan: SparkStagedScan =>
val tasks = getScanTasks(scan)

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.

this will bring big perf overhead here, can we avoid this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

changed to just check the scan object, and materializes them later when needed

import org.apache.iceberg.spark.SparkSchemaUtil

import java.lang.{Class, Long => JLong}
import java.lang.{Long => JLong}

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.

not necessary to use {}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this one is required for scala 2

// which will not appear in readFields, they also cannot be filtered.
val tableFields = spec.schema().columns().asScala.map(_.name()).toSet
val voidTransformFields = getTable(sparkScan)
.spec()

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.

we already use foreach getScanTasks(sparkScan) to get task.spec, can we skip the .spec() here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

task.spec() only gives the partition spec used to write the file, we still need table.spec() for the fields dropped from the current spec using void transforms

Copilot AI 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.

🟡 Changes recommended

The new staged-scan test relies on brittle class-name string matching, and the staged scan support check currently materializes full task lists (avoidable overhead) in the planning path.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR extends Gluten’s Iceberg V2 scan offload to support Iceberg’s SparkStagedScan (used by CALL ... rewrite_data_files), allowing rewrite reads to use the native Iceberg scan transformer path instead of falling back to vanilla Spark execution.

Changes:

  • Add scan-support detection for SparkStagedScan and extract staged scan tasks from taskGroups().
  • Reuse existing Iceberg table/schema/partition/file-format extraction logic for both batch and staged scans.
  • Add a regression test asserting rewrite_data_files uses IcebergScanTransformer with a staged scan.
File summaries
File Description
gluten-iceberg/src/test/scala/org/apache/gluten/execution/IcebergSuite.scala Adds a regression test covering rewrite_data_files and staged-scan offload behavior.
gluten-iceberg/src/main/scala/org/apache/iceberg/spark/source/GlutenIcebergSourceUtil.scala Introduces unified scan support + staged-task extraction to drive existing schema/partition/format logic.
gluten-iceberg/src/main/scala/org/apache/gluten/execution/IcebergScanTransformer.scala Switches scan support gating to the new unified supportsScan logic.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 45 to 52
def supportsScan(sparkScan: Scan): Boolean = sparkScan match {
case _: SparkBatchQueryScan => true
case scan: SparkStagedScan =>
val tasks = getScanTasks(scan)
tasks.nonEmpty &&
(tasks.forall(_.isFileScanTask) || tasks.forall(_.isInstanceOf[CombinedScanTask]))
case _ => false
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

Comment on lines +87 to +91
qe.executedPlan.exists {
case scan: IcebergScanTransformer =>
scan.scan.getClass.getSimpleName == "SparkStagedScan"
case _ => false
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

@infvg
infvg force-pushed the feature/iceberg-staged-scan branch from d7e40b6 to f4706f2 Compare September 3, 2026 16:54
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

Run Gluten Clickhouse CI on x86

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support iceberg RewriteDataFile

3 participants