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

dtenedor pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-4.x by this push:
     new 7b1f6f6f6a14 [SPARK-57328][SQL] Extract matchedIdentifier into a 
static, resolver-parameterized helper
7b1f6f6f6a14 is described below

commit 7b1f6f6f6a14dd501165080fc7903f4c5892e940
Author: Daniel Tenedorio <[email protected]>
AuthorDate: Wed Jun 17 17:31:20 2026 -0700

    [SPARK-57328][SQL] Extract matchedIdentifier into a static, 
resolver-parameterized helper
    
    ## What changes were proposed in this pull request?
    
    This PR refactors the relation-identifier suffix-match logic 
(`matchedIdentifier`) out of the
    `ResolveHints.ResolveJoinStrategyHints` rule and into a static, 
resolver-parameterized helper,
    so the logic can be shared without depending on fixed-point rule state.
    
    - Added a static `ResolveHints.matchedIdentifier(identInHint, identInQuery, 
resolver)` helper that
      takes the `resolver: Resolver` (i.e. `(String, String) => Boolean`) as an 
explicit parameter.
    - Moved the explanatory doc comment (describing the tail-match semantics 
and the catalog-independent
      comparison) onto the static helper.
    - `ResolveJoinStrategyHints` now keeps a thin private `matchedIdentifier` 
that delegates to the
      static helper, passing its `resolver`.
    
    Passing the `resolver` in explicitly gives both the fixed-point analyzer 
and the single-pass
    resolver one source of truth for hint-to-relation matching, without either 
having to depend on the
    other's rule state.
    
    No behavior change: the helper body is identical to the original.
    
    ## How was this patch tested?
    
    Existing join-hint resolution test coverage (e.g. join hint suites) pins 
the behavior; this is a
    pure extraction with no functional change.
    
    ## Was this patch authored or co-authored using generative AI tooling?
    
    No
    
    Closes #56478 from dtenedor/refactor-matched-identifier.
    
    Authored-by: Daniel Tenedorio <[email protected]>
    Signed-off-by: Daniel Tenedorio <[email protected]>
    (cherry picked from commit d31cf1921c18ee87f385cd670d495c994b26a354)
    Signed-off-by: Daniel Tenedorio <[email protected]>
---
 .../spark/sql/catalyst/analysis/ResolveHints.scala | 55 +++++++++++++---------
 1 file changed, 34 insertions(+), 21 deletions(-)

diff --git 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveHints.scala
 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveHints.scala
index f6d20dd71b1f..c352f0552339 100644
--- 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveHints.scala
+++ 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveHints.scala
@@ -38,6 +38,38 @@ import org.apache.spark.sql.internal.SQLConf
  */
 object ResolveHints {
 
+  /**
+   * Checks if the given multi-part identifiers are matched with each other.
+   *
+   * The [[ResolveJoinStrategyHints]] rule is applied before the resolution 
batch in the analyzer
+   * and we cannot semantically compare them at this stage. Therefore, we 
follow a simple rule;
+   * they match if an identifier in a hint is a tail of an identifier in a 
relation. This process
+   * is independent of a session catalog (`currentDb` in [[SessionCatalog]]) 
and it just compares
+   * them literally.
+   *
+   * For example,
+   *  - in a query `SELECT /*+ BROADCAST(t) */ * FROM db1.t JOIN t`,
+   *    the broadcast hint will match both tables, `db1.t` and `t`,
+   *    even when the current db is `db2`.
+   *  - in a query `SELECT /*+ BROADCAST(default.t) */ * FROM default.t JOIN 
t`,
+   *    the broadcast hint will match the left-side table only, `default.t`.
+   *
+   * The `resolver` is passed in explicitly so that this logic can be shared 
by both the
+   * fixed-point analyzer and the single-pass resolver, neither of which has 
to depend on the
+   * other's rule state.
+   */
+  def matchedIdentifier(
+      identInHint: Seq[String],
+      identInQuery: Seq[String],
+      resolver: Resolver): Boolean = {
+    if (identInHint.length <= identInQuery.length) {
+      identInHint.zip(identInQuery.takeRight(identInHint.length))
+        .forall { case (i1, i2) => resolver(i1, i2) }
+    } else {
+      false
+    }
+  }
+
   /**
    * The list of allowed join strategy hints is defined in 
[[JoinStrategyHint.strategies]], and a
    * sequence of relation aliases can be specified with a join strategy hint, 
e.g., "MERGE(a, c)",
@@ -65,27 +97,8 @@ object ResolveHints {
             
_.toUpperCase(Locale.ROOT)).contains(hintName.toUpperCase(Locale.ROOT))))
     }
 
-    // This method checks if given multi-part identifiers are matched with 
each other.
-    // The [[ResolveJoinStrategyHints]] rule is applied before the resolution 
batch
-    // in the analyzer and we cannot semantically compare them at this stage.
-    // Therefore, we follow a simple rule; they match if an identifier in a 
hint
-    // is a tail of an identifier in a relation. This process is independent 
of a session
-    // catalog (`currentDb` in [[SessionCatalog]]) and it just compares them 
literally.
-    //
-    // For example,
-    //  * in a query `SELECT /*+ BROADCAST(t) */ * FROM db1.t JOIN t`,
-    //    the broadcast hint will match both tables, `db1.t` and `t`,
-    //    even when the current db is `db2`.
-    //  * in a query `SELECT /*+ BROADCAST(default.t) */ * FROM default.t JOIN 
t`,
-    //    the broadcast hint will match the left-side table only, `default.t`.
-    private def matchedIdentifier(identInHint: Seq[String], identInQuery: 
Seq[String]): Boolean = {
-      if (identInHint.length <= identInQuery.length) {
-        identInHint.zip(identInQuery.takeRight(identInHint.length))
-          .forall { case (i1, i2) => resolver(i1, i2) }
-      } else {
-        false
-      }
-    }
+    private def matchedIdentifier(identInHint: Seq[String], identInQuery: 
Seq[String]): Boolean =
+      ResolveHints.matchedIdentifier(identInHint, identInQuery, resolver)
 
     private def extractIdentifier(r: SubqueryAlias): Seq[String] = {
       r.identifier.qualifier :+ r.identifier.name


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

Reply via email to