Write SQL migration scripts inside your schema package's slot folders. SchemaQuench executes them in order and tracks what's already run so scripts never repeat.
SchemaQuench remembers what it has already run, so you never have to worry about a migration script executing twice.
A migration script is a plain .sql file that lives inside one of the script-folder slots in your schema package. SchemaSmith doesn't parse or transform the contents — whatever SQL you write, SchemaQuench executes against the target database when that slot runs. This is where hand-written SQL earns its place in an otherwise declarative workflow: one-time data fixes, backfills, grants, archive operations, anything the declarative table definitions can't express on their own.
Every platform supports migration scripts the same way: same slot semantics, same tracking mechanism, same file-naming rules. Only the SQL dialect inside each script is platform-specific — you write SQL Server T-SQL in a SQL Server package, PostgreSQL in a PostgreSQL package, MySQL in a MySQL package.
Migration scripts are part of the schema package format. For details on product- and template-level configuration, see schema packages. For how SchemaQuench executes the full deployment lifecycle, see SchemaQuench.
Migration scripts live in one of four slots, ordered by when they run inside the deployment lifecycle. Each slot's scripts run in alphabetical order, and each script's completion is recorded so it won't run again on subsequent quenches.
One-time migration scripts that run after initial object creation and new table creation, but before table modifications. Use for data preparation that must happen before columns are altered or dropped.
Migration scripts that need the table structure to exist but must run before foreign key constraints are enforced. Typical use: populating a new NOT NULL column before FKs block the data load.
Migration scripts that depend on the final table and key structure but must run before triggers are deployed.
Final migration scripts. Run after all database objects and data are deployed.
Migration scripts are tracked in the SchemaSmith.CompletedMigrationScripts table inside your target database. On each quench run, SchemaQuench checks which scripts in each slot have already been recorded. Scripts that appear in the tracking table are skipped. Scripts that don't appear are executed, and on success a tracking entry is inserted.
| Column | Description |
|---|---|
ProductName | The product name from Product.json. |
QuenchSlot | The slot the script belongs to. |
ScriptPath | The relative path of the script within the template. |
QuenchDate | Timestamp when the script was executed. |
Scripts with [ALWAYS] in the filename (before the .sql extension) run on every quench regardless of tracking:
001_SeedReferenceData [ALWAYS].sql
002_RefreshPermissions [ALWAYS].sql
[ALWAYS] scripts are never recorded in the tracking table.
To force a tracked script to run again, either delete the corresponding row from SchemaSmith.CompletedMigrationScripts in the target database, or rename the script file (tracking is by path, so a renamed script is treated as new).
When SchemaQuench processes a slot, it compares the tracking table entries against the scripts currently present in the package. Entries for scripts that no longer exist in the package are automatically removed.
Migration scripts within each slot execute in alphabetical order by filename. Use numeric prefixes to control execution order:
001_CreateStagingTable.sql
002_MigrateData.sql
003_DropStagingTable.sql
Scripts within a slot run in alphabetical order by filename. Use 001_, 002_, 003_ prefixes so a future name change doesn't silently re-sort the execution sequence.
Reserve the [ALWAYS] suffix for scripts that are safe to rerun every deployment — seed-data refreshes, permissions grants, lookup-table upserts. Never use it for DDL or one-time migrations; those must be tracked so they run exactly once.
Even tracked scripts should guard their own work. Check for the target state before making changes (IF NOT EXISTS / IF EXISTS) so a partial-failure rerun or manual re-execution doesn't double-apply the change.
Tracking is keyed on the script's relative path. If you need a tracked script to run again on all environments, rename it (for example, bump the numeric prefix to 002b_). The rename registers as a new script on every target database.
Last reviewed May 2026 by the SchemaSmith Team.