Skip to content

Commit c76f561

Browse files
committed
fix: handle results that can't be mapped to the changeset in bulk_create
If the identity used has attibutes that can be generated by the datalayer, we can't map the result back to the changeset and we need to just zip the results with the changesets and return them that way.
1 parent eb3f916 commit c76f561

File tree

2 files changed

+66
-16
lines changed

2 files changed

+66
-16
lines changed

lib/data_layer.ex

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2120,35 +2120,63 @@ defmodule AshPostgres.DataLayer do
21202120
{Map.take(r, keys), r}
21212121
end)
21222122

2123-
results =
2124-
changesets
2125-
|> Enum.map(fn changeset ->
2126-
identity =
2127-
changeset.attributes
2128-
|> Map.take(keys)
2123+
cant_map_results_to_changesets =
2124+
any_generated_keys_missing?(keys, resource, changesets)
21292125

2130-
result_for_changeset = Map.get(results_by_identity, identity)
2131-
2132-
if result_for_changeset do
2126+
results =
2127+
if cant_map_results_to_changesets do
2128+
results
2129+
|> Enum.zip(changesets)
2130+
|> Enum.map(fn {result, changeset} ->
21332131
if !opts[:upsert?] do
2134-
maybe_create_tenant!(resource, result_for_changeset)
2132+
maybe_create_tenant!(resource, result)
21352133
end
21362134

21372135
case get_bulk_operation_metadata(changeset) do
21382136
{index, metadata_key} ->
2139-
Ash.Resource.put_metadata(result_for_changeset, metadata_key, index)
2137+
Ash.Resource.put_metadata(result, metadata_key, index)
21402138

21412139
nil ->
21422140
# Compatibility fallback
21432141
Ash.Resource.put_metadata(
2144-
result_for_changeset,
2142+
result,
21452143
:bulk_create_index,
21462144
changeset.context[:bulk_create][:index]
21472145
)
21482146
end
2149-
end
2150-
end)
2151-
|> Enum.filter(& &1)
2147+
end)
2148+
else
2149+
changesets
2150+
|> Enum.map(fn changeset ->
2151+
identity =
2152+
changeset.attributes
2153+
|> Map.take(keys)
2154+
2155+
result_for_changeset = Map.get(results_by_identity, identity)
2156+
2157+
if result_for_changeset do
2158+
if !opts[:upsert?] do
2159+
maybe_create_tenant!(resource, result_for_changeset)
2160+
end
2161+
2162+
case get_bulk_operation_metadata(changeset) do
2163+
{index, metadata_key} ->
2164+
Ash.Resource.put_metadata(result_for_changeset, metadata_key, index)
2165+
2166+
nil ->
2167+
# Compatibility fallback
2168+
Ash.Resource.put_metadata(
2169+
result_for_changeset,
2170+
:bulk_create_index,
2171+
changeset.context[:bulk_create][:index]
2172+
)
2173+
end
2174+
end
2175+
end)
2176+
|> Enum.concat(results)
2177+
|> Enum.filter(& &1)
2178+
|> Enum.uniq_by(&Map.take(&1, keys))
2179+
end
21522180

21532181
{:ok, results}
21542182
end
@@ -3734,6 +3762,16 @@ defmodule AshPostgres.DataLayer do
37343762
end
37353763
end
37363764

3765+
# checks if any of the attributes in the list of keys are generated and missing from any of the changesets
3766+
# if so, we can't match the created record to the changeset by the identity and just need to zip the return
3767+
# values with the changesets
3768+
defp any_generated_keys_missing?(keys, resource, changesets) do
3769+
Enum.any?(keys, fn key ->
3770+
Ash.Resource.Info.attribute(resource, key).generated? &&
3771+
Enum.any?(changesets, fn changeset -> is_nil(changeset.attributes[key]) end)
3772+
end)
3773+
end
3774+
37373775
defp get_bulk_operation_metadata(changeset) do
37383776
changeset.context
37393777
|> Enum.find_value(fn

test/bulk_create_test.exs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
defmodule AshPostgres.BulkCreateTest do
66
use AshPostgres.RepoCase, async: false
7-
alias AshPostgres.Test.{Post, Record}
7+
alias AshPostgres.Test.{Post, Record, IntegerPost}
88

99
require Ash.Query
1010
import Ash.Expr
@@ -356,6 +356,18 @@ defmodule AshPostgres.BulkCreateTest do
356356
|> Ash.Query.load(:ratings)
357357
|> Ash.read!()
358358
end
359+
360+
test "bulk creates with integer primary key return records" do
361+
%Ash.BulkResult{records: records} =
362+
Ash.bulk_create!(
363+
[%{title: "first"}, %{title: "second"}, %{title: "third"}],
364+
IntegerPost,
365+
:create,
366+
return_records?: true
367+
)
368+
369+
assert length(records) == 3
370+
end
359371
end
360372

361373
describe "validation errors" do

0 commit comments

Comments
 (0)