From dd198bb30e57335179659c324343d7c7b37f991f Mon Sep 17 00:00:00 2001 From: adibmbrk Date: Tue, 1 Sep 2026 06:55:02 +0530 Subject: [PATCH] fix: route translate through codegen dispatcher by default Mix NativeOptInAvailable into CometStringTranslate so it runs natively by default via the JVM codegen dispatcher (Spark-compatible), instead of falling back to Spark unless allowIncompatible is set. The incompatible native DataFusion path becomes opt-in via allowIncompatible, matching CometInitCap and CometStringReplace. Docs updated to Hybrid. Closes #5585 Signed-off-by: adibmbrk --- docs/source/user-guide/latest/expressions.md | 2 +- .../org/apache/comet/serde/strings.scala | 28 +++++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/source/user-guide/latest/expressions.md b/docs/source/user-guide/latest/expressions.md index 2ba71e93c4d..6e4734e8e4c 100644 --- a/docs/source/user-guide/latest/expressions.md +++ b/docs/source/user-guide/latest/expressions.md @@ -610,7 +610,7 @@ The type-name conversion functions (`bigint`, `binary`, `boolean`, `date`, `deci | `to_char` | ✅ | Codegen dispatch | | | `to_number` | ✅ | Codegen dispatch | | | `to_varchar` | ✅ | Codegen dispatch | | -| `translate` | ✅ | Native | DataFusion's `translate` iterates over Unicode graphemes (Spark uses code points) and substitutes U+0000 instead of treating it as a deletion sentinel, so the native path is opt-in via allowIncompatible | +| `translate` | ✅ | Hybrid | Routes through the JVM codegen dispatcher by default; the native path (which iterates Unicode graphemes rather than code points and substitutes U+0000 instead of a deletion sentinel) is opt-in via allowIncompatible | | `trim` | ✅ | Native | | | `try_to_binary` | ✅ | — | Runs natively (rewrites to `try_eval(to_binary(...))`) | | `try_to_number` | ✅ | Codegen dispatch | Routed through the JVM codegen dispatcher | diff --git a/spark/src/main/scala/org/apache/comet/serde/strings.scala b/spark/src/main/scala/org/apache/comet/serde/strings.scala index 665c0d3b54e..0d0c7c2c235 100644 --- a/spark/src/main/scala/org/apache/comet/serde/strings.scala +++ b/spark/src/main/scala/org/apache/comet/serde/strings.scala @@ -109,15 +109,37 @@ object CometOctetLength extends CometScalarFunction[OctetLength]("octet_length") } } -object CometStringTranslate extends CometScalarFunction[StringTranslate]("translate") { +object CometStringTranslate + extends CometScalarFunction[StringTranslate]("translate") + with NativeOptInAvailable { private val incompatReason = "DataFusion's translate iterates over Unicode graphemes (Spark uses code points) and" + " substitutes U+0000 instead of treating it as a deletion sentinel" override def getIncompatibleReasons(): Seq[String] = Seq(incompatReason) - override def getSupportLevel(expr: StringTranslate): SupportLevel = Incompatible( - Some(incompatReason)) + override def getSupportLevel(expr: StringTranslate): SupportLevel = + if (!CometConf.isExprAllowIncompat(getExprConfigName(expr))) { + Compatible(nativeOptIn = + Some(NativeOptIn(CometConf.getExprAllowIncompatConfigKey(getExprConfigName(expr))))) + } else { + Compatible() + } + + override def convert( + expr: StringTranslate, + inputs: Seq[Attribute], + binding: Boolean): Option[Expr] = { + if (CometConf.isExprAllowIncompat(getExprConfigName(expr))) { + // Native path: faster but iterates Unicode graphemes and substitutes U+0000 rather than + // treating it as a deletion sentinel, so it is only used when incompatibility is allowed. + super.convert(expr, inputs, binding) + } else { + // Default: run Spark's own generated code inside the Comet pipeline for exact + // compatibility. Falls back to Spark when the codegen dispatcher is disabled. + CometScalaUDF.emitJvmCodegenDispatch(expr, inputs, binding) + } + } } object CometLevenshtein extends CometExpressionSerde[Levenshtein] {