Skip to content

Commit

Permalink
improvement: support ash main upsert_condition logic
Browse files Browse the repository at this point in the history
  • Loading branch information
zachdaniel committed Sep 3, 2024
1 parent 7285b33 commit 2015aa6
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 1 deletion.
3 changes: 2 additions & 1 deletion lib/data_layer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1930,7 +1930,8 @@ defmodule AshPostgres.DataLayer do
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
%{source: source} when not is_nil(source) ->
source
Expand Down
27 changes: 27 additions & 0 deletions lib/sql_implementation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,33 @@ defmodule AshPostgres.SqlImplementation do
{:ok, Ecto.Query.dynamic(fragment("'[]'::jsonb")), acc}
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
binding =
AshSql.Bindings.get_binding(
Expand Down
47 changes: 47 additions & 0 deletions test/bulk_create_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ defmodule AshPostgres.BulkCreateTest do
use AshPostgres.RepoCase, async: false
alias AshPostgres.Test.{Post, Record}

import Ash.Expr

describe "bulk creates" do
test "bulk creates insert each input" do
Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create)
Expand Down Expand Up @@ -109,6 +111,51 @@ defmodule AshPostgres.BulkCreateTest do
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.
# leaving this test here for posterity
# test "bulk creates can upsert with id" do
Expand Down
6 changes: 6 additions & 0 deletions test/support/resources/post.ex
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,12 @@ defmodule AshPostgres.Test.Post do
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
change(atomic_update(:title, expr(author.first_name)))
end
Expand Down

0 comments on commit 2015aa6

Please sign in to comment.