Skip to content

Commit

Permalink
Add ON CONSTRAINT support to conflict target, see elixir-ecto#2081
Browse files Browse the repository at this point in the history
  • Loading branch information
José Valim committed Aug 6, 2017
1 parent 5db866c commit 2df88c1
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 0 deletions.
14 changes: 14 additions & 0 deletions integration_test/cases/repo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,20 @@ defmodule Ecto.Integration.RepoTest do
assert c4.uuid != c1.uuid
end

@tag :with_conflict_target
@tag :with_conflict_target_on_constraint
test "on conflict keyword list and conflict target on constraint" do
on_conflict = [set: [title: "new"]]
post = %Post{title: "old"}
{:ok, inserted} = TestRepo.insert(post, on_conflict: on_conflict, conflict_target: {:constraint, :posts_pkey})
assert inserted.id

{:ok, updated} = TestRepo.insert(%{post | id: inserted.id}, on_conflict: on_conflict, conflict_target: {:constraint, :posts_pkey})
assert updated.id == inserted.id
assert updated.title != "new"
assert TestRepo.get!(Post, inserted.id).title == "new"
end

@tag :returning
@tag :with_conflict_target
test "on conflict keyword list and conflict target and returning and field source" do
Expand Down
2 changes: 2 additions & 0 deletions lib/ecto/adapters/postgres/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ if Code.ensure_loaded?(Postgrex) do
defp on_conflict({query, _, targets}, _header),
do: [" ON CONFLICT ", conflict_target(targets), "DO " | update_all(query, "UPDATE SET ")]

defp conflict_target({:constraint, constraint}),
do: ["ON CONSTRAINT ", quote_name(constraint), ?\s]
defp conflict_target([]),
do: []
defp conflict_target(targets),
Expand Down
4 changes: 4 additions & 0 deletions lib/ecto/repo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,8 @@ defmodule Ecto.Repo do
* `:conflict_target` - Which columns to verify for conflicts. If
none is specified, the conflict target is left up to the database
and is usually made of primary keys and/or unique/exclusion constraints.
May also be `{:constraint, constraint_name_as_atom}` in databases
that support the "ON CONSTRAINT" expression.
See the "Shared options" section at the module documentation for
remaining options.
Expand Down Expand Up @@ -701,6 +703,8 @@ defmodule Ecto.Repo do
* `:conflict_target` - Which columns to verify for conflicts. If
none is specified, the conflict target is left up to the database
and is usually made of primary keys and/or unique/exclusion constraints.
May also be `{:constraint, constraint_name_as_atom}` in databases
that support the "ON CONSTRAINT" expression.
See the "Shared options" section at the module documentation.
Expand Down
3 changes: 3 additions & 0 deletions lib/ecto/repo/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,9 @@ defmodule Ecto.Repo.Schema do
metadata(schema, prefix, source, autogen_id, context, opts)
end

defp conflict_target({:constraint, constraint}, _dumper) when is_atom(constraint) do
{:constraint, constraint}
end
defp conflict_target(conflict_target, dumper) do
for target <- List.wrap(conflict_target) do
case dumper do
Expand Down
5 changes: 5 additions & 0 deletions test/ecto/adapters/postgres_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -640,12 +640,14 @@ defmodule Ecto.Adapters.PostgresTest do
end

test "insert with on conflict" do
# For :nothing
query = SQL.insert(nil, "schema", [:x, :y], [[:x, :y]], {:nothing, [], []}, [])
assert query == ~s{INSERT INTO "schema" ("x","y") VALUES ($1,$2) ON CONFLICT DO NOTHING}

query = SQL.insert(nil, "schema", [:x, :y], [[:x, :y]], {:nothing, [], [:x, :y]}, [])
assert query == ~s{INSERT INTO "schema" ("x","y") VALUES ($1,$2) ON CONFLICT ("x","y") DO NOTHING}

# For :update
update = from("schema", update: [set: [z: "foo"]]) |> normalize(:update_all)
query = SQL.insert(nil, "schema", [:x, :y], [[:x, :y]], {update, [], [:x, :y]}, [:z])
assert query == ~s{INSERT INTO "schema" AS s0 ("x","y") VALUES ($1,$2) ON CONFLICT ("x","y") DO UPDATE SET "z" = 'foo' RETURNING "z"}
Expand All @@ -660,6 +662,9 @@ defmodule Ecto.Adapters.PostgresTest do

query = SQL.insert(nil, "schema", [:x, :y], [[:x, :y]], {:replace_all, [], []}, [])
assert query == ~s{INSERT INTO "schema" ("x","y") VALUES ($1,$2) ON CONFLICT DO UPDATE SET "x" = EXCLUDED."x","y" = EXCLUDED."y"}

query = SQL.insert(nil, "schema", [:x, :y], [[:x, :y]], {:replace_all, [], {:constraint, :foo}}, [])
assert query == ~s{INSERT INTO "schema" ("x","y") VALUES ($1,$2) ON CONFLICT ON CONSTRAINT \"foo\" DO UPDATE SET "x" = EXCLUDED."x","y" = EXCLUDED."y"}
end

test "update" do
Expand Down

0 comments on commit 2df88c1

Please sign in to comment.