Skip to main content

PG.L1.DROP_TABLE — Dropping a table is a deploy you cannot undo

  • Category: safety
  • Level: 1
  • Confidence: deterministic
  • Downtime class: online
  • Stability: stable
  • Suites: lint
  • Applies to: PostgreSQL 18

DROP TABLE removes the table and every row in it. There is no WHERE clause to undo, no transaction to roll back once the deploy is past, and — with CASCADE — no boundary on the loss: views, foreign keys and anything else built on the table go with it.

The statement itself is fast. That is exactly why it is easy to ship.

Why level 1 and not a higher one

Level 1 is the destructive band, and it is deliberately the lowest one above zero: a project that turns the linter on at all should see these. The rule does not judge whether the table is still needed — it cannot know that — it judges that the change is irreversible, and asks for that to be stated rather than assumed.

The downtime class is online, and that is not a contradiction

DROP TABLE acquires an ACCESS EXCLUSIVE lock, but only for as long as it takes to unlink the relation — it does not scan or rewrite anything. The cost of this operation is not deploy time, it is deploy permanence. The class prices the first; level 1 prices the second.

Flagged

Schema::drop('legacy_events');

Preferred

// Stop reading the table in a shipped release first, then drop it later behind
// #[SqlensAllowDestructive]. A rename keeps a rollback path in the meantime:
Schema::rename('legacy_events', 'legacy_events_deprecated');

A rename is reversible while the code that used the table is being retired. Once no shipped version reads it, the drop is a decision somebody makes on purpose.

Saying yes on purpose

A drop that is genuinely intended is not a finding to silence — it is a statement to make:

#[SqlensAllowDestructive('the events table was replaced by the audit log in 2.4')]
final class DropLegacyEventsTable extends Migration { /* … */ }

The annotation carries a reason, which is the difference between a suppression and a decision. A bare ignore would hide the next drop too.

What the rule does not claim

It says nothing about whether the data is still needed anywhere, because a linter reading a migration cannot see your reporting jobs, your backups or your obligations. It says the change cannot be undone by the deploy that makes it — and that this deserves a sentence somebody wrote.

Sources

The fix material this rule carries

A finding from this rule carries machine-readable fix material, using this sequence:

  • deploy_window_drop — Drop across two deploy windows so a rollback in between still finds what it needs.

The payload is material for you or an agent to apply. SQLens writes no migration and runs no DDL. See the remediation payload for every field, the placeholder semantics, and the version rules a consumer has to follow.