Skip to content

CONCEPT Cited by 1 source

Column-order conflict

Definition

A column-order conflict is a schema-merge conflict class where two concurrent deploy branches each add a distinct column to the same table, both without specifying position — so the final column ordering differs depending on the merge order. Even though both orderings produce schemas that are functionally equivalent in terms of stored data, they differ in physical column position and are therefore treated as conflicting under the diff commutativity check.

Canonical verbatim framing (Noach, 2023 PlanetScale):

"The order of columns in a table matters. Queries that run a SELECT * FROM customer and use positional arguments will get different columns at positions 3 and 4. The two branches conflict with each other. This is similar to a Git merge conflict where two branches append different rows to the end of a file." (Source: sources/2026-04-21-planetscale-database-branching-three-way-merge-for-schema-changes)

The mechanism

Baseline main:

CREATE TABLE customer (
    id int,
    name varchar(255) NOT NULL DEFAULT '',
    PRIMARY KEY (id)
);

Two branches independently add columns:

-- branch1: adds subscription_type
ALTER TABLE customer
  ADD COLUMN subscription_type enum('free', 'promotional', 'paid');

-- branch2: adds joined_at
ALTER TABLE customer
  ADD COLUMN joined_at timestamp NOT NULL DEFAULT current_timestamp();

Composed in either order, both compositions succeed (MySQL is happy to apply each diff). But the resulting column orderings differ:

-- diff1(diff2(main)):
customer(id, name, joined_at, subscription_type)

-- diff2(diff1(main)):
customer(id, name, subscription_type, joined_at)

The commutativity check compares the final schemas for equality — these two differ — so the merge is flagged as a conflict.

Why column order matters in MySQL

Column order affects:

  • SELECT * output order. Clients that index the result set by position (row[2], row[3]) break when the positions change.
  • INSERT without column list. INSERT INTO customer VALUES (…) is positional; adding a column shifts the meaning of the tuple.
  • Binary log format ROW replication. Row images are ordered by column position, so replication consumers may need coordination.
  • SHOW CREATE TABLE and migration tooling round-trip. Tools that compare schemas textually will see different output.

Even though none of these affect stored data values, all affect consumers observing the table.

Resolution: anchor the column

The conflict is resolved by one branch explicitly anchoring the new column with AFTER <existing-column>:

-- branch1, revised to anchor position:
ALTER TABLE customer
  ADD COLUMN subscription_type enum()
  AFTER id;

With this, branch1's new column is pinned to position 2, and branch2's joined_at (unanchored) will go last regardless of merge order. The two orderings now produce identical schemas, and the commutativity check passes.

From the post:

"We could avoid the conflict if one of the branches positioned the new column anywhere but last."

Analogy to Git

Git exhibits the same pattern when two branches both append lines to the end of a file. Unlike source code, MySQL can reorder columns mechanically — so in principle the conflict is resolvable by a merge policy ("new columns go at the end, branch1 first"). PlanetScale chooses instead to flag it and let the developer decide, treating physical ordering as a meaningful schema property.

Contrast with indexes (deliberate asymmetry)

The post explicitly exempts index ordering from the commutativity check: "PlanetScale disregards index ordering." The reasoning is that index order is observable only through SHOW CREATE TABLE and INFORMATION_SCHEMA introspection — not through query semantics. Column order, by contrast, affects SELECT * positional semantics used by real application code.

This asymmetry reflects a pragmatic judgement: flag the conflict class that clients can observe through data-path queries; exempt the class that only changes metadata-path output.

Limitations

  • Physical-position-only. Column-order conflict is purely structural. It does not extend to semantic-order conflicts (e.g., two branches reshaping the primary key differently).
  • Platform-dependent. Column order is less significant in some databases (particularly NoSQL or column-store engines where physical column order is an optimisation detail, not a schema invariant).
  • Policy, not mathematics. A different system could legitimately treat column-order differences as non-conflicts by adopting a "new columns go last" canonicalisation. PlanetScale's choice is conservative rather than mathematically forced.

Seen in

Last updated · 550 distilled / 1,221 read