cloud-fan commented on code in PR #58896:
URL: https://github.com/apache/spark/pull/58896#discussion_r4053408482


##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/unresolved.scala:
##########
@@ -394,6 +394,10 @@ case class UnresolvedGenerator(name: FunctionIdentifier, 
children: Seq[Expressio
  * Represents an unresolved function that is being invoked. The analyzer will 
resolve the function
  * arguments first, then look up the function by name and arguments, and 
return an expression that
  * can be evaluated to get the result of this function invocation.
+ *
+ * `boundOwner`, when set, is the fully-qualified candidate that direct-star 
preprocessing selected

Review Comment:
   **Nit (P3):** resolutionCandidates can return the original two-part 
builtin.<name> candidate when persistentCatalogFirst is enabled, and that value 
is stored here before resolveFunctionCandidate qualifies it against the current 
catalog. Calling boundOwner fully qualified therefore overstates the 
representation and can mislead future consumers into assuming the catalog was 
frozen during preprocessing. Could this describe it as the selected SQL PATH 
candidate instead?



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/FunctionResolution.scala:
##########
@@ -456,20 +470,68 @@ class FunctionResolution(
     }
   }
 
-  // All routed SQL/JSON functions (JSON_ARRAY, JSON_VALUE, JSON_QUERY, 
JSON_EXISTS) forbid a direct
-  // star argument (a bare `*` or a qualified `t.*`). Derived from the single 
registry list so a
-  // newly routed function is covered without editing this file too.
+  // All routed SQL/JSON functions forbid a direct star argument (a bare `*` 
or a qualified
+  // `t.*`). Derived from the single registry list so a newly routed function 
is covered without
+  // editing this file too.
   private val starDisallowedSqlJsonFunctions = 
FunctionRegistry.routedSqlJsonFunctionNames
 
   /**
-   * True if `nameParts` resolves to Spark's stock built-in routed SQL/JSON 
function that forbids a
-   * direct star. The `isStockBuiltinFunction` check excludes an 
`injectFunction` replacement of
-   * the name, whose expanded star is passed through rather than rejected.
+   * Resolves, once, who owns a routed SQL/JSON call (e.g. `json_array(*)`) 
that carries a direct
+   * star, so direct-star preprocessing and the later [[resolveFunction]] 
consume a single SQL PATH
+   * decision. Otherwise each phase probes ownership independently, and a 
temp/persistent shadow
+   * dropped between them lets preprocessing expand the star for the shadow 
while resolution falls
+   * through to the stock built-in, which no longer sees a [[Star]] and skips
+   * `INVALID_USAGE_OF_STAR_OR_REGEX`. See [[RoutedSqlJsonStarOwner]] for 
outcomes.
+   */
+  def selectRoutedSqlJsonDirectStarOwner(nameParts: Seq[String]): 
RoutedSqlJsonStarOwner = {
+    routedSqlJsonBuiltinName(nameParts) match {
+      case None => RoutedSqlJsonStarOwner.NoBinding
+      case Some(name) => routedSqlJsonStarOwnerFromPath(nameParts, name)
+    }
+  }
+
+  /**
+   * One ordered pass over the SQL PATH candidates: bind the first shadow that 
owns the call ahead
+   * of the stock `system.builtin`; reach `system.builtin` first and the stock 
builder owns it
+   * (reject the star, unless an injectFunction replacement holds the slot -- 
keep pass-through).
+   * A single pass avoids two independent probes disagreeing when a shadow is 
dropped between them,
+   * which could bind the stock built-in with the [[Star]] already expanded 
away.
    */
-  def resolvesToStarDisallowedSqlJsonFunction(nameParts: Seq[String]): Boolean 
=
-    starDisallowedSqlJsonFunctions.exists { name =>
-      functionNameResolvesToBuiltin(nameParts, name) &&
-        v1SessionCatalog.isStockBuiltinFunction(name)
+  private def routedSqlJsonStarOwnerFromPath(
+      nameParts: Seq[String],
+      name: String): RoutedSqlJsonStarOwner = {
+    for (candidate <- resolutionCandidates(nameParts)) {
+      if (isSystemBuiltinCandidate(candidate)) {
+        return if (v1SessionCatalog.isStockBuiltinFunction(name)) {
+          RoutedSqlJsonStarOwner.RejectStockBuiltin
+        } else {

Review Comment:
   **Nit (P3):** resolutionCandidates eagerly maps the complete effective PATH 
before this loop can return. With system.builtin first, it still allocates 
every unused suffix candidate for each routed direct-star call, and that work 
scales with the configured PATH length. Could candidate generation be lazy 
while preserving order for the existing consumers, so this owner walk stops 
allocating after the first owner?



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