spencer.christensen

spencer.christensen

Ecto Migration: `not_null_violation` when modifying column null->non-null & adding default value

Howdy y’all,

I’m brand new to Elixir and have what is likely a simple problem. I created an Ecto schema with a votes column that is type :integer and nullable. I later realized it should be non-nullable and default to 0. I made a migration to perform this update, however I am getting the error:

** (Postgrex.Error) ERROR 23502 (not_null_violation) column "votes" of relation "faqs" contains null values

Migration

defmodule Pento.Repo.Migrations.AlterFaqsAddDefaultVoteValue do
  use Ecto.Migration

  def change do
    alter table(:faqs) do
      modify :votes, :integer, default: 0, null: false
    end
  end
end

See below for my existing table and full error output. I found a comment from Jose Valim stating “The default value in migration is used to set the value for existing rows.” in 2015. I read that as stating that the default value in a migration should change any existing null fields to the stipulated value. Is that not the case and should I just use a separate execute for altering existing rows?

Existing Table

id question answer votes inserted_at updated_at
1 Question 1 Answer 1 NULL 2024-02-04 02:54:39 2024-02-04 12:51:01
2 Question 2 Answer 2 NULL 2024-02-04 02:56:24 2024-02-04 02:56:24
CREATE TABLE "public"."faqs" (
    "id" int8 NOT NULL DEFAULT nextval('faqs_id_seq'::regclass),
    "question" varchar(255),
    "answer" varchar(255),
    "votes" int4,
    "inserted_at" timestamp(0) NOT NULL,
    "updated_at" timestamp(0) NOT NULL,
    PRIMARY KEY ("id")
);

Output

07:05:36.729 [info] == Running 20240204125254 Pento.Repo.Migrations.AlterFaqsAddDefaultVoteValue.change/0 forward

07:05:36.730 [info] alter table faqs
** (Postgrex.Error) ERROR 23502 (not_null_violation) column "votes" of relation "faqs" contains null values

    table: faqs
    column: votes
    (ecto_sql 3.11.1) lib/ecto/adapters/sql.ex:1054: Ecto.Adapters.SQL.raise_sql_call_error/1
    (elixir 1.16.0) lib/enum.ex:1700: Enum."-map/2-lists^map/1-1-"/2
    (ecto_sql 3.11.1) lib/ecto/adapters/sql.ex:1161: Ecto.Adapters.SQL.execute_ddl/4
    (ecto_sql 3.11.1) lib/ecto/migration/runner.ex:348: Ecto.Migration.Runner.log_and_execute_ddl/3
    (elixir 1.16.0) lib/enum.ex:1700: Enum."-map/2-lists^map/1-1-"/2
    (ecto_sql 3.11.1) lib/ecto/migration/runner.ex:311: Ecto.Migration.Runner.perform_operation/3
    (stdlib 5.2) timer.erl:270: :timer.tc/2
    (ecto_sql 3.11.1) lib/ecto/migration/runner.ex:25: Ecto.Migration.Runner.run/8

Thank you for any help!

Most Liked

dimitarvp

dimitarvp

Either make one migration that only introduces the default (without the non-null constraint) or just make a data migration that fills up all null column values with the default value.

I am not sure the former would work at all so I’d go for the latter i.e. something like this:

defmodule YourApp.Repo.Migrations.SetDefaultValueToFaqs do
  use Ecto.migration

  def up do
    execute("UPDATE faqs SET votes = 0 WHERE votes IS NULL")
  end

  def down do
    # Do the reverse here if it makes sense.
  end
end

After this one above, make the one that introduces the non-null constraint.

al2o3cr

al2o3cr

You may need to split this into two migrations, since the alter implementation will always change the NOT NULL constraint before the DEFAULT:

I’d guess this was more relevant in older PGs when setting a default on a nullable column was an expensive “rewrite every row that has a NULL” operation.

dimitarvp

dimitarvp

Will you not lose information this way? What about rows that have, say, faqs = 10?

spencer.christensen

spencer.christensen

Thank you for pointing me to that file! I’m no expert in Postgres, but it looks like this is in fact a limitation in PG as running both:

ALTER TABLE faqs ALTER COLUMN votes SET NOT NULL, ALTER COLUMN votes SET DEFAULT 0 (the unquoted ordering from connection.ex)

and

ALTER TABLE faqs ALTER COLUMN votes SET DEFAULT 0, ALTER COLUMN votes SET NOT NULL

results in the same error:

ERROR: column "votes" of relation "faqs" contains null values

Changing the order of the statements has no effect.

Thus it looks like this is indeed a multi-step migration: fill existing rows and then set the default and not null:

defmodule Pento.Repo.Migrations.AlterFaqsAddDefaultVoteValue do
  use Ecto.Migration

  def change do
    execute "UPDATE faqs SET votes = 0"

    alter table(:faqs) do
      modify :votes, :integer, default: 0, null: false
    end
  end
end

Where Next?

Popular in Questions Top

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
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
LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
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
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
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
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
Exadra37
Sometimes I want to check if the input into a function is not a blank string. My first approach: defmodule Example do def do_stuff(s...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New

Other popular topics Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
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
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
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
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
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
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
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

We're in Beta

About us Mission Statement