andreh

andreh

How to set PRAGMA foreign_keys=OFF in Ecto migration?

Hello,

Using SQLite, in order to remove a NOT NULL constraint on a column, I’m trying to follow this procedure: https://www.sqlite.org/lang_altertable.html#making_other_kinds_of_table_schema_changes

The problem is, we must execute PRAGMA foreign_keys=OFF before the transaction, because SQLite’s documentation says:

PRAGMA foreign_keys = boolean ;
This pragma is a no-op within a transaction; foreign key constraint enforcement may only be enabled or disabled when there is no pending BEGIN or SAVEPOINT.

I’ve tried this approach:

defmodule Brinjel.Repo.Migrations.RelaxNotNullConstraintForOwnerId do
  use Ecto.Migration

  @disable_ddl_transaction true

  def change do
    execute("PRAGMA foreign_keys=OFF;")

    execute("PRAGMA busy_timeout=5000;")

    execute("BEGIN TRANSACTION")

    create table(:new_farms, primary_key: false, options: "STRICT") do
      add :farm_id, :bigserial, primary_key: true
      add :name, :text, null: false
      add :slug, :text, null: false
      add :default_provider_id, :integer
      add :locked, :boolean, null: false, default: false
      add :trial_expiry_date, :date, null: false

      add :owner_id, references(:users)

      timestamps()
    end

    execute """
      INSERT INTO new_farms(farm_id, name, slug, default_provider_id, locked, trial_expiry_date, owner_id, inserted_at, updated_at)
      SELECT farm_id, name, slug, default_provider_id, locked, trial_expiry_date, owner_id, inserted_at, updated_at 
      FROM farms;
    """

    flush()

    drop table("farms")

    rename table("new_farms"), to: table("farms")

    create unique_index(:farms, [:slug])

    execute("COMMIT")

    execute("PRAGMA foreign_keys=ON")
  end
end

and unfortunately got this error:

17:20:26.716 [info] execute "PRAGMA foreign_keys=OFF;"

17:20:26.716 [info] execute "PRAGMA busy_timeout=5000;"

17:20:26.716 [info] execute "BEGIN TRANSACTION"

17:20:26.716 [info] create table new_farms

17:20:26.720 [info] execute "  INSERT INTO new_farms(farm_id, name, slug, default_provider_id, locked, trial_expiry_date, owner_id, inserted_at, updated_at)\n  SELECT farm_id, name, slug, default_provider_id, locked, trial_expiry_date, owner_id, inserted_at, updated_at \n  FROM farms;\n"

17:20:26.721 [info] drop table farms
** (Exqlite.Error) Database busy
DROP TABLE "farms"
    (ecto_sql 3.11.3) lib/ecto/adapters/sql.ex:1054: Ecto.Adapters.SQL.raise_sql_call_error/1
    (elixir 1.16.1) lib/enum.ex:1700: Enum."-map/2-lists^map/1-1-"/2
    (ecto_sql 3.11.3) lib/ecto/adapters/sql.ex:1161: Ecto.Adapters.SQL.execute_ddl/4
    (ecto_sql 3.11.3) lib/ecto/migration/runner.ex:348: Ecto.Migration.Runner.log_and_execute_ddl/3
    (elixir 1.16.1) lib/enum.ex:1700: Enum."-map/2-lists^map/1-1-"/2
    (stdlib 5.2) timer.erl:270: :timer.tc/2
    (ecto_sql 3.11.3) lib/ecto/migration/runner.ex:25: Ecto.Migration.Runner.run/8
    (ecto_sql 3.11.3) lib/ecto/migrator.ex:365: Ecto.Migrator.attempt/8

Any idea why dropping the table produces a “Database busy” error?

Marked As Solved

andreh

andreh

Sorry, I just saw your answers! I’ve switched to PostgreSQL long ago. Too many hours have been wasted on this issue, and it was generally too frustrating to migrate data using SQLite. I also had scaling issues with my one-db-per-tenant architecture. Sure, I now have to deal with Postgres updates, but it’s still really fast because I use a socket connection on the same server.

I still like SQLite for small projects, but Postgres is now my default database for larger ones.

Also Liked

dimitarvp

dimitarvp

I’ll get you guys sorted soon enough. My SQLite library is nearly done and I’m moving to Ecto integration very soon, migrations included.

a-nassim

a-nassim

I cannot explain the “Database busy” error

But I had a similar issue which I could work around with a dynamic repo Ecto.Repo — Ecto v3.12.5

I only tried this approach with up and down functions

Adapted to your snippet, this would look like this

defmodule Brinjel.Repo.Migrations.RelaxNotNullConstraintForOwnerId do
  use Ecto.Migration

  import Ecto.Query

  @disable_ddl_transaction true

  def up do
    repo().start_link(name: :migration, foreign_keys: :off)
    repo().put_dynamic_repo(:migration)

    repo().transaction(fn ->
      create table(:new_farms, primary_key: false, options: "STRICT") do
        add(:farm_id, :bigserial, primary_key: true)
        add(:name, :text, null: false)
        add(:slug, :text, null: false)
        add(:default_provider_id, :integer)
        add(:locked, :boolean, null: false, default: false)
        add(:trial_expiry_date, :date, null: false)

        add(:owner_id, references(:users))

        timestamps()
      end

      execute("""
        INSERT INTO new_farms(farm_id, name, slug, default_provider_id, locked, trial_expiry_date, owner_id, inserted_at, updated_at)
        SELECT farm_id, name, slug, default_provider_id, locked, trial_expiry_date, owner_id, inserted_at, updated_at 
        FROM farms;
      """)

      drop(table("farms"))

      rename(table("new_farms"), to: table("farms"))

      create(unique_index(:farms, [:slug]))

      execute(fn ->
        count = repo().one(from(fragment("pragma_foreign_key_check()"), select: count()))

        if count > 0 do
          raise "Foreign key check failed"
        end
      end)

      flush()
    end)
  end

  def down do
    # reverse
  end
end

Where Next?

Popular in Questions Top

jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
logicmason
Hi there, I'm working through my first release with elixir/phoenix. I've built a release with distillery and found that it crashes when I...
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
hariharasudhan94
I would like to know what is the best IDE for elixir development?
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
hpopp
To simplify some tasks at work, I wrote and published this package yesterday. It’s a simple macro that enables Access behaviour on struct...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
Mooodi
Given a string, how can I get access to its character by index? Enum.at("my_string", 2) doesn't work. Or rather, not char, but a substr...
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
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

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
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
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
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
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New

We're in Beta

About us Mission Statement