All rules Rule SL023 · DS
SQLite · beta
critical
DS · Destructive changes
free in the CLI, --engine=sqlite

Can I drop a table that a view or trigger in the same migration depends on?

Dropping what a view or trigger in this migration depends on

Critical: SQLite refuses the statement, so the migration fails here.

What happens

This migration creates a view or trigger and then drops the table or column it depends on. SQLite does not stop it: the view fails with "no such column" the first time it is queried, and a trigger that names the column makes every later write to its table fail. Older releases refuse the DROP COLUMN instead. Either way the failure is guaranteed by the file itself, not by what production holds.

Why it is dangerous on a populated table

SQLite holds one write lock for the whole database, so anything that rebuilds a table blocks every writer for as long as the copy takes, and that time grows with the table.

Fires on

CREATE VIEW open_orders AS SELECT id, status FROM orders WHERE status = 'open';
ALTER TABLE orders DROP COLUMN status;

The safe pattern

Drop or redefine the view or trigger first, then drop the column or table, then recreate the view without the dependency.

DROP VIEW IF EXISTS open_orders;
ALTER TABLE orders DROP COLUMN status;
CREATE VIEW open_orders AS SELECT id FROM orders;

Fixtures

The rule ships with these files and the test suite runs them on every change: the first set must fire, the second must stay silent.

Fires (3)

drop column under trigger
CREATE TRIGGER stamp AFTER UPDATE ON orders BEGIN UPDATE orders SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; END;
ALTER TABLE orders DROP COLUMN updated_at;
drop column under view
CREATE VIEW open_orders AS SELECT id, status FROM orders WHERE status = 'open';
ALTER TABLE orders DROP COLUMN status;
drop table under view
CREATE VIEW order_totals AS SELECT order_id, SUM(qty) AS qty FROM lines GROUP BY order_id;
DROP TABLE lines;

Stays silent (2)

unrelated column
CREATE VIEW open_orders AS SELECT id, status FROM orders WHERE status = 'open';
ALTER TABLE orders DROP COLUMN legacy_flag;
view dropped first
CREATE VIEW open_orders AS SELECT id, status FROM orders WHERE status = 'open';
DROP VIEW open_orders;
ALTER TABLE orders DROP COLUMN status;
CREATE VIEW open_orders AS SELECT id FROM orders;

How to check locally

Catch this before it ships

SQLite support is in beta: this rule runs locally in the free CLI, static only, and not in the hosted service yet. No install, nothing leaves your machine:

npx bolvrk check migration.sql --engine=sqlite