Skip to content

Commit d2a5d68

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 d54f93c commit d2a5d68

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
@@ -2123,35 +2123,63 @@ defmodule AshPostgres.DataLayer do
21232123
{Map.take(r, keys), r}
21242124
end)
21252125

2126-
results =
2127-
changesets
2128-
|> Enum.map(fn changeset ->
2129-
identity =
2130-
changeset.attributes
2131-
|> Map.take(keys)
2126+
cant_map_results_to_changesets =
2127+
any_generated_keys_missing?(keys, resource, changesets)
21322128

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

21402138
case get_bulk_operation_metadata(changeset) do
21412139
{index, metadata_key} ->
2142-
Ash.Resource.put_metadata(result_for_changeset, metadata_key, index)
2140+
Ash.Resource.put_metadata(result, metadata_key, index)
21432141

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

21562184
{:ok, results}
21572185
end
@@ -3737,6 +3765,16 @@ defmodule AshPostgres.DataLayer do
37373765
end
37383766
end
37393767

3768+
# checks if any of the attributes in the list of keys are generated and missing from any of the changesets
3769+
# if so, we can't match the created record to the changeset by the identity and just need to zip the return
3770+
# values with the changesets
3771+
defp any_generated_keys_missing?(keys, resource, changesets) do
3772+
Enum.any?(keys, fn key ->
3773+
Ash.Resource.Info.attribute(resource, key).generated? &&
3774+
Enum.any?(changesets, fn changeset -> is_nil(changeset.attributes[key]) end)
3775+
end)
3776+
end
3777+
37403778
defp get_bulk_operation_metadata(changeset) do
37413779
changeset.context
37423780
|> 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.{IntegerPost, Post, Record}
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)