snmvaughan commented on code in PR #7171:
URL: https://github.com/apache/datafusion-comet/pull/7171#discussion_r4378333531
##########
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:
Done in 1755de3: the else branch is now `checkSparkError(sql(query),
"INTERNAL_ERROR")`. Spark 3.4 and 3.5 raise the same
`internalError("Unreachable code reached.")`, and their shims map it to
`SparkException.internalError`, so it should hold on those profiles too.
##########
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:
Thanks, added in 1755de3. The entry now says that SPARK-60118 counts the
digits in a long, what released Spark does with those three strings (and with
`'0e2147483647'`), and that the native cast matches the fixed releases outside
ANSI and raises `CAST_INVALID_INPUT` under it, with a link to #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:
Good catch. In 1755de3 the released path counts a zero's integral digits as
`1 - scale` with a wrapping `i32` subtraction, as Spark's int arithmetic does,
so `'0e2147483647'` casts to 0 there and no other input changes.
`test_parse_string_to_decimal_zero_with_large_exponent` now passes
`'0e2147483647'` in both modes, and still rejects `'0e2147483646'` and
`'0.0e2147483647'`, whose counts don't wrap, on the released path.
##########
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:
Thanks for running both suites with the RC classes. I'll handle `(ansi)
failing cast of a literal raises Spark's own exception when reached` together
with the negative-scale test in the SPARK-60119 change, and I've noted it on
#7187.
##########
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:
I've taken the scaladoc route here: in 1755de3 it names the two fixes it
covers and points to `RegrSparkVersions` and `ArraySetSupport.normalizesFloats`
for the others. Moving those in is worth doing for the reason you give. I'd do
it in a separate PR, since the two read versions a little differently:
`RegrSparkVersions` accepts any string that starts with `x.y` and falls back to
a string comparison, while `SparkFixes` uses Spark's `VersionUtils` and treats
a version it can't parse as unfixed. Folding them together means picking one
rule, so I've opened #7241 for it.
--
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]