sunchao commented on code in PR #6826:
URL: https://github.com/apache/datafusion-comet/pull/6826#discussion_r4236061109


##########
spark/src/main/scala/org/apache/comet/expressions/CometCast.scala:
##########
@@ -119,10 +121,10 @@ object CometCast
       inputs: Seq[Attribute],
       binding: Boolean): Option[ExprOuterClass.Expr] = {
     val cometEvalMode = evalMode(cast)
-    cast.child match {
-      case _: Literal =>
-        exprToProtoInternal(Literal.create(cast.eval(), cast.dataType), 
inputs, binding)
-      case _ =>
+    foldLiteralCast(cast) match {
+      case Some(folded) =>
+        exprToProtoInternal(folded, inputs, binding)
+      case None =>

Review Comment:
   [P2] [P2] Preserve decimal failures even when the scale fits in i32. With 
ANSI enabled and a Parquet table `t(id BIGINT)` containing `(0), (1)`, run 
`SELECT CASE WHEN id = 1 THEN CAST('1e-2147483647' AS DECIMAL(10,2)) ELSE 
CAST(0 AS DECIMAL(10,2)) END FROM t`. On the tested JDK 21 runtime, Spark 
retains this cast and throws `ArithmeticException: BigInteger would overflow 
supported range` when the branch is selected. This failed-fold route instead 
admits the native decimal cast, whose parser returns `0.00`. The base raised 
the Spark exception through `cast.eval()`, so successful literal evaluation is 
newly exposed by this PR. This case has an in-range scale and survives the new 
overflow guard. Please retain Spark execution for such failed folds or 
reproduce its runtime failure, and correct the new Rust assertion that 
explicitly expects zero for this input.
   
   Evidence: Bounded Catalyst probes on Spark 3.5.9, 4.1.3 and 4.2.0 with 
OpenJDK 21.0.12.1 printed `foldLiteralCast=None retainedCasts=1`, returned 
`0.00` for id=0, and threw `BigInteger would overflow supported range` for 
id=1. The exact-head extracted Rust parser, built in release mode, returned 
`Ok(Some(0))`. Source tracing confirms `canCastFromString` marks DecimalType 
compatible, scalar `spark_cast` reaches `cast_string_to_decimal128_impl`, and 
`Ok(Some(0))` is accepted in ANSI mode. No end-to-end Comet query was run.



##########
spark/src/test/resources/sql-tests/expressions/cast/cast_literal_ansi.sql:
##########
@@ -0,0 +1,93 @@
+-- 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.
+
+-- A cast of a literal that fails under ANSI must not fail the query while 
Comet plans it. Spark
+-- raises the error only when a row reaches the cast, so a branch no row 
chooses returns rows.
+-- ConstantFolding is left enabled: it keeps a failing cast unfolded inside a 
conditional branch,
+-- which is how such a cast reaches Comet in a real query.
+
+-- Config: spark.sql.ansi.enabled=true
+-- ConstantFolding: enabled
+
+statement
+CREATE TABLE test_cast_literal_ansi(id bigint) USING parquet
+
+statement
+INSERT INTO test_cast_literal_ansi VALUES (0), (1), (2)
+
+-- no row reaches the failing cast
+query
+SELECT id, CASE WHEN id = 5 THEN CAST('bad' AS BIGINT) ELSE id END FROM 
test_cast_literal_ansi
+
+query
+SELECT id, IF(id > 5, CAST('2147483648' AS INT), 0) FROM test_cast_literal_ansi
+
+query
+SELECT id, COALESCE(id, CAST('bad' AS INT)) FROM test_cast_literal_ansi
+
+-- Java only allows a D/F suffix after a decimal number, so these special 
values are invalid
+query
+SELECT id, CASE WHEN id = 5 THEN CAST('NaNF' AS DOUBLE) ELSE 0D END,
+  IF(id > 5, CAST('InfinityD' AS FLOAT), 0F) FROM test_cast_literal_ansi
+
+-- java.math.BigDecimal rejects a scale outside the int range
+query
+SELECT id, CASE WHEN id = 5 THEN CAST('1e-2147483648' AS DECIMAL(10,2))
+  ELSE CAST(0 AS DECIMAL(10,2)) END FROM test_cast_literal_ansi
+
+-- a cast of a literal that does not fail is still accepted
+query
+SELECT id, CASE WHEN id = 1 THEN CAST('7' AS BIGINT) ELSE id END FROM 
test_cast_literal_ansi
+
+-- a row reaches the failing cast
+query expect_error(CAST_INVALID_INPUT)
+SELECT id, CASE WHEN id = 1 THEN CAST('bad' AS BIGINT) ELSE id END FROM 
test_cast_literal_ansi
+
+query expect_error(CAST_INVALID_INPUT)
+SELECT id, CASE WHEN id = 1 THEN CAST('NaNF' AS DOUBLE) ELSE 0D END FROM 
test_cast_literal_ansi
+
+query expect_error(CAST_INVALID_INPUT)
+SELECT id, IF(id > 1, CAST('InfinityD' AS FLOAT), 0F) FROM 
test_cast_literal_ansi
+
+query expect_error(CAST_INVALID_INPUT)
+SELECT id, CASE WHEN id = 1 THEN CAST('1e-2147483648' AS DECIMAL(10,2))
+  ELSE CAST(0 AS DECIMAL(10,2)) END FROM test_cast_literal_ansi
+
+query expect_error(CAST_OVERFLOW)
+SELECT id, IF(id > 1, CAST(2147483648L AS INT), 0) FROM test_cast_literal_ansi
+
+-- The same floating-point parsing applies to a column. Only the exact-case 
signed NaN is valid.
+statement
+CREATE TABLE test_cast_float_special(s string) USING parquet
+
+statement
+INSERT INTO test_cast_float_special VALUES ('NaNF'), ('NaND'), ('nanf'), 
('-nan'), ('+nan'),
+  ('-NaN'), ('+NaN'), ('NaN'), ('InfinityD'), ('infF'), ('-InfinityF'), 
('Infinity'), ('-inf'),
+  ('1.5d'), ('-.5F'), ('1e5d'), ('d'), ('1.0dd'), (NULL)
+
+query
+SELECT s, try_cast(s AS DOUBLE), try_cast(s AS FLOAT) FROM 
test_cast_float_special
+
+statement
+CREATE TABLE test_cast_decimal_scale(s string) USING parquet
+
+statement
+INSERT INTO test_cast_decimal_scale VALUES ('1e-2147483648'), 
('1.0e-2147483647'),
+  ('0e-2147483648'), ('1e2147483648'), ('1e-9999999999'), ('1.5e-3'), 
('12.345e1'), (NULL)

Review Comment:
   [P2] [P2] Use a JDK-independent invalid exponent in the result-comparison 
fixture. The added `'1e2147483648'` row makes the final plain `query` fail on 
OpenJDK 21.0.12.1: Spark's `try_cast(s AS DECIMAL(10,2))` throws 
`ArithmeticException: Underflow` instead of returning NULL. This breaks the new 
fixture on a supported JDK 21 configuration even though default JDK 17 CI can 
pass. This JDK accepts the string's BigDecimal scale of `Integer.MIN_VALUE`, 
then fails during Spark's decimal conversion. Please replace this sample with 
an unambiguously invalid exponent such as `'1e2147483649'` for the NULL-result 
comparison, or isolate it with the correct JDK-specific exceptional behavior.
   
   Evidence: Executed the final SELECT against a Parquet string column on Spark 
3.5.9 and an RDD-backed string column on Spark 4.1.3, both using OpenJDK 
21.0.12.1. Both generated execution plans contained `try_cast`, and collection 
failed with root cause `java.lang.ArithmeticException: Underflow`. Catalyst 
probes reproduced it on Spark 4.2.0 as well. OpenJDK source comparison confirms 
JDK 17 rejects the exponent before decimal construction, explaining why its 
behavior differs.



-- 
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]

Reply via email to