jmurphyweb

jmurphyweb

I’ve run into this a couple of times and am wondering if I’m approaching data migrations wrong.

A week ago I had an Offer schema:

  schema "offers" do
    field(:regular_site_id, :id)
  end

I want to update offers with a regular_site_id, then add a NOT NULL constraint to the regular_site_id column:

  def up do
    offer_query = from(o in Offer, where: is_nil(o.regular_site_id))
    
    offer_query
    |> Repo.all()
    |> update_each_offers_regular_site()

    flush()
    
    alter table(:offers) do
      modify(:regular_site_id, :id, null: false)
    end
  end

This all runs fine, all environments now have run the above migration. But now I want to write a new migration:

  def up do
    alter table(:offers) do
      add(:something_new, :string, null: false)
    end
  end

Adding a field to the offer so the schema now looks like:

  schema "offers" do
    field(:regular_site_id, :id)
    field(:something_new, :string)
  end

Replicating our CI, which attempts to build out the database from scratch, if I run MIX_ENV=test mix ecto.reset I will get an error:

** (Postgrex.Error) ERROR 42703 (undefined_column): column o0.something_new does not exist
    (ecto) lib/ecto/adapters/sql.ex:431: Ecto.Adapters.SQL.execute_and_cache/7
    (ecto) lib/ecto/repo/queryable.ex:133: Ecto.Repo.Queryable.execute/5
    (ecto) lib/ecto/repo/queryable.ex:37: Ecto.Repo.Queryable.all/4
    priv/repo/migrations/20190406130008_add_regular_site_ids.exs:10: Ev2.Repo.Migrations.AddRegularSiteIds.up/0
    (stdlib) timer.erl:197: :timer.tc/3
    (ecto) lib/ecto/migration/runner.ex:25: Ecto.Migration.Runner.run/6
    (ecto) lib/ecto/migrator.ex:128: Ecto.Migrator.attempt/6
    (ecto) lib/ecto/migrator.ex:72: anonymous fn/4 in Ecto.Migrator.do_up/4

I can get around this by just commenting out the data migration part, which is ok because the migration has now run in all environments.

But it seems like this would be a fairly common issue, am I approaching the data migration wrong? The problem would also go away if I wrote the migration with pure SQL but that isn’t always possible.

Showing Posts 1 to 3

hubertlepicki

hubertlepicki

This is your problem right here. You are using piece of your application code, i.e. schema that has added new fields in it, and expect them to be present in database. This works once, in the time you write the new migration, but will fail likely later wien Offer schema is changed in your application, and no longer matches whatever the migration is expecting to find there.

The rule of thumb I am applying is: only use migration DSL and “execute” statements in migrations. If you have to alter some data in database just write:

execute "UPDATE .... " 

with hand-written SQL instead of using your Ecto schemas.

Possibly you could also use Ecto.DSL if you really want here, with “shemaless query” but I did never attempt this (Ecto’s insert_all and schemaless queries « Plataformatec Blog)

jmurphyweb

jmurphyweb OP

Great ok so just need to be more disciplined with using SQL in our migrations. Thanks!

kirega

kirega

Thanks so much for the link on how to perform schemaless queries, it worked like a charm.
To add more context to your answer, according to the blog post, @jmurphyweb should probably do something like this instead.

  def up do
    offer_query = from(o in "offer", where: is_nil(o.regular_site_id), select: [id: o.id])
    ...
  end

in the select be as specific as possible for the fields you need.
Hopefully, this will make it easier for anyone else down the line. Also here is a more up-to-date link to the same

— All posts loaded —

Where Next? Top

Trending in Questions Top

water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New

Other Trending Topics Top

budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews