-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Elixir version
Elixir 1.18.3 (compiled with Erlang/OTP 27)
Database and Version
PostgreSQL 16.3
Ecto Versions
3.13.2
Database Adapter and Versions (postgrex, myxql, etc)
3.12.1
Current behavior
- Create a schema with only
:id
and:other_field
.
defmodule EctoTest.TestSchema do
use Ecto.Schema
import Ecto.Query
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
schema "test_schema" do
field :other_field, :string
end
def break() do
query =
__MODULE__
|> select([ts], %{
id: fragment("gen_random_uuid()"),
other_field: ts.other_field
})
EctoTest.Repo.insert_all(__MODULE__, query,
on_conflict: {:replace_all_except, [:id]},
conflict_target: :other_field
)
end
end
defmodule EctoTest.Repo.Migrations.AddTestSchema do
use Ecto.Migration
def change do
create table(:test_schema) do
add(:other_field, :string)
end
end
end
- Try to run
insert_all
like inEctoTest.TestSchema.break/0
above - Note the error:
iex(1)> EctoTest.TestSchema.break()
** (ArgumentError) empty list of fields to update, use the `:replace` option instead
(ecto 3.13.2) lib/ecto/repo/schema.ex:927: Ecto.Repo.Schema.on_conflict/6
(ecto 3.13.2) lib/ecto/repo/schema.ex:74: Ecto.Repo.Schema.do_insert_all/7
iex:1: (file)
Expected behavior
Should this fail? I'll confess I haven't spent a lot of time in this particular space, so the logic of it doesn't come to me quickly.
Why should a conflict_target
itself not be replaced? Is this a requirement for HOT updates?
If this is indeed a bug, I wonder if the bug is in https://github.com/elixir-ecto/ecto/blob/cd0f70b4cdd949767ea7cbe7d635e70917384b38/lib/ecto/repo/schema.ex#L924
:
-- to_remove = List.wrap(conflict_target) ++ fields
++ to_remove = fields -- List.wrap(conflict_target)
Such that we ensure that the conflict_target
is always in the list of fields that get replaced. I wonder if to_remove
is misleading here, since it seems to suggest that it's the list of fields to "replace", but in fact it's the list of fields to remove from the list of fields to replace.
Is this all accurate, or have I missed something?