pgdesign v0.26.0 /Changelog
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-consequential to skip the prompt in scripts. --dry-run, --quiet and --verbose are provided by the framework on every command instead of being declared per-command, so --dry-run now works everywhere and a dry run prints a DRY RUN header.

#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 a v1.0.0 for this module that was never a real release, so go install github.com/smm-h/pgdesign/cmd/pgdesign@latest installed 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 built jdbc:postgresql://null/db from 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 squash no 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 sibling migrations/archive/ directory.
  • **Actionable error when safegit is missing during build auto-commit.** pgdesign build auto-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-commit instead 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 json and serve's /api/schema are 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 serve payload keys changed.** generate json and serve's /api/schema response are now a single envelope {format_version, revision, model, diagnostics?} produced by one serializer. The schema body moved under the model key -- serve previously returned {schema, diagnostics} -- so HTTP API and JSON consumers must read .model instead of .schema (or the former top-level object).
  • Generated Java code now uses one public type per file. The types, jpa, and constraints Java 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 XxxValues constant and a parseXxx function to validate untyped input (JSON, DB rows) at boundaries; the string literal union type is unchanged. Use parseXxx at 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 with parse()/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 @Convert AttributeConverter (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 upgrade once per project.** Migrations are no longer semver-numbered .toml files but content-derived edges in migrations/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-time migrate upgrade command, 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, every migrate subcommand hard-errors against a pre-upgrade (legacy tracking-table) database, naming migrate upgrade.
  • **generate --idempotent now 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, and identifier/pgtype/sql-expression lexemes) through strictspec before parsing; a document without format_version is rejected, and keys that previously produced only a W001 unknown-key warning now reject the document outright. Remediation: run scripts/stamp_format_version.sh <file-or-dir> to stamp existing schemas.
  • **migrate plan is now pure and database-free in chain projects.** It enumerates the pending chain edges from genesis (or a --from revision) to the head without connecting to any database. The --db flag now applies only to legacy migration projects; use diff --live to preview drift against a running database.

