DBML
DBML is a compact way to write down tables, columns, enums, indexes and relationships. Ptah reads it as a desired schema and writes it back out, so a diagram-as-code document and a database can be the same thing rather than two descriptions that drift.
DBML -> Ptah -> reviewed migration plan -> databasedatabase -> Ptah -> DBMLDBML is a format adapter around Ptah’s own schema model. It is not a second planner, there is no DBML-to-SQL shortcut, and nothing here runs Node.js or a subprocess — the grammar is read in Go.
Read a document
Section titled “Read a document”A .dbml file is a schema source like any other, so every command that takes
--schema-file accepts one:
ptah schema validate --schema-file ./schema.dbml --dialect postgresptah schema render --schema-file ./schema.dbml --dialect postgresptah schema plan --schema-file ./schema.dbml --db-url "$DATABASE_URL"ptah migrations generate --schema-file ./schema.dbml --db-url "$DATABASE_URL"Write one
Section titled “Write one”ptah schema export --schema-file ./schema.hcl --to dbml --out ./schema.dbmlptah schema inspect --db-url "$DATABASE_URL" --format dbml > schema.dbmlBoth write the same canonical form: LF endings, one trailing newline, and an
order that comes from the schema rather than from a map. Two exports of one
schema are the same bytes, so a checked-in .dbml file diffs cleanly.
Columns keep the order they were declared in. Enums, tables, indexes and references are sorted by identity — the order a schema states for its columns is part of what it says, and the others carry no order to preserve.
What a document can say
Section titled “What a document can say”| DBML | Ptah |
|---|---|
Table, with schema.name |
a table, in that schema |
| a column, with its type | a column |
pk, increment, unique, not null |
the matching column property |
default: 'text' |
a literal default |
default: `expr` |
an expression default |
note: and Note: |
a column or table comment |
Indexes { … } with unique, type, name |
an index |
Ref and [ref: > table.column] |
a foreign key, with delete: / update: |
Enum |
an enum type |
A literal default and an expression default stay apart in both directions.
default: 'now()' is the six-character string; default: `now()` is the
call. They are different columns, and Ptah keeps them different.
What it cannot
Section titled “What it cannot”DBML describes tables, columns, enums, indexes and references. It has no syntax for views, functions, triggers, sequences, domains, composite types, ranges, policies, roles, extensions, synonyms, extended properties, hypertables, continuous aggregates or virtual tables.
DBML also has no lossless spelling for Ptah API export metadata: api_name,
openapi_name, graphql_name, proto_name, api_type, and api_expose are
not DBML settings. Ptah deliberately does not hide them in note: text or
presentation settings, because another DBML reader would not recover the same
contract. An attempted table or column setting fails explicitly:
DBML cannot represent export metadata "api_name" on a table; use YAML, HCL, or Go annotationsGoing the other direction is fail-closed too. Exporting any desired schema that
already carries API metadata to DBML fails before stdout is written or an
existing --out file is opened. Use YAML, HCL, or
Go annotations when the source must own API names, type
overrides, or exposure. A DBML source without that metadata can still generate
OpenAPI, GraphQL, and Protobuf; Ptah derives the contract from persistence
names and types.
That has a consequence worth understanding before you apply a DBML document to an existing database: Ptah records those families as not described, so a sequence or a policy the database already holds is left alone rather than read as something the document asked to remove. Silence in a format that cannot speak is not intent.
Going the other way, an export names what it left behind:
$ ptah schema export --schema-file ./schema.hcl --to dbml --out ./schema.dbmlwarning: DBML cannot express views (2); the export leaves them outwarning: DBML cannot express triggers (1); the export leaves them outThe warnings go to standard error, so redirecting the document to a file keeps the document clean.
What is refused, and why
Section titled “What is refused, and why”A many-to-many relationship — Ref: a.id <> b.id — has no foreign key
behind it. A database expresses one with a join table, and Ptah will not invent
a table the document never declared. Declare the join table and two references
to it.
use, reuse, include and import are refused. Ptah reads one document;
a directive that pulls in another describes a schema it has not read, and
carrying on would hand back a model missing whatever that file declared —
silently, and looking complete.
An unsupported setting on a column, an index or a reference is refused rather than ignored. A property Ptah does not implement is one it would apply differently than the document reads, and dropping the difference quietly is how a declared property disappears from a database.
What is read and not applied
Section titled “What is read and not applied”Some DBML describes the diagram rather than the database. Ptah reads it, does not apply it, and says so:
warning: schema file /path/to/schema.dbml:1:1: project describes the diagramrather than the database, and is not appliedProject and TableGroup are in that group. So are Records, and for a
sharper reason: they are seed rows, and Ptah has
reference data with its own declaration, keys
and safety gates. Turning diagram rows into rows a migration writes would apply
data nobody asked to have applied, so Ptah reports the block and leaves it —
once per block, not once per row.
Diagnostics
Section titled “Diagnostics”A syntax or binding error names the file, the line and the column:
$ ptah schema validate --schema-file ./schema.dbml --dialect postgrespostgres: source: error parsing schema file: /path/to/schema.dbml:3:1: unsupported column setting "unlock_everything"Related
Section titled “Related”- Work with a source — how
--schema-filepicks a format. - API schema export — the OpenAPI and GraphQL targets.
- Visualize a schema — diagrams from the same model.