sam-1112 commented on code in PR #5409:
URL: https://github.com/apache/datafusion-comet/pull/5409#discussion_r3837540189


##########
spark/src/main/scala/org/apache/comet/serde/strings.scala:
##########
@@ -178,29 +182,84 @@ object CometStringReplace
     extends CometScalarFunction[StringReplace]("replace")
     with NativeOptInAvailable {
 
+  /**
+   * The DataFusion `replace` kernel matches Spark only for a non-empty search 
string. Kernel
+   * compatibility is not enough: `CometLiteral` serializes strings via 
`UTF8String.toString`
+   * (malformed UTF-8 becomes U+FFFD), DataFusion evaluates every child before 
`replace` (so a
+   * NULL `src` does not skip a throwing replacement), and scalar literals are 
broadcast into
+   * Arrow `Utf8` arrays that overflow 32-bit offsets on a large batch.
+   *
+   * The default native path is therefore limited to a plan-time subset that 
avoids those
+   * boundaries. Non-default collations stay on the dispatcher.
+   * https://github.com/apache/datafusion-comet/issues/4496
+   */
+  private def nativeSafeSubset(expr: StringReplace): Boolean = {
+    val children = expr.children
+    if (children.length != 3) {
+      return false
+    }
+    val searchIsSafe = children(1) match {
+      case Literal(v: UTF8String, _) => isNativeSafeStringLiteral(v, 
allowEmpty = false)
+      case _ => false
+    }
+    val replacementIsSafe = children(2) match {
+      case Literal(null, _) => true
+      case Literal(v: UTF8String, _) => isNativeSafeStringLiteral(v, 
allowEmpty = true)
+      case _: Attribute | _: BoundReference => true
+      case _ => false
+    }
+    val utf8BinaryCollation =
+      !children.exists(c => QueryPlanSerde.isStringCollationType(c.dataType))
+    utf8BinaryCollation && searchIsSafe && replacementIsSafe

Review Comment:
   You're right — I only gated search and replacement, not source. 
`replace(substring(s, 1, CAST(1 / n AS INT)), 'a', 'x')` still went native 
because `'a'`/`'x'` looked safe. Spark short-circuits inside `substring` when 
`s` is NULL; native evaluates the whole source tree and hits `1/0`. Same hole 
for a malformed or huge literal as source, including one nested under `concat`. 
Source now uses the same whitelist as replacement: a short, well-formed literal 
or a column. `substring` / `concat` / other nested expressions stay on the 
dispatcher. I'm not trying to prove an arbitrary source tree is safe. Tests 
cover your three cases (substring + ANSI `1/n`, `CAST(X'FF' AS STRING)` as 
source, `concat(...)` with a nested malformed literal) plus a large `repeat` as 
source. Result matches Spark, EXPLAIN still says dispatcher.



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