Creating Migration Scripts
A script is the atomic, versioned, content-hashed unit of change. This page covers authorship, the target-resolution model, type classification, versioning semantics, and dependency declaration.
Authorship Paths
Upload or paste
On Migration Scripts, choose Upload Script. Upload Files takes SQL (.sql, .ddl, .dml), MongoDB (.js), Cassandra (.cql), Neo4j (.cypher, .cyp), Redis (.redis) and Elasticsearch (.json) files; any other file is refused with its name. Paste Script takes a name and the script; the panel shows which engine the script is for as you type.
Pull from provider
With a configured repository/storage source, open it under Repo Sources and choose Pull Scripts; every file under the source's path (including subfolders) that matches its patterns is imported as a Pending script named by its path inside the pulled folder, in the environment you choose (and on the instance and database you choose, if any). Providers: GitHub, GitLab, Bitbucket, Azure DevOps, S3, Azure Blob, GCS, Dropbox. See Environments & Repository Sources.
Choosing the Target
Every uploaded or pasted script is for one environment, and its target is an instance in that environment:
- Environment lists the environments that exist (Environments page). A script is saved under the environment's own name.
- Instance lists only the instances in that environment, and only those of the script's engine: a Redis script is offered Redis instances, a SQL script SQL instances. When there are none, the list says so, for example No Redis instances in Staging. Connections that are not in any environment are not offered; add them to an environment first.
- Database lists the databases on that instance, read through its engine: PostgreSQL, CockroachDB and TimescaleDB databases, MySQL and MariaDB schemas, SQL Server databases, MongoDB databases, Oracle open pluggable databases (or the connected service), Redis numbered databases with their key counts, and SQLite's main database. System databases are listed apart. Engines without a list of databases (Cassandra, Neo4j, Elasticsearch, ClickHouse) say so, and their scripts run against the database set on the connection. If the instance cannot be reached the reason is shown with Retry.
Changing the environment clears the instance and database. When the chosen instance lists the database set on its connection, that database is chosen for you. The same picker is used when you edit a script: choosing an instance there picks its database the same way, and opening the editor leaves the saved database as it is.
A script's instance is always in the script's environment. If a connection is later moved to another environment, the scripts that target it keep theirs, so they no longer agree: the script's card reads Instance is in Staging (hover it for the details), and its detail and editor say, for example, Demo PostgreSQL (Pagila) is in Staging, not Development: choose an instance in Development, or set the script's environment to Staging. Changing the script's environment or instance is refused until the two agree; other edits are not held up.
Author-Set Properties
| Property | Required | Notes |
|---|---|---|
| Name | Yes | Shown exactly as written, up to 500 characters, e.g. alter-orders-add-status-column. A file's name without its extension. |
| Content | Yes | The script; up to 5 MB. |
| Type | Yes | Release (promotes through environments in a release) or Ad-hoc (runs on its own). |
| Environment | Yes | An environment that exists. Scripts pulled from a repository source have none until edited. |
| Instance | When uploading | An instance in the environment, of the script's engine. |
| Database | When the instance lists databases | A database on that instance. |
| Dependencies | No | Scripts that must complete a run first. |
Each card on Migration Scripts shows the script's name, approval status and where it runs (instance / database · environment), with its version, author and creation date. A note on the card says when the script's instance is in another environment, and why that matters. Opening a card shows its version, author, creation date, environment and target. The list can be filtered by status, environment and type.
Editing a Script
Open a script and choose Edit. The editor shows the version, author, creation time and status, then the same fields as uploading: Dependencies (Optional) with a search box (each chosen script appears as a chip; select the chip to remove it), Script Configuration (Type, Environment and, for a Release script, the release) and Target (Instance and Database). Saved values show with a pencil button; select it to change the value. Below them is the Script with its character and line count. Save Changes stores the script and shows Script saved; changing the SQL of an approved script returns it to Pending.
Compare with Target
Compare with Target puts the script next to the current definition of the object it changes, read from a live instance:
- It starts from the script's own instance and database. The instance list holds the instances that can run the script, each with its engine and environment, so you can also compare against another environment's copy.
- The object is read from the script's first statement that names one:
CREATE,ALTERorDROPof a table, view, function, procedure, sequence, package or trigger;CREATE INDEXreads its table;INSERT,UPDATEandDELETEread their table. Quoted names ("...",[...],\`...\`) are understood. You can change the type, schema and name. - Load Definition shows the definition beside the script. When the object does not exist the editor says so, for example The view app.recent_orders does not exist in shop; that is expected for a script that creates it.
| Engine | Object types |
|---|---|
| PostgreSQL, CockroachDB, TimescaleDB, Redshift, Aurora PostgreSQL, YugabyteDB | Table, View, Function, Procedure, Sequence |
| MySQL, MariaDB, Aurora MySQL, TiDB, SQL Server | Table, View, Function, Procedure |
| Oracle | Table, View, Function, Procedure, Package, Sequence |
| SQLite | Table, View, Index, Trigger |
| ClickHouse | Table, View, Dictionary |
Scripts for MongoDB, Redis, Cassandra, Neo4j and Elasticsearch have no object definitions, so the editor does not offer Compare with Target for them.
System-Managed Properties
| Property | Semantics |
|---|---|
| Approval Status | Pending → Approved | Rejected; a rejected script returns to Pending when resubmitted, and an approved one when its SQL or rollback SQL changes or its approval is revoked. Moves to Approved or Rejected only through the Approve / Reject actions; it cannot be set by creating or editing a script. |
| Version | Integer, starts at 1, increments on every content edit. |
| Content Hash | SHA-256 of content; binds reviewed bytes to executed bytes and gates approval invalidation. |
| Created / Updated At, Created By | Provenance timestamps and author. |
Edit Semantics & Approval Invalidation
Changing the SQL or the rollback SQL of an approved script returns it to Pending: the approval covered what was reviewed, including the rollback that rolling back one of its runs executes, so the new version needs approval again. Any signed-off release that contains the script also returns to Pending and must be signed off again before it runs. Changes that leave the SQL and rollback SQL as they were (name, description, target) keep the approval.
Edits are validated by the same rules as a create: the name length limit, the maximum SQL content size, and the allowed execution-mode values all apply, and an edit that breaks any of them is refused with the reason rather than saved partially. An edit cannot change approval status. An update request whose approval status differs from the script's stored status is refused with 400 Bad Request and the error Approval status changes only through approve or reject. An update that sends back the stored status unchanged, as the edit form does, is accepted. Status changes only through Approve or Reject, so every decision passes the separation-of-duties check and lands in the audit log.
A request that addresses a script by a malformed identifier returns 404 Not Found, the same response as a script that does not exist.
Delete removes a script only after the server confirms it. A refused delete, for example while a release, execution, or approval still references the script, or when your role cannot delete scripts, keeps the script in place and shows the reason.
A script cannot be deleted while a release contains it, an execution ran it, or an approval request exists for it. Such a delete is refused with that reason and nothing is removed. To delete a script that belongs to a release, remove it from its releases first. Scripts with run or approval history are kept for audit and cannot be deleted.
Pre-flight Checks
A script can guard itself with lines like -- @check no_null_emails: (SELECT count(*) FROM users WHERE email IS NULL) = 0. Each time the script runs, alone or in a release, its checks are evaluated read-only first, and the script does not run if one fails. See Schema Ops → Pre-flight Checks for the format and the supported engines.
Dependencies
Declaring "B depends on A" means A must complete successfully before B runs. Dependencies are added via search and shown as removable badges, and each must be an existing script listed once.
- Running a single script: refused with the names of any dependencies that have not completed a run yet.
- Signing off a release: refused when its scripts depend on each other in a loop, or when a script depends on a script that is neither in the release nor has completed a run. On sign-off the release's scripts are put in the order they will run: each after the scripts it depends on, otherwise in the order you listed them.
- Running a release: the scripts run in that order and the run stops at the first failure, so dependents are never attempted.
Cycle Detection
Saving a script whose dependencies would form a loop (A→B→C→A) is refused, and the message shows the loop.
Canonical patterns
- Referential ordering:
create-users→create-orders(FK to users) →create-order-items. - Structure-before-data:
add-status-column→populate-default-statuses. - Reference-data layering:
insert-countries→insert-states→insert-cities.
Authoring Discipline
- One logical change per script, keep the reviewable and rollbackable unit atomic; split compound changes.
- Idempotency,
IF NOT EXISTS/IF EXISTSguards so re-execution is safe under retry. - Lock-awareness, flag long ALTERs on hot tables; prefer online/concurrent variants and the appropriate execution mode.
- Consistent tag taxonomy, agree a scheme (
schema,data,index,hotfix, project) and apply it uniformly for fleet-wide filtering. - Promote through lower environments before production; never edit between stages.
Lifecycle
- Create, author content + properties. Status: Pending, revision 1.
- Request Approval, open an approval request for the current SQL. The script stays Pending.
- Review, reviewer decides. Status: Approved | Rejected.
- Resubmit (if rejected), edit (a change to the SQL or the rollback SQL moves the script to its next revision), then back to Pending.
- Compose, add scripts to a release; the release can be signed off once every script is approved.
- Execute, run against the resolved target; each run is recorded under Executions. The script stays Approved. A release script runs only once it is approved; an ad-hoc script can also run before approval, except against a connection in an environment that requires approval.