Add `alter_pk/3` for changing primary key columns.
Hi, I'm Max new to the elixir ecosystem. I started using it with my current role, as part of it I recently had to change the primary keys of more then 40 tables in the project. To make those kind of task a bit more enjoyable I'd like to propose a new migration function which essentially just swaps the pk to a given column of a given table.
So migrations like this:
def up do
# 1. Drop foreign keys
execute "ALTER TABLE some_table DROP CONSTRAINT some_table_status_id_fkey"
# 2. Add new FK column
alter table(:some_table) do
add :some_table_status_code, :string
end
# 3. Migrate data
execute """
UPDATE some_table a
SET some_table_status_code = s.code
FROM some_table_statuses s
WHERE a.some_table_status_id =
s.id """
# 4. Drop old PK (manual SQL)
execute "ALTER TABLE some_table_statuses DROP CONSTRAINT some_table_statuses_pkey"
# 5. Add new PK (manual SQL)
execute "ALTER TABLE some_table_statuses ADD PRIMARY KEY (code)"
# 6. Recreate FK
execute """
ALTER TABLE some_table
ADD CONSTRAINT some_table_status_code_fkey
FOREIGN KEY (some_table_status_code) REFERENCES some_table_statuses(code)
"""
# 7. Clean up
alter table(:some_table_statuses) do
remove :id
end
alter table(:some_table) do
remove :some_table_status_id
end
end
Could be simplified like this:
def change do
# 1. Drop foreign keys
drop constraint("some_table", "some_table_status_id_fkey")
# 2. Add new FK column
alter table(:some_table) do
add :some_table_status_code, :string
end
# 3. Migrate data
execute """
UPDATE some_table a
SET some_table_status_code = s.code
FROM some_table_statuses s
WHERE a.some_table_status_id =
s.id """
# 4-5. Change PK (simplified!)
alter_pk table(:some_table_statuses), :id, to: :code
# 6. Recreate FK
alter table(:some_table) do
modify :some_table_status_code, references(:some_table_statuses, column: :code)
end
# 7. Clean up
alter table(:some_table_statuses) do
remove :id
end
alter table(:some_table) do
remove :some_table_status_id
end
end
Features:
1. No CASCADE - Fails if foreign keys exist (forces manual handling)
2. No automatic data migration - User handle data themselves
3. Structure-only - Only changes the primary key constraint
4. Reversible - Works with change/0 migrations
Implementation Status:
I have a working implementation for Postgres including:
- Ecto.Migration.alter_pk/3
- Ecto.Adapter.Postgres.Connection.execute_ddl/1
- Ecto.Migration.Runner.command/1
- Ecto.Migration.Runner.reverse/1
I would extend to MySQL, MSSQL, tests, and documentation
I'm excited to hear your thoughts on this!