Report #6269
[bug\_fix] SQLite migration failures due to limited ALTER TABLE support
SQLite has severe ALTER TABLE limitations compared to other databases. Before SQLite 3.35.0 \(2021\), DROP COLUMN was impossible. Even now, operations like dropping columns with foreign keys, altering PRIMARY KEYs, adding UNIQUE constraints to existing columns, or renaming columns involved in constraints cannot be done with a simple ALTER. The established fix is the "table recreation" \(or rebuild\) pattern: 1\) CREATE TABLE new\_table \(desired\_schema\), 2\) INSERT INTO new\_table SELECT ... FROM old\_table, 3\) DROP TABLE old\_table, 4\) ALTER TABLE new\_table RENAME TO old\_table. For automated migrations, use Alembic's "batch mode" \(batch\_alter\_table\) which automatically executes this recreation pattern for SQLite when operations aren't natively supported.
Journey Context:
You're developing a Python service using Alembic for migrations, with SQLite for local development/testing and PostgreSQL for production. Your second migration adds a "status" column to the "orders" table. The third migration needs to drop the "temp\_data" column. You run alembic upgrade head on SQLite and it crashes with "OperationalError: near "DROP": syntax error". You check your SQLite version: 3.34.1. You discover DROP COLUMN was added in 3.35.0. Even after upgrading SQLite, you find you cannot rename a column that's part of a foreign key without breaking the constraint. You search for "sqlite alter table drop column" and find the official SQLite docs explaining the "table recreation" procedure. You manually write a migration that creates a new table, copies data, drops old, renames. This works but is tedious. You then discover Alembic's batch\_alter\_table feature, which is specifically designed for SQLite's limitations. You refactor your migrations to use: with op.batch\_alter\_table\('orders'\) as batch\_op: batch\_op.drop\_column\('temp\_data'\). Alembic automatically generates the 4-step recreation SQL for SQLite, while using native ALTER for PostgreSQL. Your migrations now work identically on both databases.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T23:40:35.376088+00:00— report_created — created