@@ -95,9 +95,25 @@ The `transform()` method (db.py lines 1853-1917) implements the following safety
9595| Rename non-referenced column | Works, FKs intact | Works, FKs intact |
9696| Rename referenced column | FAILS (rollback) | Works, FKs BROKEN! |
9797| Drop referenced column | FAILS (rollback) | Works, FKs BROKEN! |
98+ | Change PK away from referenced col | FAILS (rollback) | Works, FKs BROKEN! |
9899| Self-referential FK | Works, FKs intact | Works, FKs intact |
99100| Multiple tables with FKs | Works, FKs intact | Works, FKs intact |
100101
102+ ## Cases That Break Incoming FKs
103+
104+ There are ** three** scenarios that break incoming FK constraints:
105+
106+ 1 . ** Rename referenced column** : The FK references ` authors(id) ` but the column is
107+ renamed to ` author_pk ` - the FK now references a non-existent column.
108+
109+ 2 . ** Drop referenced column** : The FK references ` authors(id) ` but the column is
110+ dropped entirely - the FK now references a non-existent column.
111+
112+ 3 . ** Remove PK/UNIQUE from referenced column** : SQLite requires that FK targets be
113+ either PRIMARY KEY or UNIQUE. If you change ` id ` from ` INTEGER PRIMARY KEY ` to
114+ just ` INTEGER ` , the FK becomes invalid ("foreign key mismatch") even though the
115+ column still exists.
116+
101117## Known Issue: Leftover Temp Table on Failure
102118
103119When ` transform() ` fails (e.g., due to FK check failure), there may be a leftover
@@ -120,6 +136,62 @@ a data integrity problem.
1201363 . ** Use ` foreign_key_check ` ** after bulk operations with FK enforcement off to
121137 verify data integrity
122138
139+ ## Automatic Detection and Fixing
140+
141+ ### Detection (Easy)
142+
143+ To detect if a transform will break incoming FKs:
144+
145+ ``` python
146+ def get_incoming_fks (db , table_name ):
147+ """ Find all FKs from other tables that reference this table."""
148+ incoming = []
149+ for other_table in db.table_names():
150+ if other_table == table_name:
151+ continue
152+ for fk in db[other_table].foreign_keys:
153+ if fk.other_table == table_name:
154+ incoming.append({
155+ " from_table" : fk.table,
156+ " from_column" : fk.column,
157+ " to_column" : fk.other_column,
158+ })
159+ return incoming
160+ ```
161+
162+ Then check if any ` to_column ` values are in the ` rename ` or ` drop ` sets, or if
163+ they're losing their PK/UNIQUE status.
164+
165+ ### Automatic Fixing for Column Renames (Moderate Complexity)
166+
167+ For column renames, it's possible to automatically update the referencing tables:
168+
169+ 1 . Before transforming table A, find all tables with FKs to A
170+ 2 . For each table B with ` FK(col -> A.old_col) ` :
171+ - Transform B with ` foreign_keys= ` parameter to update to ` FK(col -> A.new_col) `
172+ 3 . Then transform A with the rename
173+
174+ ### Challenges
175+
176+ - ** Circular references** : A -> B -> A requires careful ordering
177+ - ** Chain reactions** : Transforming B might affect table C
178+ - ** Transaction safety** : All transforms should succeed or all fail
179+
180+ ### Proposed API Enhancement
181+
182+ ``` python
183+ # Option 1: Auto-update with a flag
184+ db[" authors" ].transform(
185+ rename = {" id" : " author_pk" },
186+ update_incoming_fks = True # Automatically update FKs in other tables
187+ )
188+
189+ # Option 2: Better error message
190+ # "Cannot rename 'id': books.author_id references this column.
191+ # First transform 'books' to update its foreign key, or use
192+ # update_incoming_fks=True to do this automatically."
193+ ```
194+
123195## Code References
124196
125197- ` transform() ` method: sqlite_utils/db.py:1853-1917
0 commit comments