{"record":{"id":"60e7a8f0cfee6098","repo":"apache/iceberg","slug":"alter-table-contains-multiple-ordering-clauses-60e7a8","errorCode":null,"errorMessage":"ALTER TABLE contains multiple ordering clauses","messagePattern":"ALTER TABLE contains multiple ordering clauses","errorType":"exception","errorClass":"IcebergAnalysisException","httpStatus":null,"severity":"error","filePath":"spark/v4.0/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/parser/extensions/IcebergSqlExtensionsAstBuilder.scala","lineNumber":261,"sourceCode":"\n    val ordering = if (orderingSpec != null && orderingSpec.order != null) {\n      toSeq(orderingSpec.order.fields).map(typedVisit[(Term, SortDirection, NullOrder)])\n    } else {\n      Seq.empty\n    }\n\n    SetWriteDistributionAndOrdering(tableName, distributionMode, ordering)\n  }\n\n  private def toDistributionAndOrderingSpec(\n      writeSpec: WriteSpecContext): (WriteDistributionSpecContext, WriteOrderingSpecContext) = {\n\n    if (writeSpec.writeDistributionSpec.size > 1) {\n      throw new IcebergAnalysisException(\"ALTER TABLE contains multiple distribution clauses\")\n    }\n\n    if (writeSpec.writeOrderingSpec.size > 1) {\n      throw new IcebergAnalysisException(\"ALTER TABLE contains multiple ordering clauses\")\n    }\n\n    val distributionSpec = toBuffer(writeSpec.writeDistributionSpec).headOption.orNull\n    val orderingSpec = toBuffer(writeSpec.writeOrderingSpec).headOption.orNull\n\n    (distributionSpec, orderingSpec)\n  }\n\n  /**\n   * Create an order field.\n   */\n  override def visitOrderField(ctx: OrderFieldContext): (Term, SortDirection, NullOrder) = {\n    val term = Spark3Util.toIcebergTerm(typedVisit[Transform](ctx.transform))\n    val direction = Option(ctx.ASC)\n      .map(_ => SortDirection.ASC)\n      .orElse(Option(ctx.DESC).map(_ => SortDirection.DESC))\n      .getOrElse(SortDirection.ASC)\n    val nullOrder = Option(ctx.FIRST)","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.0/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/parser/extensions/IcebergSqlExtensionsAstBuilder.scala#L243-L279","documentation":"An ALTER TABLE write-spec statement may contain at most one ordering clause (e.g. ORDERED BY / LOCALLY ORDERED BY / UNORDERED). The AST builder throws IcebergAnalysisException when writeOrderingSpec appears more than once, because the resulting ordering would be ambiguous.","triggerScenarios":"`ALTER TABLE t WRITE ORDERED BY id ORDERED BY ts` or `ORDERED BY a UNORDERED` — any statement with two writeOrderingSpec clauses.","commonSituations":"Script concatenation appending a new ordering without removing the old one, or trying to express multi-column ordering by repeating ORDERED BY instead of listing columns.","solutions":["Combine columns in a single clause: ORDERED BY a, b DESC","Remove the redundant/old ordering clause","Use UNORDERED alone to clear ordering"],"exampleFix":"// before\nALTER TABLE t WRITE ORDERED BY id ORDERED BY ts;\n// after\nALTER TABLE t WRITE ORDERED BY id, ts;","handlingStrategy":"validation","validationCode":"def countOrdering(stmt: String): Int = {\n  val s = stmt.toUpperCase(Locale.ROOT)\n  (\"ORDERED BY|r?\".r.findAllIn(s).size) + s.sliding(\"UNORDERED\".length).count(_ == \"UNORDERED\")\n}","typeGuard":null,"tryCatchPattern":"try { spark.sql(stmt) } catch { case e: IcebergAnalysisException if e.getMessage.contains(\"multiple ordering clauses\") => log.error(\"Combine ordering columns into one clause\", e) }","preventionTips":["Express multi-column ordering as ORDERED BY a, b, not repeated clauses","Replace (not append) ordering clauses in generated DDL","Use UNORDERED alone to clear ordering"],"tags":["spark","sql","alter-table","conflict"],"backgroundTag":"conflicting-config-options","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"}