transform() now works for tables referenced by views

Bracket the ALTER TABLE ... RENAME TO statements emitted by
transform_sql() with PRAGMA legacy_alter_table=ON/OFF. Since SQLite
3.25 the rename would otherwise rewrite references in every view
definition, which failed with "no such table" when a view referenced
the just-dropped table, and with keep_table= silently repointed
dependent views at the frozen backup table.

View definitions are now left byte-for-byte unchanged by a transform.

Closes #831

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Simon Willison 2026-08-06 22:08:37 -07:00
commit 8f264e8d2a
5 changed files with 179 additions and 2 deletions

View file

@ -2824,17 +2824,25 @@ class Table(Queryable):
new_cols=", ".join(quote_identifier(col) for col in new_cols),
)
sqls.append(copy_sql)
# Drop (or keep) the old table
# Drop (or keep) the old table, then rename the new one into place.
# Since SQLite 3.25 ALTER TABLE ... RENAME TO rewrites references to
# the renamed table in every view definition, which fails if a view
# references the table that was just dropped - and with keep_table=
# would silently repoint views at the backup table. These renames are
# an implementation detail of transform(), so use legacy_alter_table
# to leave view definitions untouched.
if keep_table:
sqls.append("PRAGMA legacy_alter_table=ON;")
sqls.append(
f"ALTER TABLE {quote_identifier(self.name)} RENAME TO {quote_identifier(keep_table)};"
)
else:
sqls.append(f"DROP TABLE {quote_identifier(self.name)};")
# Rename the new one
sqls.append("PRAGMA legacy_alter_table=ON;")
sqls.append(
f"ALTER TABLE {quote_identifier(new_table_name)} RENAME TO {quote_identifier(self.name)};"
)
sqls.append("PRAGMA legacy_alter_table=OFF;")
# Re-add existing indexes
for index in self.indexes:
if index.origin != "pk":