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


##########
spark/src/main/scala/org/apache/comet/serde/strings.scala:
##########
@@ -713,7 +713,34 @@ object CometBase64 extends CometExpressionSerde[Base64] {
   }
 }
 
-object CometUnBase64 extends CometCodegenDispatch[UnBase64]
+// Base64.getMimeDecoder() semantics: skips non-alphabet bytes and matches 
Spark's codegen
+// path. The native path handles the default UnBase64 (failOnError = false, 
reachable from SQL
+// `unbase64(...)`). When failOnError = true (from `to_binary('base64')` / 
`try_to_binary`),
+// Spark uses a stricter RFC 4648 validator, so those cases stay on the JVM 
codegen dispatcher
+// via CodegenDispatchFallback. Error messages match Spark byte-for-byte 
(pinned in the Rust
+// unit tests), but the wrapping exception class does not; kept as 
Compatible() because Spark
+// surfaces these as bare IllegalArgumentException without a SQL error class.
+object CometUnBase64 extends CometExpressionSerde[UnBase64] with 
CodegenDispatchFallback {
+
+  private val failOnErrorReason =
+    "unbase64 with failOnError = true uses stricter RFC 4648 validation that 
is not yet" +
+      " implemented natively"
+
+  override def getUnsupportedReasons(): Seq[String] = Seq(failOnErrorReason)
+
+  override def getSupportLevel(expr: UnBase64): SupportLevel = {
+    if (expr.failOnError) Unsupported(Some(failOnErrorReason)) else 
Compatible()
+  }
+
+  override def convert(expr: UnBase64, inputs: Seq[Attribute], binding: 
Boolean): Option[Expr] = {
+    val childExpr = exprToProtoInternal(expr.child, inputs, binding)

Review Comment:
   [P2] Preserve null-short-circuiting when lowering the child tree
   
   Recursively serializing `expr.child` changes evaluation of trees that 
previously ran entirely through Spark's codegen dispatcher. With a Parquet 
table `t(n STRING, bad STRING)` containing `(NULL, 'A')`, `SELECT 
unbase64(concat(n, cast(unbase64(bad) AS STRING))) FROM t` returns NULL in 
Spark's default generated execution and with the pre-change dispatcher, but 
this head fails with `Last unit does not have enough valid bits` (reproduced on 
Spark 3.5.9/JDK 17 with `CometProject` + `CometNativeScan`). Spark's generated 
concat stops after its first NULL argument; native `ScalarFunctionExpr` 
evaluates the inner decoder before concat can apply its null mask. An isolated 
control using the exact base serializer restores NULL. Please retain whole-tree 
dispatch for affected children, or preserve their short-circuit semantics 
natively, before marking all `failOnError = false` trees compatible.



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