SQL DDL Frontend¶
erdify can generate an ERD directly from .sql files — no database connection, no
ORM models required. The SQL frontend is an optional extra that pulls in
sqlglot as the only additional dependency.
Installation¶
pip install "erdify[sql]"
# or with uv
uv add "erdify[sql]"
The core erdify package remains dependency-free; sqlglot is only installed when
you request the [sql] extra.
Usage¶
Point erdify at a single .sql file, or at a directory together with
--include '*.sql' to discover .sql files recursively. Passing a directory
without --include uses the default models.py pattern and will not pick
up any .sql files.
# Single file — no --include needed
erdify schema.sql
# Directory — must add --include to discover .sql files
erdify ./db/migrations --include '*.sql'
# Deeper trees use the same pattern
erdify ./db --include '*.sql'
# Specify the SQL dialect explicitly
erdify schema.sql --sql-dialect postgres
--sql-dialect¶
erdify passes the dialect to sqlglot's parser. Omitting it uses sqlglot's generic dialect, which handles standard SQL well but silently drops dialect-specific constructs. Notable case:
CREATE TYPE ... AS ENUMis a PostgreSQL extension. It is only recognised when you pass--sql-dialect postgres. Under the generic dialect those statements are skipped and no enum entities are emitted.
The flag is also settable in pyproject.toml:
[tool.erdify]
sql_dialect = "postgres"
Supported DDL Subset¶
| Construct | Support | Notes |
|---|---|---|
CREATE TABLE |
✅ | Table name becomes the entity name; schema-qualified names (schema.table) are normalised to the table name part |
Primary keys — inline (col INT PRIMARY KEY) |
✅ | |
Primary keys — table constraint (PRIMARY KEY (col, …)) |
✅ | Composite PKs supported |
Foreign keys — inline (col INT REFERENCES other(id)) |
✅ | |
Foreign keys — table constraint (FOREIGN KEY … REFERENCES …) |
✅ | |
Foreign keys — ALTER TABLE … ADD FOREIGN KEY |
✅ | Two-pass resolution; forward references resolved after all tables are parsed |
NOT NULL |
✅ | Reflected in the nullable field attribute |
DEFAULT |
✅ | Default expression captured as a string |
CREATE TYPE … AS ENUM |
✅ | Requires --sql-dialect postgres; silently skipped under the generic dialect |
CREATE INDEX |
✅ (JSON only) | Detected and emitted in --format json; not drawn in PlantUML or Mermaid diagrams (consistent with the Indexes note in the feature matrix) |
NOTE: Functional/expression indexes (e.g.
CREATE INDEX ... ON t(lower(col))) are not mapped to a column — only plain-column indexes set the JSONindexflag. Expression indexes are silently ignored.
Relationship Cardinality¶
All foreign keys are rendered as N:1 in v1. UNIQUE constraint → 1:1 inference is deferred to issue #97.
Deferred / Not Supported¶
The following constructs are intentionally out of scope for v1:
| Item | Status |
|---|---|
UNIQUE → 1:1 relationship |
Deferred — #97 |
CREATE VIEW |
Not supported |
CHECK constraints |
Not supported |
| Triggers / stored procedures | Not supported |
| Live database introspection | Not supported (no connection required by design) |
Example¶
-- schema.sql (PostgreSQL)
CREATE TYPE user_role AS ENUM ('admin', 'member', 'guest');
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
role user_role NOT NULL DEFAULT 'member'
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id),
total NUMERIC(10, 2) NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
erdify schema.sql --sql-dialect postgres --format mermaid
produces a Mermaid ERD with users, orders and the user_role enum, with a
foreign-key edge from orders.user_id to users.