feat(pgpm): pgpm diff — identity-keyed semantic diff + migration generation - #1572
Merged
Conversation
Contributor
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Part C of constructive-planning#1340 (Phase 5 of #1329):
pgpm diff <A> <B>compares two schema sources semantically and can emit the delta as a deployable pgpm module.Each side may be a pgpm module dir (flattened in plan order via
loadModuleSource), a raw.sqlfile, or a live database (postgres://DSN ordb:<name>, read viapg_dump --schema-only --no-owner). Both sides are normalized into an identity-keyed object set (identityOf-derived keys), so the diff is dial-invariant: the same schema authored atomic vs consolidated vs differently named/partitioned diffs as empty (tested explicitly).The diff core stays in
@pgpmjs/transform(diffChangeSets/diffSchemasinsemantic-diff-driver.ts, landed with #1569) rather than a new@pgpmjs/diffpackage — it already ownsrestructureChanges, granularity, and the statement graph the emission pipeline needs; a separate package would just re-export those. Source ingestion (loadDiffSideFromDisk,sqlToDiffChanges,stripDumpPreamble,deltaChangesToRows) lives in@pgpmjs/exportnext toloadModuleSourceand the catalog-equivalence helpers; the CLI is a thin wrapper.Semantics:
ALTER TABLE ADD/DROP/ALTER COLUMN,ADD/DROP CONSTRAINT(never a rebuild); functions/views/policies/etc. modified →DROP+CREATE(neverCREATE OR REPLACE)-- TODO (not derivable)comment; removed+added similar objects are never guessed as renames--emit-migration: topo-sorted pgpm module (creates in dependency order, drops in reverse), spec-derived paths, graph-derived requires, generated revert/verify per change--verify: oracle mode — deploy A + emitted migration into a scratch DB, assert catalog equivalence with B deployed fresh (reusessnapshotCatalog/diffCatalogSnapshotsfrompgpm transform --check). Column comparison is order-insensitive (withoutColumnOrder): a drop+add migration can never reproduce a fresh deploy's physical column ordinals.Two fixes in the existing driver surfaced by the e2e:
@pgpmjs/transformalso bumps@pgsql/scriptsto^18.2.0(installed 18.0.1 predatedinvertStatement/existenceCheck).Tests
pgpm/export/__tests__/diff-source.test.ts: side-kind resolution, dump-preamble stripping, module/sql loading, dial-invariance (atomic module vs consolidated module vs flat .sql → empty diff), delta→row conversion incl. alteration-path collisions.pgpm/transform/__tests__/semantic-diff-driver.test.ts: constraint drop-before-add ordering, RLS enable/disable inversion, non-derivable warnings (plus existing per-kind add/remove/modify, column sub-diffs, normalization, reverse-topo drops).pgpm/cli/__tests__/diff-e2e.test.ts(live Postgres): v1→v2 module (new table, new column, dropped column, changed function body, new policy, dropped index, changed constraint) →--emit-migration→ deploy v1+migration → catalog equivalence with v2 → migration's own verify passes → revert returns to v1; plus--verifyoracle run and module-vs-.sql parity.Note:
pgpm/exporthas pre-existing failures onmain(export-utils/export-meta/export-flow/cross-flow-parity, schema-prefix renames) unrelated to this PR — verified by running the same suite on a cleanmaincheckout.Link to Devin session: https://app.devin.ai/sessions/798445577ac8487abf80e28f9ff7a916
Requested by: @pyramation