Skip to content

feat(pgpm): subsystem substitution in apply — exclude, rebind, cascade-safe materialization - #1530

Merged
pyramation merged 1 commit into
mainfrom
feat/apply-substitution
Jul 30, 2026
Merged

feat(pgpm): subsystem substitution in apply — exclude, rebind, cascade-safe materialization#1530
pyramation merged 1 commit into
mainfrom
feat/apply-substitution

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

Makes apply able to substitute a subsystem: import a source package but drop one of its schemas wholesale, rebinding every surviving reference onto a replacement provider (an ordinary pgpm module deployed via requires). The final glue over #1528 (routing profile) + #1529 (exclusion analysis) + @pgsql/transform@18.7.0 (name rebinding).

Spec surface (pgpm.apply.json / workspace portability profile):

{
  "source": "crm-module",
  "schemas": { "app": "crm" },
  "exclude": { "schemas": ["identity"] },          // NEW
  "route": [
    { "fromSchema": "identity", "kind": "table",    "name": "users",
      "toSchema": "app_auth" },                     // move FK targets
    { "fromSchema": "identity", "kind": "function", "name": "current_actor",
      "toSchema": "app_auth", "toName": "current_user_id" }  // NEW: rebind to a different object
  ],
  "requires": ["auth-provider"]
}

Route entries now take toSchema?: string | null (null = emit unqualified, resolve via search_path) and/or toName?: string (rebind to a different object) — previously only a required toSchema: string.

Materialization (materializeApplyModule): when exclude is present,

  1. cascade-safety gate over the whole source bundle's deploy SQL (excludeSubsystem): every surviving reference into an excluded schema must have a route/rebind target, else refuse with each unsatisfied object named (FK targets flagged);
  2. changes whose deploy is entirely subsystem statements become tombstones — deploy/revert/verify all blanked (headers + transaction control kept), identity not renamed, so plan order and registry attribution stay intact;
  3. mixed scripts get subsystem statements sliced out by parser location (stripSubsystemSql, survivors byte-exact), plus provably subsystem-targeted opaque statements (DROP/COMMENT ON them);
  4. survivors flow through the existing router transform, which now carries the rebinds.

New in @pgpmjs/slice: stripSubsystemSql (analysis + statement removal in one pass) and blankScriptSql.

Tested end-to-end on live PG (__fixtures__/apply/substitution, vendor-neutral): a crm-module with an identity subsystem is applied with identity excluded — its FK and RLS-policy accessor rebound onto an installed auth-provider module — then deploy/verify/revert all pass; the refusal path is asserted too. types 6 / transform 31 / slice 48 / bundle 52 / core 459 tests green.

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

@pyramation pyramation self-assigned this Jul 30, 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 3a1dd68 into main Jul 30, 2026
16 checks passed
@pyramation
pyramation deleted the feat/apply-substitution branch July 30, 2026 10:03
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