Skip to content
Commit Detail

Commit 0ad3f10

Author
Samuel Merritt <smerritt@cloudflare.com> 2024-04-26 12:14:48 -0700
Parents
4e282ed
Tree
afb768f
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

Files changed

1 file changed~1 modified