Skip to content

feat(pgpm): change-granularity dial — one change per alteration (planning #1342) - #1585

Merged
pyramation merged 1 commit into
mainfrom
feat/pgpm-change-granularity
Aug 1, 2026
Merged

feat(pgpm): change-granularity dial — one change per alteration (planning #1342)#1585
pyramation merged 1 commit into
mainfrom
feat/pgpm-change-granularity

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

The fourth dial (planning constructive-planning#1342): statement granularity (--granularity atomic|object|consolidated, SQL shape within a change) is now orthogonal to change granularity (--change-granularity object|alteration, how statements are distributed across pgpm changes). Default object is byte-identical to today.

With alteration, every single-command ALTER TABLE ADD COLUMN / ADD CONSTRAINT becomes its own plan entry with its own deploy/revert/verify and requires:

schemas/app/tables/users/table                                CREATE TABLE ()
schemas/app/tables/users/columns/id/column                    ADD COLUMN id …        [table]
schemas/app/tables/users/constraints/users_pkey/constraint    ADD CONSTRAINT …       [table, columns/id]

revert is DROP COLUMN / DROP CONSTRAINT; verify checks information_schema.columns / table_constraints — so a single column can deploy or revert independently.

How it works:

  • @pgpmjs/naming-spec: new column identity kind → schemas/{s}/tables/{t}/columns/{c}/column.
  • @pgpmjs/transform sub-object.ts: subObjectIdentityOf(facts) recovers the column/constraint name from the raw parse node (facts.stmt) that identityOf summarizes away — no parser changes needed. nameUnnamedConstraints(sql) rewrites ADD PRIMARY KEY (id)ADD CONSTRAINT users_pkey PRIMARY KEY (id) (the exact name Postgres would assign, via defaultConstraintName from feat(transform): constraint-placement-invariant semantic diff (planning #1341) #1584) so each constraint change is revertible.
  • restructureChanges({ changeGranularity: 'alteration' }) groups those statements by sub-object identity instead of owning object, and adds finer edges the statement graph (keyed by table) can't express: alteration → its table's change, constraint → the column changes it keys on (keys/fk_attrs).
  • Wired through restructureExportRows, diffChangeSets, importDumpRows, and the pgpm transform / pgpm diff / pgpm import CLIs.

Semantic invariance holds across both axes: examples/pgpm-projections now asserts atomic ≡ object ≡ consolidated ≡ alteration at the identity-keyed model (the old "atomic is only catalog-equivalent" caveat is removed — #1584 fixed that), and live-Postgres e2e (transform-e2e, diff-e2e) proves alteration-mode modules deploy to the identical catalog, verify, and revert clean.

Link to Devin session: https://app.devin.ai/sessions/798445577ac8487abf80e28f9ff7a916
Requested by: @pyramation

@pyramation pyramation self-assigned this Aug 1, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit 4277ca9 into main Aug 1, 2026
17 checks passed
@pyramation
pyramation deleted the feat/pgpm-change-granularity branch August 1, 2026 05:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant