Skip to content

Commit 701c99e

Browse files
authored
Add support for ON DELETE SET DEFAULT (#677)
1 parent 7959022 commit 701c99e

File tree

11 files changed

+183
-9
lines changed

11 files changed

+183
-9
lines changed

integration_test/myxql/test_helper.exs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,11 @@ excludes = [
116116
:map_boolean_in_expression,
117117
# MySQL doesn't support indexed parameters
118118
:placeholders,
119-
# MySQL doesn't support specifying columns for ON DELETE SET NULL
119+
# MySQL doesn't support ON DELETE SET DEFAULT
120+
:on_delete_default_all,
121+
# MySQL doesn't support specifying columns for ON DELETE SET NULL or ON DELETE SET DEFAULT
120122
:on_delete_nilify_column_list,
123+
:on_delete_default_column_list,
121124
# MySQL doesnt' support anything except a single column in DISTINCT
122125
:multicolumn_distinct,
123126
# uncertain whether we can support this. needs more exploring

integration_test/pg/test_helper.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ excludes = [:selected_as_with_having, :selected_as_with_order_by_expression]
115115
excludes_above_9_5 = [:without_conflict_target]
116116
excludes_below_9_6 = [:add_column_if_not_exists, :no_error_on_conditional_column_migration]
117117
excludes_below_12_0 = [:plan_cache_mode]
118-
excludes_below_15_0 = [:on_delete_nilify_column_list]
118+
excludes_below_15_0 = [:on_delete_nilify_column_list, :on_delete_default_column_list]
119119

120120
exclude_list = excludes ++ excludes_above_9_5
121121

integration_test/sql/migration.exs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,64 @@ defmodule Ecto.Integration.MigrationTest do
254254
end
255255
end
256256

257+
defmodule OnDeleteDefaultAllMigration do
258+
use Ecto.Migration
259+
260+
def up do
261+
create table(:parent, primary_key: [type: :bigint]) do
262+
add :col1, :integer
263+
add :col2, :integer
264+
end
265+
266+
create unique_index(:parent, [:id, :col1, :col2])
267+
268+
create table(:ref) do
269+
add :col1, :integer, default: 2
270+
add :col2, :integer, default: 3
271+
272+
add :parent_id,
273+
references(:parent,
274+
with: [col1: :col1, col2: :col2],
275+
on_delete: :default_all
276+
), default: 1
277+
end
278+
end
279+
280+
def down do
281+
drop table(:ref)
282+
drop table(:parent)
283+
end
284+
end
285+
286+
defmodule OnDeleteDefaultColumnsMigration do
287+
use Ecto.Migration
288+
289+
def up do
290+
create table(:parent, primary_key: [type: :bigint]) do
291+
add :col1, :integer
292+
add :col2, :integer
293+
end
294+
295+
create unique_index(:parent, [:id, :col1, :col2])
296+
297+
create table(:ref) do
298+
add :col1, :integer, default: 2
299+
add :col2, :integer, default: 3
300+
301+
add :parent_id,
302+
references(:parent,
303+
with: [col1: :col1, col2: :col2],
304+
on_delete: {:default, [:parent_id, :col2]}
305+
), default: 1
306+
end
307+
end
308+
309+
def down do
310+
drop table(:ref)
311+
drop table(:parent)
312+
end
313+
end
314+
257315
defmodule CompositeForeignKeyMigration do
258316
use Ecto.Migration
259317

@@ -683,4 +741,35 @@ defmodule Ecto.Integration.MigrationTest do
683741

684742
:ok = down(PoolRepo, num, OnDeleteNilifyColumnsMigration, log: false)
685743
end
744+
745+
@tag :on_delete_default_all
746+
test "default all on_delete constraint", %{migration_number: num} do
747+
assert :ok == up(PoolRepo, num, OnDeleteDefaultAllMigration, log: false)
748+
749+
PoolRepo.insert_all("parent", [%{id: 1, col1: 2, col2: 3}])
750+
{id, col1, col2} = {Enum.random(10..1000), Enum.random(10..1000), Enum.random(10..1000)}
751+
752+
PoolRepo.insert_all("parent", [%{id: id, col1: col1, col2: col2}])
753+
PoolRepo.insert_all("ref", [%{parent_id: id, col1: col1, col2: col2}])
754+
PoolRepo.delete_all(from p in "parent", where: p.id == ^id)
755+
assert [{1, 2, 3}] == PoolRepo.all from r in "ref", select: {r.parent_id, r.col1, r.col2}
756+
757+
:ok = down(PoolRepo, num, OnDeleteDefaultAllMigration, log: false)
758+
end
759+
760+
@tag :on_delete_default_column_list
761+
test "default list of columns on_delete constraint", %{migration_number: num} do
762+
assert :ok == up(PoolRepo, num, OnDeleteDefaultColumnsMigration, log: false)
763+
764+
PoolRepo.insert_all("parent", [%{id: 1, col1: 20, col2: 3}])
765+
766+
{id, col2} = {Enum.random(10..1000), Enum.random(10..1000)}
767+
768+
PoolRepo.insert_all("parent", [%{id: id, col1: 20, col2: col2}])
769+
PoolRepo.insert_all("ref", [%{parent_id: id, col1: 20, col2: col2}])
770+
PoolRepo.delete_all(from p in "parent", where: p.id == ^id)
771+
assert [{1, 20, 3}] == PoolRepo.all from r in "ref", select: {r.parent_id, r.col1, r.col2}
772+
773+
:ok = down(PoolRepo, num, OnDeleteDefaultColumnsMigration, log: false)
774+
end
686775
end

integration_test/tds/test_helper.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ ExUnit.start(
5757
:selected_as_with_having,
5858
# MSSQL can't reference aliased columns in ORDER BY expressions
5959
:selected_as_with_order_by_expression,
60-
# MSSQL doesn't support specifying columns for ON DELETE SET NULL
60+
# MSSQL doesn't support specifying columns for ON DELETE SET NULL or ON DELETE SET DEFAULT
6161
:on_delete_nilify_column_list,
62+
:on_delete_default_column_list,
6263
# MSSQL doesnt' support anything except a single column in DISTINCT
6364
:multicolumn_distinct,
6465
# MSSQL doesnt' support subqueries in group by or in distinct

lib/ecto/adapters/myxql/connection.ex

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,6 +1484,20 @@ if Code.ensure_loaded?(MyXQL) do
14841484
)
14851485
end
14861486

1487+
defp reference_on_delete(:default_all) do
1488+
error!(
1489+
nil,
1490+
"MySQL adapter does not support the `:default_all` action for `:on_delete`"
1491+
)
1492+
end
1493+
1494+
defp reference_on_delete({:default, _columns}) do
1495+
error!(
1496+
nil,
1497+
"MySQL adapter does not support the `{:default, columns}` action for `:on_delete`"
1498+
)
1499+
end
1500+
14871501
defp reference_on_delete(:delete_all), do: " ON DELETE CASCADE"
14881502
defp reference_on_delete(:restrict), do: " ON DELETE RESTRICT"
14891503
defp reference_on_delete(_), do: []

lib/ecto/adapters/postgres/connection.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,6 +1912,11 @@ if Code.ensure_loaded?(Postgrex) do
19121912
defp reference_on_delete({:nilify, columns}),
19131913
do: [" ON DELETE SET NULL (", quote_names(columns), ")"]
19141914

1915+
defp reference_on_delete(:default_all), do: " ON DELETE SET DEFAULT"
1916+
1917+
defp reference_on_delete({:default, columns}),
1918+
do: [" ON DELETE SET DEFAULT (", quote_names(columns), ")"]
1919+
19151920
defp reference_on_delete(:delete_all), do: " ON DELETE CASCADE"
19161921
defp reference_on_delete(:restrict), do: " ON DELETE RESTRICT"
19171922
defp reference_on_delete(_), do: []

lib/ecto/adapters/tds/connection.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,6 +1674,15 @@ if Code.ensure_loaded?(Tds) do
16741674
error!(nil, "Tds adapter does not support the `{:nilify, columns}` action for `:on_delete`")
16751675
end
16761676

1677+
defp reference_on_delete(:default_all), do: " ON DELETE SET DEFAULT"
1678+
1679+
defp reference_on_delete({:default, _columns}) do
1680+
error!(
1681+
nil,
1682+
"Tds adapter does not support the `{:default, columns}` action for `:on_delete`"
1683+
)
1684+
end
1685+
16771686
defp reference_on_delete(:delete_all), do: " ON DELETE CASCADE"
16781687
defp reference_on_delete(:nothing), do: " ON DELETE NO ACTION"
16791688
defp reference_on_delete(_), do: []

lib/ecto/migration.ex

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,8 +1515,8 @@ defmodule Ecto.Migration do
15151515
the example above), or `nil`.
15161516
* `:type` - The foreign key type, which defaults to `:bigserial`.
15171517
* `:on_delete` - What to do if the referenced entry is deleted. May be
1518-
`:nothing` (default), `:delete_all`, `:nilify_all`, `{:nilify, columns}`,
1519-
or `:restrict`. `{:nilify, columns}` expects a list of atoms for `columns`
1518+
`:nothing` (default), `:delete_all`, `:nilify_all`, `{:nilify, columns}`, `:default_all`, `{:default, columns}`
1519+
or `:restrict`. `{:nilify, columns}` and `{:default, columns}` expect a list of atoms for `columns`
15201520
and is not supported by all databases.
15211521
* `:on_update` - What to do if the referenced entry is updated. May be
15221522
`:nothing` (default), `:update_all`, `:nilify_all`, or `:restrict`.
@@ -1561,13 +1561,14 @@ defmodule Ecto.Migration do
15611561
end
15621562

15631563
defp check_on_delete!(on_delete)
1564-
when on_delete in [:nothing, :delete_all, :nilify_all, :restrict],
1564+
when on_delete in [:nothing, :delete_all, :nilify_all, :default_all, :restrict],
15651565
do: :ok
15661566

1567-
defp check_on_delete!({:nilify, columns}) when is_list(columns) do
1567+
defp check_on_delete!({option, columns})
1568+
when option in [:nilify, :default] and is_list(columns) do
15681569
unless Enum.all?(columns, &is_atom/1) do
15691570
raise ArgumentError,
1570-
"expected `columns` in `{:nilify, columns}` to be a list of atoms, got: #{inspect(columns)}"
1571+
"expected `columns` in `{#{inspect(option)}, columns}` to be a list of atoms, got: #{inspect(columns)}"
15711572
end
15721573

15731574
:ok

test/ecto/adapters/myxql_test.exs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,6 +1781,25 @@ defmodule Ecto.Adapters.MyXQLTest do
17811781

17821782
msg = "MySQL adapter does not support the `{:nilify, columns}` action for `:on_delete`"
17831783
assert_raise ArgumentError, msg, fn -> execute_ddl(create) end
1784+
1785+
create =
1786+
{:create, table(:posts),
1787+
[
1788+
{:add, :category_1, %Reference{table: :categories, on_delete: :default_all}, []}
1789+
]}
1790+
1791+
msg = "MySQL adapter does not support the `:default_all` action for `:on_delete`"
1792+
assert_raise ArgumentError, msg, fn -> execute_ddl(create) end
1793+
1794+
create =
1795+
{:create, table(:posts),
1796+
[
1797+
{:add, :category_1, %Reference{table: :categories, on_delete: {:default, [:category_1]}},
1798+
[]}
1799+
]}
1800+
1801+
msg = "MySQL adapter does not support the `{:default, columns}` action for `:on_delete`"
1802+
assert_raise ArgumentError, msg, fn -> execute_ddl(create) end
17841803
end
17851804

17861805
test "create table with options" do

test/ecto/adapters/postgres_test.exs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2231,6 +2231,19 @@ defmodule Ecto.Adapters.PostgresTest do
22312231
table: :categories,
22322232
with: [here: :there, here2: :there2],
22332233
on_delete: {:nilify, [:here, :here2]}
2234+
}, []},
2235+
{:add, :category_15, %Reference{table: :categories, on_delete: :default_all}, []},
2236+
{:add, :category_16,
2237+
%Reference{
2238+
table: :categories,
2239+
with: [here: :there, here2: :there2],
2240+
on_delete: :default_all
2241+
}, []},
2242+
{:add, :category_17,
2243+
%Reference{
2244+
table: :categories,
2245+
with: [here: :there, here2: :there2],
2246+
on_delete: {:default, [:here, :here2]}
22342247
}, []}
22352248
]}
22362249

