Skip to content

Change your schema

Change the database with versioned SQL files, and see what the change breaks in the App before it runs.

Terminal window
npx tablewalk migrate new add_payoff_quote --app . # writes migrations/V2__add_payoff_quote.sql
npx tablewalk check --app . --pending # the App against the schema to come
npx tablewalk migrate up --app . # apply, regenerate the types, check
  • check --pending applies the pending files to a scratch copy (--pending-on <url>: to a scratch database you name, such as a branch, where the role cannot create databases) and names what a dropped or renamed column breaks ([unknown-column] view "tasks": "due" is not a column on task), before anything changes.
  • migrate up applies each file in its own transaction, rewrites tablewalk-schema.d.ts, and runs check (exit 4 when the App no longer fits).
  • migrate status lists applied, pending and drift, and whether the App’s types are older than the database.
  • Use tablewalk migrate when the App’s team owns the schema: forward-only V2__name.sql files, plus R__views.sql files that rerun whenever they change.
  • Keep your existing tool when one already runs migrations; run tablewalk check --app . after it, which rewrites the types and checks the App.
  • Use migrate stores instead for tablewalk’s own tables (sign-in, history, inbox, jobs, workflows, a tenant App’s tenant and sandbox stores): they move only through it, and serving never migrates them.
Command What it does
new <name> The next V file (--repeatable: an R__ one).
up Applies what is pending (--dry-run prints the SQL).
status Applied, pending, drift and stale types.
validate Exit 2 unless nothing is changed, missing, failed or pending and there is no drift.
baseline Records an existing database as version 0; nothing in it changes.
repair Removes failed entries, accepts changed files, marks missing ones deleted.
stores status, stores up tablewalk’s own stores, each step in its own transaction, safe to run twice (--suite suite.json).

Files live in migrations/ in the App, the connection’s "migrations", or --migrations. A run holds a lock (a PostgreSQL advisory lock, MySQL GET_LOCK, SQLite’s write lock), so two deploys never migrate at once, and it owns only the schemas the App’s sources use. On PostgreSQL, --pending copies the database with CREATE DATABASE … TEMPLATE, which needs CREATEDB; a V3__index.sql.conf saying executeInTransaction=false runs a file outside a transaction (CREATE INDEX CONCURRENTLY).

Names, checksums and the history layout are Flyway’s. To take over from Flyway, name its table (--history-table flyway_schema_history); --tool flyway runs a command through an installed Flyway (on the PATH, or TABLEWALK_FLYWAY).

  • up refuses a database that has tables and no history → run migrate baseline once; nothing in it is changed.
  • [pending-migration] → a pending file failed on the scratch copy, or the files and their history disagree; fix the file, or repair after an edit you meant.
  • [schema-types-stale] → someone changed the database outside migrations; tablewalk check rewrites the types, and status lists the drift until a migration settles it (ADD COLUMN IF NOT EXISTS note).
  • A MySQL migration failed halfway → MySQL commits DDL as it runs; finish or undo it by hand, then repair.