{"record":{"id":"fdb795af31b12617","repo":"apache/iceberg","slug":"alter-table-has-no-changes-missing-both-distribut-fdb795","errorCode":null,"errorMessage":"ALTER TABLE has no changes: missing both distribution and ordering clauses","messagePattern":"ALTER TABLE has no changes: missing both distribution and ordering 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":230,"sourceCode":"  override def visitDropIdentifierFields(ctx: DropIdentifierFieldsContext): DropIdentifierFields =\n    withOrigin(ctx) {\n      DropIdentifierFields(\n        typedVisit[Seq[String]](ctx.multipartIdentifier),\n        toSeq(ctx.fieldList.fields).map(_.getText))\n    }\n\n  /**\n   * Create a [[SetWriteDistributionAndOrdering]] for changing the write distribution and ordering.\n   */\n  override def visitSetWriteDistributionAndOrdering(\n      ctx: SetWriteDistributionAndOrderingContext): SetWriteDistributionAndOrdering = {\n\n    val tableName = typedVisit[Seq[String]](ctx.multipartIdentifier)\n\n    val (distributionSpec, orderingSpec) = toDistributionAndOrderingSpec(ctx.writeSpec)\n\n    if (distributionSpec == null && orderingSpec == null) {\n      throw new IcebergAnalysisException(\n        \"ALTER TABLE has no changes: missing both distribution and ordering clauses\")\n    }\n\n    val distributionMode = if (distributionSpec != null) {\n      Some(DistributionMode.HASH)\n    } else if (orderingSpec.UNORDERED != null) {\n      Some(DistributionMode.NONE)\n    } else if (orderingSpec.LOCALLY() != null) {\n      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    }","sourceCodeStart":212,"sourceCodeEnd":248,"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#L212-L248","documentation":"The AST builder for `ALTER TABLE ... WRITE (DISTRIBUTED ... | ORDERED ...)` requires at least one of a distribution clause or an ordering clause; if writeSpec contains neither, the statement would silently be a no-op, so Iceberg rejects it with this IcebergAnalysisException.","triggerScenarios":"Executing `ALTER TABLE t WRITE` (or a WRITE spec with an empty body) without any `DISTRIBUTED BY ...`/`PARTITIONED BY ...` hash clause and without any `ORDERED BY ...`/`LOCALLY ORDERED BY ...` clause.","commonSituations":"Copy-pasted ALTER statements where the clause part was deleted; scripted DDL generation emitting an empty write-spec; users expecting `WRITE` alone to reset distribution/ordering to defaults.","solutions":["Add a distribution clause, e.g. `ALTER TABLE t WRITE DISTRIBUTED BY PARTITION` or `WRITE DISTRIBUTED BY HASH (col)`.","Or add an ordering clause, e.g. `ALTER TABLE t WRITE ORDERED BY ts`.","Or specify both: `ALTER TABLE t WRITE DISTRIBUTED BY PARTITION ORDERED BY ts`.","To reset write distribution/ordering, use `ALTER TABLE t WRITE UNORDERED` / `WRITE NONE` forms instead of omitting the clause."],"exampleFix":"-- before\nALTER TABLE my_table WRITE\n-- after\nALTER TABLE my_table WRITE DISTRIBUTED BY PARTITION ORDERED BY ts","handlingStrategy":"validation","validationCode":"// Reject empty WRITE specs before submitting\nval stmt = s\"ALTER TABLE $table WRITE $writeSpec\"\nrequire(writeSpec.nonEmpty, \"ALTER TABLE ... WRITE requires a DISTRIBUTED and/or ORDERED clause\")\nspark.sql(stmt)","typeGuard":null,"tryCatchPattern":"try { spark.sql(stmt) } catch {\n  case e: IcebergAnalysisException if e.getMessage.contains(\"missing both distribution and ordering\") =>\n    spark.sql(s\"ALTER TABLE $table WRITE UNORDERED\")\n}","preventionTips":["Always include DISTRIBUTED BY, PARTITION, ORDERED BY, LOCALLY ORDERED BY, or UNORDERED in WRITE specs","Use WRITE UNORDERED/NONE explicitly when resetting instead of omitting clauses","Template DDL generators to emit a default clause","Review generated ALTER statements for empty spec bodies"],"tags":["spark","sql-ddl","write-spec","missing-clause"],"backgroundTag":"missing-required-argument","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"}