Ryzey

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

josevalim

Creator of Elixir

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

josevalim

Creator of Elixir

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

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 :blush:).

What I learnt about Postgres 9.6 - SQL Insert today (open to correction):

  1. INSERT permits a single ON CONFLICT clause to be specified;
  2. A conflict_target is optional for ON CONFLICT DO NOTHING and, if omitted, conflicts with all usable constraints and unique indexes are handled. However, a conflict_target must be provided for ON CONFLICT UPDATE;
  3. The conflict_target either performs unique index inference or names a constraint explicitly;
  4. 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.

Where Next?

Popular in Questions Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
pgiesin
This should be a simple problem but I just can’t seem to figure it out. I have a standalone Elixir app that won’t find the database. Dep...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New
jc00ke
Expanding on this topic: https://forum.elixirforum.net/t/map-typespec-question/19217 Let’s say I have a map with required and optional k...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement