Thanks Simon. I believe you're correct in that I can recreate all dependant tables. I had attempted this trick earlier, but was doing so in the context of immediate mode constraints, and that made the re-insertion of data and dropping of tables exceptionally complicated in some cases (such as circular references between tables).
So to summarize, the strategy for modifying a table with foreign constraints enabled is to: 1. Find all direct and indirect dependants of the table being modified. 2. Create temporary tables for all of these. 3. Copy the data from the main tables into these temporary tables. 3a. If no circular dependencies, do a topological sort on the tables to get the correct insertion order. 3b. If circular dependencies, either use deferred constraints or come up with a sophisticated algorithm to reinsert the original data (needs to take into account not null columns with circular references present). 4. Drop all the original tables, again with similar steps to 3a and 3b. 5. Rename all the temporary tables to their original names. I will try this algorithm today and report back if I fail. Since I don't have time to imagine an algorithm to delete/insert/update rows in an order that doesn't break constraints, I've ended up using deferred constraints (undesirable in my case) just to support table modification. It would be great if the kind of complexity above was somehow encapsulated in the database engine, instead of having users work around it with non-trivial steps. As a side note, the above algorithm isn't likely to be particularly performant on databases with significant data present. In the general case of multiple individual modifications to tables (such as in the context of a database change manangement framework), the amount of work being done by the DB to modify the table is quite time consuming. Thanks for your help with this. Patrick Earl On Mon, May 10, 2010 at 5:18 AM, Simon Slavin <slav...@bigfraud.org> wrote: > > On 10 May 2010, at 7:34am, Patrick Earl wrote: > >> PRAGMA foreign_keys = ON; >> >> CREATE TABLE ParkingLot (Id int NOT NULL PRIMARY KEY); >> CREATE TABLE Car (Id int NOT NULL PRIMARY KEY, ParkingLotId int >> NOT NULL REFERENCES ParkingLot (Id) DEFERRABLE INITIALLY DEFERRED); >> INSERT INTO ParkingLot (Id) VALUES (1); >> INSERT INTO Car (Id, ParkingLotId) VALUES (2, 1); >> >> BEGIN TRANSACTION; >> CREATE TABLE ParkingLotTemp (Id int NOT NULL PRIMARY KEY); >> INSERT INTO ParkingLotTemp (Id) SELECT Id FROM ParkingLot; >> DROP TABLE ParkingLot; >> ALTER TABLE ParkingLotTemp RENAME TO ParkingLot; >> COMMIT TRANSACTION; >> >> Even though at the end of the transaction you can select and find the >> appropriate rows in the car and parking lot tables, committing the >> transaction causes a foreign constraint violation. > > I'm not sure how you expected this to work. You declare ParkingLot as a > parent table of Car, but then you DROP TABLE ParkingLot, leaving Car an > orphan. The only legitimate way to do this is to DROP TABLE Car first, or to > remove the foreign key constraint from it (which SQLite doesn't let you do). > The fact that you rename another table 'ParkingLot' later has nothing to do > with your constraint: the constraint is linked to the table, not to the > table's name. > > If you're going to make a temporary copy of ParkingLot, then make a temporary > copy of Car too: > > CREATE TABLE CarTemp (Id int NOT NULL PRIMARY KEY, ParkingLotId int > NOT NULL REFERENCES ParkingLotTemp (Id) DEFERRABLE INITIALLY DEFERRED); > INSERT INTO CarTemp (Id) SELECT Id FROM Car; > > Then you can drop both original tables and rename both 'temp' tables. > However, I don't see why you're doing any of this rather than just adding and > removing rows from each table as you need. > > Simon. _______________________________________________ sqlite-users mailing list sqlite-users@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users