Foreign Key Enforcement on ALTER TABLE ADD COLUMN – Current State and Unresolved Behavior
0 reputation · 13 Mar 2023, 00:38 UTC
0 reputation · 13 Mar 2023, 00:38 UTC
Determine how SQLite’s foreign key enforcement behaves when a new constraint is added via ALTER TABLE … ADD COLUMN on a database where PRAGMA foreign_keys = ON is active.
ALTER TABLE child ADD COLUMN parent_id INTEGER REFERENCES parent(id);, does SQLite automatically check all existing rows in child for violations of the new foreign key?PRAGMA foreign_key_check) can be used to enforce the constraint after the schema change?ON DELETE/UPDATE CASCADE and user-defined triggers?35275 reputation · 13 Mar 2023, 04:27 UTC
No, SQLite does not automatically validate existing data against a foreign key constraint when adding a new column via ALTER TABLE … ADD COLUMN. Even if PRAGMA foreign_keys = ON is active, the enforcement mechanism triggers on INSERT, UPDATE, and DELETE operations, but it does not perform a retroactive scan of existing rows during the schema modification itself.
Because the ALTER TABLE statement does not verify existing data, you must manually trigger a validation to ensure referential integrity. The primary mechanism for this is the foreign_key_check pragma.
PRAGMA foreign_key_check; to scan the entire database for violations.PRAGMA foreign_key_check('child_table'); to limit the scan to the specific table modified.If this pragma returns rows, those rows represent foreign key violations that were ignored during the ADD COLUMN operation.
SQLite evaluates foreign key constraints in the order they were defined in the CREATE TABLE statement. When a transaction involves multiple constraints on the same parent row:
ON DELETE CASCADE or ON UPDATE CASCADE actions are executed before the final commit of the transaction. If a cascade triggers further changes in other tables, those subsequent changes trigger their own foreign key constraints recursively.BEFORE or AFTER). However, foreign key constraints are checked after BEFORE triggers and before AFTER triggers for the operation that triggered them.To safely add a foreign key to an existing table, follow this sequence:
-- 1. Add the column
ALTER TABLE child ADD COLUMN parent_id INTEGER REFERENCES parent(id);
-- 2. Identify violations
PRAGMA foreign_key_check('child');
-- 3. Remediate orphaned rows (Example: set to NULL or valid ID)
UPDATE child SET parent_id = [valid_id] WHERE parent_id IS NOT NULL AND ...;
Diagnostic Note: To provide a more specific recommendation on cascade ordering, please specify if you are using deferred foreign keys (DEFERRABLE INITIALLY DEFERRED), as this changes the validation timing from the statement level to the transaction commit level.
Use comments to ask for clarification. Post a solution as an answer.
6,030 reputation · 13 Mar 2023, 02:58 UTC
The PRAGMA foreign_key_check; statement returns a result set with five columns: id (the rowid of the offending row), table (the child table name), rowid (same as id for clarity), parent (the key value in the parent table that is missing or mismatched), and fkid (the internal foreign key identifier). Running this pragma after an ALTER TABLE … ADD COLUMN will list any existing rows that violate the newly added constraint, even if PRAGMA foreign_keys = ON was active during the schema change.
foreign_key_check runs outside of any transaction and sees only committed data. If you need to check rows that are part of an uncommitted transaction, you must either commit first or run the check in an exclusive connection; otherwise the pragma may miss recent changes.
When a statement (e.g., DELETE or UPDATE) triggers several foreign key constraints that reference the same parent row, SQLite processes those constraints in the exact order they appear in the table’s definition (the order of the REFERENCES clauses in CREATE TABLE). Each constraint’s action (RESTRICT, SET NULL, SET DEFAULT, or CASCADE) is applied immediately, so a later constraint sees the state resulting from any earlier cascades. User‑defined BEFORE triggers fire first, then all foreign key actions in definition order, and finally AFTER triggers.