What is the problem the feature request solves?
Comet doesn't natively scan the Text file format ([COMET: Unsupported file format Text]). In isolation that's fine, but when the unsupported source sits on the build side of a broadcast join, it cascades: the build branch (text scan → transforms → BroadcastExchange) stays on Spark, so the exchange never becomes a CometBroadcastExchange, so the BroadcastHashJoin can't go native — even though the large probe side is a fully native scan+filter. A tiny lookup table read from text disqualifies acceleration of the entire join over the big stream.
Describe the potential solution
The core problem isn't that Text is unsupported — it's that an unsupported leaf on a broadcast build side vetoes native execution of the whole join, including the large probe stream. One possible fix is to give the build branch a columnar bridge so it can reach the BroadcastExchange as native. There are two options
Option A — auto-insert SparkToColumnar at unsupported leaves on broadcast build sides
CometSparkToColumnarExec wraps a Spark leaf and is delivered as a CometScanWrapper (a CometNativeExec), so operators above it (explode/Filter/Project) can convert and the exchange becomes a CometBroadcastExchange. Today it's off by default and its sparkToColumnar.supportedOperatorList excludes FileSourceScan, so this path is never hit
Change: when an unsupported leaf scan feeds a broadcast build side and nothing else bloc ks the branch, insert CometSparkToColumnarExec at that leaf automatically. This is safe precisely because the build side is broadcast — it's bounded and small, so the row→Arrow copy cost is negligible, and the payoff is a native join over the (large) probe input.
Guards:
- Only on broadcast build sides (or gate by a size/row threshold) to avoid converting large row inputs where the copy would dominate.
- Keep the existing
isSchemaSupported check.
- Respect a config, e.g.
spark.comet.sparkToColumnar.broadcastBuildSide.enabled (default true), plus the existing opt-in for the general case.
As an interim, this is already achievable by config — document it:
--conf spark.comet.sparkToColumnar.enabled=true
--conf spark.comet.sparkToColumnar.supportedOperatorList=Range,InMemoryTableScan,RDDScan,OneRowRelation,FileSourceScan
Option B — native Text scan
Add a minimal native Text reader (single value: string column, line-delimited), mirroring the CSV native scan path, gated by e.g. spark.comet.scan.text.enabled. This removes the row→columnar copy entirely and gives native I/O on the build side, not just a bridge. More work, but it's the general fix and closes the "unsupported file format" gap directly.
Note / follow-up
Whatever the bridge, plan-time conversion of the build chain still hinges on the transforms above the leaf converting (e.g. explode over a Scala UDF returning array<string> — supported at plan time via codegen dispatch). Runtime execution of a native Explode over a JvmScalarUdf child should be validated separately; it's orthogonal to enabling the columnar bridge but worth testing in the same change.
Additional context
Steps to reproduce
Join a large Parquet/Iceberg fact against a small allowlist loaded from a text file:
val big = spark.read.format("iceberg").load("db.events") // native Comet scan
val allow = spark.read.text("s3a://example-bucket/config/allow.txt") // Text -> Spark only
.select(explode(to_hosts($"value")).as("host"), $"value".as("pattern"))
big.join(broadcast(allow), big("host") === allow("host"))
.where(rlike(big("url"), allow("pattern")))
Explain: only CometIcebergNativeScan + CometFilter on the probe side convert; Generate, Filter, Project, BroadcastExchange, and the BroadcastHashJoin all remain Spark. Summary: accelerated 2 of 12 operators (16%).
Expected
One of:
- Native scan support for Text (even a minimal single-
value-column reader), or
- Make
spark.comet.sparkToColumnar.enabled cover file-source leaves like Text by default (or document adding FileSourceScan to sparkToColumnar.supportedOperatorList), so a small build side can be adapted to Arrow and let the join go native.
Additional context
- With
sparkToColumnar.enabled=true and FileSourceScan added to the supported list, the build chain (SparkToColumnar leaf → explode → filter → project) does convert at plan time, which flips the exchange and join native — but this is non-obvious and off by default.
- Impact is huge: a small text lookup on the build side blocks native execution of a join over a very large probe input.
What is the problem the feature request solves?
Comet doesn't natively scan the Text file format (
[COMET: Unsupported file format Text]). In isolation that's fine, but when the unsupported source sits on the build side of a broadcast join, it cascades: the build branch (text scan → transforms →BroadcastExchange) stays on Spark, so the exchange never becomes aCometBroadcastExchange, so theBroadcastHashJoincan't go native — even though the large probe side is a fully native scan+filter. A tiny lookup table read from text disqualifies acceleration of the entire join over the big stream.Describe the potential solution
The core problem isn't that Text is unsupported — it's that an unsupported leaf on a broadcast build side vetoes native execution of the whole join, including the large probe stream. One possible fix is to give the build branch a columnar bridge so it can reach the
BroadcastExchangeas native. There are two optionsOption A — auto-insert
SparkToColumnarat unsupported leaves on broadcast build sidesCometSparkToColumnarExecwraps a Spark leaf and is delivered as aCometScanWrapper(aCometNativeExec), so operators above it (explode/Filter/Project) can convert and the exchange becomes aCometBroadcastExchange. Today it's off by default and itssparkToColumnar.supportedOperatorListexcludesFileSourceScan, so this path is never hitChange: when an unsupported leaf scan feeds a broadcast build side and nothing else bloc ks the branch, insert
CometSparkToColumnarExecat that leaf automatically. This is safe precisely because the build side is broadcast — it's bounded and small, so the row→Arrow copy cost is negligible, and the payoff is a native join over the (large) probe input.Guards:
isSchemaSupportedcheck.spark.comet.sparkToColumnar.broadcastBuildSide.enabled(default true), plus the existing opt-in for the general case.As an interim, this is already achievable by config — document it:
Option B — native Text scan
Add a minimal native Text reader (single
value: stringcolumn, line-delimited), mirroring the CSV native scan path, gated by e.g.spark.comet.scan.text.enabled. This removes the row→columnar copy entirely and gives native I/O on the build side, not just a bridge. More work, but it's the general fix and closes the "unsupported file format" gap directly.Note / follow-up
Whatever the bridge, plan-time conversion of the build chain still hinges on the transforms above the leaf converting (e.g.
explodeover a Scala UDF returningarray<string>— supported at plan time via codegen dispatch). Runtime execution of a nativeExplodeover aJvmScalarUdfchild should be validated separately; it's orthogonal to enabling the columnar bridge but worth testing in the same change.Additional context
Steps to reproduce
Join a large Parquet/Iceberg fact against a small allowlist loaded from a text file:
Explain: only
CometIcebergNativeScan+CometFilteron the probe side convert;Generate,Filter,Project,BroadcastExchange, and theBroadcastHashJoinall remain Spark. Summary:accelerated 2 of 12 operators (16%).Expected
One of:
value-column reader), orspark.comet.sparkToColumnar.enabledcover file-source leaves like Text by default (or document addingFileSourceScantosparkToColumnar.supportedOperatorList), so a small build side can be adapted to Arrow and let the join go native.Additional context
sparkToColumnar.enabled=trueandFileSourceScanadded to the supported list, the build chain (SparkToColumnarleaf → explode → filter → project) does convert at plan time, which flips the exchange and join native — but this is non-obvious and off by default.