Skip to content

Commit 8936bc1

Browse files
committed
fix: handle dropping primary key columns properly
1 parent 5313f46 commit 8936bc1

File tree

2 files changed

+187
-13
lines changed

2 files changed

+187
-13
lines changed

lib/migration_generator/migration_generator.ex

Lines changed: 125 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,12 @@ defmodule AshPostgres.MigrationGenerator do
12981298
defp after?(%Operation.RemovePrimaryKeyDown{}, _), do: true
12991299
defp after?(_, %Operation.RemovePrimaryKeyDown{}), do: false
13001300

1301+
defp after?(%Operation.AddPrimaryKeyDown{}, _), do: false
1302+
defp after?(_, %Operation.AddPrimaryKeyDown{}), do: true
1303+
1304+
defp after?(%Operation.AddPrimaryKey{}, _), do: true
1305+
defp after?(_, %Operation.AddPrimaryKey{}), do: false
1306+
13011307
defp after?(
13021308
%Operation.AddCustomStatement{},
13031309
_
@@ -1803,7 +1809,9 @@ defmodule AshPostgres.MigrationGenerator do
18031809

18041810
defp do_fetch_operations(snapshot, old_snapshot, opts, acc) do
18051811
attribute_operations = attribute_operations(snapshot, old_snapshot, opts)
1806-
pkey_operations = pkey_operations(snapshot, old_snapshot, attribute_operations, opts)
1812+
1813+
{pkey_operations, attribute_operations} =
1814+
pkey_operations(snapshot, old_snapshot, attribute_operations, opts)
18071815

18081816
rewrite_all_identities? = changing_multitenancy_affects_identities?(snapshot, old_snapshot)
18091817

@@ -2097,7 +2105,7 @@ defmodule AshPostgres.MigrationGenerator do
20972105

20982106
defp pkey_operations(snapshot, old_snapshot, attribute_operations, opts) do
20992107
if old_snapshot[:empty?] do
2100-
[]
2108+
{[], attribute_operations}
21012109
else
21022110
must_drop_pkey? =
21032111
Enum.any?(
@@ -2115,11 +2123,38 @@ defmodule AshPostgres.MigrationGenerator do
21152123
} ->
21162124
true
21172125

2126+
%Operation.RemoveAttribute{
2127+
attribute: %{primary_key?: true}
2128+
} ->
2129+
true
2130+
21182131
_ ->
21192132
false
21202133
end
21212134
)
21222135

