Parser¶
stratum's parser is a small, hand-written, pure-Go recursive-descent parser
over the subset of PostgreSQL DDL an ERD needs. It is deliberately defensive:
statements and clauses it does not model are skipped rather than failing, so
real-world migrations containing DML, DO $$ … $$ blocks, functions, and
extension setup parse cleanly. The parser never panics — a promise backed by a
fuzz target (FuzzParseSQL) in the test suite.
What it understands¶
CREATE TABLE— columns, inline and table-level primary keys, foreign keys (REFERENCES),UNIQUE,CHECK,NOT NULL,DEFAULT; composite and self-referential keys;serial/bigserialandGENERATED … AS IDENTITYcolumns (treated asNOT NULL);COLLATE; multi-word types (double precision,character varying(n),timestamp with time zone, arrays)ALTER TABLE— add/drop/rename column, alter column type, set/drop default, set/dropNOT NULL, add constraint (FK / UNIQUE / CHECK), drop constraint (FK, UNIQUE, named CHECK, named PRIMARY KEY), rename tableCREATE [UNIQUE] INDEXandDROP INDEXDROP TABLEandDROP SCHEMA- Multiple schemas (
public.users, etc.); unqualified names default topublic
DML (INSERT/UPDATE/DELETE), procedural DO $$ … $$ blocks / function
bodies, comments, and other non-structural statements are skipped (their ;
inside dollar-quotes won't break parsing). Unrecognized statements are skipped,
never fatal.
Relationship cardinality¶
Cardinality in the ERD reflects the declared constraints rather than a fixed symbol:
| FK columns | Mermaid | Meaning |
|---|---|---|
NOT NULL |
}o--\|\| |
mandatory many-to-one |
| nullable | }o--o\| |
optional many-to-one |
NOT NULL + unique |
\|o--\|\| |
mandatory one-to-one |
| nullable + unique | \|o--o\| |
optional one-to-one |
A foreign key is considered unique when its column set is exactly covered by a unique constraint or unique index.
Design notes¶
- Replay model — migrations are applied in order to an in-memory schema
IR; the final state is rendered.
DROP/RENAME/ALTERtherefore behave like they do in a real database: what you see is the schema as it exists after the last migration. - Lexer first — a permissive tokenizer handles the parts that affect statement boundaries (line/block comments, quoted identifiers, string literals, and dollar-quoted bodies), so the parser can skip anything it doesn't model without desyncing.
- Deterministic output — tables and relations are sorted; identical input
yields byte-identical output, enabling
--check.
Known limitations¶
DROP INDEXmatches by bare index name across schemas.- Inline
REFERENCES … DEFERRABLE NOT NULLordering can drop the trailingNOT NULL. CREATE TABLE (LIKE other)parsesLIKEas a column.