@@ -2252,6 +2265,9 @@ defmodule Ecto.Adapters.PostgresTest do
22522265
"category_12" bigint, CONSTRAINT "posts_category_12_fkey" FOREIGN KEY ("category_12","here") REFERENCES "categories"("id","there"),
22532266
"category_13" bigint, CONSTRAINT "posts_category_13_fkey" FOREIGN KEY ("category_13","here") REFERENCES "categories"("id","there") MATCH FULL ON UPDATE RESTRICT,
22542267
"category_14" bigint, CONSTRAINT "posts_category_14_fkey" FOREIGN KEY ("category_14","here","here2") REFERENCES "categories"("id","there","there2") ON DELETE SET NULL ("here","here2"),
2268+
"category_15" bigint, CONSTRAINT "posts_category_15_fkey" FOREIGN KEY ("category_15") REFERENCES "categories"("id") ON DELETE SET DEFAULT,
2269+
"category_16" bigint, CONSTRAINT "posts_category_16_fkey" FOREIGN KEY ("category_16","here","here2") REFERENCES "categories"("id","there","there2") ON DELETE SET DEFAULT,
2270+
"category_17" bigint, CONSTRAINT "posts_category_17_fkey" FOREIGN KEY ("category_17","here","here2") REFERENCES "categories"("id","there","there2") ON DELETE SET DEFAULT ("here","here2"),
22552271
PRIMARY KEY ("id"))
22562272
"""
22572273
|> remove_newlines

0 commit comments

Comments
 (0)