mirror of
https://github.com/ash-project/ash_postgres.git
synced 2024-09-19 21:13:19 +12:00
improvement: support ash main upsert_condition logic
This commit is contained in:
parent
7285b3382e
commit
2015aa658c
4 changed files with 82 additions and 1 deletions
|
@ -1930,7 +1930,8 @@ defmodule AshPostgres.DataLayer do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_source_for_upsert_field(field, resource) do
|
@doc false
|
||||||
|
def get_source_for_upsert_field(field, resource) do
|
||||||
case Ash.Resource.Info.attribute(resource, field) do
|
case Ash.Resource.Info.attribute(resource, field) do
|
||||||
%{source: source} when not is_nil(source) ->
|
%{source: source} when not is_nil(source) ->
|
||||||
source
|
source
|
||||||
|
|
|
@ -41,6 +41,33 @@ defmodule AshPostgres.SqlImplementation do
|
||||||
{:ok, Ecto.Query.dynamic(fragment("'[]'::jsonb")), acc}
|
{:ok, Ecto.Query.dynamic(fragment("'[]'::jsonb")), acc}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def expr(
|
||||||
|
query,
|
||||||
|
%Ash.Query.UpsertConflict{attribute: attribute},
|
||||||
|
_bindings,
|
||||||
|
_embedded?,
|
||||||
|
acc,
|
||||||
|
_type
|
||||||
|
) do
|
||||||
|
query.__ash_bindings__.resource
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
Ecto.Query.dynamic(
|
||||||
|
[],
|
||||||
|
fragment(
|
||||||
|
"EXCLUDED.?",
|
||||||
|
literal(
|
||||||
|
^to_string(
|
||||||
|
AshPostgres.DataLayer.get_source_for_upsert_field(
|
||||||
|
attribute,
|
||||||
|
query.__ash_bindings__.resource
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
), acc}
|
||||||
|
end
|
||||||
|
|
||||||
def expr(query, %AshPostgres.Functions.Binding{}, _bindings, _embedded?, acc, _type) do
|
def expr(query, %AshPostgres.Functions.Binding{}, _bindings, _embedded?, acc, _type) do
|
||||||
binding =
|
binding =
|
||||||
AshSql.Bindings.get_binding(
|
AshSql.Bindings.get_binding(
|
||||||
|
|
|
@ -2,6 +2,8 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
use AshPostgres.RepoCase, async: false
|
use AshPostgres.RepoCase, async: false
|
||||||
alias AshPostgres.Test.{Post, Record}
|
alias AshPostgres.Test.{Post, Record}
|
||||||
|
|
||||||
|
import Ash.Expr
|
||||||
|
|
||||||
describe "bulk creates" do
|
describe "bulk creates" do
|
||||||
test "bulk creates insert each input" do
|
test "bulk creates insert each input" do
|
||||||
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
|
||||||
|
@ -109,6 +111,51 @@ defmodule AshPostgres.BulkCreateTest do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "bulk upsert skips with upsert_condition" do
|
||||||
|
assert [
|
||||||
|
{:ok, %{title: "fredfoo", uniq_if_contains_foo: "1foo", price: 10}},
|
||||||
|
{:ok, %{title: "georgefoo", uniq_if_contains_foo: "2foo", price: 20}},
|
||||||
|
{:ok, %{title: "herbert", uniq_if_contains_foo: "3", price: 30}}
|
||||||
|
] =
|
||||||
|
Ash.bulk_create!(
|
||||||
|
[
|
||||||
|
%{title: "fredfoo", uniq_if_contains_foo: "1foo", price: 10},
|
||||||
|
%{title: "georgefoo", uniq_if_contains_foo: "2foo", price: 20},
|
||||||
|
%{title: "herbert", uniq_if_contains_foo: "3", price: 30}
|
||||||
|
],
|
||||||
|
Post,
|
||||||
|
:create,
|
||||||
|
return_stream?: true,
|
||||||
|
return_records?: true
|
||||||
|
)
|
||||||
|
|> Enum.sort_by(fn {:ok, result} -> result.title end)
|
||||||
|
|
||||||
|
assert [
|
||||||
|
{:ok, %{title: "georgefoo", uniq_if_contains_foo: "2foo", price: 20_000}},
|
||||||
|
{:ok, %{title: "herbert", uniq_if_contains_foo: "3", price: 30}}
|
||||||
|
] =
|
||||||
|
Ash.bulk_create!(
|
||||||
|
[
|
||||||
|
%{title: "fredfoo", uniq_if_contains_foo: "1foo", price: 10},
|
||||||
|
%{title: "georgefoo", uniq_if_contains_foo: "2foo", price: 20_000},
|
||||||
|
%{title: "herbert", uniq_if_contains_foo: "3", price: 30}
|
||||||
|
],
|
||||||
|
Post,
|
||||||
|
:upsert_with_no_filter,
|
||||||
|
return_stream?: true,
|
||||||
|
upsert_condition: expr(price != upsert_conflict(:price)),
|
||||||
|
return_errors?: true,
|
||||||
|
return_records?: true
|
||||||
|
)
|
||||||
|
|> Enum.sort_by(fn
|
||||||
|
{:ok, result} ->
|
||||||
|
result.title
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
# confirmed that this doesn't work because it can't. An upsert must map to a potentially successful insert.
|
# confirmed that this doesn't work because it can't. An upsert must map to a potentially successful insert.
|
||||||
# leaving this test here for posterity
|
# leaving this test here for posterity
|
||||||
# test "bulk creates can upsert with id" do
|
# test "bulk creates can upsert with id" do
|
||||||
|
|
|
@ -278,6 +278,12 @@ defmodule AshPostgres.Test.Post do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create :upsert_with_no_filter do
|
||||||
|
upsert?(true)
|
||||||
|
upsert_identity(:uniq_if_contains_foo)
|
||||||
|
upsert_fields([:price])
|
||||||
|
end
|
||||||
|
|
||||||
update :set_title_from_author do
|
update :set_title_from_author do
|
||||||
change(atomic_update(:title, expr(author.first_name)))
|
change(atomic_update(:title, expr(author.first_name)))
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue