shki generate
shki generate <NAME> [--down] [--custom]Alias: shki gen. Runs the same compile-and-diff as
shki diff, then renders the Migration Plan to disk.
What it writes
Section titled “What it writes”migrations/<prefix>_<name>.sql # the migrationmigrations/<prefix>_<name>.down.sql # with --downmigrations/_meta/<prefix>_<name>.snapshot.jsonmigrations/_meta/_journal.json # updatedIt prints the paths it wrote followed by a preview of the diff. Nothing is
applied to the live database — that’s shki migrate.
Options
Section titled “Options”| Flag | Purpose |
|---|---|
--down | Also write a Down Migration. |
--custom | Create a Custom Migration instead (same as shki create). |
-T, --table <NAME> | Migrations table name. |
--prefix <index|timestamp|unix> | File name prefix style. Default index. |
--generate-down | Config-equivalent of --down. |
--shadow-database-url <URL> | External Shadow Database. |
--pg-version <14…18> | Embedded PostgreSQL major version. |
Set [migrations] generate_down = true in shki.toml to make Down Migrations
the default.
Concurrent indexes
Section titled “Concurrent indexes”Declare an index CREATE INDEX CONCURRENTLY in the Declarative Schema to get
the online rollout generated for you. generate prompts for confirmation, then
splits the plan in two migrations:
migrations/<prefix>_<name>.sql # every other change, one transactionmigrations/<prefix>_<name>-indexes.sql # -- shki:no-transactionFor example, adding a column and a concurrent index to the schema:
-- schema/main.sql — declare the desired shape, no ALTERs neededCREATE TABLE scan ( id bigint PRIMARY KEY, payload text NOT NULL, captured_at timestamptz NOT NULL);CREATE INDEX CONCURRENTLY scan_captured_at_idx ON scan (captured_at);$ shki generate capture-at? 1 index(es) are declared CONCURRENTLY and will be written as a separate no-transaction migration (CREATE INDEX CONCURRENTLY IF NOT EXISTS). Continue? (Y/n)-- 0007_capture-at.sqlALTER TABLE "scan" ADD COLUMN "captured_at" TIMESTAMPTZ NOT NULL;-- 0008_capture-at-indexes.sql-- shki:no-transaction
CREATE INDEX CONCURRENTLY IF NOT EXISTS "scan_captured_at_idx" ON "scan" ("captured_at");The second file builds each declared index with
CREATE INDEX CONCURRENTLY IF NOT EXISTS, one --> +statement segment per
index, so nothing takes a write-blocking lock on a live table. Its down
migration is the matching no-transaction DROP INDEX CONCURRENTLY IF EXISTS.
The idempotency guard exists only here, where a partial failure replays the
file from the top — everything else shki generates is strict DDL, so a name
collision fails loudly instead of silently keeping an index whose definition
may not match. Declining the prompt fails the whole generation and writes
nothing; non-interactive runs fail the same way. If the only change is
concurrent indexes, just the one no-transaction migration is written. Each
migration gets its own Snapshot: the first records the intermediate state
(everything except the not-yet-built indexes), the second records the full
desired state, keeping the Snapshot chain complete.
CONCURRENTLY is a creation strategy, not schema state — it isn’t recorded in
the Snapshot, and adding or removing the keyword on an already-existing index
diffs as no change. See
Declarative Schema.
Renames
Section titled “Renames”When the diff finds objects that look renamed rather than dropped and recreated,
generate prompts before rendering. Accepting a rename replaces the
drop/create pair with a rename statement where the dialect supports one — which
preserves the data a drop would have destroyed. Renaming a table can surface
follow-up prompts for the objects that depend on it.
Examples
Section titled “Examples”shki generate add_users_tableshki generate add_users_table --downshki generate backfill_emails --custom # hand-written SQL insteadshki generate add_index --prefix timestamp- “No schema changes detected” means there is nothing to generate.
- Commit the migration, its Snapshot, and the updated Journal together — the
Snapshot is the baseline the next
generatediffs against.
See also: Migrations guide,
shki drop to undo a generated migration locally.