0lai0 commented on code in PR #5050:
URL: https://github.com/apache/datafusion-comet/pull/5050#discussion_r4288703046


##########
spark/src/main/scala/org/apache/comet/expressions/CometCast.scala:
##########
@@ -441,13 +437,18 @@ object CometCast
     case _ => unsupported(DataTypes.DoubleType, toType)
   }
 
-  private def canCastFromDecimal(toType: DataType): SupportLevel = toType 
match {
-    case DataTypes.FloatType | DataTypes.DoubleType | DataTypes.ByteType | 
DataTypes.ShortType |
-        DataTypes.IntegerType | DataTypes.LongType | DataTypes.BooleanType |
-        DataTypes.TimestampType =>
-      Compatible()
-    case _ => Unsupported(Some(s"Cast from DecimalType to $toType is not 
supported"))
-  }
+  private def canCastFromDecimal(fromType: DecimalType, toType: DataType): 
SupportLevel =
+    toType match {
+      // Negative-scale source overflows natively; see #5013.
+      case DataTypes.ByteType | DataTypes.ShortType | DataTypes.IntegerType | 
DataTypes.LongType |
+          DataTypes.TimestampType if fromType.scale < 0 =>

Review Comment:
   Good call, thanks. Restructured, though not as an early return.
   
   An early return would not have helped, because the guard only covers a 
subset of the target types, so the `Byte | Short | Integer | Long | Timestamp` 
list had to appear twice either way. The duplication was the problem, not the 
branch position.
   
   It now matches that list once and does the `fromType.scale < 0` check inside 
that case, with the comment about why float and double are deliberately 
excluded sitting on the case that excludes them. Semantics unchanged, 
`CometNativeCastSuite` and `CometExpressionSuite` pass.



##########
spark/src/main/scala/org/apache/comet/serde/arithmetic.scala:
##########
@@ -91,6 +90,25 @@ trait MathBase {
       Unsupported(Some(s"Unsupported datatype $dt"))
     }
 
+  // Native decimal Add/Subtract/Divide/IntegralDivide/Remainder scale-aligns 
operands by
+  // multiplying by 10^|delta|, which overflows (subtract-with-overflow panic 
in debug, silent
+  // wrap in release) whenever any operand or the result has negative scale. 
See issue #5013.
+  private[comet] val negScaleDecimalArithmeticReason: String =
+    "Arithmetic on negative-scale decimal is not supported natively"
+
+  private[comet] def negScaleDecimalRejection(expr: BinaryArithmetic): 
Option[Unsupported] = {

Review Comment:
   You were right @comphead, and most of this file's changes have since come 
out. Sorry for the delay.
   
   I had attributed the panic to the operators scale-aligning by `10^|delta|`, 
which was wrong. Add and subtract align by `10^(max_scale - s1)`, never 
negative. The real cause was two width tests computing `max(s1, s2) as u8 + 
max(p1 - s1 as u8, p2 - s2 as u8)`, where a negative scale wraps `s as u8` to 
255 and underflows the subtraction. Both are now rewritten in `i16`.
   
   So the guards on `CometAdd`, `CometSubtract` and `CometRemainder` are gone, 
along with their `getUnsupportedReasons` overrides. I verified each runs 
natively and matches Spark over `Decimal(10,-1)`, `Decimal(20,-5)` and 
`Decimal(38,-5)`, and they are pinned in `safe ops on negative-scale decimal 
run natively`.
   
   Only `CometDivide` and `CometIntegralDivide` still need it. 
`spark_decimal_div` computes its exponents in `u32`, so `s1 = -5` becomes 
~4.29e9 and the BigInt branch attempts `10.pow(~4.29e9)`, an unbounded 
allocation that hangs rather than fails. `decimal_div` now rejects a negative 
scale up front, but there is still no usable native result. The comment carries 
that reason now.



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