You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by ge...@apache.org on 2026/06/03 03:52:19 UTC

(spark) branch master updated: [SPARK-57173][SQL] Simplify regexp pattern-compile codegen by extracting a static Java helper

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

gengliangwang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/master by this push:
     new dffbe1adb2ae [SPARK-57173][SQL] Simplify regexp pattern-compile codegen by extracting a static Java helper
dffbe1adb2ae is described below

commit dffbe1adb2aecfee868fefde1b3f4c673ecf5a32
Author: Gengliang Wang <ge...@apache.org>
AuthorDate: Tue Jun 2 20:52:07 2026 -0700

    [SPARK-57173][SQL] Simplify regexp pattern-compile codegen by extracting a static Java helper
    
    ### What changes were proposed in this pull request?
    
    Add `ExpressionImplUtils.compileRegexPattern(String regex, int flags, String funcName)`, which wraps `Pattern.compile` and maps a `PatternSyntaxException` to the user-facing INVALID_PARAMETER_VALUE.PATTERN error. Route both the shared codegen (`RegExpUtils.initLastMatcherCode`, used by the whole regexp expression family -- `RLike`, `RegExpReplace`, `RegExpExtract`, `RegExpExtractAll`, `RegExpInStr`, etc.) and the eval helper (`RegExpUtils.getPatternAndLastRegex`) through it.
    
    `initLastMatcherCode` previously emitted a 5-line inline `try { Pattern.compile(...) } catch (PatternSyntaxException)` block; it now emits a single helper call. The per-stage mutable-state caching (`lastRegex` / `pattern`) is preserved in the generated code.
    
    ### Why are the changes needed?
    
    Part of SPARK-56908 (umbrella). This block is emitted by every regexp expression in every stage that uses one; collapsing it to a single call shrinks the generated Java across the whole family, helping with the JVM 64KB method / constant-pool limits, Janino compile time, and JIT work.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No. The compiled behavior is identical; only the emitted Java source text changes.
    
    ### How was this patch tested?
    
    ```
    build/sbt "catalyst/testOnly *RegexpExpressionsSuite"
    ```
    
    21/21 pass (exercised both with and without whole-stage codegen).
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Opus 4.8)
    
    Closes #56223 from gengliangwang/spark-regexp-compile-codegen.
    
    Authored-by: Gengliang Wang <ge...@apache.org>
    Signed-off-by: Gengliang Wang <ge...@apache.org>
---
 .../catalyst/expressions/ExpressionImplUtils.java    | 17 +++++++++++++++++
 .../sql/catalyst/expressions/regexpExpressions.scala | 20 +++++++-------------
 2 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/sql/catalyst/src/main/java/org/apache/spark/sql/catalyst/expressions/ExpressionImplUtils.java b/sql/catalyst/src/main/java/org/apache/spark/sql/catalyst/expressions/ExpressionImplUtils.java
index 7bad7c430b86..1053650a3709 100644
--- a/sql/catalyst/src/main/java/org/apache/spark/sql/catalyst/expressions/ExpressionImplUtils.java
+++ b/sql/catalyst/src/main/java/org/apache/spark/sql/catalyst/expressions/ExpressionImplUtils.java
@@ -25,6 +25,8 @@ import java.text.BreakIterator;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
 import java.util.zip.CRC32;
 import javax.crypto.Cipher;
 import javax.crypto.spec.GCMParameterSpec;
@@ -344,6 +346,21 @@ public class ExpressionImplUtils {
     return UTF8String.fromString(qtChar + sp + qtChar);
   }
 
+  /**
+   * Compiles {@code regex} with the given {@code flags} for the regexp expression
+   * family, translating a {@link PatternSyntaxException} into the user-facing
+   * INVALID_PARAMETER_VALUE.PATTERN error. Shared by the regexp eval and codegen
+   * paths so the generated Java is a single call instead of an inline try/catch
+   * around {@code Pattern.compile}.
+   */
+  public static Pattern compileRegexPattern(String regex, int flags, String funcName) {
+    try {
+      return Pattern.compile(regex, flags);
+    } catch (PatternSyntaxException e) {
+      throw QueryExecutionErrors.invalidPatternError(funcName, e.getPattern(), e);
+    }
+  }
+
   /**
    * Computes the CRC32 checksum of {@code bytes} for the {@code crc32} expression.
    * Shared by the eval and codegen paths so the per-stage generated Java is a
diff --git a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/regexpExpressions.scala b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/regexpExpressions.scala
index 5ad360a54e8d..c2c01d2c7815 100644
--- a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/regexpExpressions.scala
+++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/regexpExpressions.scala
@@ -1253,17 +1253,15 @@ object RegExpUtils {
     val termLastRegex = ctx.addMutableState("UTF8String", "lastRegex")
     val termPattern = ctx.addMutableState(classNamePattern, "pattern")
     val collationRegexFlags = CollationSupport.collationAwareRegexFlags(collationId)
+    val utils = classOf[ExpressionImplUtils].getName
 
     s"""
        |if (!$regexp.equals($termLastRegex)) {
        |  // regex value changed
-       |  try {
-       |    UTF8String r = $regexp.clone();
-       |    $termPattern = $classNamePattern.compile(r.toString(), $collationRegexFlags);
-       |    $termLastRegex = r;
-       |  } catch (java.util.regex.PatternSyntaxException e) {
-       |    throw QueryExecutionErrors.invalidPatternError("$prettyName", e.getPattern(), e);
-       |  }
+       |  UTF8String r = $regexp.clone();
+       |  $termPattern =
+       |    $utils.compileRegexPattern(r.toString(), $collationRegexFlags, "$prettyName");
+       |  $termLastRegex = r;
        |}
        |java.util.regex.Matcher $matcher = $termPattern.matcher($subject.toString());
        |""".stripMargin
@@ -1272,12 +1270,8 @@ object RegExpUtils {
   def getPatternAndLastRegex(p: Any, prettyName: String, collationId: Int): (Pattern, UTF8String) =
   {
     val r = p.asInstanceOf[UTF8String].clone()
-    val pattern = try {
-      Pattern.compile(r.toString, CollationSupport.collationAwareRegexFlags(collationId))
-    } catch {
-      case e: PatternSyntaxException =>
-        throw QueryExecutionErrors.invalidPatternError(prettyName, e.getPattern, e)
-    }
+    val pattern = ExpressionImplUtils.compileRegexPattern(
+      r.toString, CollationSupport.collationAwareRegexFlags(collationId), prettyName)
     (pattern, r)
   }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@spark.apache.org
For additional commands, e-mail: commits-help@spark.apache.org