Skip to content

Commit be60b77

Browse files
authored
HOT optimization for upserts with :replace and :replace_all_except (#4617)
1 parent c87024c commit be60b77

File tree

2 files changed

+22
-1
lines changed

2 files changed

+22
-1
lines changed

lib/ecto/repo/schema.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ defmodule Ecto.Repo.Schema do
754754
raise ArgumentError, ":on_conflict option with `{:replace, fields}` requires a non-empty list of fields"
755755

756756
{:replace, keys} when is_list(keys) ->
757+
keys = keys -- List.wrap(conflict_target)
757758
{{replace_fields!(dumper, keys), [], conflict_target}, []}
758759

759760
:replace_all ->
@@ -764,7 +765,8 @@ defmodule Ecto.Repo.Schema do
764765
{{replace_all_fields!(:replace_all, schema, to_remove), [], conflict_target}, []}
765766

766767
{:replace_all_except, fields} ->
767-
{{replace_all_fields!(:replace_all_except, schema, fields), [], conflict_target}, []}
768+
to_remove = List.wrap(conflict_target) ++ fields
769+
{{replace_all_fields!(:replace_all_except, schema, to_remove), [], conflict_target}, []}
768770

769771
[_ | _] = on_conflict ->
770772
from = if schema, do: {source, schema}, else: source

test/ecto/repo_test.exs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1904,6 +1904,25 @@ defmodule Ecto.RepoTest do
19041904
assert_received {:insert, %{source: "my_schema", on_conflict: {^fields, [], []}}}
19051905
end
19061906

1907+
test "includes conflict target in the field list given to :replace_all_except" do
1908+
fields = [:map, :z, :yyy, :x]
1909+
TestRepo.insert(%MySchema{id: 1}, on_conflict: {:replace_all_except, [:array]}, conflict_target: [:id])
1910+
assert_received {:insert, %{source: "my_schema", on_conflict: {^fields, [], [:id]}}}
1911+
end
1912+
1913+
test "excludes conflict target from the field list given to :replace" do
1914+
fields = [:id, :map, :z, :x]
1915+
TestRepo.insert(%MySchema{id: 1}, on_conflict: {:replace, fields}, conflict_target: [:id])
1916+
expected_fields = fields -- [:id]
1917+
assert_received {:insert, %{source: "my_schema", on_conflict: {^expected_fields, [], [:id]}}}
1918+
end
1919+
1920+
test "excludes conflict target from :replace_all" do
1921+
fields = [:map, :array, :z, :yyy, :x]
1922+
TestRepo.insert(%MySchema{id: 1}, on_conflict: :replace_all, conflict_target: [:id])
1923+
assert_received {:insert, %{source: "my_schema", on_conflict: {^fields, [], [:id]}}}
1924+
end
1925+
19071926
test "converts keyword list into query" do
19081927
TestRepo.insert(%MySchema{id: 1}, on_conflict: [set: [x: "123", y: "456"]])
19091928
assert_received {:insert, %{source: "my_schema", on_conflict: {query, ["123", "456"], []}}}

0 commit comments

Comments
 (0)