pgddl is an in-memory PostgreSQL DDL semantic layer implemented in pure Go. It takes parsed ASTs from pgparser and maintains a catalog that tracks schema state, validates DDL operations, resolves types, and deparses view definitions — all without requiring a running PostgreSQL instance.
Target PostgreSQL Version: 17 (translated from PG 17 source code)
- 100% Native Go: No CGO, no external C dependencies.
- Faithful Translation: Every function is a 1:1 translation of the PostgreSQL C source — same control flow, same error codes, same error messages.
- Full DDL Coverage: CREATE/ALTER/DROP for tables, views, indexes, sequences, schemas, types (enum, domain, composite, range), functions, procedures, triggers, policies, and more.
- Type System: Complete type resolution and coercion with all 193 built-in types, 229 casts, 799 operators, and 3,314 functions loaded from PG catalog data.
- View Deparse:
pg_get_viewdefequivalent that produces character-for-character identical output to PostgreSQL (141/141 PG compat tests pass). - pg_catalog Queries: 14 system catalog tables exposed via
Query*methods (pg_class, pg_attribute, pg_type, pg_constraint, etc.). - Dry-Run Validation: Execute DDL statements against an in-memory catalog to catch semantic errors before hitting a real database.
go get github.com/pgplex/pgddl
go install github.com/pgplex/pgddl/cmd/pgddl@latest
package main
import (
"fmt"
"log"
"github.com/pgplex/pgddl"
)
func main() {
sql := `
CREATE TABLE users (
id integer PRIMARY KEY,
name text NOT NULL,
email text UNIQUE
);
CREATE INDEX users_name_idx ON users (name);
CREATE VIEW active_users AS SELECT id, name FROM users WHERE id > 0;
`
cat, err := pgddl.LoadSQL(sql)
if err != nil {
log.Fatalf("DDL error: %v", err)
}
// Query catalog state.
rel := cat.GetRelation("", "users")
fmt.Printf("Table: %s (%d columns)\n", rel.Name, len(rel.Columns))
// Get view definition (pg_get_viewdef equivalent).
def, _ := cat.GetViewDefinition("", "active_users")
fmt.Printf("View: %s\n", def)
}
package main
import (
"fmt"
"log"
"github.com/pgplex/pgddl"
"github.com/pgplex/pgddl/catalog"
)
func main() {
// Load existing schema.
base, err := pgddl.LoadSQL(`CREATE TABLE users (id integer PRIMARY KEY, name text);`)
if err != nil {
log.Fatal(err)
}
// Dry-run a migration against the existing catalog.
migration := `
ALTER TABLE users ADD COLUMN email text;
CREATE INDEX users_email_idx ON users (email);
ALTER TABLE users ADD COLUMN name text; -- duplicate column!
`
stmts, _ := pgddl.ParseStatements(migration)
results := base.Clone().DryRunStatements(stmts, &catalog.DryRunOptions{ContinueOnError: true})
for _, r := range results {
if r.Error != nil {
fmt.Printf("ERROR line %d: %v\n", r.Line, r.Error)
}
}
}
The pgddl CLI validates SQL files, dry-runs migrations, and inspects schema state — all without a running database. It reads from files or stdin, and supports structured JSON output for CI integration.
Validate SQL files for syntax and semantic correctness:
pgddl validate schema.sql
# schema.sql: 42 statements OK
pgddl validate -continue schema.sql
# continues past errors, reporting all issues
pgddl validate -warnings schema.sql
# also shows notices (e.g., IF NOT EXISTS skips)
cat schema.sql | pgddl validate
# reads from stdin
Load a base schema, then dry-run migration SQL against a clone of that catalog:
pgddl dry-run -base schema.sql migration.sql
# base: 42 statements loaded
# migration.sql: 5 statements, 1 error
# multiple base files
pgddl dry-run -base schema.sql -base extensions.sql migration.sql
Load SQL and print a catalog summary (schemas, tables, views, indexes, sequences):
pgddl inspect schema.sql
# Schemas:
# public 3 tables, 1 view, 2 indexes
#
# Tables:
# public.users id integer, name text, email text
# public.orders id integer, user_id integer, total numeric
# ...
All commands support -json for structured output, useful in CI pipelines:
pgddl validate -json schema.sql
pgddl dry-run -json -base schema.sql migration.sql
pgddl inspect -json schema.sql
Errors include PostgreSQL-compatible SQLSTATE codes:
{
"files": [{
"file": "migration.sql",
"total": 5,
"errors": 1,
"results": [{
"index": 2,
"line": 4,
"sql": "ALTER TABLE users ADD COLUMN name text",
"error": { "code": "42701", "message": "column \"name\" of relation \"users\" already exists" }
}]
}]
}
| Category | Statements |
|---|---|
| Tables | CREATE TABLE, ALTER TABLE (~30 sub-commands), DROP TABLE, TRUNCATE |
| Views | CREATE VIEW, CREATE OR REPLACE VIEW, DROP VIEW |
| Indexes | CREATE INDEX, CREATE UNIQUE INDEX, DROP INDEX |
| Sequences | CREATE SEQUENCE, ALTER SEQUENCE, DROP SEQUENCE |
| Schemas | CREATE SCHEMA, DROP SCHEMA |
| Types | CREATE TYPE (enum, domain, composite, range), ALTER TYPE, DROP TYPE |
| Functions | CREATE FUNCTION, CREATE PROCEDURE, ALTER FUNCTION, DROP FUNCTION |
| Triggers | CREATE TRIGGER, DROP TRIGGER |
| Policies | CREATE POLICY, ALTER POLICY, DROP POLICY |
| Privileges | GRANT, REVOKE |
| Materialized Views | CREATE MATERIALIZED VIEW, REFRESH MATERIALIZED VIEW |
| Comments | COMMENT ON (table, column, index, type, function, etc.) |
- pgparser - Pure Go PostgreSQL parser (provides AST input for pgddl)
- pgschema - PostgreSQL schema management tool
PostgreSQL License - see LICENSE for details.