yesodweb / yesodweb/persistent
SQLite: FOREIGN KEY constraint failed when migrating
Nobody has claimed this yet.
- Dominant language
- Haskell
- Stars
- 486
- Forks
- 306
- PR merge metrics
- No merged PRs in 30d
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 ()
stack ghcthe script above and run.- Edit the script, uncomment 2 lines and comment 1 line.
stack ghcthe script and run again.- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the supplied Haskell reproducer and inspect runMigration, focusing on how SQLite schema changes are wrapped in transactions. Compare that flow with SQLite’s documented 12-step schema-change procedure and verify the second run completes without the FOREIGN KEY constraint failure while adding the nullable age column.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- haskell, sqlite
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100