{"record":{"id":"9fbac6746a200e59","repo":"apache/iceberg","slug":"cannot-create-view-s-s-that-references-temporary-9fbac6","errorCode":null,"errorMessage":"Cannot create view %s.%s that references temporary %s: %s","messagePattern":"Cannot create view (.+?)\\.(.+?) that references temporary (.+?): (.+?)","errorType":"exception","errorClass":"IcebergAnalysisException","httpStatus":null,"severity":"error","filePath":"spark/v4.0/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/analysis/RewriteViewCommands.scala","lineNumber":135,"sourceCode":"\n      case UnresolvedIdentifier(CatalogAndIdentifier(catalog, ident), _)\n          if ViewUtil.isViewCatalog(catalog) =>\n        Some(ResolvedIdentifier(catalog, ident))\n\n      case _ =>\n        None\n    }\n  }\n\n  /**\n   * Permanent views are not allowed to reference temp objects\n   */\n  private def verifyTemporaryObjectsDontExist(\n      identifier: ResolvedIdentifier,\n      child: LogicalPlan): Unit = {\n    val tempViews = collectTemporaryViews(child)\n    if (tempViews.nonEmpty) {\n      throw invalidRefToTempObject(\n        identifier,\n        tempViews.map(v => v.quoted).mkString(\"[\", \", \", \"]\"),\n        \"view\")\n    }\n\n    val tempFunctions = collectTemporaryFunctions(child)\n    if (tempFunctions.nonEmpty) {\n      throw invalidRefToTempObject(identifier, tempFunctions.mkString(\"[\", \", \", \"]\"), \"function\")\n    }\n  }\n\n  private def invalidRefToTempObject(\n      ident: ResolvedIdentifier,\n      tempObjectNames: String,\n      tempObjectType: String) = {\n    new IcebergAnalysisException(\n      String.format(\n        \"Cannot create view %s.%s that references temporary %s: %s\",","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.0/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/analysis/RewriteViewCommands.scala#L117-L153","documentation":"Iceberg's Spark 4.0 RewriteViewCommands rule throws this AnalysisException when a CREATE VIEW on a view catalog (e.g., Iceberg's REST view catalog) resolves to a plan referencing session-scoped temporary views. Permanent views must be reproducible in any session, so temporary views in their definition are rejected. verifyTemporaryObjectsDontExist performs the check during analysis.","triggerScenarios":"`CREATE VIEW iceberg_catalog.db.v AS SELECT ... FROM temp_view` where temp_view is a session temp view (CREATE TEMPORARY VIEW). The exception message includes the quoted temp view names and the target view identifier.","commonSituations":"Notebook pipelines that stage data as temp views then persist a 'final' Iceberg view referencing them; CI jobs that recreate temp views per run and then create permanent views; migrations from Spark session views to Iceberg views where the body was copied verbatim.","solutions":["Persist the temp view's source as a table or permanent view first, then reference that in the new view definition.","Inline the temp view's SELECT statement into the new view's body.","Register temp views' data in the Iceberg catalog rather than the session catalog.","Use CREATE TEMPORARY VIEW for the outer view too if it is genuinely session-scoped."],"exampleFix":"// before (fails)\nCREATE TEMPORARY VIEW staging AS SELECT * FROM raw.events;\nCREATE VIEW rest_cat.db.final_view AS SELECT * FROM staging;\n\n// after (works)\nCREATE TABLE rest_cat.db.staging AS SELECT * FROM raw.events;\nCREATE VIEW rest_cat.db.final_view AS SELECT * FROM rest_cat.db.staging;","handlingStrategy":"validation","validationCode":"// Spark 4.0: verify the view's source plan contains no temp views before CREATE VIEW\nval analyzed = spark.sessionState.executePlan(df.queryExecution.logical).analyzed\nval temps = analyzed.flatMap {\n  case r: org.apache.spark.sql.catalyst.analysis.UnresolvedRelation\n    if spark.sessionState.catalog.isTempView(r.multipartIdentifier) => Seq(r.multipartIdentifier)\n  case _ => Seq.empty\n}\nif (temps.nonEmpty) throw new IllegalStateException(s\"resolve temp views first: $temps\")","typeGuard":"def bodyReferencesTempView(viewSql: String, spark: SparkSession): Boolean =\n  spark.sessionState.catalog.listTempViews().exists(v => viewSql.contains(v.name))","tryCatchPattern":null,"preventionTips":["Treat session temp views as ephemeral staging; never build catalog views on them.","Persist the staging result with CREATE TABLE before defining a permanent view.","Inline temp view definitions into the permanent view's SQL where feasible.","Enforce in code review: CREATE VIEW bodies must reference only catalog-qualified objects.","Re-run view creation in a clean session as a CI check."],"tags":["spark","view","temporary-object","analysis-exception","spark4"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}