2136+
must_add_primary_key? =
2137+
must_drop_pkey? &&
2138+
Enum.any?(snapshot.attributes, fn attribute ->
2139+
attribute.primary_key?
2140+
end)
2141+
2142+
must_add_primary_key_in_down? =
2143+
must_drop_pkey? &&
2144+
Enum.any?(snapshot.attributes, fn attribute ->
2145+
attribute.primary_key? &&
2146+
!Enum.any?(attribute_operations, fn
2147+
%Operation.AlterAttribute{} = operation ->
2148+
operation.new_attribute.source == attribute.source
2149+
2150+
%Operation.AddAttribute{} = operation ->
2151+
operation.attribute.source == attribute.source
2152+
2153+
_ ->
2154+
false
2155+
end)
2156+
end)
2157+
21232158
drop_in_down? =
21242159
Enum.any?(attribute_operations, fn
21252160
%Operation.AlterAttribute{
@@ -2148,17 +2183,94 @@ defmodule AshPostgres.MigrationGenerator do
21482183
false
21492184
end)
21502185

2151-
[
2152-
must_drop_pkey? &&
2153-
%Operation.RemovePrimaryKey{schema: snapshot.schema, table: snapshot.table},
2154-
must_drop_pkey? && drop_in_down? &&
2155-
%Operation.RemovePrimaryKeyDown{
2156-
commented?: opts.dont_drop_columns && drop_in_down_commented?,
2157-
schema: snapshot.schema,
2158-
table: snapshot.table
2159-
}
2160-
]
2161-
|> Enum.filter(& &1)
2186+
attribute_operations =
2187+
if must_add_primary_key? do
2188+
Enum.map(
2189+
attribute_operations,
2190+
fn
2191+
%Operation.AlterAttribute{} = operation ->
2192+
%{
2193+
operation
2194+
| new_attribute: %{
2195+
operation.new_attribute
2196+
| primary_key?: operation.old_attribute.primary_key?
2197+
}
2198+
}
2199+
2200+
%Operation.AddAttribute{} = operation ->
2201+
%{operation | attribute: %{operation.attribute | primary_key?: false}}
2202+
2203+
other ->
2204+
other
2205+
end
2206+
)
2207+
else
2208+
attribute_operations
2209+
end
2210+
2211+
attribute_operations =
2212+
if must_add_primary_key_in_down? do
2213+
Enum.map(
2214+
attribute_operations,
2215+
fn
2216+
%Operation.AlterAttribute{} = operation ->
2217+
%{
2218+
operation
2219+
| old_attribute: %{
2220+
operation.old_attribute
2221+
| primary_key?: operation.new_attribute.primary_key?
2222+
}
2223+
}
2224+
2225+
%Operation.RemoveAttribute{} = operation ->
2226+
%{operation | attribute: %{operation.attribute | primary_key?: false}}
2227+
2228+
other ->
2229+
other
2230+
end
2231+
)
2232+
else
2233+
attribute_operations
2234+
end
2235+
2236+
{[
2237+
must_drop_pkey? &&
2238+
%Operation.RemovePrimaryKey{schema: snapshot.schema, table: snapshot.table},
2239+
must_drop_pkey? && drop_in_down? &&
2240+
%Operation.RemovePrimaryKeyDown{
2241+
commented?: opts.dont_drop_columns && drop_in_down_commented?,
2242+
schema: snapshot.schema,
2243+
table: snapshot.table
2244+
},
2245+
must_add_primary_key? &&
2246+
%Operation.AddPrimaryKey{
2247+
schema: snapshot.schema,
2248+
table: snapshot.table,
2249+
keys:
2250+
Enum.flat_map(snapshot.attributes, fn attribute ->
2251+
if attribute.primary_key? do
2252+
[attribute.source]
2253+
else
2254+
[]
2255+
end
2256+
end)
2257+
},
2258+
must_add_primary_key_in_down? &&
2259+
%Operation.AddPrimaryKeyDown{
2260+
schema: old_snapshot.schema,
2261+
table: old_snapshot.table,
2262+
remove_old?: must_add_primary_key? && !(must_drop_pkey? && drop_in_down?),
2263+
keys:
2264+
Enum.flat_map(old_snapshot.attributes, fn attribute ->
2265+
if attribute.primary_key? do
2266+
[attribute.source]
2267+
else
2268+
[]
2269+
end
2270+
end)
2271+
}
2272+
]
2273+
|> Enum.filter(& &1), attribute_operations}
21622274
end
21632275
end
21642276

lib/migration_generator/operation.ex

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,68 @@ defmodule AshPostgres.MigrationGenerator.Operation do
10511051
end
10521052
end
10531053

1054+
defmodule AddPrimaryKey do
1055+
@moduledoc false
1056+
defstruct [:schema, :table, :keys, no_phase: true]
1057+
1058+
def up(%{schema: schema, table: table, keys: keys}) do
1059+
keys = Enum.join(keys, ", ")
1060+
1061+
if schema do
1062+
"""
1063+
execute("ALTER TABLE \\\"#{schema}.#{table}\\\" ADD PRIMARY KEY (#{keys})")
1064+
"""
1065+
else
1066+
"""
1067+
execute("ALTER TABLE \\\"#{table}\\\" ADD PRIMARY KEY (#{keys})")
1068+
"""
1069+
end
1070+
end
1071+
1072+
def down(_) do
1073+
""
1074+
end
1075+
end
1076+
1077+
defmodule AddPrimaryKeyDown do
1078+
@moduledoc false
1079+
defstruct [:schema, :table, :keys, :remove_old?, no_phase: true]
1080+
1081+
def up(_) do
1082+
""
1083+
end
1084+
1085+
def down(%{schema: schema, table: table, remove_old?: remove_old?, keys: keys}) do
1086+
keys = Enum.join(keys, ", ")
1087+
1088+
if schema do
1089+
remove_old =
1090+
if remove_old? do
1091+
"""
1092+
execute("ALTER TABLE \\\"#{schema}.#{table}\\\" DROP constraint #{table}_pkey")
1093+
"""
1094+
end
1095+
1096+
"""
1097+
#{remove_old}
1098+
execute("ALTER TABLE \\\"#{schema}.#{table}\\\" ADD PRIMARY KEY (#{keys})")
1099+
"""
1100+
else
1101+
remove_old =
1102+
if remove_old? do
1103+
"""
1104+
execute("ALTER TABLE \\\"#{table}\\\" DROP constraint #{table}_pkey")
1105+
"""
1106+
end
1107+
1108+
"""
1109+
#{remove_old}
1110+
execute("ALTER TABLE \\\"#{table}\\\" ADD PRIMARY KEY (#{keys})")
1111+
"""
1112+
end
1113+
end
1114+
end
1115+
10541116
defmodule RemovePrimaryKey do
10551117
@moduledoc false
10561118
defstruct [:schema, :table, no_phase: true]

0 commit comments

Comments
 (0)