andygrove commented on code in PR #5225:
URL: https://github.com/apache/datafusion-comet/pull/5225#discussion_r3706832148


##########
spark/src/main/scala/org/apache/comet/serde/arithmetic.scala:
##########
@@ -263,7 +263,7 @@ object CometDivide extends CometExpressionSerde[Divide] 
with MathBase {
     // For now, use NullIf to swap zeros with nulls.
     val rightExpr =
       if (expr.evalMode != EvalMode.ANSI) nullIfWhenPrimitive(expr.right) else 
expr.right
-    val divideExpr = createMathExpression(
+    createMathExpression(

Review Comment:
   One thing I want to check here. The guard you removed fired on 
`expr.dataType.isInstanceOf[DecimalType]` alone. The `promote` rule requires 
both operands to match `DecimalExpression` as well, and `getSupportLevel` only 
looks at `expr.left.dataType`. So if a decimal-typed `Divide` with a 
non-decimal operand ever reaches this serde, there is no `CheckOverflow` 
anywhere and the native `i128::MAX` sentinel comes back as a real value.
   
   I could not construct that shape through Spark's type coercion, so I believe 
it is unreachable today. Since the failure mode is silent wrong data rather 
than a fallback, would you be up for a defensive guard? Returning `Unsupported` 
from `getSupportLevel` when `expr.dataType` is decimal but the operands would 
not match `promote`'s pattern would keep the invariant checkable next to the 
code that depends on it.



##########
spark/src/test/spark-4.1+/org/apache/spark/sql/comet/CometDecimalArithmeticViewSuite.scala:
##########
@@ -119,4 +104,38 @@ class CometDecimalArithmeticViewSuite extends 
CometTestBase {
       }
     }
   }
+
+  test("issue #5190: recursive serialization does not duplicate decimal 
CheckOverflow") {
+    val left = "CAST(id AS DECIMAL(10, 0))"
+    val right = "CAST(id + 1 AS DECIMAL(10, 0))"
+    val operations: Seq[(String, Boolean, String, ExprOuterClass.Expr => 
Boolean, Boolean)] = Seq(

Review Comment:
   This tuple has two unlabeled booleans in it, and `("divide TRY", true, ..., 
false)` is hard to read without counting positions back to the destructuring on 
the next block. A small case class with named fields, or moving `ansiEnabled` 
and `failOnError` next to their names, would make this easier to extend when 
someone adds a case later.



##########
spark/src/test/spark-4.1+/org/apache/spark/sql/comet/CometDecimalArithmeticViewSuite.scala:
##########
@@ -119,4 +104,38 @@ class CometDecimalArithmeticViewSuite extends 
CometTestBase {
       }
     }
   }
+
+  test("issue #5190: recursive serialization does not duplicate decimal 
CheckOverflow") {

Review Comment:
   This regression does not depend on anything 4.1-specific, but sitting under 
`spark-4.1+` means 3.4, 3.5, and 4.0 CI never runs it, and the duplication it 
guards against exists on all of them. Could it move to a version-agnostic suite 
under `spark/src/test/scala/org/apache/spark/sql/comet/`? A new 
`CometDecimalPromotionSuite` would work. The other two tests in this file can 
stay where they are since they need `NumericEvalContext`.
   
   While it is moving, two additions would be worth it. ANSI is only exercised 
for `divide`, but the collapse guard compares `nullOnOverflow`, so ANSI is 
exactly the case that would catch a wrong guard for add, subtract, multiply, 
and remainder. And a direct assertion that `promote(promote(e)) == promote(e)` 
would state the idempotency property outright instead of leaving it to be 
inferred from the `array_contains` proto shape.



##########
spark/src/main/scala/org/apache/spark/sql/comet/DecimalPrecision.scala:
##########
@@ -45,6 +45,15 @@ object DecimalPrecision {
       // happen if the Spark version is < 3.4
       case e: BinaryArithmetic if e.left.prettyName == "promote_precision" => e
 
+      // Recursive exprToProto calls can re-promote every decimal binary 
operator below. Collapse
+      // only equivalent wrappers so the shared promotion rule remains 
idempotent.
+      case outer @ CheckOverflow(

Review Comment:
   This is a good fix, and the bottom-up collapse converges correctly at any 
depth.
   
   The re-promotion it works around comes from serdes calling the public 
`exprToProto` on children that `promote` has already visited. There are 64 such 
call sites in the serde package. The aggregate ones genuinely need the 
promoting entry point, but the ones in `arrays.scala` and `bitwise.scala` are 
re-walking an already-promoted tree, and each walk drags `liftCoverageTags` 
along with it. That is separate work from this PR, but could you file a 
tracking issue for it and link it here? Otherwise it will not get picked up.



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