This is an automated email from the ASF dual-hosted git repository.

mbutrovich pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-comet.git


The following commit(s) were added to refs/heads/main by this push:
     new 83339d8891 feat: hint at native pyarrow UDF path when the feature is 
disabled (#4892)
83339d8891 is described below

commit 83339d88914ed1e588f541882f3b6ddc27819325
Author: Andy Grove <[email protected]>
AuthorDate: Fri Jul 17 09:18:11 2026 -0600

    feat: hint at native pyarrow UDF path when the feature is disabled (#4892)
    
    * feat: hint at native pyarrow UDF path when the feature is disabled
    
    When spark.comet.exec.pyarrowUdf.enabled is off (the default), mapInArrow
    and mapInPandas operators silently fall back to Spark. Annotate the operator
    with a non-fallback [COMET-INFO] hint naming the config to opt into the
    native CometMapInBatchExec path, reusing the existing NativeOptIn wording.
    
    The hint fires only on operators that would actually run natively if the
    flag were flipped: the eligibility check is unchanged, the conf gate simply
    moves from the extractor into the rewrite arm.
    
    * docs: remove emphasis from EliminateRedundantTransitions comment
---
 .../rules/EliminateRedundantTransitions.scala      | 49 ++++++++++++++--------
 .../spark/sql/comet/CometMapInBatchSuite.scala     | 23 +++++++++-
 2 files changed, 54 insertions(+), 18 deletions(-)

diff --git 
a/spark/src/main/scala/org/apache/comet/rules/EliminateRedundantTransitions.scala
 
b/spark/src/main/scala/org/apache/comet/rules/EliminateRedundantTransitions.scala
index 477c2afd99..4b4b8c13bd 100644
--- 
a/spark/src/main/scala/org/apache/comet/rules/EliminateRedundantTransitions.scala
+++ 
b/spark/src/main/scala/org/apache/comet/rules/EliminateRedundantTransitions.scala
@@ -30,6 +30,8 @@ import org.apache.spark.sql.execution.adaptive.QueryStageExec
 import org.apache.spark.sql.execution.exchange.ReusedExchangeExec
 
 import org.apache.comet.CometConf
+import org.apache.comet.CometSparkSessionExtensions.withInfo
+import org.apache.comet.serde.NativeOptIn
 import org.apache.comet.shims.ShimSQLConf
 
 // This rule is responsible for eliminating redundant transitions between 
row-based and
@@ -114,13 +116,26 @@ case class EliminateRedundantTransitions(session: 
SparkSession)
       // source string/binary vectors always use 4-byte offsets while the 
destination root is
       // allocated with 8-byte offsets when this conf is on. The buffer counts 
match but the
       // offset width does not, so a direct memcpy would corrupt the offsets.
-      case EligibleMapInBatch(info, columnarChild) =>
-        CometMapInBatchExec(
-          info.func,
-          info.output,
-          columnarChild,
-          info.isBarrier,
-          info.pythonEvalType)
+      //
+      // `EligibleMapInBatch` matches whenever the operator would run natively 
if the feature were
+      // enabled. When it is disabled (the default) we leave the vanilla Spark 
operator in place
+      // but annotate it with a non-fallback `[COMET-INFO]` hint so the user 
knows the native path
+      // exists behind a config flag.
+      case p @ EligibleMapInBatch(info, columnarChild) =>
+        if (CometConf.COMET_PYARROW_UDF_ENABLED.get()) {
+          CometMapInBatchExec(
+            info.func,
+            info.output,
+            columnarChild,
+            info.isBarrier,
+            info.pythonEvalType)
+        } else {
+          withInfo(
+            p,
+            NativeOptIn.message(
+              "PyArrow UDFs (mapInArrow/mapInPandas)",
+              CometConf.COMET_PYARROW_UDF_ENABLED.key))
+        }
 
       // Spark adds `RowToColumnar` under Comet columnar shuffle. But it's 
redundant as the
       // shuffle takes row-based input.
@@ -174,19 +189,19 @@ case class EliminateRedundantTransitions(session: 
SparkSession)
   }
 
   /**
-   * Matches the plans this rule should rewrite to `CometMapInBatchExec`. 
Single extractor used in
-   * the `transformUp` arm above so the matchers and conf reads run once per 
visited plan. Returns
-   * `(info, columnarChild)` where `columnarChild` is the Comet columnar 
producer that
-   * `CometMapInBatchExec` will consume directly. Returns `None` (and the arm 
misses) when the
-   * conf is off, when `useLargeVarTypes` forces the fallback, when the plan 
is not one of the
-   * version-shimmed MapInArrow / MapInPandas operators, or when the child is 
not a Comet
-   * columnar-to-row transition we can strip.
+   * Matches the plans that could run natively as `CometMapInBatchExec`, 
independent of whether
+   * the `spark.comet.exec.pyarrowUdf.enabled` feature flag is set. The 
`transformUp` arm reads
+   * that flag to decide between rewriting the operator and merely annotating 
it with an opt-in
+   * hint. Single extractor so the matchers run once per visited plan. Returns 
`(info,
+   * columnarChild)` where `columnarChild` is the Comet columnar producer that
+   * `CometMapInBatchExec` will consume directly. Returns `None` (and the arm 
misses) when
+   * `useLargeVarTypes` forces the fallback, when the plan is not one of the 
version-shimmed
+   * MapInArrow / MapInPandas operators, or when the child is not a Comet 
columnar-to-row
+   * transition we can strip.
    */
   private object EligibleMapInBatch {
     def unapply(plan: SparkPlan): Option[(MapInBatchInfo, SparkPlan)] = {
-      if (!CometConf.COMET_PYARROW_UDF_ENABLED.get()) {
-        None
-      } else if (arrowUseLargeVarTypes(plan.conf)) {
+      if (arrowUseLargeVarTypes(plan.conf)) {
         None
       } else {
         matchMapInArrow(plan)
diff --git 
a/spark/src/test/spark-4.x/org/apache/spark/sql/comet/CometMapInBatchSuite.scala
 
b/spark/src/test/spark-4.x/org/apache/spark/sql/comet/CometMapInBatchSuite.scala
index c9acd2cc3f..09802dddaa 100644
--- 
a/spark/src/test/spark-4.x/org/apache/spark/sql/comet/CometMapInBatchSuite.scala
+++ 
b/spark/src/test/spark-4.x/org/apache/spark/sql/comet/CometMapInBatchSuite.scala
@@ -30,7 +30,7 @@ import org.apache.spark.sql.execution.python.MapInArrowExec
 import org.apache.spark.sql.types.{LongType, StructField, StructType}
 import org.apache.spark.sql.vectorized.ColumnarBatch
 
-import org.apache.comet.CometConf
+import org.apache.comet.{CometConf, ExtendedExplainInfo}
 import org.apache.comet.rules.EliminateRedundantTransitions
 
 /** Minimal CometPlan leaf used to anchor the rule's transform without 
triggering execution. */
@@ -105,6 +105,27 @@ class CometMapInBatchSuite extends CometTestBase {
     }
   }
 
+  test("rule annotates operator with opt-in hint when feature is disabled") {
+    withSQLConf(CometConf.COMET_PYARROW_UDF_ENABLED.key -> "false") {
+      val rewritten = EliminateRedundantTransitions(spark).apply(buildPlan())
+      val info = new ExtendedExplainInfo().generateExtendedInfo(rewritten)
+      assert(info.contains("[COMET-INFO:"), s"expected a [COMET-INFO: hint 
in:\n$info")
+      assert(
+        info.contains(CometConf.COMET_PYARROW_UDF_ENABLED.key),
+        s"expected the opt-in config key in the hint:\n$info")
+    }
+  }
+
+  test("rule emits no opt-in hint when feature is enabled") {
+    withSQLConf(CometConf.COMET_PYARROW_UDF_ENABLED.key -> "true") {
+      val rewritten = EliminateRedundantTransitions(spark).apply(buildPlan())
+      val info = new ExtendedExplainInfo().generateExtendedInfo(rewritten)
+      assert(
+        !info.contains(CometConf.COMET_PYARROW_UDF_ENABLED.key),
+        s"unexpected opt-in hint when feature is enabled:\n$info")
+    }
+  }
+
   test("rule rewrites chained MapInArrowExec into stacked 
CometMapInBatchExec") {
     // df.mapInArrow(...).mapInArrow(...) produces two MapInArrowExec 
operators. The outer
     // consumes rows from the inner directly (MapInArrowExec is a row 
producer), so there is


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to