Declarative schema management for MySQL. Write the desired schema
as plain SQL (CREATE TABLE / CREATE VIEW / etc.); myschema
reads the current state from information_schema, diffs the two,
and emits — or applies — the DDL that brings current → desired.
export MYSCHEMA_DSN='root@tcp(127.0.0.1:3306)/app'
myschema dump > desired.sql # capture current schema
vi desired.sql # describe the schema you want
myschema plan desired.sql # preview the DDL
myschema apply desired.sql # run itThe dump → edit → plan → apply → re-plan empty round-trip is
the core workflow. See getting-started.md
for a ten-minute walkthrough.
brew install winebarrel/myschema/myschemaDownload the latest binary from Releases.
Usage: myschema --dsn=STRING <command> [flags]
Flags:
-h, --help Show context-sensitive help.
-d, --dsn=STRING MySQL DSN including database name (e.g. root@tcp(127.0.0.1:3306)/mydb). See
https://github.com/go-sql-driver/mysql#dsn-data-source-name ($MYSCHEMA_DSN)
--password=STRING MySQL password (overrides DSN) ($MYSCHEMA_PASSWORD).
--version
Commands:
apply --dsn=STRING <files> ... [flags]
Apply schema changes to the database.
plan --dsn=STRING <files> ... [flags]
Print the schema diff SQL without applying it.
dump --dsn=STRING [flags]
Dump the current database schema as SQL.
Run "myschema <command> --help" for more information on a command.
myschema dump > current.sql # full database
myschema dump --include 'user*' --exclude 'tmp_*' # filter tables and views
myschema dump --split=./schema/ # one SQL file per table/viewdump round-trips with plan reporting no changes — the output
is the canonical desired-side shape.
myschema plan desired.sql # current → desired diff
myschema plan --allow-drop=column,index desired.sql # allow specific drops
myschema plan --alter-algorithm=INPLACE --alter-lock=NONE desired.sql
myschema plan --bulk-alter desired.sql # fold same-table ALTERsRead-only; safe to run repeatedly. Drops blocked by the policy
appear as -- skipped: lines so nothing's hidden.
myschema apply desired.sql # safe ops only
myschema apply --allow-drop=all desired.sql # destructive ops too
myschema apply --pre-sql 'SET FOREIGN_KEY_CHECKS=0;' desired.sqlapply runs whatever plan would print, in the same order. Use
plan first; apply has no separate confirmation prompt.
- Tables, columns (incl. INVISIBLE on MySQL 8+), primary / unique / check constraints, foreign keys, secondary indexes (with prefix length, DESC, INVISIBLE), generated columns, partitions (RANGE / LIST / HASH / KEY).
- Views (
CREATE OR REPLACEon apply; cross-view dependency ordering). --include/--excludeglob filtering on table and view names.--allow-drop=table,view,column,constraint,foreign_key,index,partition— destructive ops are opt-in, per category.allenables every category at once.--alter-algorithm=…/--alter-lock=…— inject MySQL online-DDL hints into every generatedALTER TABLE/CREATE INDEX.--bulk-alter— fold consecutive same-tableALTER TABLEstatements into one multi-spec ALTER.--pre-sql/--pre-sql-file— run session-level SQL on the connection before the diff (e.g.SET FOREIGN_KEY_CHECKS=0).--split=<dir>(dump only) — write one SQL file per table / view.- Directives in desired SQL:
-- myschema:renamed-from,-- myschema:convert-charset,-- myschema:execute.
Most flags have a matching MYSCHEMA_* env var (--split is
CLI-only).
- MySQL 8+. The catalog reader uses
information_schema.CHECK_CONSTRAINTS,STATISTICS.IS_VISIBLE, and emitsALTER TABLE … RENAME COLUMN. MySQL 5.7 isn't supported. - Go 1.26+ to build from source.
getting-started.md— install, first DSN, dump → plan → apply round-trip, common flags, directives.AGENTS.md— developer guide: feature surface, parser quirks, diff invariants, layout. Read before sending a PR.CAVEATS.md— operational rules, sharp edges, what myschema deliberately doesn't manage (triggers, routines, events, sequences).PARTITIONING.md— partition-diff scope and limits.