{"record":{"id":"f23aaea7ec607d92","repo":"apache/iceberg","slug":"cannotcreateviewnotenoughcolumnserror-viewnamepart","errorCode":null,"errorMessage":"cannotCreateViewNotEnoughColumnsError(viewNameParts, columns, query)","messagePattern":"cannotCreateViewNotEnoughColumnsError\\(viewNameParts, columns, query\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"spark/v4.2/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/analysis/CheckViews.scala","lineNumber":75,"sourceCode":"          case _ => // OK\n        }\n\n      case AlterViewAs(ResolvedV2View(_, _, _), _, _, _, _) =>\n        throw new IcebergAnalysisException(\n          \"ALTER VIEW <viewName> AS is not supported. Use CREATE OR REPLACE VIEW instead\")\n\n      case _ => // OK\n    }\n  }\n\n  private def verifyColumnCount(\n      ident: ResolvedIdentifier,\n      columns: Seq[String],\n      query: LogicalPlan): Unit = {\n    if (columns.nonEmpty) {\n      val viewNameParts = ident.catalog.name() +: ident.identifier.asMultipartIdentifier\n      if (columns.length > query.output.length) {\n        throw QueryCompilationErrors.cannotCreateViewNotEnoughColumnsError(\n          viewNameParts,\n          columns,\n          query)\n      } else if (columns.length < query.output.length) {\n        throw QueryCompilationErrors.cannotCreateViewTooManyColumnsError(\n          viewNameParts,\n          columns,\n          query)\n      }\n    }\n  }\n\n  // Spark's ViewHelper.checkCyclicViewReference only matches a SubqueryExpression at the root of\n  // each expression. Keep a custom traversal so cycles in predicates and other nested expressions\n  // are detected.\n  private def checkCyclicViewReference(\n      viewIdent: Seq[String],\n      plan: LogicalPlan,","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.2/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/analysis/CheckViews.scala#L57-L93","documentation":"When CREATE VIEW specifies an explicit column list, Iceberg's CheckViews validates the column count against the query output. If the declared column list is shorter than the query's output columns, cannotCreateViewNotEnoughColumnsError is thrown, preventing a view whose schema declaration is inconsistent with its definition.","triggerScenarios":"`CREATE VIEW v (c1, c2) AS SELECT a, b, c FROM t` — the named column list has fewer entries than the SELECT output; validation happens in CheckViews during analysis of CreateViewStatement.","commonSituations":"Hand-written CREATE VIEW where someone renamed/added columns to the SELECT but forgot to update the column list; generated SQL templates with fixed column names; refactoring a view's underlying query.","solutions":["Add column names to the view's column list so it matches the query output length","Remove extra output columns from the SELECT (or select only the needed ones)","Drop explicit column names entirely and let the view inherit query output names","Alias the query outputs so the un-declared names are intentional"],"exampleFix":"-- before\nCREATE VIEW v (id) AS SELECT id, name FROM t;\n-- after\nCREATE VIEW v (id, name) AS SELECT id, name FROM t;","handlingStrategy":"validation","validationCode":"val outputCols = spark.sql(query).schema.length\nrequire(namedColumns.length == outputCols,\n  s\"view column list (${namedColumns.length}) must match query output ($outputCols)\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Generate the column list from spark.sql(query).schema.columns instead of hardcoding","Always alias query outputs so explicit column lists are unnecessary","Update column lists whenever the underlying query changes"],"tags":["spark","views","schema-validation"],"backgroundTag":"column-count-mismatch","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}