huaxingao commented on code in PR #13979:
URL: https://github.com/apache/iceberg/pull/13979#discussion_r4058632461


##########
spark/v4.2/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/analysis/ResolveViews.scala:
##########
@@ -18,29 +18,211 @@
  */
 package org.apache.spark.sql.catalyst.analysis
 
+import org.apache.iceberg.catalog.LoadContext
+import org.apache.iceberg.spark.SparkSQLProperties
 import org.apache.spark.sql.SparkSession
 import org.apache.spark.sql.catalyst.analysis.ViewUtil.IcebergViewHelper
+import org.apache.spark.sql.catalyst.expressions.Alias
+import org.apache.spark.sql.catalyst.expressions.Cast
+import org.apache.spark.sql.catalyst.expressions.SubqueryExpression
+import org.apache.spark.sql.catalyst.expressions.UpCast
+import org.apache.spark.sql.catalyst.parser.ParseException
 import org.apache.spark.sql.catalyst.plans.logical.LogicalPlan
+import org.apache.spark.sql.catalyst.plans.logical.Project
+import org.apache.spark.sql.catalyst.plans.logical.SubqueryAlias
 import org.apache.spark.sql.catalyst.plans.logical.views.ResolvedV2View
+import 
org.apache.spark.sql.catalyst.plans.logical.views.UnResolvedRelationFromView
 import org.apache.spark.sql.catalyst.rules.Rule
+import org.apache.spark.sql.catalyst.trees.CurrentOrigin
+import org.apache.spark.sql.catalyst.trees.Origin
 import org.apache.spark.sql.connector.catalog.CatalogManager
+import org.apache.spark.sql.connector.catalog.CatalogPlugin
+import org.apache.spark.sql.connector.catalog.Identifier
 import org.apache.spark.sql.connector.catalog.LookupCatalog
+import org.apache.spark.sql.connector.catalog.View
+import org.apache.spark.sql.errors.QueryCompilationErrors
+import org.apache.spark.sql.execution.datasources.v2.DataSourceV2Relation
 
-/**
- * Resolves Iceberg views referenced by commands that use 
`UnresolvedTableOrView`.
- *
- * In Spark 4.2, view relations are expanded through 
`RelationCatalog.loadRelation`, so Spark owns
- * query parsing, identifier resolution, and schema-mode application. This 
rule only converts the
- * remaining command targets to `ResolvedV2View` for Iceberg command planning.
- */
 case class ResolveViews(spark: SparkSession) extends Rule[LogicalPlan] with 
LookupCatalog {
+
+  import org.apache.spark.sql.connector.catalog.CatalogV2Implicits._
+
   protected lazy val catalogManager: CatalogManager = 
spark.sessionState.catalogManager
 
-  override def apply(plan: LogicalPlan): LogicalPlan = plan resolveOperators {
+  override def apply(plan: LogicalPlan): LogicalPlan = {
+    if (referencedByEnabled) {
+      resolveWithReferencedBy(plan)
+    } else {
+      resolveViewCommands(plan)
+    }
+  }
+
+  private def resolveWithReferencedBy(plan: LogicalPlan): LogicalPlan = plan 
resolveOperators {
+    case u @ UnresolvedRelation(nameParts, _, _)
+        if catalogManager.v1SessionCatalog.isTempView(nameParts) =>
+      u
+
+    case u @ UnresolvedRelation(parts @ CatalogAndIdentifier(catalog, ident), 
_, _) =>
+      ViewUtil
+        .loadView(catalog, ident)
+        .map(createViewRelation(parts, catalog, ident, _))
+        .getOrElse(u)
+
+    case u @ UnResolvedRelationFromView(
+          tableParts @ CatalogAndIdentifier(catalog, tableIdent),
+          viewChain,
+          options,
+          isStreaming,
+          timeTravelVersion,
+          timeTravelTimestamp) =>
+      val referencedBy = ViewUtil.buildReferencedByChain(viewChain, 
catalog.name())
+      val context = LoadContext.builder().referencedBy(referencedBy).build()
+      try {
+        val table =
+          ViewUtil.loadTable(catalog, tableIdent, context, timeTravelVersion, 
timeTravelTimestamp)
+        DataSourceV2Relation.create(table, Some(catalog), Some(tableIdent), 
options)
+      } catch {
+        case _: NoSuchTableException =>
+          ViewUtil
+            .loadView(catalog, tableIdent, context)
+            .map(view => createViewRelation(tableParts, catalog, tableIdent, 
view, viewChain))
+            .getOrElse(UnresolvedRelation(tableParts, options, isStreaming))
+      }
+
+    case u @ UnresolvedTableOrView(CatalogAndIdentifier(catalog, ident), _, _, 
_) =>
+      ViewUtil
+        .loadView(catalog, ident)
+        .map(view => ResolvedV2View(catalog.asViewCatalog, ident, view))
+        .getOrElse(u)
+  }
+
+  private def resolveViewCommands(plan: LogicalPlan): LogicalPlan = plan 
resolveOperators {
     case u @ UnresolvedTableOrView(CatalogAndIdentifier(catalog, ident), _, _, 
_) =>
       ViewUtil
         .loadView(catalog, ident)
         .map(view => ResolvedV2View(catalog.asViewCatalog, ident, view))
         .getOrElse(u)
   }
+
+  private def createViewRelation(
+      nameParts: Seq[String],
+      catalog: CatalogPlugin,
+      ident: Identifier,
+      view: View,
+      existingChain: Seq[Seq[String]] = Seq.empty): LogicalPlan = {
+    val parsed = parseViewText(nameParts.quoted, view.queryText())
+    val viewCatalogAndNamespace = catalogAndNamespace(catalog, ident, view)
+    val viewChain =
+      ViewUtil.buildViewChain(nameParts, viewCatalogAndNamespace, 
existingChain, isCatalog)

Review Comment:
   `viewCatalogAndNamespace` is the creation-time catalog and namespace, but 
here we need the view's actual catalog and namespace. 
   
   @bryanck and @RjLi13 raised this earlier and you replied it was fixed using 
the session-resolved identity from `CatalogAndIdentifier`, but it looks like 
the 4.2 rework lost it.



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