MY.L1.DROP_TABLE — Dropping a table on MySQL commits itself
- Category: safety
- Level: 1
- Confidence: deterministic
- Downtime class:
online - Stability: stable
- Suites: lint
- Applies to: MySQL 8.4
DROP TABLE removes the table and every row in it. There is no WHERE clause to undo, and on
MySQL there is no transaction either.
The difference from PostgreSQL is the whole advice
MySQL's DDL is not transactional. DROP TABLE causes an implicit commit — Laravel's MySQL
grammar reports supportsSchemaTransactions() === false, and the server commits regardless of what
the migrator would have wrapped around it.
So a migration that drops a table and then fails on its next statement does not roll the drop back.
The table is gone, the migration is half-applied, and down() is the only path back — one that can
re-create the table but not its rows.
A reader given PostgreSQL's wording here would believe their transaction protects them. It does not. That is why this rule exists separately rather than as the PostgreSQL rule with a footnote.
Why this rule appeared late
It was not a decision. The destructive level-1 group was built driver by driver and MySQL never got
its turn — PostgreSQL carried four rules at this level and MySQL carried one. Until this rule
landed, Schema::drop() in a MySQL migration produced no finding at all: a green run over the
most common irreversible mistake a migration can make.
The downtime class is online, and that is not a contradiction
The statement unlinks rather than scanning or rewriting. Its cost is not deploy time, it is deploy permanence — the class prices the first, level 1 prices the second.
One caveat, stated rather than hidden: with InnoDB file-per-table, dropping a very large table can
stall the server while the filesystem removes its .ibd. MySQL 8.0 and later release the tablespace
in the background, which is why this is a caveat and not the class.
Not transferable to MariaDB
This rule reasons about MySQL 8.4 semantics — specifically that DDL causes an implicit commit there. MariaDB shares Laravel's driver key and the wire protocol, not those semantics, so the same sentence applied to it would be confident, specific and about another product. SQLens refuses that connection rather than guessing.
Flagged
Schema::drop('legacy_events');
Preferred
// A rename keeps a rollback path while the code that used the table is retired:
Schema::rename('legacy_events', 'legacy_events_deprecated');
Once no shipped version reads it, the drop is a decision somebody makes on purpose.
Saying yes on purpose
#[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. The finding is still produced and shown; the opt-in changes the presentation, not the fact.
What the rule does not claim
It says nothing about whether the data is still needed — 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 on this engine it cannot even be undone by the transaction around it.
Sources
- MySQL 8.4 — statements causing an implicit commit
— DDL statements,
DROP TABLEamong them, commit the current transaction before and after running
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.