#Features

  • New [tables.*.maintenance].schedule key schedules automatic partition maintenance via pg_cron; a partman-managed table with no schedule now warns (W029)
  • **pgdesign codegen gains --groups/--source filters and now matches build byte-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 --db and --live flag now falls back to the PGDESIGN_DB environment variable.** Set PGDESIGN_DB once 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 --hermetic suppresses the connection so database work skips instead of connecting.
  • migrate apply now 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 apply now 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 apply now 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-band ALTER that 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 in pgdesign.toml turns a column or table rename into a data-preserving ALTER ... RENAME instead 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 rebase command 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 baseline adopts an existing or drifted database onto the chain.** In chain-format projects, migrate baseline introspects 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 revise command.** 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 --output now refuses to write when sibling outputs are at a different revision, and a new check --tag revision verifies cross-artifact stamp agreement, JSON envelope revisions, and migration-chain integrity. fmt and introspect --output print 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 via ref_table = "alias:table". Alias references are scoped to FK ref_table; using one elsewhere is a hard error.
  • **pgdesign import lock/update and check --tag imports.** import lock resolves each import's git pin, vendors the referenced tables plus their type closure into imports/<alias>/, and writes a committed lockfile; import update re-pins. check --tag imports verifies 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 TABLE for 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 seed resolves imported-FK values through tiers: with --db it draws real keys from the live imported tables; offline (--format insert) it emits count-wrapped ordered-offset subqueries. Impossible cases fail loudly — offline --format copy with 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 imports enforces 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 serve project mode, loopback-bound by default.** Running pgdesign serve without --db now compiles and serves your project offline: /api/schema returns the canonical model envelope (byte-identical to generate json), with new /api/schema/graph (FK-graph projection) and /api/schema/doc endpoints, 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 --bind to change it, and note its help states plainly that the server has NO AUTHENTICATION.
  • **serve request timeouts and asynchronous audit.** The --timeout flag 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/jobs starts 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 accept live_stats=true and build honors [output.*.d2] live_stats=true to 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 explicit premake value; 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 squash now 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 migrations flag 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 <>, IN vs = 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 --live no 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 stored status = '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 Email no 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 target pg_version and 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 email vs text) now produce a non-empty diff, catching codegen-visible drift that was previously missed.
  • **migrate plan/generate no 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 --hermetic with 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 constraints output in a different directory from the Go types output no longer silently produces uncompilable code — build fails naming both directories and the co-location requirement.
  • build now hard-errors when a Go gorm codegen output is split from the Go constraints that reference its row structs and enums, and when Go types and gorm target the same directory (duplicate row-struct definitions that never compiled).
  • Multi-file projects whose tables live in public no longer false-drift: introspection namespaces now come from the model's schema= values, not the schema filenames, so upgrade, diff --live, plan, and migrate generate reconcile correctly.
  • migrate status on an upgraded (chain-mode) database no longer recreates the dropped legacy pgdesign_migrations table; it now reports the chain position, applied edges, and pending edges from the on-disk chain.
  • migrate test --shadow now refuses a pre-upgrade database (naming migrate 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_enum is now correctly classified as a safe operation.
  • diff --live no 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 --live and migrate upgrade reconcile 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 --live and migrate upgrade reconcile 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 --live and migrate 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 CONCURRENTLY migration steps now emit IF 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 rollback on 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 apply to 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 apply hit 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 ON for 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 generate reads 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. diff and 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 --live and reconcile.** Default-PERMISSIVE RLS policies, equivalently-spelled pg_partman intervals (1 month vs 1 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 --shadow replays the migration chain for chain-format projects.** Shadow testing now replays the on-disk edges (the same path migrate apply uses) 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, and rebase commands, 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 baseline now 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.
  • build now fails hard when its auto-commit step fails, instead of warning and leaving regenerated outputs uncommitted.
  • generate --strict-nf and the nf check 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, pgdesign fails the build and names both sources, instead of silently letting your table shadow the import.
  • Clearer CLI help. build --auto-commit, codegen --split-mode, the import lock/import update alias argument, and migrate baseline --description now have fuller help text explaining their behavior and options.
  • Onboarding docs now show the required format_version = 1 top-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 EXISTS guards 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. interval is now a required field, independent of retention policy.

#Features

  • Partman-capable CI. Consumer CI template gains --partman flag for postgres+partman service, SkipIfNoPartman test guard, and REQUIRE env vars.
  • **Faceted codegen __init__.py.** Generate __init__.py in faceted output directories; fix executor type annotations for mypy.
  • MaintenanceDiff. Safe part_config updates 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 EXTENSION for partman setup; scheduling docs.
  • Partition interval caution classification. Forward-only experiment guard for interval changes.
  • Library promotion. pkg/diagnostic, pkg/genkit, and pkg/diff promoted 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 baseline command.** 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 --live crash on databases with functions.** Introspection no longer errors with cannot scan char (OID 18) on the provolatile/proparallel columns.

#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] disable entries 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 pgEnum declarations and enum column builders, SQLAlchemy emits sa.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: build and check --tag build hard-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 *.pyc are exempt.
  • New feature. codegen --check verifies 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: codegen works again without --split-mode for every language and mode (the flag's absence was a parse error). The --config global 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 --mode choices 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 ./... and go get of the module root work again: a stray root-level version.go (package main with no func main) broke compilation of the root package. Release binaries now embed the version correctly (goreleaser ldflags now set main.Version, matching the declared variable; the old lowercase main.version injection 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 same DO $$ 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 VALUE migrations now use IF 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-comments becomes --comments (default true, negate with --no-comments), --no-commit becomes --auto-commit (default true, negate with --no-auto-commit), --force becomes --force-overwrite.

#Fixes

  • Fix. testdb gc --older-than was 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 of class 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 new TypeKind field on columns and a unified TypeResolver that replaces per-language inline type switches.
  • Breaking change. --split-by-file flag and split = true config replaced by --split-mode faceted|self-contained and split_mode config field.
  • Breaking change. Faceted DDL output now uses DDLStmt namedtuple (7 fields) instead of plain 4-tuples. Faceted mode now includes a shared schema_executor.py with the full execute()/verify()/create_schema()/ensure_schema() API.

#Features

  • New feature. build, generate, and codegen commands now validate the schema before generating output. Invalid schemas produce error diagnostics and abort — no more generating code from broken schemas.
  • New feature. --mode enums codegen mode generates standalone enum definitions for all 6 languages, without row dataclasses or other types.
  • New feature. E225 validates that FK on_delete is 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_KINDS constant with name validation, and extension_stubs parameter 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: source field in [output] config filters tables by TOML source file. Composes with groups as 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 -short mode by skipping WASM-dependent tests that trigger 6s parser initialization. CI still runs all tests.

#Fixes

  • Fix. --split-by-file flag 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 ddl no longer panics when --split-by-file is 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_.py (one per input TOML), and post_tables.py. Activated via --split-by-file CLI flag or split=true in pgdesign.toml output config.

#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 SourceFile field, preserved through multi-file builds and topo sort.
  • New feature. Python DDL codegen supports faceted output via --split-by-file flag or split = true in pgdesign.toml. Produces per-concern files: extensions.py, types.py, tables_<source>.py (one per input TOML), and post_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 VALUE runs inside transactions on PG 12+ instead of requiring a separate non-transactional step. Controlled by the TransactionalEnumAdd pgcap capability.

#Fixes

  • Fix. splitfmt.Decode now 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 ILIKE CHECK constraints: Go/Java use (?i) regex prefix, TypeScript uses /regex/i flag, Python uses re.IGNORECASE, Kotlin uses RegexOption.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_version is now required in pgdesign.toml for 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 with pgcap.Has() calls for 10 capabilities (PG 10-18).
  • New feature. --idempotent DDL generation now covers all statement types: sequences (IF NOT EXISTS), domains and composite types (DO 1996273 catalog check), policies (version-gated OR REPLACE on PG 15+), materialized views (DO 1996273 catalog check), and triggers (version-gated OR REPLACE on PG 14+, DROP IF EXISTS + CREATE on older).

#Fixes

  • Fix. generate, build, and check --tag build now write the resolved PG version back to the schema, ensuring version-gated validation and DDL generation use the config-level pg_version consistently.

#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.json replaced by .sqlsplit format. Re-run pgdesign testdb init to regenerate wrappers.

#Features

  • New feature. Config system redesigned with phase-separated types (RawConfig/ResolvedConfig), path newtypes (RelativePath/AbsolutePath), walk-up directory search for pgdesign.toml, and --config global flag.
  • New feature. Sealed .sqlsplit format 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-run now shows per-file freshness status ([missing]/[stale]/[fresh]) instead of just listing files.
  • New feature. pgdesign check --tag build verifies 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 of pgdesign build.
  • New feature. Zig constraint codegen mode (--mode constraints --lang zig). Generates client-side validation from CHECK/NOT NULL/enum constraints using std.mem for LIKE pattern matching.

#Fixes

  • Fix. State machine enforce_trigger override via extends now uses explicit *bool instead of bool, preventing unintended trigger suppression when extending state machine types.
  • Fix. testdb init now correctly resolves relative output paths from pgdesign.toml instead of using them raw, fixing broken .split.json references when config paths are relative.

#0.16.1

Fix diff --base extension type loading, normalize test aliases, update strictcli

#Fixes

  • Fix. diff --base now 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 teardown refuses to drop databases that don't match the ephemeral naming pattern, preventing accidental deletion of non-test databases.
  • New feature. pg_uuidv7 extension registered in builtin registry with uuid_generate_v7 function.
  • New feature. typeinfo package 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 timestamp vs timestamp(6) and similar PostgreSQL type defaults.
  • New feature. User-defined types can shadow builtin types. Sealed field enforcement prevents changing kind or base_type when shadowing. Diagnostic I101 when a builtin is shadowed.
  • New feature. extends keyword 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() and statement_timestamp() correctly remain in the list (STABLE, not IMMUTABLE). Added uuid_generate_v7() and uuid_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 kind values 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. MultiFileGenerator interface 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 validModes derived from codegen registry for lang-mode pair validation.
  • New feature. ExtractConstraints now includes domain-backed CHECK constraints via DomainBaseTypes mapping.
  • 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, and InitiallyDeferred fields correctly.
  • Fix. TypeScript testdb template adds error cause chaining; Python template replaces assert with raise 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 --language flag was missing a required strictcli configuration, causing every pgdesign invocation 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.json format.** The companion DDL file now uses {"version": 1, "statements": [...]} instead of a bare array, enabling format evolution without breaking existing wrappers.
  • **testdb gc shows active connections.** ListOrphans now exposes the active connection count per database. The gc command prints it alongside each dropped database name.
  • CI workflow generation. testdb init --ci github-actions generates a GitHub Actions workflow with a Postgres service container, per-language toolchain setup, and PGDESIGN_DB configuration.

#Fixes

  • Wrapper template hardening. Zig terminate query uses parameterized $1 instead 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.Generate signature changed: accepts SeedConfig struct, 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, --seed flag, 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, TablesByName O(1) lookup, CASCADE walkers (depth/breadth/chain). All codegen backends refactored to use FKGraph.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_size for DML operations with execution loop.
  • Shadow database testing. migrate test --shadow replays migrations against a temporary database, introspects the result, and diffs against the schema.
  • Volatile default detection. migrate detects volatile defaults (e.g., now(), random()) in add_column operations and warns about table rewrites.
  • Squash CREATE TABLE consolidation. migrate squash consolidates add_column/add_index operations into CREATE TABLE statements, strips phase annotations from squashed output.
  • Applied migration safety check. migrate squash refuses to squash applied migrations when --db is provided.
  • Multi-step rollback. migrate rollback --to rolls 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 setup creates a randomly-named database, applies DDL, and prints the connection URL. testdb teardown drops it. testdb gc cleans up orphaned test databases by age.
  • Test wrapper code generation. testdb init --language go --language python generates 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. build auto-generates a .split.json companion 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 FormatTerminal diff 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. parseSimpleDefault now 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 validate and audit commands. Use pgdesign check --tag validation and pgdesign check --tag nf instead.

#Features

  • New feature. Codegen --mode types now 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.Generate signature changed.** Returns (string, []diagnostic.Diagnostic, error) instead of (string, error). Callers must handle the new diagnostics return value.

#Features

  • Doc output in build. The doc format is now generated by pgdesign build alongside SQL, D2, JSON, and SVG.
  • VIRTUAL generated columns. PG 18+ VIRTUAL generated columns are now supported. When stored is omitted from TOML, defaults to STORED. New E218 validation catches version mismatches.
  • STORED-to-VIRTUAL transition detection. diff now detects and warns about STORED-to-VIRTUAL generated column transitions, which require destructive DROP+recreate.
  • Introspect generated columns. introspect now reads pg_attribute.attgenerated (PG 12+) to detect STORED and VIRTUAL generated columns from live databases.
  • Extended SQL expression parser. sqlexpr now supports comparison operators (<, >, <=, >=), division/modulo (/, %%), IS NULL/IS NOT NULL, IN, BETWEEN, LIKE/ILIKE, IS DISTINCT FROM, float literals, and NULL literals. 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. introspect now 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 generate now emits correct GENERATED ALWAYS AS (...) STORED|VIRTUAL DDL 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 = false for 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., vector from 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., HNSW m, ef_construction). E216 validates parameters against the index method.
  • Index WITH introspection and migration. Introspect reads reloptions from pg_class to 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 includes CREATE MATERIALIZED VIEW, DROP MATERIALIZED VIEW, and REFRESH 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 ArrayChanged now correctly generates alter_column_type migration operations (e.g., text to text[]).
  • IndexMethods wiring. Extension IndexMethods field 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 extension command group. Extension discovery moved to pgdesign introspect --extensions. --db and --strict-nf moved from global to per-command flags. diff --db renamed to diff --live.

#Features

  • New feature. pgdesign check command using strictcli check system. Tags: validation (schema rules), nf (normal form audit), coverage (constraint completeness). Replaces validate and audit for config-driven analysis.
  • New feature. pgdesign build command 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 doc produces 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 seed generates 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_conns and pool_min_conns in 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 bare DEFAULT {}.
  • Fix. Empty-string defaults (default = "") now correctly produce DEFAULT '' 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 = true property. DDL generates text[], 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-suppressed flag 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_schema column 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. generate now 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 json outputs 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 --db command 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 fmt now 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.
Search