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


##########
sql/core/src/main/scala/org/apache/spark/sql/execution/command/views.scala:
##########
@@ -105,7 +106,10 @@ case class CreateViewCommand(
     copy(
       isAnalyzed = true,
       // Collect the referred temporary functions from AnalysisContext
-      referredTempFunctions = analysisContext.referredTempFunctionNames.toSeq)
+      referredTempFunctions = analysisContext.referredTempFunctionNames.toSeq,
+      // The analyzed plan no longer mentions the variables read by IDENTIFIER 
clauses.
+      referredTempVariablesUnderIdentifier =

Review Comment:
   Confirmed in the current head: v1 CREATE now forwards the captured 
IDENTIFIER dependencies to persisted-view validation, and the scalar-subquery 
rejection is covered.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:3999372799","thread_id":"inline:3999372799","verdict_sha256":"f8b9e9e8019e17927c80e73ee028149c331391e2c415bc53aa6ac479a6ce91bb"}
 -->



##########
sql/core/src/test/scala/org/apache/spark/sql/connector/DataSourceV2MetadataViewSuite.scala:
##########
@@ -287,6 +287,34 @@ class DataSourceV2MetadataViewSuite extends 
SharedSparkSession {
     }
   }
 
+  test("v2 CREATE / ALTER VIEW rejects a temporary variable read via an 
IDENTIFIER clause") {

Review Comment:
   Confirmed: the current v2 suite now covers both CREATE and ALTER with the 
legacy flag enabled, alongside the default rejection path.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:3999372801","thread_id":"inline:3999372801","verdict_sha256":"f8b9e9e8019e17927c80e73ee028149c331391e2c415bc53aa6ac479a6ce91bb"}
 -->



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/command/views.scala:
##########
@@ -629,7 +663,8 @@ object ViewHelper extends SQLConfHelper with Logging with 
CapturesConfig {
       isTemporary: Boolean,
       viewNameParts: Seq[String],
       child: LogicalPlan,
-      referredTempFunctions: Seq[String]): Unit = {
+      referredTempFunctions: Seq[String],
+      referredTempVariablesUnderIdentifier: Seq[Seq[String]] = Seq.empty): 
Unit = {

Review Comment:
   The current head now snapshots metric-view IDENTIFIER dependencies inside 
the analysis context and validates them under the legacy gate, with 
default-rejection and legacy-success coverage. I found a separate 
session-configuration scoping issue in the new helper and will leave that as a 
new review item.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:4000506575","thread_id":"inline:4000506575","verdict_sha256":"f8b9e9e8019e17927c80e73ee028149c331391e2c415bc53aa6ac479a6ce91bb"}
 -->



##########
sql/core/src/test/resources/sql-tests/results/identifier-clause.sql.out:
##########
@@ -3197,3 +3197,826 @@ DROP SCHEMA identifier_clause_test_schema
 struct<>
 -- !query output
 
+
+
+-- !query
+CREATE OR REPLACE TEMPORARY VIEW identifier_var_target AS SELECT 1 AS c1
+-- !query schema
+struct<>
+-- !query output
+
+
+
+-- !query
+DECLARE OR REPLACE VARIABLE identifier_var_name STRING DEFAULT 
'identifier_var_target'
+-- !query schema
+struct<>
+-- !query output
+
+
+
+-- !query
+CREATE OR REPLACE TEMPORARY VIEW identifier_var_view AS
+SELECT * FROM IDENTIFIER(identifier_var_name)
+-- !query schema
+struct<>
+-- !query output
+
+
+
+-- !query
+SELECT count(*) AS row_count FROM identifier_var_view
+-- !query schema
+struct<row_count:bigint>
+-- !query output
+1
+
+
+-- !query
+DROP VIEW identifier_var_view
+-- !query schema
+struct<>
+-- !query output
+
+
+
+-- !query
+DROP VIEW identifier_var_target
+-- !query schema
+struct<>
+-- !query output
+
+
+
+-- !query
+DROP TEMPORARY VARIABLE identifier_var_name
+-- !query schema
+struct<>
+-- !query output
+
+
+
+-- !query
+CREATE TEMPORARY VIEW identifier_perm_target AS SELECT 1 AS c1
+-- !query schema
+struct<>
+-- !query output
+
+
+
+-- !query
+DECLARE OR REPLACE VARIABLE identifier_perm_name STRING DEFAULT 
'identifier_perm_target'
+-- !query schema
+struct<>
+-- !query output
+
+
+
+-- !query
+CREATE VIEW identifier_perm_view AS SELECT * FROM 
IDENTIFIER(identifier_perm_name)
+-- !query schema
+struct<>
+-- !query output
+org.apache.spark.sql.AnalysisException
+{
+  "errorClass" : "INVALID_TEMP_OBJ_REFERENCE",
+  "sqlState" : "42K0F",
+  "messageParameters" : {
+    "obj" : "VIEW",
+    "objName" : "`unknown`",

Review Comment:
   Confirmed in the current head: the early rejection now derives the resolved 
CREATE VIEW target, so the diagnostic reports the actual multipart name.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:4000506578","thread_id":"inline:4000506578","verdict_sha256":"f8b9e9e8019e17927c80e73ee028149c331391e2c415bc53aa6ac479a6ce91bb"}
 -->



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala:
##########
@@ -499,6 +523,48 @@ class Analyzer(
   private def executeSameContext(plan: LogicalPlan): LogicalPlan =
     runWithSessionConf(super.execute(plan))
 
+  /**
+   * Like [[executeAndCheck]], but also returns the temporary variables 
recorded via IDENTIFIER
+   * clauses during this analysis 
(`AnalysisContext.referredTempVariableNamesUnderIdentifier`).
+   *
+   * Those variables are absent from the analyzed plan (the placeholder is 
replaced by the plan
+   * built from the evaluated name), and the accumulator that holds them is 
discarded when the
+   * analysis scope exits. A caller that separately validates a freshly 
analyzed body against
+   * persisted-view rules (metric-view creation) therefore cannot recover them 
afterwards, so this
+   * entry point reads them inside the owning scope and freezes them into the 
returned result.
+   *
+   * It runs the fixed-point analyzer directly (in a context it owns, so the 
accumulator stays
+   * readable). The only caller analyzes a metric-view placeholder, which is 
explicitly unsupported
+   * by the single-pass resolver (see the resolver's unsupported-feature 
list), so
+   * [[executeAndCheck]] would fall back to this same fixed-point path anyway.
+   */
+  def executeAndCheckReferredTempVariablesUnderIdentifier(
+      plan: LogicalPlan,
+      tracker: QueryPlanningTracker): (LogicalPlan, Seq[Seq[String]]) = {
+    if (plan.analyzed) {
+      (plan, Seq.empty)
+    } else {
+      def analyze(): (LogicalPlan, Seq[Seq[String]]) = 
AnalysisHelper.markInAnalyzer {
+        val analyzed = QueryPlanningTracker.withTracker(tracker) {
+          executeSameContext(plan)

Review Comment:
   The current head now preserves the forced-single-pass dispatch through 
`executeAndCheck` and adds a regression case. I found a separate cross-session 
configuration-scope issue in the helper and will report it independently.
   
   <!-- SPARK_DEV_REVIEW_REPLY 
{"feedback_id":"inline:4015151240","thread_id":"inline:4015151240","verdict_sha256":"f8b9e9e8019e17927c80e73ee028149c331391e2c415bc53aa6ac479a6ce91bb"}
 -->



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