Database Migration Risk Checker
Check SQL migration risk before release with engine and table context, exposing destructive changes plus lock hazards, rewrites and backfills.| Severity | Signal | Statement | Evidence | Next action | Copy |
|---|---|---|---|---|---|
| {{ row.severity }} | {{ row.signal }} | {{ row.statement }} | {{ row.evidence }} | {{ row.action }} |
| Phase | Owner action | Trigger | Evidence to retain | Copy |
|---|---|---|---|---|
| {{ row.phase }} | {{ row.action }} | {{ row.trigger }} | {{ row.evidence }} |
Valid SQL can still be a dangerous production release. A database migration runs while applications, background jobs, replicas, and older code versions may be reading or writing the same objects. The operational question is not only whether the statement parses, but what locks, scans, rewrites, logging, and compatibility changes it can cause on the real dataset.
Small test databases hide size-dependent work. An index may finish instantly in development and block writes for minutes on a busy table. A required column or constraint can scan existing rows. A type or character-set change can rebuild stored data. An unbounded update can create enough write-ahead log, undo, or replica traffic to outlast the deploy window.
- DDL
- Data definition language that changes tables, columns, indexes, constraints, and other schema objects.
- DML
- Data manipulation language that inserts, updates, or deletes rows.
- Table rewrite
- A storage rebuild that can consume time, I/O, disk space, and stronger locks.
- Backfill
- A data movement step that changes existing rows to match a new schema or application expectation.
Database engines expose different reduced-blocking paths. PostgreSQL can build many indexes concurrently, but concurrent creation cannot run inside an ordinary transaction block. MySQL and MariaDB use algorithm and lock clauses to request a specific level of concurrency. SQL Server supports online and resumable operations only for eligible cases. An engine choice therefore changes the review of the same general SQL shape.
- Destructive changes need restore evidence, dependency review, and an explicit point of no return.
- Lock-sensitive DDL needs a production-size rehearsal and a plan for blocked sessions.
- Large backfills belong in observable, restartable batches rather than a hidden deploy transaction.
- Schema and application versions must remain compatible during both roll forward and rollback.
The safest migration shape often expands before it contracts. Add a compatible object, deploy code that can use old and new forms, backfill with checkpoints, validate, switch readers and writers, and remove the old shape only after it is unused. That sequence takes more release steps but creates more places to observe, pause, and recover.
Static review is a preflight aid. It can expose recognizable SQL patterns and focus the release conversation, but it cannot predict exact runtime from text alone. Current row counts, indexes, statistics, long transactions, replica health, disk headroom, engine settings, and real application traffic still determine production behavior.
How to Use This Tool:
Keep the SQL, engine, traffic assumption, and table inventory tied to the same release change.
- Choose the production Database engine and Production traffic. Use Generic SQL only when engine-specific online DDL checks are intentionally unavailable.
- Enter a traceable Migration label and list known large or hot tables. Names may include row counts such as
orders:28000000, size suffixes such asevents:120m, or words such ashotandcritical. - Paste the migration into Migration SQL. Semicolons outside quoted text separate statements; SQL, text, and log files can also be loaded.
- Correct any parsing or size error before using the result. The SQL limit is 262,144 characters and the table-context limit is 12,000.
- Start with Risk evidence, then use Rollout plan for the release checklist and Statement pressure to identify the statements carrying the most weighted findings.
Interpreting Results:
The release stance combines the weighted score with the highest severity. A Critical finding forces Release hold even when a low-traffic multiplier leaves the score below 80. Read each evidence row before acting because two migrations with the same score can require very different fixes.
| Stance | Exact condition | Release response |
|---|---|---|
| Release hold | Any Critical finding or score >= 80 | Require redesign or an approved runbook, rehearsal, monitoring, and rollback boundary. |
| High-risk review | 55 <= score < 80 without a Critical finding | Pause for focused database and release-owner review. |
| Needs rollout plan | 30 <= score < 55 | Attach timing, lock, batching, validation, and rollback evidence. |
| Low static signal | Score < 30 without a Critical finding | No priority text pattern dominates; production-size rehearsal still applies. |
Missing table context is a common source of false confidence. If operators know a touched table is large or latency-sensitive, add its exact or base name and rerun. A low score cannot clear an unrecognized statement, dynamic SQL, trigger behavior, or engine feature that the text rules do not model.
Technical Details:
Statement review begins after line and block comments are removed. Semicolon splitting preserves single- and double-quoted strings, backtick and bracket identifiers, and PostgreSQL dollar-quoted blocks. Common table references are extracted from alteration, index, truncate, update, delete, insert, merge, reindex, and lock statements and compared with the supplied table inventory.
Formula Core:
The score is the rounded sum of finding weights multiplied by the selected traffic factor, capped at 100. Informational findings contribute zero.
S is the displayed risk score, M is the traffic multiplier, and each w is a finding weight. Critical, High, Medium, Low, and Info weigh 34, 24, 12, 5, and 0. The multipliers are 0.85 for low traffic, 1.00 for steady traffic, 1.18 for a hot write path, and 1.35 for a critical write path.
Rule Core:
| Risk family | Representative triggers | Main response |
|---|---|---|
| Destructive | DROP TABLE, TRUNCATE, dropped columns, CASCADE, and unbounded DELETE. | Prove recovery and dependencies; prefer expand-contract removal. |
| Locking | Explicit table locks, ordinary PostgreSQL index builds, SQL Server index work without ONLINE = ON, MySQL index work without a pinned online path, and unqualified table alteration. | Request reduced-blocking DDL or schedule a controlled write pause. |
| Rewrite | VACUUM FULL, CLUSTER, MySQL copy algorithm, type changes, character-set conversion, engine changes, and volatile defaults. | Model runtime, disk, log, replica, and rollback cost. |
| Validation | Required columns without defaults, SET NOT NULL, unique indexes, foreign keys, checks, unique constraints, and primary keys. | Precheck existing rows and separate validation where supported. |
| Backfill | Unbounded updates, DML on declared large or hot tables, and large-table INSERT INTO ... SELECT. | Use bounded, idempotent batches with progress and lag monitoring. |
| Release shape | More than 12 statements in one migration. | Split unrelated or high-risk work into observable deploy steps. |
Engine-specific branches are exact. PostgreSQL CREATE INDEX without CONCURRENTLY is Medium under low or steady traffic and High under hot or critical traffic. A concurrent build found beside transaction-wrapper text is High. SQL Server index creation without ONLINE = ON, and MySQL index creation without LOCK=NONE or an in-place or instant algorithm, follow the same traffic-sensitive Medium or High pattern.
A table is considered large when its parsed row count is at least 10,000,000. Words such as hot, critical, write, large, busy, or tier-0 also mark it hot. Matching uses both schema-qualified and base table names. Touching a material table adds its own Medium finding, or High under hot or critical traffic, in addition to any statement-specific signal.
Static pattern matching does not resolve stored procedures, generated SQL, migration-framework transaction choices, triggers, existing indexes, database edition limits, live locks, or query plans. The generated rollout rows are therefore planning prompts, not claims that the SQL has been executed or proven safe.
Limitations and Privacy Notes:
The SQL and loaded files are reviewed in the browser and are never executed or sent to a database. The result cannot measure actual row counts, lock waits, disk headroom, transaction-log growth, replica lag, backup quality, or application compatibility.
- Keep credentials, customer data, and sensitive schema details out of SQL shared beyond the approved release audience.
- Rehearse high-risk statements with production-size data and observe locks, duration, storage, logs, and replicas.
- Test both roll forward and rollback across the application versions that may coexist during deployment.
Worked Examples:
Unbounded update on a hot table:
With PostgreSQL, hot traffic, invoices hot, and UPDATE invoices SET state = 'pending';, the unbounded update contributes 24 points and the known hot-table finding contributes another 24. The score is round(1.18 x 48) = 57, producing High-risk review. Move the update into checkpointed batches before release.
Critical operation below the numeric hold threshold:
DROP TABLE archive_old; contributes one Critical weight of 34. Under low-traffic review the numeric score is round(0.85 x 34) = 29, but the stance is still Release hold because any Critical finding overrides the score boundary. Restore proof and dependency review are required before removal.
References:
- CREATE INDEX, PostgreSQL Documentation.
- InnoDB and Online DDL, MySQL 8.4 Reference Manual.
- Guidelines for online index operations, Microsoft Learn.
- How to run Doctrine migrations in Symfony, Simplified Guide.
- How to create a PostgreSQL database backup, Simplified Guide.