viirya commented on code in PR #7123:
URL: https://github.com/apache/datafusion-comet/pull/7123#discussion_r4328243129
##########
spark/src/main/scala/org/apache/comet/CometConf.scala:
##########
@@ -1269,16 +1281,34 @@ object CometConf extends ShimCometConf {
conf("spark.comet.convert.broadcastBuildSide.enabled")
.category(CATEGORY_EXEC)
.doc("When enabled, Comet converts a file or V2 scan that it cannot scan
natively (for " +
- "example a Text source) to Arrow when that scan feeds a broadcast
join's build side and " +
+ "example an ORC source) to Arrow when that scan feeds a broadcast
join's build side and " +
"every scan on the probe side is already native, so the broadcast join
runs natively " +
"over the large probe. The build side is usually small
(auto-broadcasts are capped by " +
"the broadcast threshold), so the row to Arrow copy is usually cheap -
though an " +
"explicit BROADCAST hint or a selective filter above the scan can make
it larger. With " +
"dynamic partition pruning the dimension is also read a second time
for the pruning " +
- "subquery. Set this to false to keep such build sides on Spark.")
+ "subquery. Parquet, JSON and CSV scans are converted only by their own
" +
+ "`spark.comet.convert` configs. A Text scan is converted only when " +
+ "`spark.comet.convert.broadcastBuildSide.text.enabled` is also
enabled, because Text " +
+ "files can hold bytes that are not valid UTF-8. Set this to false to
keep such build " +
+ "sides on Spark.")
.booleanConf
.createWithDefault(true)
+ val COMET_CONVERT_BROADCAST_BUILD_SIDE_TEXT_ENABLED: ConfigEntry[Boolean] =
Review Comment:
Do we need both new Text configs? `spark.comet.convert.text.enabled` already
covers the build side, so this one only adds the option of converting a Text
build side while every other Text scan stays on Spark.
If Text followed the CSV, JSON and Parquet model, where a build side is
converted only by the format's own switch, the datasources paragraph could say
"Parquet, JSON, CSV and Text build sides are converted only by their own
switches". The fallback reason could point at
`spark.comet.convert.text.enabled`, and `shouldConvertTextScan` would lose its
middle branch. Most of the Latin-1 explanation that is now repeated across the
two config docs and the scaladocs on `BROADCAST_BUILD_SIDE_TAG`,
`tagBroadcastBuildSideLeaves` and `shouldConvertTextScan` would go away too.
And we would not start a pattern of one `broadcastBuildSide.<format>.enabled`
for each format we later need to exclude.
The #6008 allowlist case would set `spark.comet.convert.text.enabled=true`
instead. Is there a workload where converting the build side but not other Text
scans matters? @parthchandra's answer on the #6008 use case may settle this.
##########
spark/src/main/scala/org/apache/comet/CometConf.scala:
##########
@@ -227,6 +227,18 @@ object CometConf extends ShimCometConf {
.booleanConf
.createWithDefault(false)
+ val COMET_CONVERT_FROM_TEXT_ENABLED: ConfigEntry[Boolean] =
Review Comment:
If this config stays, two existing docs still describe `FileSourceScan` in
`spark.comet.sparkToColumnar.supportedOperatorList` as the way to convert "file
formats other than Parquet, JSON and CSV": the `supportedOperatorList` doc
further down in this file, and the "To convert any other leaf operator"
paragraph in `datasources.md`. Could they mention Text?
Related to that, the other leaves that gained their own
`spark.comet.convert` config (Range, InMemoryTableScan, RDDScan,
OneRowRelation) keep the `sparkToColumnar` path through `isConversionEnabled`,
which logs a deprecation warning. `shouldConvertTextScan` keeps the path for
Text without one. Is Text meant to stay supported there long term, or should it
be deprecated the same way? The docs should say which.
##########
docs/source/user-guide/latest/compatibility/index.md:
##########
@@ -97,9 +97,38 @@ underlying bytes rather than on the rendered text:
bytes) but `true` in Comet. Equality, joins, grouping, ordering, and
byte-based string functions
such as `contains` can therefore disagree with Spark when non-UTF-8 bytes
are involved.
-Both differences require string data that is not valid UTF-8, which does not
occur for text read from
-Parquet or produced by string expressions. Consistent handling of invalid
UTF-8 across all native
-string paths is tracked by
+Both differences require string data that is not valid UTF-8. Comet's native
Parquet scan never
+produces such strings (it rejects them, see below), and neither do most string
expressions. They can
+reach Comet in these ways, which are the ones to check before relying on a
result:
+
+- **Text files.** Spark's Text reader keeps the bytes of each line as they
are, so a Text file in an
+ encoding other than UTF-8, such as Latin-1, holds strings that are not valid
UTF-8. Comet decodes
+ them when it converts a Text scan to Arrow, which it does only when
+ `spark.comet.convert.text.enabled=true`, when the scan is on a broadcast
build side and
+ `spark.comet.convert.broadcastBuildSide.text.enabled=true`, or when
+ `spark.comet.sparkToColumnar.enabled=true` and
`spark.comet.sparkToColumnar.supportedOperatorList`
+ names the scan (see [Data sources](../datasources.md#text)). For example,
with a build-side file
+ holding the bytes `1 caf\xE9`, `hex(value)` returns `3120636166EFBFBD`
instead of Spark's
+ `3120636166E9`, and `length` of `2 na\xEFve` is 7 instead of 5. Spark
decodes CSV files as it reads
+ them, so they never hold such strings, and its JSON reader treats a line
with such bytes as
+ malformed, so only the corrupt record column keeps them. Spark's ORC and
Avro readers keep stored
+ bytes without checking them, but those formats define strings as UTF-8, so
only a malformed file is
+ affected.
+- **Comet columnar shuffle.** When Comet shuffles rows that come from a Spark
operator, such as a
+ Text scan or a Spark partial aggregate, it converts them to Arrow and
decodes their strings in the
+ same way. It does this with the default `spark.comet.shuffle.mode=auto`, so,
for example,
+ repartitioning a Latin-1 Text table can return `U+FFFD` where Spark keeps
the original bytes.
+ Setting `spark.comet.shuffle.mode=native` keeps such shuffles on Spark.
+- **`CAST(binary AS STRING)`.** Comet decodes the bytes where Spark keeps
them. Because `min` and `max`
+ over strings run natively, a cast below them is decoded before the
comparison, and the two can swap.
+ With `b` holding `X'80'` and `X'C3A9'`, Spark returns `min(CAST(b AS
STRING))` = the `0x80` string
+ and `max` = `é`, whereas Comet returns `min` = `é` and `max` = `U+FFFD`.
This is the ordering
+ difference above. If the cast input can hold bytes that are not valid UTF-8,
disable Comet for the
+ query, or set `spark.comet.expression.Min.enabled=false`,
`spark.comet.expression.Max.enabled=false`
Review Comment:
The three-config workaround seems to depend on the cast being the
aggregate's own argument. If the cast is in a projection below the aggregate,
for example `SELECT min(s) FROM (SELECT CAST(b AS STRING) AS s FROM t)` over a
native Parquet scan, Spark keeps that `Project`, because `CollapseProject` does
not fold a `Project` into an `Aggregate` above it. Comet then runs the
projection natively and decodes `b` before the Spark aggregate sees it. Did the
probe cover that shape? If the workaround does not hold there, could the
sentence say the three configs apply when the cast is directly inside `min` or
`max`, and keep disabling Comet as the general answer?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]