Ryzey
Create a unique index as a unique constraint
I have a many_to_many table with a migration that looks like this:
def change do
create table(:drivers_drivers) do
add(:child_id, references(:drivers, on_delete: :delete_all), null: false)
add(:parent_id, references(:drivers, on_delete: :delete_all), null: false)
add(:position, :integer, null: false)
end
create(unique_index(:drivers_drivers, [:parent_id, :child_id], name: "unique_parent_child"))
create(unique_index(:drivers_drivers, [:parent_id, :position], name: "unique_parent_position"))
end
This creates indexes in PostgreSQL that look like this:
Indexes:
"drivers_drivers_pkey" PRIMARY KEY, btree (id)
"unique_parent_child" UNIQUE, btree (parent_id, child_id)
"unique_parent_position" UNIQUE, btree (parent_id, "position")
I found that I was unable to reference the unique indexes as a :conflict_target in a call to insert_all/3. I was expecting that I could provide the options on_conflict: :replace_all, conflict_target: {:constraint, :unique_parent_child}, but this results in the error ERROR 42704 (undefined_object): constraint "unique_parent_child" for table "drivers_drivers" does not exist.
I was able to fix this issue by adding the following to the migration:
execute("""
ALTER TABLE drivers_drivers
ADD CONSTRAINT "unique_parent_child"
UNIQUE USING INDEX "unique_parent_child";
""")
execute("""
ALTER TABLE drivers_drivers
ADD CONSTRAINT "unique_parent_position"
UNIQUE USING INDEX "unique_parent_position";
""")
It feels like I am doing something wrong. Is there a way of referencing indexes in the :conflict_target option without defining them in PostgreSQL as constraints? Or, is there a way of specifying in the migration that an index should also be a constraint, without having to execute SQL?
A supplementary question is how can I specify more than one constraint as the :conflict_target to insert_all? It seems to only accept a tuple, with a single constraint specified as the second element in the tuple.
Most Liked
josevalim
Postgres makes a distinction between indexes and constraints. If you pass conflict_target: :unique_parent_child then it should work, without the constraint definitions.
You also cannot pass multiple constraints. You can however pass multiple field names and let postgres do the job of inferring it. Did you try passing conflict_target: [:parent_id, :child_id, :position]?
josevalim
Oh, so it can only infer a single index. Do you know if what you are trying to achieve is possible at all in Postgres? What would be the equivalent in SQL?
Ryzey
I have been trying to figure that out, but without success, so I am not sure it is possible (although my SQL knowledge is limited, so it could also be that
).
What I learnt about Postgres 9.6 - SQL Insert today (open to correction):
INSERTpermits a singleON CONFLICTclause to be specified;- A
conflict_targetis optional forON CONFLICT DO NOTHINGand, if omitted, conflicts with all usable constraints and unique indexes are handled. However, aconflict_targetmust be provided forON CONFLICT UPDATE; - The
conflict_targeteither performs unique index inference or names a constraint explicitly; - When performing inference, all unique indexes that (without regard to order) contain exactly the
conflict_target-specified columns/expressions are inferred.
So, to me, that sounds like I can either specify a single constraint explicitly or use inference, which can infer multiple constraints but only on a single set of columns/expressions. Therefore I cannot specify or infer constraints on both (parent_id, child_id) and (parent_id, position). If this is the case, then Ecto is allowing me to do everything that is possible in Postgres.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









