On this page
#Changelog
#0.26.0
New codegen, diff and introspect guides; the @v0 install pin; generated test wrappers that handle unix-socket connection URLs.
Context
Two of the three user-facing items fall out of a test-infrastructure change.
The suite used to resolve its database as "PGDESIGN_DB, or else postgres://localhost:5432/postgres". On a developer's machine that second half was not a fallback but the default: running go test connected to whatever PostgreSQL happened to be listening locally and created and dropped databases inside it. There is no default any more -- each database-backed test binary boots its own throwaway cluster on a private unix socket, and CI provisions PostgreSQL 18 with pg_partman and pgvector on the runner host instead of running a service container. Running the suite against a socket DSN for the first time exposed two real defects in the wrappers pgdesign testdb init generates for consumers: the TypeScript one dropped the username from such a URL, and the JDBC ones built an unusable jdbc:postgresql://null/db instead of saying that the driver has no unix-socket transport at all.
The @v0 pin is unrelated and permanent. The Go module proxy serves a v1.0.0 for this module that was never a real release, and @latest prefers it over every real version. It cannot be retracted: a retraction only takes effect in a version the proxy also serves, which would mean tagging 1.x, which this project does not do.
#Breaking
- Effect classification and consequential prompts. Every command declares its strictcli effect, and six database-mutating commands (
migrate apply,migrate rollback,migrate upgrade,migrate baseline,testdb teardown,testdb gc) now ask for confirmation before running; pass--approve-consequentialto skip the prompt in scripts.--dry-run,--quietand--verboseare provided by the framework on every command instead of being declared per-command, so--dry-runnow works everywhere and a dry run prints aDRY RUNheader.
#Features
- New architecture page: the laws. The kernel's algebra, laws L1-L10, boundary doctrine, decision provenance, and the ruled-out designs register are now permanent documentation at docs/architecture-laws.md.
- New documentation: codegen, diff and introspect guides. Three new guides cover code generation for all six target languages, the three diff modes (
--live,--against,--base), and live-database introspection. The format reference gained its missing sections, and the semantic-type and validation-rule pages are now generated from the source of truth instead of hand-maintained.
#Fixes
- **Install with
@v0, not@latest.** The Go module proxy permanently serves av1.0.0for this module that was never a real release, sogo install github.com/smm-h/pgdesign/cmd/pgdesign@latestinstalled it in preference to every real version. Every install instruction now pins@v0, and the docs explain why the phantom version cannot be retracted. - Generated test wrappers handle unix-socket connection URLs. The generated TypeScript wrapper dropped the username when rewriting a socket DSN (
postgresql://someuser@/db?host=/run/postgresql), silently connecting as the process owner instead. The Java and Kotlin wrappers builtjdbc:postgresql://null/dbfrom the same URL and failed much later with an opaque driver error; they now refuse it up front, since the PostgreSQL JDBC driver has no unix-socket transport.
#1.0.0
#Breaking
- Renamed from pgspec to pgdesign.
#0.25.3
Make the live-stats test tolerate PostgreSQL's non-deterministic n_live_tup estimate so CI passes and the release can publish.
Context
0.25.0, 0.25.1, and 0.25.2 were tagged but never reached npm/PyPI: the publish gate refused because CI failed. The last blocker was TestBuildLiveStatsPopulatesD2, which asserted an exact n_live_tup value; that stat is a non-deterministic estimate (PG < 18 double-counts and updates asynchronously). The test now asserts only that a plausible row count is rendered. This infra release carries only that test-determinism fix; it is the first artifact to actually publish, delivering the user-facing 0.25.1 fixes (see the 0.25.1 CHANGELOG section) to registries.
#Infrastructure
- Make the live-stats test tolerate PostgreSQL's non-deterministic n_live_tup estimate so CI passes and the release can publish.
#0.25.2
Make a PostgreSQL-version-dependent live-stats test deterministic so CI passes and the release can publish.
Context
0.25.0 and 0.25.1 were tagged but never reached npm/PyPI: the publish gate refused because CI failed. 0.25.1 fixed the earlier saferm/safegit tool dependencies, but CI still failed on TestBuildLiveStatsPopulatesD2, which hardcoded an n_live_tup estimate that PostgreSQL 17 double-counts (6 vs 3). This infra release carries only that test-determinism fix; it is the first artifact to actually publish, delivering the user-facing 0.25.1 fixes (see the 0.25.1 CHANGELOG section) to registries. No user-facing delta over 0.25.1.
#Infrastructure
- Make a PostgreSQL-version-dependent live-stats test deterministic so CI passes and the release can publish.
#0.25.1
Fix the CI/publish-blocking dependency on developer-machine tools: migrate squash now archives originals with a built-in file move, and the safegit-missing build error is actionable.
Context
v0.25.0's publish gate refused to publish because CI failed on the release commit: the legacy migrate squash flow shelled out to a developer-machine file-archival utility (absent on CI runners and consumer machines), and the revise/build auto-commit tests required a developer-machine safegit that CI lacks. 0.25.1 carries the identical feature set as 0.25.0 plus these fixes, so 0.25.1 is the first artifact actually published to npm and PyPI.
#Fixes
- **
migrate squashno longer requires an external file-archival tool.** The legacy squash flow shelled out to a developer-machine utility absent on CI and consumer machines, which failed the operation; originals are now archived with a built-in file move into a siblingmigrations/archive/directory. - **Actionable error when
safegitis missing duringbuildauto-commit.**pgdesign buildauto-commits generated outputs with safegit; when it is not installed the failure now names the tool, links where to get it, and points at--no-auto-commitinstead of surfacing a cryptic exec error.
#0.25.0
The kernel rewrite. pgdesign gains a content-addressed schema identity: every object and whole-model revision is a hash of its canonical form, and migrations become content-derived edges in a Merkle-DAG chain with integrity guarantees (path-finder, journal-driven rollback, precondition/reconcile checks, store consistency checking). Existing projects convert with a one-time migrate upgrade. Alongside it: a single canonical serializer and JSON envelope, cross-repo schema imports, branded type-safe codegen across all six languages with compile checks, the one-command pgdesign revise, a DB-free serve project mode, and a richer D2 diagram suite.
Context
This release implements the roadmap kernel: a small algebra (content-addressed object store, a category of schema migrations, one syntactic equivalence relation) whose laws eliminate whole defect classes by construction rather than case-by-case. Silent divergence classes are closed structurally -- one normalizer computes equivalence everywhere (differ, predicates, reconcile), so diff no longer reports false or missed drift on equivalent SQL; identity is hash(canonical bytes), so equivalent schemas produce byte-identical output and revisions; provenance is total, so every generated artifact carries the revision that produced it and freshness is extensional equality; and apply is a verified functor -- preconditions check the world is where expected, reconcile checks it arrived where intended, and drift is always a loud error, never absorbed.
Consumer adaptation is required and is a one-time, one-release break:
- Regenerate ALL codegen output (run
pgdesign codegen/pgdesign build).
Generated code is version-coupled to the binary that produced it. Two forces make regeneration mandatory: the unified provenance header wording (scripts that grep the old header text must update) and per-language enum branding (Go opaque validating types + branded GORM fields and constraints validators; TS parse() at boundaries; Python parse()/members replacing raw-string enum construction; SQLAlchemy sa.Enum(PyEnumClass); Java/Kotlin fromValue() + JPA AttributeConverter; Zig wrapper struct; Java one-public-type-per-file layout). Note from rehearsals: regeneration diffs are dominated by canonical reordering and expression normalization, NOT the header alone -- treat every consumer as a full regenerate-and-review, never a header-only edit.
- Databases already tracked by pgdesign migrations must run
migrate upgrade
once to fold the legacy semver files into the content-addressed chain; until then every migrate subcommand hard-errors against a pre-upgrade database, naming the command.
- JSON/API consumers:
generate jsonandserve's/api/schemaare now a
revision-stamped envelope; the schema body lives under the model key (serve previously returned {schema, diagnostics}).
Adaptation was rehearsed against real consumers at four checkpoints (throwaway DB copies and working trees, no release, no commits to consumer repos); the rehearsals validated the regenerate-and-review guidance above and confirmed the RAISE-on-mismatch semantics for generate --idempotent stand.
#Breaking
- Removed pkg/diff. The exported diff package was an unusable interface-only stub (zero importers). A public differ will be reconsidered only when a second flat-schema consumer actually exists.
- Generated-file provenance header unified to one wording. All codegen, SQL, D2, GraphQL, and seed outputs now carry a single provenance header line --
Code generated by pgdesign. DO NOT EDIT.(Go files now match the standard^// Code generated .* DO NOT EDIT\.$tooling convention) plus a revision stamp, replacing the seven-plus divergent wordings that existed before. Consumer scripts that grep the old header text must update to the new wording, and all generated output must be regenerated after upgrading. - **JSON output is now a revision-stamped envelope and
servepayload keys changed.**generate jsonandserve's/api/schemaresponse are now a single envelope{format_version, revision, model, diagnostics?}produced by one serializer. The schema body moved under themodelkey --servepreviously returned{schema, diagnostics}-- so HTTP API and JSON consumers must read.modelinstead of.schema(or the former top-level object). - Generated Java code now uses one public type per file. The
types,jpa, andconstraintsJava codegen modes emit a directory of single-public-type files (e.g.Accounts.java,Role.java,Constraints.java) instead of one multi-type file, which is the layout the Java compiler requires. Regenerate Java output after upgrading. - Go enum codegen now emits validating opaque types. Generated Go enums are opaque structs with package-level var members, a ParseXxx constructor, and validating JSON/SQL/text boundaries instead of a bare string type; the zero value is detectably invalid. GORM struct fields and the Go constraints validators now use these branded enum types as well (validating via
.IsValid()/.String()). String-literal comparisons against generated enums no longer compile -- parse or compare against the generated members. Regenerate Go codegen output after upgrading. - TypeScript enum codegen now emits a validating parse function. Each generated enum gains a
XxxValuesconstant and aparseXxxfunction to validate untyped input (JSON, DB rows) at boundaries; the string literal union type is unchanged. UseparseXxxat ingress points after upgrading. - Python enum codegen is now branded end-to-end. Generated StrEnum enums gain a
parse()classmethod, and the query-layer package defines/imports its enum classes and coerces Row fields to them via__post_init__so both backends yield enum-typed fields. Regenerate Python codegen and replace raw-string enum construction withparse()/members after upgrading. - SQLAlchemy codegen now emits sa.Enum(PyEnumClass) columns. Enum and state-machine columns use generated StrEnum classes as both the column type and the mapped attribute type instead of string-literal sa.Enum; the models gain enum class definitions and imports. Regenerate SQLAlchemy models after upgrading.
- Java and Kotlin enum codegen is now branded. Generated enums gain a value-based
fromValue(); JPA enum columns persist via a generated@ConvertAttributeConverter (never@Enumerated(STRING)), and constraints validators compare against the enum's value. Regenerate Java/Kotlin codegen and add the generated converters to your JPA setup after upgrading. - Zig enum codegen now emits a validating wrapper struct. Generated Zig enums are a wrapper struct with named member constants and a
parse()that rejects undefined values, replacing bare string constants; state-machine transition maps reference the branded members. Regenerate Zig codegen after upgrading. - **The migration system is now a content-addressed chain; run
migrate upgradeonce per project.** Migrations are no longer semver-numbered.tomlfiles but content-derived edges inmigrations/chain/that link revision manifests in a Merkle DAG, so parallel edges and concurrent branches can never collide. Existing projects convert with the new one-timemigrate upgradecommand, which folds the legacy semver files into the chain and migrates any tracked database's applied-migration rows in a single verified transaction. Until you run it, everymigratesubcommand hard-errors against a pre-upgrade (legacy tracking-table) database, namingmigrate upgrade. - **
generate --idempotentnow fails loudly on CHECK constraint drift.* Re-applying idempotent DDL against a database where a CHECK constraint of the same name already exists with a different* definition now RAISEs (naming the object, expected, and found definitions) instead of silently skipping. Semantically-equivalent spellings still converge and do not raise. - **Schema documents now require a top-level
format_version = 1, and unknown keys are now hard errors.** pgdesign validates the document shape (unknown keys, field types, andidentifier/pgtype/sql-expressionlexemes) through strictspec before parsing; a document withoutformat_versionis rejected, and keys that previously produced only a W001 unknown-key warning now reject the document outright. Remediation: runscripts/stamp_format_version.sh <file-or-dir>to stamp existing schemas. - **
migrate planis now pure and database-free in chain projects.** It enumerates the pending chain edges from genesis (or a--fromrevision) to the head without connecting to any database. The--dbflag now applies only to legacy migration projects; usediff --liveto preview drift against a running database.
#Features
- New
[tables.*.maintenance].schedulekey schedules automatic partition maintenance via pg_cron; a partman-managed table with no schedule now warns (W029) - **
pgdesign codegengains--groups/--sourcefilters and now matchesbuildbyte-for-byte.** Standalone codegen routes through the same planner, filters, and orphan-refusal write path as build. - Canonical schema output normalizes SQL expressions. Expression fields (defaults, CHECK constraints, index and exclusion predicates, RLS policy expressions, generated columns) are now emitted in a single canonical form, so equivalent spellings produce byte-identical SQL, JSON, and revisions. Existing schemas with expression-bearing fields will see one-time formatting changes in generated output (e.g. operator spacing).
- **Every
--dband--liveflag now falls back to thePGDESIGN_DBenvironment variable.** SetPGDESIGN_DBonce and omit the flag on any database-backed command; an explicit flag still wins. Commands that require a database still fail loudly when neither the flag nor the env supplies a URL, and--hermeticsuppresses the connection so database work skips instead of connecting. migrate applynow checks a precondition for every migration op against the live database and hard-errors on schema drift, naming the object, the expected state, and the state found. Concurrent index build/drop and enum-add steps journal via an intent/confirm protocol so an interrupted apply resumes safely (an interrupted concurrent index build is rebuilt to a valid index on the next apply).- **
migrate applynow catches attribute-level schema drift** — wrong column type, changed CHECK definition, altered nullability or default — before running each op, with errors naming object/expected/found. - Journal-driven rollback for chain-mode projects.
migrate rollback(and--to <revision>) now reverses applied chain edges by executing the recorded down-ops from the migration journal in reverse order, never re-deriving inverses from files. Non-invertible ops are refused before anything runs, and the upgrade/baseline boundary is rollback-frozen. - **
migrate applynow verifies the database matches the target schema after every apply.** Once all pending migrations land, pgdesign re-introspects the database and diffs it against the target model; any residual difference (for example an out-of-bandALTERthat drifted the schema) aborts with a hard error listing every divergent object. This codomain check is always on and cannot be skipped from the CLI. - Migration rename gate. A declared
[renames]section inpgdesign.tomlturns a column or table rename into a data-preservingALTER ... RENAMEinstead of a destructive drop+create. An undeclared or ambiguous plausible rename (a dropped and an added object with identical definitions) now hard-errors before generation, naming the pair — pointing you to[renames]or to making the definitions differ if the drop is intentional. - **New
migrate rebasecommand resolves a forked migration chain.** When two branches each append a migration (a two-head fork),migrate rebase --head <ref>re-parents the other branch's tail onto the kept head; databases already stamped at a rebased-away revision are served forward to the new head instead of being orphaned. - **
migrate baselineadopts an existing or drifted database onto the chain.** In chain-format projects,migrate baselineintrospects the live database and records it as a baseline boundary without executing any migration SQL, so a database created by other means (or one that intentionally drifted from the schema) can be brought under pgdesign migration control. - **New
pgdesign revisecommand.** Regenerates all outputs, chains the migration, runs blocking normal-form and structural checks, and commits — the one-command project revision. Live analyses (FD discovery, pg_stat) run as a non-retroactive DB tier. - Revision enforcement.
codegen --outputnow refuses to write when sibling outputs are at a different revision, and a newcheck --tag revisionverifies cross-artifact stamp agreement, JSON envelope revisions, and migration-chain integrity.fmtandintrospect --outputprint follow-up notices when they change or introduce project source. - Cross-repo schema imports. Declare another pgdesign project's schema under
[imports.<alias>]in pgdesign.toml (git URL, ref, target schema) and reference its tables from a foreign key viaref_table = "alias:table". Alias references are scoped to FK ref_table; using one elsewhere is a hard error. - **
pgdesign import lock/updateandcheck --tag imports.**import lockresolves each import's git pin, vendors the referenced tables plus their type closure intoimports/<alias>/, and writes a committed lockfile;import updatere-pins.check --tag importsverifies offline that the vendored surface still matches the project's references, reporting column-level drift without touching the remote. - Imported foreign keys now resolve. An FK referencing an imported table (
ref_table = "alias:table") resolves against the vendored surface, so it no longer trips a spurious "non-existent table" error, and imported targets appear in the relationship graph without polluting your project's own DDL, audit, or codegen. - Imported tables render as reference shapes. SQL DDL never emits
CREATE TABLEfor an imported table (its FK constraints reference it schema-qualified), and D2 diagrams and GraphQL SDL show imported tables as distinct, schema-qualified reference shapes rather than dropping the schema or leaving dangling edges. - Seed data for imported foreign keys.
pgdesign seedresolves imported-FK values through tiers: with--dbit draws real keys from the live imported tables; offline (--format insert) it emits count-wrapped ordered-offset subqueries. Impossible cases fail loudly — offline--format copywith a NOT NULL imported FK, or a UNIQUE distinguished solely by an imported FK offline — instead of silently emitting random UUIDs. - Cross-repo import safety checks.
pgdesign revise's database tier verifies each imported table and referenced column exists in the live database with a matching type;check --tag importsenforces that your project re-declares every extension the imported surface requires and targets a compatible PostgreSQL version; and an imported type name that collides with one of your own is a hard error. Imported enums are usable in your own columns. - **DB-free
serveproject mode, loopback-bound by default.** Runningpgdesign servewithout--dbnow compiles and serves your project offline:/api/schemareturns the canonical model envelope (byte-identical togenerate json), with new/api/schema/graph(FK-graph projection) and/api/schema/docendpoints, and state-machine diagrams that render; database-backed endpoints return an explicit 503 in this mode. The server now binds 127.0.0.1 by default — use--bindto change it, and note its help states plainly that the server has NO AUTHENTICATION. - **
serverequest timeouts and asynchronous audit.** The--timeoutflag is now enforced as a per-request deadline that cancels the request and returns 503 on expiry. The audit endpoint moved from a synchronous run to a job model: POST/api/audit/jobsstarts a cancellable, bounded job; GET/api/audit/jobs/{id}polls it; DELETE cancels it. - D2 diagram options.
[output.<name>.d2]config subsection and serve query params control diagram layout (dagre/elk), theme, and direction. - Richer D2 diagrams. Diagrams now show index/unique/nullable column markers, table comments as tooltips, CHECK constraints as notes, RLS/append-only markers, and enum types as rectangles — each layer individually disableable.
- D2 diagram filtering. Diagrams support include/exclude table globs, include-dependencies depth expansion along FK references, and a summary mode (names and edges only); edges to excluded tables are dropped without dangling and self-referencing FKs are preserved.
- D2 relationship cardinality. FK edges render native crow's-foot arrowheads: 1:1 when the FK columns are unique, 1:N by default, and strict junction tables (exactly two FKs forming the whole primary key, no other columns) collapse into a single M:N edge.
- D2 heat maps and live stats. Diagrams can color table borders by fan-in or fan-out on a fixed colorblind-safe scale, and render caller-provided live row-count and sequential-scan-ratio annotations as tooltips.
- Live D2 stats.
serve's d2/svg endpoints acceptlive_stats=trueandbuildhonors[output.*.d2] live_stats=trueto annotate diagrams with live row counts from a database (opt-in; requires--db/PGDESIGN_DB). - Documentation overhaul. New guides for the content-addressed migration chain and cross-repository imports; quickstart, format reference (
[imports],[renames], D2 diagram options, partition maintenance), and validation-rules (import/info/seed diagnostics and project integrity checks) brought to the as-built reality; plus a blog post on content-addressed schemas.
#Fixes
- Python DDL codegen passed retention as the partman partition interval. Generated create_parent calls now use the declared interval (sibling of the earlier generate-path fix).
[tables.*.maintenance]now requires an explicitpremakevalue; omitting it is a hard error instead of silently disabling partman premaking- Partman config migrations now apply. Changing partition retention or premake generated migration ops that silently rendered as '-- unknown op' comments and did nothing at apply time; they now emit the correct UPDATE partman.part_config statements.
migrate squashnow requires--db: the M200 applied-version safety check is mandatory, so an applied (or offline) range can no longer be squashed into a tracking-table desync- Fixed path traversal in the serve migration-version endpoint. A crafted version path parameter could escape the migrations directory; escapes are now rejected with 400.
- GraphQL SDL nullability could be wrong for same-named tables across schemas. FK-field nullability was computed from a bare-name table map (last schema won); it now reads the owning table's columns.
- **An explicit
--dir migrationsflag is now honored over config.** Previously indistinguishable from the default, so pgdesign.toml's migrations_dir silently won. - Schema diff no longer reports false or missed drift on equivalent SQL expressions. CHECK constraints, partial-index predicates, RLS policy expressions, and defaults now compare through a single normalizer: equivalent spellings (extra parentheses,
!=vs<>,INvs= ANY(ARRAY[...])) no longer show as changes, and case-distinct literal defaults like 'Active' vs 'active' are correctly detected as different. - Diff no longer reports false drift for long constraint/index names. A content-derived constraint or index name longer than 63 bytes is now matched against its NAMEDATALEN-truncated form as introspected from a live database, instead of appearing as a simultaneous drop and add.
- **
diff --liveno longer reports false drift from PostgreSQL's cast materialization.** Desired-side CHECK, partial-index, exclusion, and RLS-policy expressions are round-tripped through the target database so equivalent forms (e.g.status = 'active'vs the storedstatus = 'active'::text) converge instead of appearing as changes. - Trigger WHEN conditions no longer report false drift on equivalent spellings. They now compare through the same expression normalizer as CHECK constraints and policies.
- Fixed mixed-case unquoted index/exclusion key columns false-drifting in diff. A desired unquoted key column such as
Emailno longer reports a spurious change against PostgreSQL's lowercased introspected form; unquoted identifiers now fold to lowercase (matching PG), while quoted identifiers stay case-sensitive. - Schema diff now reports PostgreSQL version and table-group changes.
diff(and the migrate plan/diff surfaces built on it) previously ignored changes to the targetpg_versionand to[groups], even though both alter the schema; they are now surfaced as changes. - diff --against/--base now detect semantic type changes. Two schemas that are DDL-identical but declare different semantic type names (e.g. a pure-alias scalar
emailvstext) now produce a non-empty diff, catching codegen-visible drift that was previously missed. - **
migrate plan/generateno longer spuriously drift on unpinned pg_version.** An in-sync project with an unpinned pg_version previously lost "No changes detected" (plan) and wrote a zero-op migration file (generate); the live server version is now resolved before diffing, and generate refuses to write an empty-op migration. - Hermetic checks no longer connect via a config database URL. Running
check --hermeticwith the database env unset but a URL configured in pgdesign.toml previously connected anyway; it now skips DB-backed checks visibly. - Split-directory Go types/constraints outputs are now a hard error. Configuring a Go
constraintsoutput in a different directory from the Gotypesoutput no longer silently produces uncompilable code —buildfails naming both directories and the co-location requirement. buildnow hard-errors when a Gogormcodegen output is split from the Goconstraintsthat reference its row structs and enums, and when Gotypesandgormtarget the same directory (duplicate row-struct definitions that never compiled).- Multi-file projects whose tables live in
publicno longer false-drift: introspection namespaces now come from the model'sschema=values, not the schema filenames, soupgrade,diff --live,plan, andmigrate generatereconcile correctly. migrate statuson an upgraded (chain-mode) database no longer recreates the dropped legacypgdesign_migrationstable; it now reports the chain position, applied edges, and pending edges from the on-disk chain.migrate test --shadownow refuses a pre-upgrade database (namingmigrate upgrade) instead of silently proceeding to create a shadow database.- Generating a migration that adds an enum type no longer prints spurious error-severity risk warnings;
create_enumis now correctly classified as a safe operation. diff --liveno longer reports a spurious 'pg_version changed' when the schema's declared [meta].version differs from the live server version; the live version is resolved before diffing, matching the migrate paths.- A column or domain default written with an explicit cast (e.g.
'{}'::jsonb) no longer false-drifts against a live database that stores it as the bare literal;diff --liveandmigrate upgradereconcile it cleanly. - An index defined without an explicit access method no longer false-drifts against a live database (where the method materializes to
btree);diff --liveandmigrate upgradereconcile such indexes cleanly. - A UNIQUE constraint's backing index is no longer reported as a separate index during introspection, so a schema declaring the unique constraint no longer false-drifts (spurious removed index) against a live database in
diff --liveandmigrate upgrade. - An append-only table (
append_only = true) no longer false-drifts against a live database; introspection now detects the managed deny-mutation trigger and marks the table append-only. DROP INDEX CONCURRENTLYmigration steps now emitIF EXISTS, so re-applying a migration after an interrupted concurrent index drop no longer fails on the already-absent index.- Rollback no longer drops adopted objects.
migrate rollbackon a baseline row (a database whose schema pgdesign adopted as-is) previously ran the migration file's down-ops, dropping tables pgdesign never created. Rollback now refuses baseline rows. - Corrupt migration stores now fail loudly during apply. A revision manifest that references an object the store no longer contains (store corruption) previously caused
migrate applyto silently skip that operation's precondition check, masking real schema drift. Apply now stops with a hard error naming the reconstruction failure. A genuinely absent manifest (no recorded pre-state) still falls back to an existence-only check as before. - Drifted concurrent-index and enum-add migrations no longer leave orphan journal rows. When
migrate applyhit schema drift on a non-transactional operation (CREATE/DROP INDEX CONCURRENTLY, or a pre-PG12 enum value add), it wrote an intent row to the migration journal before detecting the drift, leaving an orphaned intent that complicated resume. The drift is now caught before any journal write, so a refused operation leaves the journal clean. - Migration comments now reach the database. Chain-applied migrations emit
COMMENT ONfor tables, columns, and every commented object; comment-only changes now produce a migration instead of silently doing nothing. - Fixed unqualified enum/domain types in generated migrations. A column whose type is a user-defined enum or domain in a non-public schema now renders schema-qualified in
CREATE TABLE, so the migration applies instead of failing with "type does not exist". - Migration generation is now pure and always large-table-safe.
migrate generatereads no database: foreign keys are always added with NOT VALID then VALIDATE, columns becoming NOT NULL are always backfilled before SET NOT NULL, and the type-narrowing advisory now fires on every narrowing regardless of table size. - Schema diff detects state-machine type changes.
diffand migration generation now recognize added, removed, or modified state-machine type definitions (states, transitions, initial state, comment), so chained migrations correctly carry state-machine type identity. - **Round-trip fidelity fixes for
diff --liveand reconcile.** Default-PERMISSIVE RLS policies, equivalently-spelled pg_partman intervals (1 monthvs1 mon), and domain-backed columns (including domains in non-search-path schemas) no longer report spurious drift when comparing a schema against a live database. - **
migrate test --shadowreplays the migration chain for chain-format projects.** Shadow testing now replays the on-disk edges (the same pathmigrate applyuses) into the throwaway shadow database before diffing against the schema, so it validates the real chain rather than legacy migration files. - Migration guide rewritten for the chain-based migration model. The docs now describe content-addressed edges, the path-finder, journal-driven rollback, and the
upgrade,baseline, andrebasecommands, replacing the retired semver-file description. - Rename gate no longer defeated by auto-derived names. An undeclared rename of a foreign-key-bearing table is now correctly blocked (previously it could silently become a drop+create, losing data), and a declared rename of such a table now resolves to a data-preserving ALTER ... RENAME instead of being rejected.
- Baseline refuses a project that already has a migration chain.
migrate baselinenow hard-errors when the on-disk chain already has a live head (baseline adopts foreign databases into empty chains only), naming the remediation, instead of silently forking the chain into two irreconcilable heads. buildnow fails hard when its auto-commit step fails, instead of warning and leaving regenerated outputs uncommitted.generate --strict-nfand thenfcheck now also flag BCNF violations (W103), not just 1NF/2NF/3NF.- Seed uniqueness retries no longer emit duplicates. When a unique/primary-key value collides, seed generation now varies the value across retry attempts and hard-errors if the domain is genuinely too small, instead of silently emitting a duplicate row that would fail on insert.
- Imported/owned table-name collisions are now a hard error. When a vendored imported reference table shares its (schema, name) with one of your own tables,
pgdesignfails the build and names both sources, instead of silently letting your table shadow the import. - Clearer CLI help.
build --auto-commit,codegen --split-mode, theimport lock/import updatealias argument, andmigrate baseline --descriptionnow have fuller help text explaining their behavior and options. - Onboarding docs now show the required
format_version = 1top-level key, so schemas copied from the quickstart no longer produce documents the shape gate rejects.
#0.24.4
Idempotent mode now emits ADD COLUMN IF NOT EXISTS guards for every column.
Context
Previously, idempotent mode generated CREATE TABLE IF NOT EXISTS but plain ADD COLUMN for alter statements, causing silent column drift on re-apply. All column additions now use IF NOT EXISTS guards.
#Fixes
- Fix. Idempotent mode now emits
ADD COLUMN IF NOT EXISTSguards for every column, preventing silent column drift on re-apply.
#0.24.3
Fix extension DDL name resolution (e.g., pgvector generates CREATE EXTENSION vector instead of pgvector).
#Fixes
- Fix. Extension names with DDL/TOML mismatches (e.g., pgvector → vector) now generate correct CREATE EXTENSION statements.
#0.24.2
Bug fixes for JDBC testdb URL handling and migrate generate column serialization.
Context
Java and Kotlin testdb consumers should re-run pgdesign testdb init to pick up the JDBC URL fix. No codegen template changes in this release.
#Fixes
- Fix. JDBC testdb templates now correctly handle PostgreSQL URLs with embedded credentials by encoding user/password as query parameters instead of passing userinfo in the authority.
- Fix. Migration files generated for new tables now include column definitions in serialized operations, fixing a roundtrip loss where re-read migrations produced empty tables.
#0.24.1
CI fixes: partman schema-qualified extension install, JUnit Platform launcher for JVM conformance, ListOrphans conn-busy fix.
#Fixes
- Bug fix. testdb ListOrphans no longer fails with 'conn busy'; partman and JVM conformance test fixes.
#0.24.0
Partition lifecycle completeness, migrate baseline fix, library promotion (pkg/diff, pkg/genkit, pkg/diagnostic).
Context
Partman integration reaches end-to-end coverage: CI template gains --partman flag with postgres+partman service, introspection excludes partman-managed children from drift, MaintenanceDiff safely updates part_config with hard error on interval changes, and extension setup is schema-qualified. Partition interval is now separated from retention and required explicitly (breaking). Migrate baseline records all versions with additive idempotency and out-of-order guard. Three internal packages promoted to shared libraries: pkg/diff, pkg/genkit, pkg/diagnostic.
#Breaking
- Separate partition interval from retention.
intervalis now a required field, independent of retention policy.
#Features
- Partman-capable CI. Consumer CI template gains
--partmanflag for postgres+partman service,SkipIfNoPartmantest guard, andREQUIREenv vars. - **Faceted codegen
__init__.py.** Generate__init__.pyin faceted output directories; fix executor type annotations for mypy. - MaintenanceDiff. Safe
part_configupdates with hard error on interval changes that require manual partition restructuring. - Partman-aware introspection. Exclude partman-managed partition children from drift detection.
- Extension schema qualification. Schema-qualified
CREATE EXTENSIONfor partman setup; scheduling docs. - Partition interval caution classification. Forward-only experiment guard for interval changes.
- Library promotion.
pkg/diagnostic,pkg/genkit, andpkg/diffpromoted to shared packages with stable interfaces.
#Fixes
- Migrate baseline fix. Record all versions in migration history, enforce additive idempotency, add out-of-order guard.
#0.23.1
Fix pypi CI to run in the pypi/ working directory
Context
The pypi CI job ran uv sync --locked at the repo root where there is no pyproject.toml (it lives in pypi/), failing CI and gating npm/pypi publish. No library code changed.
#Infrastructure
- Fix pypi CI to run in the pypi/ working directory
#0.23.0
migrate baseline command; fix live-diff crash on provolatile/proparallel columns
Context
The provolatile/proparallel char-column cast fix unblocks downstream deploy pipelines whose post-deploy audit runs pgdesign diff --live against a database containing functions.
#Features
- **Added
migrate baselinecommand.** Marks an existing database as being at a specific migration version without executing any SQL, so projects with pre-existing schema can adopt pgdesign-native migrations going forward. Idempotent.
#Fixes
- **Fixed
diff --livecrash on databases with functions.** Introspection no longer errors withcannot scan char (OID 18)on theprovolatile/proparallelcolumns.
#0.22.0
Valid idempotent enum DDL, E228 cascade guardrail, unsuppressible errors, ORM enum support, orphan detection, codegen --check, declaration-order preservation, and full struct-handler CLI.
Context
Driven by a consumer's Python-DDL adoption report and a full CLI-safety overhaul.
DDL correctness: idempotent enum DDL was invalid SQL on every PG version (CREATE TYPE IF NOT EXISTS); now DO-block catalog checks, proven by an execution-backed matrix that runs every emitted statement against live PostgreSQL. Constraints codegen was non-deterministic; state machine states and composite fields lost TOML declaration order. Domain columns were unqualified in CREATE TABLE.
Guardrails: E228 rejects cascade-into-append-only contradictions (consumer schemas fixed first); E-codes can no longer be suppressed or disabled; owned output directories hard-error on orphan files; codegen --check verifies committed output without writing.
CLI: migrated to strictcli v0.19.0 struct handlers end-to-end, retiring the kwargs map API that produced two shipped bugs (hyphen/underscore key mismatches, nil type assertions). The --split-mode parse-time catch-22 is fixed at the strictcli root; --config is wired for real.
#Breaking
- Breaking change. Error-severity diagnostics can no longer be suppressed or disabled:
[suppress]keys and[validate] disableentries targeting E-codes are themselves config errors (E229). Fix the schema instead.
#Features
- New feature. E228 rejects FKs on append-only tables whose
on_delete(CASCADE, SET NULL, SET DEFAULT) would write into the deny-mutation-protected table — a contradiction that always fails at runtime. Transitive cascade chains are analyzed; the error names the full FK chain and suggests RESTRICT or NO ACTION. - New feature. ORM codegen understands enums: Drizzle emits
pgEnumdeclarations and enum column builders, SQLAlchemy emitssa.Enum(...)column types, GraphQL maps enum columns via the column's TypeKind, and introspection now populates TypeKind so live-database schemas get the same treatment. State machine columns are covered everywhere. - New feature. Multi-file codegen output directories are now owned:
buildandcheck --tag buildhard-error on unexpected (orphan) files — never deleting them — closing the hole where renamed schema sources left stale generated files committed and green forever.__pycache__/and*.pycare exempt. - New feature.
codegen --checkverifies committed generated output without writing: regenerates in memory, byte-compares against--output, orphan-scans multi-file directories, reports[missing]/[stale]/[orphan], exits non-zero on drift. - New feature. Upgraded to go-strictcli v0.19.0:
codegenworks again without--split-modefor every language and mode (the flag's absence was a parse error). The--configglobal flag is now actually wired — it bypasses pgdesign.toml directory search with hard errors on missing paths. - Improvement. CLI internals migrated to strictcli struct handlers (wave 1: globals, testdb group, stats, fmt, build). Flag surface unchanged except repeatable/variadic parameters now schema-typed as
list[str]. - Improvement. CLI struct-handler migration wave 2: generate, introspect, diff, serve, codegen, seed. The codegen
--modechoices are now registry-driven via choices_from. - Improvement. CLI struct-handler migration wave 3: all seven migrate subcommands; the legacy kwargs map API is fully retired from the codebase.
#Fixes
- Fix.
go build ./...andgo getof the module root work again: a stray root-levelversion.go(package mainwith nofunc main) broke compilation of the root package. Release binaries now embed the version correctly (goreleaser ldflags now setmain.Version, matching the declared variable; the old lowercasemain.versioninjection was a no-op). - Fix. Idempotent enum DDL no longer emits
CREATE TYPE IF NOT EXISTS(a syntax error on every PostgreSQL version). Enums and state machines now use the sameDO $$pg_type catalog-check block as domains and composites — generated idempotent output is finally executable and re-runnable on real databases. - Fix.
ALTER TYPE ADD VALUEmigrations now useIF NOT EXISTS, so retrying a partially-failed migration with enum additions no longer errors. - Fix. Constraints-mode codegen output was non-deterministic (random map iteration order) in all 6 languages, making freshness checks flap. All generators now emit in sorted order, and a determinism contract test locks byte-stability for every (mode, lang) combination in the registry.
- Fix. State machine states and composite type fields now preserve TOML declaration order end-to-end. Previously states were randomized per build (flapping freshness checks) and composite fields were silently alphabetized — both semantically wrong since PostgreSQL enum order and composite field order are meaningful. Duplicate composite field names are now rejected (E103).
- Fix. W013/W014 cascade warnings attributed cascades to the wrong end of the FK chain (walkers traversed child-to-parent while ON DELETE CASCADE flows parent-to-child). Messages now name the true delete origin.
- Fix. Python DDL tuples for sequences, policies, triggers, materialized views, and append-only triggers now carry real
idempotent_sql(previously silently fell back to non-idempotent SQL). Domain-backed columns are schema-qualified in CREATE TABLE (generated DDL failed on fresh databases without search_path luck). An execution-backed test matrix now runs every emitted statement — idempotent variants twice — against live PostgreSQL.
#0.21.0
Flag renames for strictcli v0.18.0 compliance, fix testdb gc --older-than bug.
Context
strictcli v0.18.0 bans bare --force and --no-* prefixed flags. Three flags renamed: --no-comments becomes --comments (Default(true)), --no-commit becomes --auto-commit (Default(true)), --force becomes --force-overwrite. Also fixed --older-than flag on testdb gc which was silently broken due to hyphen-to-underscore normalization mismatch.
#Breaking
- Breaking change. Three flags renamed for strictcli v0.18.0 compliance:
--no-commentsbecomes--comments(default true, negate with--no-comments),--no-commitbecomes--auto-commit(default true, negate with--no-auto-commit),--forcebecomes--force-overwrite.
#Fixes
- Fix.
testdb gc --older-thanwas silently broken — the kwargs key used hyphens instead of underscores, causing the flag to always be ignored.
#0.20.0
Type resolution overhaul, StrEnum, faceted output v2, self-contained split mode, source routing, executor features, and validation gates.
Context
Major codegen overhaul driven by the orxtra migration wishlist. Key changes:
TypeKind on Column + TypeResolver: enum-typed columns now emit the actual enum class name instead of string in all 6 languages. The type resolution system is unified through a single TypeResolver that dispatches on col.TypeKind.
Python enums use StrEnum (3.11+) instead of (str, Enum). Standalone --mode enums for enum-only output. E225 validates FK on_delete values. Build/generate/codegen validate the schema before generating.
Faceted output redesigned: DDLStmt namedtuple (7 fields) replaces 4-tuples. Shared executor in faceted mode. Self-contained split mode with idempotent type preamble. exclude_sections + SECTION_KINDS + name validation on executor. extension_stubs for test environments. Source-aware output routing with AND composition with groups.
#Breaking
- Breaking change. Python enum codegen now emits
class Foo(StrEnum):instead ofclass Foo(str, Enum):. Requires Python 3.11+. - Breaking change. Enum-typed columns in generated code now emit the enum class name (e.g.,
RunStatus) instead of the language's string type. Affects all 6 languages across types, constraints, GORM, and query-layer codegen. Powered by a newTypeKindfield on columns and a unifiedTypeResolverthat replaces per-language inline type switches. - Breaking change.
--split-by-fileflag andsplit = trueconfig replaced by--split-mode faceted|self-containedandsplit_modeconfig field. - Breaking change. Faceted DDL output now uses
DDLStmtnamedtuple (7 fields) instead of plain 4-tuples. Faceted mode now includes a sharedschema_executor.pywith the fullexecute()/verify()/create_schema()/ensure_schema()API.
#Features
- New feature.
build,generate, andcodegencommands now validate the schema before generating output. Invalid schemas produce error diagnostics and abort — no more generating code from broken schemas. - New feature.
--mode enumscodegen mode generates standalone enum definitions for all 6 languages, without row dataclasses or other types. - New feature. E225 validates that FK
on_deleteis one of the 5 valid PostgreSQL actions (CASCADE, RESTRICT, SET NULL, SET DEFAULT, NO ACTION). E201 suggestion updated to list all 5. - New feature. Generated executor gains
exclude_sections,SECTION_KINDSconstant with name validation, andextension_stubsparameter for substituting extension DDL in test environments. - New feature. Self-contained split mode (
--split-mode self-contained): each per-source file includes an idempotent type preamble, making it independently executable without ordering dependencies. - New feature. Source-aware output routing:
sourcefield in[output]config filters tables by TOML source file. Composes withgroupsas AND. Types pass through unfiltered.
#0.19.3
Fix --split-by-file flag being silently ignored, and 3x faster test suite in -short mode.
#Features
- Improvement. Test suite runs ~3x faster in
-shortmode by skipping WASM-dependent tests that trigger 6s parser initialization. CI still runs all tests.
#Fixes
- Fix.
--split-by-fileflag now works correctly — the kwargs key was using hyphens instead of underscores (strictcli normalizes flag names to underscores). Previously the flag was silently ignored.
#0.19.2
Fix panic in Python DDL codegen when --split-by-file is not passed.
#Fixes
- Fix.
pgdesign codegen --lang python --mode ddlno longer panics when--split-by-fileis not passed. The nil type assertion on the flag value crashed all Python DDL codegen.
#0.19.1
Fix SourceFile storing absolute paths, which broke CI golden tests.
#Fixes
- Fix. SourceFile now stores the filename only (not the absolute path), fixing golden test failures on CI where paths differ between machines.
#0.19.0
Source file provenance tracking and faceted Python DDL output (--split-by-file).
Context
Adds SourceFile tracking to all 8 model types, enabling per-source-file code generation. The Python DDL codegen gains a faceted output mode that splits output by DDL concern: extensions.py, types.py, tables_
#Features
- New feature. All schema objects (tables, views, enums, domains, composite types, sequences, functions, materialized views) now track which source TOML file defined them via a
SourceFilefield, preserved through multi-file builds and topo sort. - New feature. Python DDL codegen supports faceted output via
--split-by-fileflag orsplit = truein pgdesign.toml. Produces per-concern files:extensions.py,types.py,tables_<source>.py(one per input TOML), andpost_tables.py— instead of a single monolithic file.
#0.18.1
ILIKE constraint codegen fix, splitfmt hardening, transactional enum additions on PG 12+, and Plan unit tests.
#Features
- New feature.
ALTER TYPE ADD VALUEruns inside transactions on PG 12+ instead of requiring a separate non-transactional step. Controlled by theTransactionalEnumAddpgcap capability.
#Fixes
- Fix.
splitfmt.Decodenow caps pre-allocation to prevent OOM on malformed input and rejects trailing bytes after valid content. - Fix. Constraint codegen now emits case-insensitive matching for
ILIKECHECK constraints: Go/Java use(?i)regex prefix, TypeScript uses/regex/iflag, Python usesre.IGNORECASE, Kotlin usesRegexOption.IGNORE_CASE. Zig skips ILIKE patterns with a comment (locale-aware folding unavailable).
#0.18.0
PG capability registry, mandatory pg_version, and complete --idempotent coverage for all DDL types.
Context
Introduces a formal PG capability registry (internal/pgcap) that replaces scattered inline version comparisons with a declarative table mapping capabilities to minimum PG versions. All version-gated behavior now uses pgcap.Has() for consistency.
pg_version is now mandatory for DDL-generating commands (generate, build, check, migrate). Commands that don't produce DDL (fmt, codegen, diff, seed) are unaffected. This eliminates the entire class of "unknown PG version" bugs where the tool had to guess behavior.
The --idempotent flag now covers every DDL statement type: sequences (IF NOT EXISTS), domains and composite types (DO $$ catalog check), policies (CREATE OR REPLACE on PG 15+, catalog check on older), materialized views (DO $$ catalog check), and all three trigger types (CREATE OR REPLACE on PG 14+, DROP IF EXISTS + CREATE on older). Previously, triggers, domains, composite types, policies, matviews, and sequences were silently non-idempotent.
#Breaking
- Breaking change.
pg_versionis now required inpgdesign.tomlfor DDL-generating commands (generate,build,check,migrate). Commands that don't generate DDL (fmt,codegen,diff,seed) are unaffected.
#Features
- New feature. PG capability registry (
internal/pgcap) provides declarative version-to-feature mapping. All inline PG version comparisons replaced withpgcap.Has()calls for 10 capabilities (PG 10-18). - New feature.
--idempotentDDL generation now covers all statement types: sequences (IF NOT EXISTS), domains and composite types (DO 1996273catalog check), policies (version-gatedOR REPLACEon PG 15+), materialized views (DO 1996273catalog check), and triggers (version-gatedOR REPLACEon PG 14+,DROP IF EXISTS + CREATEon older).
#Fixes
- Fix.
generate,build, andcheck --tag buildnow write the resolved PG version back to the schema, ensuring version-gated validation and DDL generation use the config-levelpg_versionconsistently.
#0.17.0
Config phase-separation, build freshness checking, sealed .sqlsplit format, opaque testdb handles, and Zig constraint codegen.
Context
Major architectural changes across five areas:
Config system redesigned with generic path types (RelativePath/AbsolutePath) and phase-separated RawConfig/ResolvedConfig. Config files are now discovered via walk-up directory search (like git finding .git/).
Build command refactored into Plan/Apply architecture with a new freshness check (pgdesign check --tag build) that enforces the working tree as a fixed point of pgdesign build.
Testdb fixtures redesigned as opaque handles -- database names are no longer publicly accessible. New ConnString()/WithRawConn() API replaces raw field access. The .split.json format is replaced by a sealed .sqlsplit format that is trivially parseable in all 6 target languages.
Zig joins Go/TS/Python/Java/Kotlin as a supported language for constraint codegen, using std.mem for LIKE pattern matching.
#Breaking
- Breaking change. Testdb fixtures redesigned as opaque handles. Database name is no longer publicly accessible. New API:
ConnString()for ORM integration,WithRawConn(callback)as escape hatch. All 6 language templates updated..split.jsonreplaced by.sqlsplitformat. Re-runpgdesign testdb initto regenerate wrappers.
#Features
- New feature. Config system redesigned with phase-separated types (
RawConfig/ResolvedConfig), path newtypes (RelativePath/AbsolutePath), walk-up directory search forpgdesign.toml, and--configglobal flag. - New feature. Sealed
.sqlsplitformat for carrying split SQL statements to test fixtures, replacing.split.json. Length-prefixed text format trivially parseable in all 6 target languages. - New feature. Build command refactored into Plan/Apply architecture.
--dry-runnow shows per-file freshness status ([missing]/[stale]/[fresh]) instead of just listing files. - New feature.
pgdesign check --tag buildverifies generated output files are up-to-date. Reports stale and missing files with a fixed-point invariant: the working tree must be a fixed point ofpgdesign build. - New feature. Zig constraint codegen mode (
--mode constraints --lang zig). Generates client-side validation from CHECK/NOT NULL/enum constraints usingstd.memfor LIKE pattern matching.
#Fixes
- Fix. State machine
enforce_triggeroverride via extends now uses explicit*boolinstead ofbool, preventing unintended trigger suppression when extending state machine types. - Fix.
testdb initnow correctly resolves relative output paths frompgdesign.tomlinstead of using them raw, fixing broken.split.jsonreferences when config paths are relative.
#0.16.1
Fix diff --base extension type loading, normalize test aliases, update strictcli
#Fixes
- Fix.
diff --basenow loads extension types from project config, fixing failures when schemas use extension-provided types like pgvector.
#0.16.0
Structured type system with typeinfo package, type extends mechanism, UUID v7 support, and safety improvements
Context
This release introduces two foundational features: the typeinfo package for structured PostgreSQL type representation (alias normalization, precision/scale parsing, DDL reconstruction), and the type extends mechanism that enables user-defined types to derive from builtins or other types with per-kind merge semantics (scalar field overlay, enum additive values, composite additive fields, state machine additive states/transitions). Also includes UUID v7/pg_uuidv7 support, DefaultExpr emission in all four ORM codegen modules, a testdb teardown safety guard, and a PyPI wrapper version sync fix.
#Features
- New feature.
testdb teardownrefuses to drop databases that don't match the ephemeral naming pattern, preventing accidental deletion of non-test databases. - New feature.
pg_uuidv7extension registered in builtin registry withuuid_generate_v7function. - New feature.
typeinfopackage provides structured PostgreSQL type representation with alias normalization, precision/scale/length parsing, and DDL reconstruction. - New feature. Diff engine now has a default-precision lookup table, avoiding false diffs for
timestampvstimestamp(6)and similar PostgreSQL type defaults. - New feature. User-defined types can shadow builtin types. Sealed field enforcement prevents changing
kindorbase_typewhen shadowing. Diagnostic I101 when a builtin is shadowed. - New feature.
extendskeyword on[types.*]enables type derivation. Scalar types can extend builtins or other user types, inheriting fields and overriding specific ones. Topological sort ensures correct resolution order. Diagnostics E115 (circular), E116 (unknown target). - New feature. All type kinds support
extends: enum (additive values), composite (additive fields with E118 collision detection), state machine (additive states/transitions with E119 collision, overridable initial state, reachability validation). - Improvement. Expanded all CLI help text to be more descriptive and useful.
#Fixes
- Fix. PyPI wrapper now uses correct version for binary download URL instead of hardcoded 0.1.0.
- Fix. Renamed volatile default detection to non-immutable default detection.
now()andstatement_timestamp()correctly remain in the list (STABLE, not IMMUTABLE). Addeduuid_generate_v7()anduuid_generate_v4()detection. - Fix. SQLAlchemy, Drizzle, GORM, and JPA codegen now emit server defaults for expression-based defaults (
DefaultExpr), not just literal defaults.
#0.15.0
State machine types, Python query-layer codegen, table groups, and constraint codegen for 3 new languages
Context
This release introduces state machine types (kind = "state_machine") as a first-class schema concept with PG enum creation, PL/pgSQL enforcement triggers, D2 state diagrams, diff/migration support, and codegen transition maps for all 6 languages. Python codegen gains two new modes: DDL-as-data (pure tuples with a section executor) and query-layer (protocol-based PgBackend and InMemoryBackend with declarative constraint registry). Constraint codegen expands from 2 to 5 languages (adding Python, Java, Kotlin). Table groups enable config-based output filtering. Breaking: unknown type kind values now error instead of silently defaulting to scalar.
#Breaking
- Breaking. Unknown type
kindvalues now produce a hard error (E104) instead of silently defaulting to scalar.
#Features
- New feature. State machine types (
kind = "state_machine") with states, transitions, initial state, and enforcement trigger generation. Includes PG enum creation, validation checks (W027/W028/E223/E224/E226), D2 state diagrams, and PL/pgSQL trigger enforcement. - New feature. State machine diff and migration support: transition changes trigger regeneration operations with phantom trigger prevention.
- New feature. State machine transition maps in codegen types and constants output for all 6 languages.
- New feature. Table groups (
[groups]in schema TOML) with config-based output filtering and group table reference validation (E227). - New feature. Python DDL codegen mode (
--mode ddl) with pure data tuples and decomposed section executor, using MultiFileGenerator interface. - New feature. Python query-layer codegen mode (
--mode query-layer) with protocol-based backends (PgBackend, InMemoryBackend), per-table delegates, declarative constraint registry, and composite file organization. - New feature. Constraint codegen for Python, Java, and Kotlin (5 languages total: Go, TS, Python, Java, Kotlin).
- New feature.
MultiFileGeneratorinterface for codegen modes that produce multiple output files. - New feature.
CAST(x AS type)syntax support in sqlexpr parser. - New feature. Zig testdb template rewritten against pg.zig 0.16 API.
- New feature. Config
validModesderived from codegen registry for lang-mode pair validation. - New feature.
ExtractConstraintsnow includes domain-backed CHECK constraints viaDomainBaseTypesmapping. - New feature. Introspect filters out
_pgdesign_sm_triggers (generated state machine enforcement).
#Fixes
- Fix. W019 now correctly flags the wider range constraint as redundant (was incorrectly flagging the stricter one).
- Fix. Migration TOML serialization now round-trips
Desc,Operators,Deferrable, andInitiallyDeferredfields correctly. - Fix. TypeScript testdb template adds error cause chaining; Python template replaces
assertwithraise ValueError.
#0.14.2
Critical panic fix, UTF-8-safe truncation, DDL error diagnostics, selfdoc coverage
Context
Fixes a critical bug where the pgdesign binary panicked on every invocation since v0.14.0 due to a missing strictcli flag configuration. Also fixes UTF-8 truncation in wrapper templates, adds SQL previews to DDL error messages, and adds selfdoc documentation for 6 previously undocumented packages.
#Features
- DDL error diagnostics. Wrapper template error messages now include the first line of the failing SQL statement (capped at 120 chars) alongside the statement index.
- selfdoc coverage. Added documentation for 6 previously undocumented packages: codegen, graph, seed, sqlparse, sqlutil, workload.
#Fixes
- Fix. CLI no longer panics on startup. The
testdb init --languageflag was missing a required strictcli configuration, causing everypgdesigninvocation to crash since v0.14.0. - Fix. Wrapper templates now truncate database names at valid UTF-8 character boundaries instead of splitting multi-byte sequences.
#0.14.1
Wrapper template hardening, versioned .split.json, CI workflow generation, consumer documentation
Context
Follow-up to v0.14.0's ephemeral test database feature. Fixes SQL interpolation and modulo bias in wrapper templates, adds a versioned .split.json format for future-proofing, generates GitHub Actions CI workflows via --ci flag, adds consumer-facing documentation and selfdoc coverage for new packages.
#Features
- **Versioned
.split.jsonformat.** The companion DDL file now uses{"version": 1, "statements": [...]}instead of a bare array, enabling format evolution without breaking existing wrappers. - **
testdb gcshows active connections.**ListOrphansnow exposes the active connection count per database. Thegccommand prints it alongside each dropped database name. - CI workflow generation.
testdb init --ci github-actionsgenerates a GitHub Actions workflow with a Postgres service container, per-language toolchain setup, andPGDESIGN_DBconfiguration.
#Fixes
- Wrapper template hardening. Zig terminate query uses parameterized
$1instead of string interpolation. TypeScript and Zig random name generation uses rejection sampling to eliminate modulo bias. Go template adds post-truncation 63-byte assertion.
#0.14.0
RLS full parity, seed overhaul, FK graph, design intelligence, migration intelligence, workload analysis, ephemeral test databases
Context
Major feature release spanning seven areas: RLS policies gain PERMISSIVE/RESTRICTIVE types and full diff/migrate/introspect support; seed generation covers all 42 PG types with CHECK/UNIQUE awareness and multiple output formats; FK graph provides O(1) lookups and cascade walkers used by all codegen backends; design intelligence adds cascade, subsumption, dead column, and row size checks; migration intelligence adds phase annotations, NOT VALID auto-split, batched DML, shadow testing, volatile default detection, squash consolidation, and multi-step rollback; workload analysis integrates pg_stat_statements with N+1 and slow query detection; ephemeral test databases (testdb commands) with native wrapper generation for 6 languages prevent tests from destroying production data.
#Breaking
- Breaking.
seed.Generatesignature changed: acceptsSeedConfigstruct, returns diagnostics instead of error. - Breaking. Diagnostic code E300 replaced by concrete NOT VALID + VALIDATE CONSTRAINT migration operations.
#Features
- RLS full parity. PERMISSIVE/RESTRICTIVE policy types, FORCE ROW LEVEL SECURITY, policy diff/migrate/introspect, PG 10+ version gate (E222), operation gap warnings (W011/W012).
- Seed overhaul. All 42 PG types covered,
--seedflag, CHECK constraint awareness (regex generation), UNIQUE tracking, Zipf/log-normal distributions, array/JSONB population, per-table row counts, FK cycle handling, NULL injection, COPY format, batch INSERT,--clean,--mode edge-cases, BEGIN/COMMIT wrapping. - FK graph on Schema. Persistent forward/reverse adjacency lists,
TablesByNameO(1) lookup, CASCADE walkers (depth/breadth/chain). All codegen backends refactored to useFKGraph.Reverse. - **Design intelligence (
--tag design).** CASCADE warnings W013-W015, natural key surfacing I001, constraint subsumption W016-W019, dead column detection I002, row size estimation I003/W021/I004. - Migration phase annotations. Expand/migrate/contract phase classification, safe-only collapse, phase-by-phase apply, phase grouping in plan output.
- NOT VALID + VALIDATE auto-split. Large-table FK additions automatically split into non-blocking NOT VALID constraint followed by VALIDATE CONSTRAINT.
- Batched DML. Configurable
batch_sizefor DML operations with execution loop. - Shadow database testing.
migrate test --shadowreplays migrations against a temporary database, introspects the result, and diffs against the schema. - Volatile default detection.
migratedetects volatile defaults (e.g.,now(),random()) in add_column operations and warns about table rewrites. - Squash CREATE TABLE consolidation.
migrate squashconsolidates add_column/add_index operations into CREATE TABLE statements, strips phase annotations from squashed output. - Applied migration safety check.
migrate squashrefuses to squash applied migrations when--dbis provided. - Multi-step rollback.
migrate rollback --torolls back to a specific migration version. - **Workload analysis (
--tag workload).** Structural index recommendations W022-W024/I005, pg_stat_statements integration, N+1 detection W025, slow query patterns W026/I006/I007, duplicate index consolidation. - Ephemeral test databases.
testdb setupcreates a randomly-named database, applies DDL, and prints the connection URL.testdb teardowndrops it.testdb gccleans up orphaned test databases by age. - Test wrapper code generation.
testdb init --language go --language pythongenerates native test helpers for 6 languages (Go, Python, TypeScript, Java, Kotlin, Zig) that create ephemeral databases using each language's PG driver. No pgdesign binary needed at test runtime. - Pre-split DDL for language wrappers.
buildauto-generates a.split.jsoncompanion file alongside SQL outputs, containing DDL split into individual statements via the PostgreSQL parser. Language wrappers read this instead of raw SQL, avoiding the need for a SQL parser in each language.
#Fixes
- Fix. Generate migration DDL for changed foreign keys (drop old + add new).
- Fix. Render functions, sequences, exclusions, triggers, collation, statistics, domains, and composites in
FormatTerminaldiff output. - Fix. Add regex operators (
~,~*,!~,!~*) to sqlexpr parser.
#0.13.0
Schema object expansion, codegen modes, BCNF audit, functions, triggers, domains
Context
Major feature release adding six new schema object types (domains, exclusion constraints, standalone sequences, composite types, functions/procedures, triggers) with full pipeline support (parse, model, DDL, diff, migrate, introspect). Codegen expanded from 3 modes to 9 (types, constraints, gorm, drizzle, sqlalchemy, jpa added). BCNF audit with decomposition algorithms, Armstrong relation counterexamples, and FD inference from PK/UNIQUE constraints. Scalar types now produce CREATE DOMAIN for database-level enforcement. Generic TopoSort and matchObjects infrastructure reduces code duplication. Multi-column partition keys, per-column collation and statistics, range/multirange types, identity column introspection, and deferrable constraint support round out the release.
#Breaking
- Diagnostic code reassignment. E100-E103 moved to E120-E123, E110 moved to E205, E215 moved to E300 to resolve cross-package collisions. Code allocation ranges now documented.
- generate.Generate signature changed. Now returns diagnostics alongside DDL output.
- Scalar types now produce CREATE DOMAIN. Scalar types with CHECK expressions generate CREATE DOMAIN DDL instead of inline constraints, changing DDL output for all schemas using scalar CHECKs.
- PartitionSpec.Column renamed to Columns. Multi-column partition keys now use a string slice, breaking existing single-column partition definitions in Go API consumers.
#Features
- FD inference from PK/UNIQUE (A100). Normal form audit now auto-infers functional dependencies from primary keys and unique constraints, requiring explicit dependency declaration.
- BCNF audit check (W103). Normal form audit now detects Boyce-Codd violations with Armstrong relation counterexamples showing concrete data that breaks the constraint.
- BCNF decomposition with verification. Suggests lossless-join decompositions for BCNF violations, verified for both lossless-join and dependency-preservation properties.
- Minimal cover visualization (I100). Audit now shows the minimal cover of functional dependencies as an informational diagnostic.
- FD source tracking. Functional dependencies now carry their source (declared, discovered, or inferred) through audit diagnostics.
- Generic TopoSort and matchObjects infrastructure. Table, view, and materialized view ordering now uses a shared generic topological sort. Diff object matching uses a shared generic matcher.
- Codegen enum generation for all 6 languages. Enums from the schema are now generated as native enum types (Go iota, TS union, Java enum, Kotlin enum, Python StrEnum, Zig enum) with name sanitization for language-specific keywords.
- Constraint validation codegen (--mode constraints). Generates type-safe CHECK constraint validation functions for Go and TypeScript from schema constraints.
- GraphQL schema generation (--format graphql). New output format generates GraphQL type definitions from the schema model.
- SQLAlchemy 2.0 model generation (--mode sqlalchemy). Generates Python SQLAlchemy declarative models from the schema.
- GORM struct tag generation (--mode gorm). Generates Go structs with GORM-compatible struct tags for ORM integration.
- Drizzle schema generation (--mode drizzle). Generates Drizzle ORM schema definitions in TypeScript.
- JPA entity generation (--mode jpa). Generates Java JPA entity classes with annotations from the schema.
- CREATE DOMAIN from scalar types. Scalar types with CHECK constraints now produce CREATE DOMAIN DDL, enforcing semantic type validations at the database level.
- Exclusion constraints. New [tables..exclusions.] TOML section for defining exclusion constraints with GiST/SP-GiST support, diffing, migration, and E221 btree_gist validation.
- Deferrable unique and exclusion constraints. Unique and exclusion constraints now support DEFERRABLE and INITIALLY DEFERRED attributes across parse, DDL, diff, migrate, and introspect.
- Standalone sequences. New [sequences.*] TOML section for defining standalone sequences with owned_by references, full DDL generation, diffing, migration, and introspection.
- Composite types. New kind=composite type definition with named fields, DDL generation, diffing, migration, introspection, and TOML export.
- Functions and procedures. New [functions.*] TOML section supporting body/file, all PostgreSQL function attributes (LANGUAGE, VOLATILITY, SECURITY, COST, ROWS), diffing, migration, and introspection.
- User-defined triggers. New [tables..triggers.] TOML section with CONSTRAINT, REFERENCING, WHEN support, DDL generation, diffing, migration, and introspection.
- Function dependency auto-detection. LANGUAGE sql functions automatically detect table references via ExtractTableRefs, populating DependsOn without manual declaration.
- Multi-column partition keys. Partition specifications now support columns=["a","b"] for multi-column PARTITION BY clauses, with pg_partman single-column validation.
- Per-column collation, index collation, and statistics targets. Columns support collation override, indexes support per-column collation, and columns support custom statistics targets across DDL, diff, migrate, and introspect.
- Range and multirange types. 12 range/multirange types (int4range, tstzrange, etc.) added to the type allowlist.
- Identity column introspection. Introspect now detects GENERATED ALWAYS/BY DEFAULT AS IDENTITY columns via attidentity.
- Shared codegen type mapping infrastructure. All codegen generators now share a unified TypeMapping system, eliminating duplicated type conversion logic.
- Shared SelectGenerator for codegen. Extracted common generator selection logic to eliminate duplicated switch trees across codegen modes.
#Fixes
- Scalar type CHECKs no longer silently lost. CHECK constraints defined on semantic types are now enforced at the database level via generated domains.
- E221 validates FD column names. Functional dependency declarations now error if they reference non-existent columns.
#0.12.3
Fix CWD-dependent panic and add view/materialized view support
Context
pgdesign panicked when run from any directory that did not contain .strictcli/checks.toml. The fix embeds checks.toml at compile time via go:embed. Additionally, validation, risk assessment, and migration support have been extended to views and materialized views.
#Features
- New feature. Extend validation, risk assessment, and migration support to views and materialized views.
#Fixes
- Bug fix. Fix panic when running pgdesign from outside its own directory.
#0.12.2
#Breaking
- Breaking. Split E217 into two codes: E217 for unknown index methods (typo/nonexistent) and E219 for known extension methods with undeclared extensions. Existing E217 diagnostics for undeclared extensions are now E219.
#0.12.1
New E217 rule for extension-provided index methods, introspection diff fixes, and documentation corrections.
#Features
- New rule. E217 validates that extension-provided index methods (hnsw, ivfflat) require the providing extension to be declared.
#Fixes
- Fix.
parseSimpleDefaultnow handles double casts and COLLATE clauses, reducing false positives when diffing introspected schemas. - Fix. Global flags documentation corrected (--db and --strict-nf are per-command since v0.9.0).
#0.12.0
Codegen types mode for all 6 languages, introspect improvements, and command consolidation
Context
This release completes the codegen --mode types feature across all supported languages (Go, TypeScript, Python, Java, Kotlin, Zig), improves introspect fidelity with unified default handling and pg_depend-based view dependencies, generates safer ALTER INDEX SET migrations for builtin method parameter changes, and removes the deprecated validate/audit commands in favor of the unified check command.
#Breaking
- Breaking. Removed
validateandauditcommands. Usepgdesign check --tag validationandpgdesign check --tag nfinstead.
#Features
- New feature. Codegen
--mode typesnow supports all 6 languages (Go, TypeScript, Python, Java, Kotlin, Zig). - New feature. Introspect unifies simple defaults into the Default field, reducing false positives when diffing TOML schemas against live databases.
- New feature. View dependency extraction uses pg_depend instead of regex heuristics.
#Fixes
- Fix. Index WITH parameter changes on builtin methods (btree, gin, gist) now generate ALTER INDEX SET instead of DROP+CREATE.
#0.11.0
SQL parser foundation, VIRTUAL generated columns, PGVersion fix, go-pgquery integration
Context
Foundation release for the SQL parser infrastructure. The sqlexpr expression parser gained comparison operators, IS NULL, IN, BETWEEN, LIKE, and arithmetic operators. The wasilibs/go-pgquery WASM-based PostgreSQL parser replaces naive statement splitting in migrations and regex-based index parsing in introspect. PGVersion is now properly threaded through to risk classification. VIRTUAL generated columns (PG 18+) are fully supported across parse, model, DDL, migration, introspect, and diff. Silent failures in validate and codegen are now surfaced as diagnostics. The generate.Generate function signature changed to return diagnostics alongside the output.
#Breaking
- **Breaking:
generate.Generatesignature changed.** Returns(string, []diagnostic.Diagnostic, error)instead of(string, error). Callers must handle the new diagnostics return value.
#Features
- Doc output in build. The
docformat is now generated bypgdesign buildalongside SQL, D2, JSON, and SVG. - VIRTUAL generated columns. PG 18+ VIRTUAL generated columns are now supported. When
storedis omitted from TOML, defaults to STORED. New E218 validation catches version mismatches. - STORED-to-VIRTUAL transition detection.
diffnow detects and warns about STORED-to-VIRTUAL generated column transitions, which require destructive DROP+recreate. - Introspect generated columns.
introspectnow readspg_attribute.attgenerated(PG 12+) to detect STORED and VIRTUAL generated columns from live databases. - Extended SQL expression parser.
sqlexprnow supports comparison operators (<,>,<=,>=), division/modulo (/,%%),IS NULL/IS NOT NULL,IN,BETWEEN,LIKE/ILIKE,IS DISTINCT FROM, float literals, andNULLliterals. Parse errors now carry byte position. - View/matview cycle warnings. DDL generation now emits a diagnostic Warning and an inline SQL comment when view or materialized view dependency cycles are detected.
- Proper SQL statement splitting. Migrations now use wasilibs/go-pgquery (WASM-based PostgreSQL parser) for statement splitting, correctly handling dollar-quoted PL/pgSQL bodies, string literals with semicolons, and all PostgreSQL syntax.
- AST-based index introspection.
introspectnow parses CREATE INDEX definitions using the PostgreSQL parser AST instead of regex, correctly handling expression indexes and complex WHERE clauses.
#Fixes
- PGVersion threading fix. Migration risk classification now receives the live PostgreSQL version, so ADD COLUMN NOT NULL DEFAULT is correctly classified as Safe on PG 11+ instead of Dangerous.
- Generated column migrations.
migrate generatenow emits correctGENERATED ALWAYS AS (...) STORED|VIRTUALDDL when adding generated columns to existing tables. - Silent failure fixes. E213 now emits a Warning when a generated column expression cannot be parsed (previously silently skipped). Codegen now distinguishes parse failures (C002) from unrecognized patterns (C001).
- Codegen double-parse eliminated. RLS policy expressions are now parsed once and the AST is reused across all 6 language validators.
- Fix TOML serialization for VIRTUAL generated columns. Migration TOML files now correctly preserve
stored = falsefor VIRTUAL generated columns.
#0.10.0
Extension types, views, materialized views, index WITH parameters, codegen types mode
Context
Adds extension-provided types as valid scalar base types (e.g., pgvector's vector type), views and materialized views across the full pipeline (parse, model, SQL, diff, migrate, introspect, diagram, docs), index WITH storage parameters with E216 validation, codegen --mode types for Go, pgvector as a built-in extension, and array migration generation fix.
#Features
- Extension types as base types. Extension-provided types (e.g.,
vectorfrom pgvector) declared via[[extensions]]in pgdesign.toml are now valid base types for scalar definitions. - pgvector builtin extension. pgvector is now a built-in extension with types, operator classes, functions, and index methods pre-registered.
- Index WITH storage parameters. Index definitions now support
with = { key = "value" }for PostgreSQL storage parameters (e.g., HNSWm,ef_construction). E216 validates parameters against the index method. - Index WITH introspection and migration. Introspect reads
reloptionsfrompg_classto round-trip WITH parameters. Migration files preserve WITH fields through parse and write. - Views. New
[views.*]schema section with query, comment, and depends_on. Full pipeline: parse, model, SQL generation, diff, migration (CREATE/DROP/CREATE OR REPLACE VIEW), introspect, D2 diagram, and doc output. - Materialized views. New
[materialized_views.*]schema section with query, comment, with_data, depends_on, and nested indexes. DDL generation includesCREATE MATERIALIZED VIEW,DROP MATERIALIZED VIEW, andREFRESH MATERIALIZED VIEW. - Materialized view diff, migration, and introspection. Diff detects materialized view additions, removals, and query changes. Migration generates DROP+CREATE for changes (no ALTER support). Introspect reads from
pg_matviews. D2 and doc output include materialized views. - Materialized view TOML export. Introspect exports materialized views to TOML format for round-tripping.
- **Codegen
--mode types(Go).** New codegen mode generates Go type definitions from the schema, including struct types with JSON tags and enum type constants.
#Fixes
- Array migration generation. Diff-detected
ArrayChangednow correctly generatesalter_column_typemigration operations (e.g.,texttotext[]). - IndexMethods wiring. Extension
IndexMethodsfield is now propagated through the registry and used for validation. Fixed field alignment in Registry struct.
#0.9.0
CLI overhaul with build, check, stats, seed, codegen for 6 languages, TOML diff, doc format, migration squash and test.
#Breaking
- Breaking. Removed
extensioncommand group. Extension discovery moved topgdesign introspect --extensions.--dband--strict-nfmoved from global to per-command flags.diff --dbrenamed todiff --live.
#Features
- New feature.
pgdesign checkcommand using strictcli check system. Tags:validation(schema rules),nf(normal form audit),coverage(constraint completeness). Replacesvalidateandauditfor config-driven analysis. - New feature.
pgdesign buildcommand for config-driven batch generation. Reads[output]section from pgdesign.toml, generates SQL/D2/JSON/doc/codegen outputs to declared paths. Auto-commits. Integrates with rlsbl release. - New feature. Coverage analysis via
pgdesign check --tag coverage. Detects: tables without check constraints (C100), FK columns without indexes (C101), unused enum types (C102), orphan tables (C103), missing FK join indexes (C104). - New feature. Go code generation for constants and RLS policy validators.
- New feature. TypeScript, Java, and Kotlin code generation for constants and RLS policy validators.
- New feature.
pgdesign diff --against <path>for comparing two TOML schemas.pgdesign diff --base <git-ref>for comparing against a git branch or commit. - New feature.
pgdesign generate --format docproduces a Markdown data dictionary with table descriptions, column details, and relationships. - New feature.
pgdesign stats --db <url>reports unused indexes, vacuum candidates, cache hit ratio, and duplicate indexes. - New feature.
pgdesign migrate squash --from <ver> --to <ver>collapses a range of migrations into one, canceling inverse operations. - New feature.
pgdesign migrate test --db <staging-url>applies migrations against a staging database in a transaction, validates, then rolls back. - New feature.
pgdesign seedgenerates type-aware test data respecting FK ordering, constraints, and semantic types.
#0.8.0
Pool configuration, OR-compound and NOT EXISTS codegen support, multi-table and multi-flag fixes.
#Features
- New feature. Connection pool configuration via
pool_max_connsandpool_min_connsin pgdesign.toml[database]section. - New feature. Codegen validators now support NOT EXISTS patterns with inverted logic.
#Fixes
- Fix. Codegen validators now correctly handle OR-compound RLS policies (e.g., ownership OR privacy lookup).
- Fix. Codegen validators now correctly reference each table when a policy has multiple EXISTS subqueries against different tables.
- Fix. Codegen validators now check all flag columns when an EXISTS subquery has multiple flag conditions.
#0.7.2
Bug fixes for array defaults, empty-string defaults, and codegen column names.
#Fixes
- Fix. Array columns with numeric base types (e.g.,
integer[]) now correctly produce quoted defaults (DEFAULT '{}') instead of bareDEFAULT {}. - Fix. Empty-string defaults (
default = "") now correctly produceDEFAULT ''in DDL instead of being silently dropped. - Fix. Codegen validators now use the correct column name from RLS policy expressions instead of hardcoding
player_id.
#0.7.1
E110 validation rule, documentation updates, JSON output determinism fix, and expanded test coverage.
#Features
- New rule. E110 catches embedded SQL quotes in default values across all types (enums, scalars, arrays). Use raw values instead of SQL literals.
- Documentation. Updated format reference, validation rules, semantic types, migration guide, and CLAUDE.md for all v0.7.0 features.
#Fixes
- Fix. JSON output format now produces deterministic output by sorting model collections before marshaling.
#0.7.0
Array types, warning suppression, append-only tables, expression-driven codegen, JSONB shape validation, and serve API improvements. Breaking: enum defaults now use raw values.
#Breaking
- Breaking. Enum defaults must be raw values (e.g.,
"created"), not SQL literals ("'created'"). New E109 diagnostic validates defaults against declared enum values.
#Features
- New feature. Array column support via
array = trueproperty. DDL generatestext[], introspection detects arrays, diff tracks array changes. - New feature. Per-table and per-column warning suppression via
[suppress]section in pgdesign.toml with mandatory reason strings.--show-suppressedflag lists active suppressions. - New feature. Append-only tables via
append_only = true. Generates BEFORE UPDATE OR DELETE trigger. Diff and migration support for trigger lifecycle. - New feature. Expression-driven RLS validator generation using SQL expression parser. Removes all domain-specific patterns. Zig validator support.
- New feature. SQL expression parser (
internal/sqlexpr) for RLS policy analysis with recursive descent parsing. - New feature. JSONB shape validation via
json_schemacolumn attribute. Generates portable CHECK constraints from JSON Schema files. Auto-suppresses W004 on shaped columns. - New feature. Per-version migration endpoint, cache hit ratio, unused index detection, and duplicate index detection in HTTP API.
#Fixes
- Fix.
generatenow returns proper errors instead of embedding error messages in output. - Fix. Codegen reports unparseable policy expressions as diagnostics instead of silently skipping them.
#0.6.0
#Features
- New feature. Per-column name constants in codegen output
#0.5.0
#Features
- New feature. SQL constants codegen for Python and Zig (
--mode constants).
#0.4.0
#Features
- New. Support DESC sort order on index columns.
- New. RLS policy support — define row-level security policies in TOML, generate ALTER TABLE ENABLE RLS and CREATE POLICY DDL.
- New. Code generation framework with Python validator backend — generate async policy check functions from TOML schema definitions.
- Config system. pgdesign.toml now configures validation rules, migration lock timeout, format preferences, and user-defined extensions across all commands.
- Per-column opclass. Indexes now support different operator classes per column via map syntax.
- JSON output format.
pgdesign generate --format jsonoutputs the resolved schema as JSON. - New validation rules. E200 (missing column type), E212 (FK missing index), E213 (generated column references generated), W003 (boolean states), W004 (JSON could be table), W007 (redundant index). Enhanced E204 (column reference check) and E211 (index name check).
- Diff engine improvements. Track generated/identity column changes. Position-aware enum diffing detects middle inserts vs safe appends. Partition diff detects strategy, key, and child changes.
- Partition support. Introspect partitioned tables (pg_partitioned_table, pg_inherits) at arbitrary depth. Generate CREATE TABLE PARTITION OF and pg_partman SQL. Generate migrations for partition add/remove.
- FD discovery improvements. New --tables flag limits discovery to specific tables. --approximate flag controls FD error threshold. Discovery runs in parallel across tables.
- Expand-contract planner. Large table migrations automatically decompose NOT NULL additions into backfill steps. Row estimates from pg_stat_user_tables drive risk classification. Dry-run mode on migrate apply. E215 warns about FK constraints without NOT VALID.
- Extension discovery. New
pgdesign extension discover --dbcommand introspects installed extensions and outputs TOML config. - SQL builder improvements. CONCURRENTLY support on CREATE INDEX. Idempotent DO $$ wrappers for constraint statements.
- Strict normal form mode. --strict-nf flag promotes NF violations to errors and blocks DDL generation.
- Documentation. Five new documentation pages: quickstart, format reference, semantic types, validation rules, and migration guide.
#Fixes
- Fix. Schema-qualify enum types in DDL output.
- Format comment preservation.
pgdesign fmtnow preserves TOML comments when reordering sections. - Fix. Schema-qualify pg_partman and pg_cron function names. Expand btree_gin/btree_gist to full opclass lists.
#0.3.0
#Features
- Multi-file schema support. Parse multiple schema files or a directory. pgdesign.toml config loading. Cross-schema FK validation. CLI accepts variadic paths.
#Fixes
- Fix auto_id SQL generation. Identity columns (GENERATED ALWAYS AS IDENTITY) no longer produce malformed DDL.
- Fix multi-schema SQL output. Schema-qualified names (e.g., auth.users) now used correctly in all DDL statements.
- Fix unique index generation. Indexes with unique=true now emit CREATE UNIQUE INDEX.
#0.2.1
#Fixes
- Idempotent publish. npm and PyPI publish steps no longer fail when a version is already published.
#0.2.0
#Features
- Core schema compiler. Parse TOML schema definitions, build resolved intermediate representation, and generate SQL DDL.
- Schema validation and formatting. Validate schemas for anti-patterns and format TOML files canonically.
- Extension registry. Validate PostgreSQL extension, opclass, and type references.
- Normal form auditing. Audit schemas for 1NF/2NF/3NF violations with functional dependency analysis and TANE-based discovery from live data.
- Risk classification. Classify migration operations by risk level for safety review.
- Database introspection. Introspect live PostgreSQL databases via pg_catalog.
- Schema diffing. Diff schema files against live databases with risk-annotated output.
- Migration system. Generate, apply, and rollback migrations with safety linting and risk classification.
- D2 diagrams. Generate D2 diagram and SVG output for schema visualization.
- HTTP API server. Serve schema introspection, validation, diffing, and visualization via HTTP.
#0.1.2
- No user-facing changes.
#0.1.1
- No user-facing changes.
#0.1.0
- No user-facing changes.