Case Studies

Renaming a PostgreSQL Column Without Losing Data in ForgeSchema

A small team is tidying up an old schema. The customers table has a column called customer_name, which is redundant inside a table called customers. They want full_name. They edit the YAML, swap the name, and run a diff.

Most diff-based migration tools see exactly what you’d expect from text. One column disappeared, another appeared. The cheap way to get from one to the other is drop and add, and the cheap way deletes every name in the table.

ForgeSchema won’t guess. With only the two schema files, the old column counts as removed, and in the 0.2 alpha a removal blocks the plan outright. The planner exits 3, writes out the reasons and produces no SQL at all, not even for the parts of the change it could handle.

Saying what you mean

The fix is a third file that states the intent:

version: 1
columns:
  - table: customers
    from: customer_name
    to: full_name

Pass it to the planner:

./bin/forgeschema plan --explain current.json desired.yaml renames.yaml

Now the review starts with a rename step. It says PostgreSQL keeps the column’s data and updates existing key and index references, and the SQL underneath is a single quoted ALTER TABLE ... RENAME COLUMN. No copy, no rewrite, no dropped values.

The rename follows the column everywhere it’s used. If customer_name sat in a unique constraint, an index or a foreign key, the comparison carries those references across. A foreign key in another table pointing at a renamed column comes along too. The index and constraint names themselves stay as they were, since renaming those isn’t supported yet.

The file is strict on purpose. Chains, swaps, reusing an old name, or a mapping that doesn’t match both schemas are all input errors. Renaming an identity column is blocked, because PostgreSQL keeps the old sequence name behind it and the result would be confusing.

Why the extra file is worth it

It’s one more thing to write. It’s also the only record anywhere that says “this was a rename”. A reviewer reading the pull request sees it. The plan artifact keeps it, so changing the rename intent later makes the saved plan fail validation. And once applied, the exact artifact sits in forgeschema_meta.migrations with its plan ID.

The walkthrough in the demo runs this same rename against a populated synthetic table, and both customer rows come out the other side intact.