PRAGMA foreign_keys cannot be changed inside a transaction, and the
defer_foreign_keys fallback only defers violation checks, not ON DELETE
actions - so calling table.transform() inside db.atomic() with
PRAGMA foreign_keys enabled would silently fire ON DELETE CASCADE /
SET NULL / SET DEFAULT actions on referencing tables when the old table
is dropped.
transform() now raises TransactionError in that situation, naming the
offending foreign keys and suggesting either running it outside the
transaction or turning the pragma off before opening it. Tables with
only non-destructive inbound foreign keys (NO ACTION, RESTRICT) can
still be transformed inside a transaction via defer_foreign_keys.
Closes#794
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014StVTWQJpFhfZJK2CYVBwv
> Add a test that covers what happens if you run transform against a table with ON CASCADE DELETE for one of its foreign keys - those records should not be deleted during the transform even though the table is dropped as part of that procedure
* Fix transform() corrupting TRUE/FALSE/NULL column defaults
quote_default_value() passed keyword-literal defaults (TRUE, FALSE, NULL)
through self.quote(), wrapping them in quotes. So transform() rebuilt a
column declared "INTEGER DEFAULT TRUE" as "INTEGER DEFAULT 'TRUE'", and a
later default insert stored the text 'TRUE' instead of the integer 1
(likewise 'FALSE' for 0 and 'NULL' for null) - silent value corruption.
Return these keyword literals unquoted, as already done for the
CURRENT_TIME/DATE/TIMESTAMP literals.
PR #764
* Recreate indexes when calling transform when possible and raise an error when they cannot be retained automatically
* Docs for sqlite_utils.db.TransformError
Co-authored-by: Simon Willison <swillison@gmail.com>
Closes#577
This should solve all sorts of problems seen by users of platforms that throw errors on writable_schema.
Also added `add_foreign_keys=` and `foreign_keys=` parameters to `table.transform()`.