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

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
Kurisu
For example for a current url like http://localhost:4000/cosmetic/products?_utf8=✓&query=perfume&page=2, I would like to get: ...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lists...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41989 114
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39467 209
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

We're in Beta

About us Mission Statement