Migrations and Versioning
A migration is a file. Its name says what kind it is and when it runs; its contents decide its checksum. Everything DBLift knows about your schema history follows from those two facts.
Kinds of script
The first character classifies the file, and it must be followed by a digit for the versioned kinds. That strictness is deliberate: it stops validate.sql or update.sql being mistaken for a migration.
| Prefix | Example | Runs |
|---|---|---|
V{version}__ | V1_0_0__create_users.sql | Once, in version order. The backbone of your history. |
R__ | R__customer_dashboard_view.sql | Every time its checksum changes. No version. Views, functions, grants. |
U{version}__ | U1_0_0__drop_users.sql | Only on undo, reversing the V file of the same version. |
D{timestamp}__ | D20260618143022__fix_orphaned_orders.sql | Data corrections — audited DML, ordered by timestamp rather than version. Addressed by id: dblift data undo D20260618143022. See Data Corrections. |
R1__setup.sql — a versioned repeatable — is rejected. Repeatables carry no version.
Checksums
Every migration's contents hash to a CRC32 computed line by line, with line separators excluded — the same algorithm Flyway uses, and a signed 32-bit integer like a Java int, so a negative value is normal. Excluding line separators means checking the same file out on Windows and Linux does not change its checksum.
The checksum recorded at apply time is what makes an edited migration detectable. Change an applied file and validate reports drift; change a repeatable and the next migrate re-runs it. Same mechanism, opposite intent.
Order and selection
Versioned migrations apply in version order. Which ones are considered can be narrowed at the command line — by version, by tag, or by directory.
V1_0_0__create_users[core].sqlV1_1_0__users_created_at[core].sqlV1_2_0__add_note[auth].sql
dblift migrate --tags=auth --dry-run --show-sqldblift migrate --exclude-tags=billingdblift migrate --versions 1.1.0 --dry-run --show-sqldblift migrate --target-version 1.1.0dblift info --tags=auth
| Flag | Effect |
|---|---|
--target-version | Stop at a version instead of applying everything pending. |
--tags, --exclude-tags | Include or skip migrations carrying a tag, comma-separated. Matching is case-sensitive. |
--versions, --exclude-versions | Run or skip an explicit list of versions. |
--strict | Fail if a previously applied migration has gone missing, and require strict version order. |
Tags live in the filename, in square brackets: V1_2_0__add_note[auth].sql. See Naming conventions. The same flags work on info, validate, undo and diff.
Out-of-order applies are visible after the fact too: the history table's installed_rank records the sequence rows were written, so a low rank on a high version is the fingerprint of a migration that arrived late.
SQL and Python migrations
The same naming rules apply to Python migrations — V1_0_0__create_users_container.py is a versioned migration like any other. Python is the only option on engines without SQL DDL: a .sql migration against Azure Cosmos DB or MongoDB fails with DBLIFT-NOSQL-001.
Python migrations are not linted by validate-sql
Policy packs read .sql files only, so a rule that blocks a dangerous statement in SQL will not see the same statement issued from Python.
See Naming Conventions for the full filename grammar and the callback events.