yesodweb/persistent

SQLite: FOREIGN KEY constraint failed when migrating

Open

#1,125 opened on Sep 18, 2020

View on GitHub
 (7 comments) (5 reactions) (0 assignees)Haskell (302 forks)auto 404
good first issue

Repository metrics

Stars
 (484 stars)
PR merge metrics
 (PR metrics pending)

Description

{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

import Database.Persist.Sqlite
import Database.Persist.TH

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Person
    name String
    -- Uncomment this line in the second run
    -- age Int Maybe
    deriving Show
BlogPost
    title String
    authorId PersonId
    deriving Show
|]

main :: IO ()
main = runSqlite "db" $ do
    runMigration migrateAll
    -- Comment this line in the second run
    johnId <- insert $ Person "John Doe"
    -- Uncomment this line in the second run
    -- johnId <- insert $ Person "John Doe" $ Just 35
    insert $ BlogPost "post" johnId
    pure ()
  1. stack ghc the script above and run.
  2. Edit the script, uncomment 2 lines and comment 1 line.
  3. stack ghc the script and run again.
  4. You will see:
Migrating: CREATE TEMP TABLE "person_backup"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL)
Migrating: INSERT INTO "person_backup"("id","name") SELECT "id","name" FROM "person"
Migrating: DROP TABLE "person"
main: SQLite3 returned ErrorConstraint while attempting to perform step: FOREIGN KEY constraint failed

The correct way to do migrations in SQLite is documented here: https://sqlite.org/lang_altertable.html#making_other_kinds_of_table_schema_changes

In short, you need to disable foreign keys temporarily using PRAGMA commands when migrating. But this seems impossible because I can only send raw commands inside an automatically started transaction. And inside a transaction, PRAGMA foreign_keys=OFF has no effect.

Could you please change the logic of runMigration so that it follows the 12 steps in SQLite document precisely?

Contributor guide