{"record":{"id":"a149de033971e40a","repo":"apache/iceberg","slug":"alter-table-contains-multiple-distribution-clauses-a149de","errorCode":null,"errorMessage":"ALTER TABLE contains multiple distribution clauses","messagePattern":"ALTER TABLE contains multiple distribution clauses","errorType":"exception","errorClass":"IcebergAnalysisException","httpStatus":null,"severity":"error","filePath":"spark/v4.2/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/parser/extensions/IcebergSqlExtensionsAstBuilder.scala","lineNumber":257,"sourceCode":"      None\n    } else {\n      Some(DistributionMode.RANGE)\n    }\n\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)","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.2/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/parser/extensions/IcebergSqlExtensionsAstBuilder.scala#L239-L275","documentation":"The grammar allows collecting multiple write-distribution specs into writeSpec.writeDistributionSpec, but a WRITE spec may define at most one distribution mode; the AST builder rejects any statement with more than one, since a table cannot simultaneously be written with two distribution strategies.","triggerScenarios":"Executing an ALTER TABLE WRITE statement containing two distribution clauses, e.g. `ALTER TABLE t WRITE DISTRIBUTED BY PARTITION DISTRIBUTED BY HASH(col)`, or concatenating generated DDL fragments that each add a distribution clause.","commonSituations":"Programmatic DDL builders appending a default distribution plus a user-specified one; copy-paste merging of two ALTER statements.","solutions":["Keep exactly one distribution clause; delete the redundant `DISTRIBUTED BY ...` occurrence.","Decide the desired mode (NONE / HASH / PARTITION) and express it in a single clause.","If both a partition-based and hash-based intent exist, choose HASH with the explicit column list, e.g. `WRITE DISTRIBUTED BY HASH (col)`."],"exampleFix":"-- before\nALTER TABLE t WRITE DISTRIBUTED BY PARTITION DISTRIBUTED BY HASH(id)\n-- after\nALTER TABLE t WRITE DISTRIBUTED BY HASH(id)","handlingStrategy":"validation","validationCode":"val distCount = \"DISTRIBUTED\\\\s+BY|DISTRIBUTED\\\\s+BY\\\\s+PARTITION|PARTITIONED\\\\s+BY\\\\s+PARTITION\".r\n  .findAllMatchIn(writeSpec).size\nrequire(distCount <= 1, \"Only one WRITE distribution clause is allowed\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build WRITE specs from a single source of truth so only one distribution clause can be emitted","Replace existing clauses rather than appending when regenerating DDL","Prefer the HASH(col) form with an explicit column list over stacking clauses"],"tags":["spark","sql-ddl","write-spec","duplicate-clause"],"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"}