DBLift Plan Report
review-snapshot.json
Reports/ review-snapshot.json/ 20261113T215827Z
Snapshot
review-snapshot.json
Command
plan
Fail on
error
Generated
20261113T215827Z
Target version
1
Already applied
1 · rank 1
Will run
1
1 versioned · 0 repeatable
Errors
1
Warnings
2
Info
2
SQL rules
PASS
1
Replayed
0
Deployment Delta
1 item
VersionScriptDescriptionTypeChecksum
3 V3__widen_integer.sql widen_integer Versioned -280238009
SQL Validation
PASS
scope
pending
files_checked
1
errors
0
warnings
2

changes a column's type: the engine may rewrite the table (row count unknown — severity not adjusted for table size)

No SQL validation findings.

Impact Analysis
V3__widen_integer.sql (snapshot captured 2026-11-13T21:58:25.888811+00:00)
RiskLockDurationObject typeObjectEst. rowsEvidence
errorACCESS EXCLUSIVEtable_scanTABLEwiden_demo.readings1,000verified
Alternative (advisory)
  1. EXPAND: add the target-type column and mirror writes with a trigger
  2. BACKFILL: cast existing rows into the new column
  3. CONTRACT: cut over: drop the trigger and old column, rename the new one
Full SQL (advisory; does not replace the author's script)
-- Schema Synchronization Script (zero-downtime safe form)
-- =========================================================

-- Dangerous operations have been rewritten into lock-friendly, zero-downtime equivalents.

-- Generated: 2026-11-13T16:58:27.657574

-- ===== PHASE: EXPAND — add the target-type column and mirror writes with a trigger =====
--   Note: The new column is nullable during the migration; the trigger casts every insert/update of the old column into it.
--   Note: This makes the change lock-friendly, NOT loss-proof — confirm the cast preserves every existing value before the cutover.

-- ALTER COLUMN: widen_demo.readings.id
ALTER TABLE widen_demo.readings ADD COLUMN "id_new" bigint;


-- CREATE FUNCTION: widen_demo.readings.id
CREATE OR REPLACE FUNCTION "readings_id_sync"() RETURNS trigger AS $$
BEGIN
  NEW."id_new" := CAST(NEW."id" AS bigint);
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;


-- CREATE TRIGGER: widen_demo.readings.id
CREATE TRIGGER "readings_id_sync_trg" BEFORE INSERT OR UPDATE ON widen_demo.readings FOR EACH ROW EXECUTE FUNCTION "readings_id_sync"();


-- ===== PHASE: BACKFILL — cast existing rows into the new column =====
--   Note: Converts every row in this one statement: the CONTRACT below drops the old column in this same file, so a bounded pass would leave rows unconverted. On a very large table, run this UPDATE in batches of your own before deploying the CONTRACT.

-- UPDATE DATA: widen_demo.readings.id
UPDATE widen_demo.readings SET "id_new" = CAST("id" AS bigint) WHERE "id_new" IS DISTINCT FROM CAST("id" AS bigint);



-- ============================================================
-- DEPLOY THE STATEMENTS BELOW IN A LATER RELEASE
-- Run them once the application no longer depends on what they
-- remove or change. Where the backfill above is a bounded batch,
-- run it until it reports 0 rows first. On a very large table,
-- split this section into a separate migration.
-- ============================================================

-- ===== PHASE: CONTRACT — cut over: drop the trigger and old column, rename the new one =====
--   Note: Deploy this only after the backfill is complete AND the application no longer reads or writes the old column's type.
--   Note: If the original column was NOT NULL, add an ALTER COLUMN … SET NOT NULL on the new column before the rename.

-- DROP TRIGGER: widen_demo.readings.id
DROP TRIGGER "readings_id_sync_trg" ON widen_demo.readings;


-- DROP FUNCTION: widen_demo.readings.id
DROP FUNCTION "readings_id_sync"();


-- ALTER TABLE: widen_demo.readings.id
ALTER TABLE widen_demo.readings DROP COLUMN "id";


-- ALTER COLUMN: widen_demo.readings.id
ALTER TABLE widen_demo.readings RENAME COLUMN "id_new" TO "id";
Findings
Errors
1

Issues that block a safe release until they are resolved.

IssueLocationMessage
Alter type change rewrite
impact.alter.type-change-rewrite
V3__widen_integer.sql
via Impact Analysis
ALTER widen_demo.readings: ACCESS EXCLUSIVE lock (column type change rewrites the table)
Warnings
2

Review these before promoting the plan.

IssueLocationMessage
alter-column-type
alter-column-type
V3__widen_integer.sql
via DBLift
changes a column's type: the engine may rewrite the table (row count unknown — severity not adjusted for table size)
pg-missing-lock-timeout
pg-missing-lock-timeout
V3__widen_integer.sql
via DBLift
takes a table lock with no lock_timeout set earlier in the script, so queries on the table can queue behind it; start with SET LOCAL lock_timeout = '5s'
Info
2

Informational notes that do not fail the run.

IssueLocationMessage
Pending
plan.pending
V3__widen_integer.sql
via Migration Planner
Migration V3__widen_integer.sql is pending
Safe rewrite
plan.safe_rewrite
V3__widen_integer.sql
via Migration Planner
Dangerous DDL can be made zero-downtime (3 step(s)): EXPAND: add the target-type column and mirror writes with a trigger; BACKFILL: cast existing rows into the new column; CONTRACT: cut over: drop the trigger and old column, rename the new one
Report Bundle
html approval-integer-html.html
Raw Metadata
Show raw metadata
success
False
snapshot_model
review-snapshot.json
target_last_version
1
target_installed_rank
1
already_applied_count
1
pending[0].script
V3__widen_integer.sql
pending[0].version
3
pending[0].description
widen_integer
pending[0].type
SQL
pending[0].checksum
-280238009
pending[0].path
migrations/V3__widen_integer.sql
sql_validation.enabled
True
sql_validation.scope
pending
sql_validation.status
PASS
sql_validation.files_checked
1
sql_validation.errors
0
sql_validation.warnings
2
sql_validation.messages[0]
changes a column's type: the engine may rewrite the table (row count unknown — severity not adjusted for table size)
sql_validation.messages[1]
takes a table lock with no lock_timeout set earlier in the script, so queries on the table can queue behind it; start with SET LOCAL lock_timeout = '5s'
sql_validation.findings[0].severity
warning
sql_validation.findings[0].code
alter-column-type
sql_validation.findings[0].message
changes a column's type: the engine may rewrite the table (row count unknown — severity not adjusted for table size)
sql_validation.findings[0].file
migrations/V3__widen_integer.sql
sql_validation.findings[1].severity
warning
sql_validation.findings[1].code
pg-missing-lock-timeout
sql_validation.findings[1].message
takes a table lock with no lock_timeout set earlier in the script, so queries on the table can queue behind it; start with SET LOCAL lock_timeout = '5s'
sql_validation.findings[1].file
migrations/V3__widen_integer.sql
error
impact_reports[0].script
V3__widen_integer.sql
impact_reports[0].path
migrations/V3__widen_integer.sql
impact_reports[0].stats_source
snapshot
impact_reports[0].snapshot_captured_at
2026-11-13T21:58:25.888811+00:00
impact_reports[0].note
impact_reports[0].impact.summary.error
1
impact_reports[0].impact.summary.warning
0
impact_reports[0].impact.summary.info
0
impact_reports[0].impact.impacts[0].statement_index
0
impact_reports[0].impact.impacts[0].statement_type
ALTER
impact_reports[0].impact.impacts[0].object_name
widen_demo.readings
impact_reports[0].impact.impacts[0].object_type
TABLE
impact_reports[0].impact.impacts[0].lock_class
read_write_blocking
impact_reports[0].impact.impacts[0].lock_label
ACCESS EXCLUSIVE
impact_reports[0].impact.impacts[0].duration
table_scan
impact_reports[0].impact.impacts[0].risk
error
impact_reports[0].impact.impacts[0].code
impact.alter.type-change-rewrite
impact_reports[0].impact.impacts[0].notes[0]
column type change rewrites the table
impact_reports[0].impact.impacts[0].table_name
widen_demo.readings
impact_reports[0].impact.impacts[0].estimated_rows
1000
impact_reports[0].impact.impacts[0].refined
True
impact_reports[0].impact.impacts[0].stats_source
snapshot
impact_reports[0].impact.impacts[0].evidence_status
verified
impact_reports[0].safe_rewrite_steps[0]
EXPAND: add the target-type column and mirror writes with a trigger
impact_reports[0].safe_rewrite_steps[1]
BACKFILL: cast existing rows into the new column
impact_reports[0].safe_rewrite_steps[2]
CONTRACT: cut over: drop the trigger and old column, rename the new one
impact_reports[0].safe_rewrite_sql
-- Schema Synchronization Script (zero-downtime safe form) -- ========================================================= -- Dangerous operations have been rewritten into lock-friendly, zero-downtime equivalents. -- Generated: 2026-11-13T16:58:27.657574 -- ===== PHASE: EXPAND — add the target-type column and mirror writes with a trigger ===== -- Note: The new column is nullable during the migration; the trigger casts every insert/update of the old column into it. -- Note: This makes the change lock-friendly, NOT loss-proof — confirm the cast preserves every existing value before the cutover. -- ALTER COLUMN: widen_demo.readings.id ALTER TABLE widen_demo.readings ADD COLUMN "id_new" bigint; -- CREATE FUNCTION: widen_demo.readings.id CREATE OR REPLACE FUNCTION "readings_id_sync"() RETURNS trigger AS $$ BEGIN NEW."id_new" := CAST(NEW."id" AS bigint); RETURN NEW; END; $$ LANGUAGE plpgsql; -- CREATE TRIGGER: widen_demo.readings.id CREATE TRIGGER "readings_id_sync_trg" BEFORE INSERT OR UPDATE ON widen_demo.readings FOR EACH ROW EXECUTE FUNCTION "readings_id_sync"(); -- ===== PHASE: BACKFILL — cast existing rows into the new column ===== -- Note: Converts every row in this one statement: the CONTRACT below drops the old column in this same file, so a bounded pass would leave rows unconverted. On a very large table, run this UPDATE in batches of your own before deploying the CONTRACT. -- UPDATE DATA: widen_demo.readings.id UPDATE widen_demo.readings SET "id_new" = CAST("id" AS bigint) WHERE "id_new" IS DISTINCT FROM CAST("id" AS bigint); -- ============================================================ -- DEPLOY THE STATEMENTS BELOW IN A LATER RELEASE -- Run them once the application no longer depends on what they -- remove or change. Where the backfill above is a bounded batch, -- run it until it reports 0 rows first. On a very large table, -- split this section into a separate migration. -- ============================================================ -- ===== PHASE: CONTRACT — cut over: drop the trigger and old column, rename the new one ===== -- Note: Deploy this only after the backfill is complete AND the application no longer reads or writes the old column's type. -- Note: If the original column was NOT NULL, add an ALTER COLUMN … SET NOT NULL on the new column before the rename. -- DROP TRIGGER: widen_demo.readings.id DROP TRIGGER "readings_id_sync_trg" ON widen_demo.readings; -- DROP FUNCTION: widen_demo.readings.id DROP FUNCTION "readings_id_sync"(); -- ALTER TABLE: widen_demo.readings.id ALTER TABLE widen_demo.readings DROP COLUMN "id"; -- ALTER COLUMN: widen_demo.readings.id ALTER TABLE widen_demo.readings RENAME COLUMN "id_new" TO "id";
filters.versions
3