From 0ad3f10efb458584a9235a9b429eedeb9d92844d Mon Sep 17 00:00:00 2001 From: Samuel Merritt Date: Fri, 26 Apr 2024 12:14:48 -0700 Subject: [PATCH] sqlite: allow PRAGMA legacy_alter_table There's certain kinds of schema changes that are really painful to do in SQLite; this makes things a little easier. For example, let's say you want to change the "users.email" column's collation to be case-insensitive (COLLATE NOCASE). You can't do that with ALTER TABLE, so what you have to do instead is a three-step dance*: 1: rename users to users_old 2: create a new table users with the same schema as users_old, except for the collation, and copy the data over 3: drop users_old The problem comes in with foreign keys. If you've got a "favorites.user_id" column that's a foreign key to users.id, then doing the above dance will delete all your users' favorites (assuming ON DELETE CASCADE, otherwise it'll error out). 1: after the rename, all the foreign keys now reference users_old.id 2: no effect 3: dropping users_old cascades and deletes all the rows from favorites This is where legacy_alter_table is helpful. If you start off by setting this, then you can do the dance without losing other rows. 0: PRAGMA legacy_alter_table=on 1: rename users to users_old. This now leaves the foreign-key relationship in a dangling state; favorites.user_id references users.id, but there is no users table. 2: create the new table and copy the data over. The foreign keys are now pointing to the new table. 3: drop users_old; since there are no foreign keys referencing it, nothing gets deleted It's a pretty confusing behavior, which is why it's off by default, but SQLite's ALTER TABLE isn't flexible enough for all situations so sometimes users need this kind of workaround. * yes, you could specify COLLATE NOCASE in every query that touches the column instead of relying on the default, but not everybody is willing to do that --- src/workerd/util/sqlite.c++ | 1 + 1 file changed, 1 insertion(+) diff --git a/src/workerd/util/sqlite.c++ b/src/workerd/util/sqlite.c++ index 2ecbc2c6df2..f8248afe32e 100644 --- a/src/workerd/util/sqlite.c++ +++ b/src/workerd/util/sqlite.c++ @@ -297,6 +297,7 @@ static constexpr PragmaInfo ALLOWED_PRAGMAS[] = { { "foreign_keys"_kj, PragmaSignature::BOOLEAN }, { "defer_foreign_keys"_kj, PragmaSignature::BOOLEAN }, { "ignore_check_constraints"_kj, PragmaSignature::BOOLEAN }, + { "legacy_alter_table"_kj, PragmaSignature::BOOLEAN }, { "recursive_triggers"_kj, PragmaSignature::BOOLEAN }, { "reverse_unordered_selects"_kj, PragmaSignature::BOOLEAN },