Skip to content
PtahPtah

Work with a desired schema

Ptah reads the desired schema from SQL, YAML, HCL, or DBML files, OCI schema artifacts, Go annotations, and explicit or configured external loaders. Every source parses into the same internal representation before anything is planned or rendered, so the operations on this page work the same way whichever you keep.

What differs is one flag.

Terminal window
ptah schema render --schema-file schema.sql --dialect sqlite

--schema-file and --root-dir repeat, and they mix: two files and one Go root become one composite desired schema. An explicit external loader uses --schema-cmd ... --schema-format sql|hcl|yaml. A configured loader uses --config ptah.yaml --allow-external-schema; the opt-in is mandatory because Ptah executes the configured program.

ptah schema render shows the statements a source becomes for one dialect, without connecting to anything. It is the fastest way to check that Ptah read a file the way you meant it:

CREATE TABLE "accounts" (
"id" INTEGER PRIMARY KEY,
"email" TEXT NOT NULL
);

The same table declared as SQL, as YAML and as Go annotations renders byte-identical output. An HCL column is NOT NULL unless it says null = true, and its type is written as declared, so the HCL form of that table renders "id" integer NOT NULL PRIMARY KEY — the same schema, spelled the way that source spells it.

Terminal window
ptah schema compare --schema-file schema.sql --db-url "$DATABASE_URL"

The output is the SQL that would reconcile the database with the source, under a Reconciling SQL: heading. Nothing is executed.

ptah schema drift answers the same question as a check rather than as a report: it exits 1 when the database has diverged and 0 when it has not, so it can be a pipeline step.

Terminal window
ptah schema drift --schema-file schema.sql --db-url "$DATABASE_URL"

--severity destructive narrows the failure to changes that remove something, and --ignore tables=audit_log excludes objects Ptah does not manage. --format json writes the findings as a document on stdout, exit code and all.

The difference a source describes reaches a database two ways, and both take any source:

As reviewed migration files ptah migrations generate
As a direct apply ptah schema apply

Neither is a property of the format. A YAML file can produce PostgreSQL migrations, and the same Go annotations can drive a direct apply, without remodeling anything.

Sources merge into one composite desired schema:

Terminal window
ptah schema render \
--schema-file schema.sql \
--schema-file audit.sql \
--dialect sqlite

Objects are matched by their database identity and identical definitions are deduplicated. A conflict stops the command before it renders anything:

error: error merging composite schema: conflicting field "email" definitions on table "accounts"

The merge rules, including what counts as identical, are on Composite desired schema.

One source, several targets, no database:

Terminal window
ptah schema validate \
--schema-file schema.sql \
--dialect postgres \
--dialect mysql

It prints nothing and exits 0 when every named dialect can express the schema. Otherwise it exits 1 and prints every structural problem it found, one line per problem, each naming the dialect it was found under. That is the cheapest check to put in front of a review: it needs no server and no migration directory. Validate and format schema files covers the verb in full, and ptah schema fmt beside it.