andygrove commented on code in PR #7171:
URL: https://github.com/apache/datafusion-comet/pull/7171#discussion_r4371020889
##########
spark/src/test/scala/org/apache/comet/CometArrayExpressionSuite.scala:
##########
@@ -1776,4 +1776,41 @@ class CometArrayExpressionSuite extends CometTestBase
with AdaptiveSparkPlanHelp
}
}
}
+
+ // SparkFixesSuite covers the variant for each Spark version.
+ test("sequence asks for the native variant of the Spark version it runs on")
{
+ val start = AttributeReference("start", LongType)()
+ val stop = AttributeReference("stop", LongType)()
+ val step = AttributeReference("step", LongType)()
+ val proto = CometSequence.convert(
+ Sequence(start, stop, Some(step)),
+ Seq(start, stop, step),
+ binding = false)
+ assert(
+ proto.map(_.getScalarFunc.getFunc) ==
+ Some(CometSequence.nativeFunction(org.apache.spark.SPARK_VERSION)))
+ }
+
+ // When stop - start overflows a long but the sequence is short, Spark
returns its elements
+ // where releases without SPARK-58821 raise INTERNAL_ERROR("Unreachable code
reached.").
+ test("sequence whose stop - start overflows a long") {
+ val rows = Seq(
+ (Long.MinValue, Long.MaxValue, Long.MaxValue),
+ (Long.MinValue, 0L, 1L << 62),
+ (Long.MaxValue, Long.MinValue, -Long.MaxValue))
+ withParquetTable(rows, "t") {
+ val query = "SELECT _1, _2, _3, sequence(_1, _2, _3) FROM t"
+ if
(SparkFixes.sequenceRecomputesOverflowedLength(org.apache.spark.SPARK_VERSION))
{
+ checkSparkAnswerAndOperator(query)
+ } else {
+ val df = sql(query)
Review Comment:
Nit, optional: the else branch only checks that both messages contain
"Unreachable code reached", so it would still pass if the native error came
back as a `CometNativeException` with the same text instead of Spark's
`INTERNAL_ERROR`. Could it be `checkSparkError(sql(query), "INTERNAL_ERROR")`?
That checks the plan is native and that the exception class, error class and
SQLSTATE match Spark's. I tried that form on 4.1.3 and it passes.
##########
spark/src/main/scala/org/apache/comet/serde/SparkFixes.scala:
##########
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.comet.serde
+
+import org.apache.spark.sql.comet.util.Utils
+
+/**
+ * Spark fixes that change a result Comet reproduces natively, and the Spark
releases that carry
Review Comment:
Nit, optional: the scaladoc reads like the one place for Spark fixes that
Comet picks by version. But `RegrSparkVersions` (SPARK-48719, SPARK-55969) and
`ArraySetSupport.normalizesFloats` (SPARK-54918, SPARK-59602) keep their own
version tables, and `RegrSparkVersions` has its own version parser. Could
`SparkFixes` take those as well, with their tables moving into
`SparkFixesSuite`? A fork that backports one of these fixes would then have one
file to change. If that's more than this PR should take on, a scaladoc that
says it covers these two would work too.
##########
native/spark-expr/src/conversion_funcs/string.rs:
##########
@@ -725,8 +741,9 @@ fn parse_string_to_decimal(input_str: &str, precision: u8,
scale: i8) -> SparkRe
mantissa.checked_mul(pow10_i128(scale_adjustment as u32).unwrap())
} else {
// Need to divide (decrease scale)
- // Spark's `BigDecimal.setScale` also fails for a very large
difference that still fits in
- // an int, while this rounds to zero:
https://github.com/apache/datafusion-comet/issues/6966
+ // Before SPARK-60119 (Spark 4.0.5, 4.1.4 and 4.2.1), Spark's
`BigDecimal.setScale` also
Review Comment:
Nit, optional: a heads-up for the Spark bump. With the 4.1.4-rc4
`Decimal.scala` on the test classpath, `(ansi) failing cast of a literal raises
Spark's own exception when reached` in `CometNativeCastSuite` fails with "Spark
did not fail", because SPARK-60119 makes `CAST('1e-2147483647' AS
DECIMAL(10,2))` return `0.00`. #7187 mentions the negative-scale test but not
this one, so the SPARK-60119 change will need to handle both. Across all of
`CometNativeCastSuite` and `CometSqlFileTestSuite`, those two were the only
failures I could put down to these releases.
##########
docs/source/contributor-guide/expression-audits/conversion_funcs.md:
##########
@@ -27,6 +27,7 @@
- Spark 3.5.8 (audited 2026-05-27): baseline. `Cast(child, dataType,
timeZoneId, evalMode)`; eval modes are `LEGACY`, `ANSI`, `TRY`. The legacy
`Cast.canCast` matrix and the `Cast.canAnsiCast` matrix decide acceptance per
type pair. Comet routes via `CometCast`
(`spark/src/main/scala/org/apache/comet/expressions/CometCast.scala`) using a
per-source-type support matrix that returns `Compatible`,
`Incompatible(reason)`, or `Unsupported(reason)`; literal children are
short-circuited to `Compatible()` so `CometLiteral` validates them. The
serialized `Cast` proto carries `datatype`, `evalMode`, `timezone` (default
`UTC`), and `isSpark4Plus`; `allowIncompatible` is enforced only by the JVM
planner and is not sent to the native cast kernel. The native side
(`native/spark-expr/src/conversion_funcs/cast.rs`) implements explicit
per-eval-mode branches for narrowing numeric casts that match Spark's overflow
exceptions, and falls through to DataFusion `cast_with_options(safe = !ANSI)`
for the r
est.
- Spark 4.0.1 (audited 2026-05-27): `VariantType` added; `StringType` literals
replaced with `_: StringType` to accommodate collated strings. `(TimestampType,
ByteType|ShortType|IntegerType)` added to `canAnsiCast`. `NullIntolerant` ->
`nullIntolerant: Boolean` refactor. New `ToPrettyString.BinaryFormatter`
semantics for `Binary -> String` are replicated natively via
`spark_binary_formatter`. Numeric-to-numeric matrix unchanged.
- Spark 4.1.1 (audited 2026-05-27): `TimeType` added; many `TimeType` arms in
`canCast`/`canAnsiCast`. Geospatial `GeographyType` / `GeometryType` types
added with their own conversion rules. Numeric-to-numeric matrix unchanged.
+- Spark 4.0.5, 4.1.4 and 4.2.1 (release candidates; in 4.0.5-rc3, 4.1.4-rc4
and 4.2.1-rc3): `Decimal.fromString` and `fromStringANSI` no longer count
integral digits for zero (SPARK-60118), so a zero with a large exponent such as
`'0e39'` casts to 0 instead of NULL (or `NUMERIC_OUT_OF_SUPPORTED_RANGE` under
ANSI). `CometCast` sets the `decimal_zero_any_exponent` cast option from
`SparkFixes.castsZeroWithAnyExponentToDecimal`, and `parse_string_to_decimal`
skips the zero limit when it is set. The same releases skip
`BigDecimal.setScale` in `Decimal.changePrecision` when the value's magnitude
already decides the result (SPARK-60119), so a string far below the target
scale's ulp casts to the rounded value instead of throwing
`ArithmeticException`. `'1e-2147483647'` to `DECIMAL(10,2)` gives `0.00`, which
the native cast already returns, so that case of
[#6966](https://github.com/apache/datafusion-comet/issues/6966) applies only to
earlier releases. Its negative-scale case (`spark.sql.le
gacy.allowNegativeScaleOfDecimal=true`) still differs: `'1e-2147483647'` to
`DECIMAL(10,-2)` gives 0 on these releases, while the native cast raises
`CAST_INVALID_INPUT` under ANSI and returns NULL otherwise.
Review Comment:
Nit, optional: SPARK-60118 also fixes the overflow in the integral-digit
check itself. It now counts the digits in a long, so `'1e2147483647'`,
`'12e2147483647'` and `'1.5e2147483647'` return NULL, or raise
`NUMERIC_OUT_OF_SUPPORTED_RANGE` under ANSI. Released Spark throws a raw
`ArithmeticException` there in every mode. I ran these on 4.1.3, and on 4.1.3
with the 4.1.4-rc4 `Decimal.scala` on the test classpath and `SPARK_VERSION`
reporting 4.1.4. Comet returns NULL in legacy and try mode on both, so it
matches the new releases there. Under ANSI it raises `CAST_INVALID_INPUT`,
which is #7188. Could this entry describe that half too and link #7188?
##########
native/spark-expr/src/conversion_funcs/string.rs:
##########
@@ -695,9 +708,11 @@ fn parse_string_to_decimal(input_str: &str, precision: u8,
scale: i8) -> SparkRe
// validate and parse mantissa and exponent or bubble up the error
let (mantissa, exponent) = parse_decimal_str(trimmed, input_str,
precision, scale)?;
- // Early return mantissa 0, Spark checks if it fits digits and throw error
in ansi
+ // Early return mantissa 0, Spark checks if it fits digits and throw error
in ansi. Spark
+ // counts precision - scale integral digits for zero too, so "0e38" (scale
-38) has 39 and is
+ // over the 38-digit limit. Spark stopped applying that limit to zero in
SPARK-60118.
if mantissa == 0 {
- if exponent < -37 {
+ if !zero_any_exponent && exponent < -37 {
Review Comment:
Nit, optional: the same overflow lets one zero through on released Spark.
For `'0e2147483647'` the digit count is 1 - (-2147483647), which wraps negative
in Spark's int arithmetic and skips the limit. So 4.1.3 returns `0.00`, while
this branch returns NULL, or `NUMERIC_OUT_OF_SUPPORTED_RANGE` under ANSI. Since
the `zero_any_exponent = false` path now stands for the released rule, would it
be worth letting an exponent of exactly 2147483647 through here, and adding it
to `test_parse_string_to_decimal_zero_with_large_exponent`?
--
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]