Skip to content

fix(cli): skip pg_dump in db pull when using pg-delta diff engine#5255

Open
avallete wants to merge 19 commits into
developfrom
claude/pg-deltas-db-pull-ayJdw
Open

fix(cli): skip pg_dump in db pull when using pg-delta diff engine#5255
avallete wants to merge 19 commits into
developfrom
claude/pg-deltas-db-pull-ayJdw

Conversation

@avallete
Copy link
Copy Markdown
Member

@avallete avallete commented May 18, 2026

What

Switches supabase db pull --diff-engine pg-delta to drive its initial pull from pg-delta's catalog diff instead of pg_dump, and lands the supporting plumbing (remote TLS handling, debug bundles, and a local pg-delta dev workflow) needed to make that path reliable against Cloud.

Closes CLI-1472.

Why

The legacy initial-pull pipeline is pg_dump → restore into a local shadow → diff. That round-trip silently destroys ownership for any object the local postgres role can't assume: pg_dump emits ALTER ... OWNER TO supabase_admin (etc.), the restore runs as a non-superuser so the owner is dropped, and every restored object then reports owner = postgres. pg-delta's supabase integration filter drops platform-managed objects by owner ∈ SUPABASE_SYSTEM_ROLES, so once the owner signal is gone those objects leak into the user's migration file and break supabase db reset (CLI-1469 FDW ACLs, CLI-1470 Wasm wrappers).

pg-delta already speaks pg_catalog directly via extractCatalog, preserving ownership, so diffing the remote catalog against an empty shadow produces a clean initial migration without pg_dump in the loop.

What changed

Initial-pull routing

  • db pull no longer runs dumpRemoteSchema on the initial pull when the pg-delta diff engine is selected; the shadow diff is the sole producer of the migration file (internal/db/pull/pull.go, cmd/db.go).
  • shouldUseDeclarativePgDeltaPull makes explicit --diff-engine pg-delta keep the migration-file workflow even when [experimental.pgdelta] enabled = true would otherwise default to the declarative export path.
  • New swallowInitialInSync / ensureMigrationWritten helpers distinguish "pg-delta produced no statements" from "pg_dump already wrote content", so an empty pg-delta result still surfaces No schema changes found correctly.

Diff plumbing

  • DiffDatabase now returns a DatabaseDiff struct (SQL plus an optional debug capture) rather than a bare string; callers in diff.go and pull.go updated accordingly.
  • Removed the now-unused diffWithStream and its golangci exclusion.
  • The diff template (templates/pgdelta.ts) sets skipDefaultPrivilegeSubtraction: true and emits structured diagnostics under PGDELTA_DEBUG.

Remote TLS handling for pg-delta

  • New internal/gen/types/pgdelta_conn.go: PreparePgDeltaPostgresRef writes the CA bundle into the workspace, forces sslmode=verify-ca + sslrootcert, and always uses the embedded bundle for Supabase-hosted hosts even when the SSL probe is skipped (e.g. --debug). Source/target now use distinct PGDELTA_SOURCE_SSLROOTCERT / PGDELTA_TARGET_SSLROOTCERT env vars. pgdelta.go and pgcache/cache.go refactored onto this helper.

Debug bundles for empty pulls

  • New PGDELTA_DEBUG flag (separate from --debug; keeps SSL on). When a pg-delta pull returns zero statements, the CLI writes a bundle under supabase/.temp/pgdelta/debug/<timestamp>/: source-catalog.json, target-catalog.json, pgdelta-stderr.txt, connection.txt (redacted), and error.txt, plus a per-schema catalog object summary to stderr (internal/db/diff/pgdelta_debug.go, internal/db/pull/pgdelta_pull_debug.go).
  • DiffPgDeltaRefDetailed surfaces edge-runtime stderr; declarative.DebugBundle gains inline-catalog, stderr, and connection-info fields.

Local pg-delta dev workflow

  • PGDELTA_NPM_REGISTRY (pkg/config/pgdelta_local.go, internal/utils/pgdelta_local.go) routes Deno's npm:@supabase/pg-delta resolution through a local Verdaccio registry. The edge-runtime helper gained composable WithExtraFile / WithExtraEnv options to drop a scoped .npmrc and forward NPM_CONFIG_REGISTRY; all pg-delta edge-runtime calls now pass PgDeltaNpmRegistryOption(). Documented in CONTRIBUTING.md.

Version, build, and docs

  • DefaultPgDeltaNpmVersion bumped to 1.0.0-alpha.27.
  • apps/cli/package.json adds a build:go-sidecar step that copies the Go binary into dist/.
  • docs/supabase/db/pull.md documents the new initial-pull behavior, the experimental declarative default, the direct-connection recommendation for --db-url, and the PGDELTA_DEBUG workflow.

Reviewer notes

  • The FDW/server ACL and CREATE FOREIGN DATA WRAPPER suppression rules in @supabase/pg-delta become defense-in-depth on this path rather than load-bearing.
  • Scope is schema-only / migration generation: supabase db dump and the declarative export path still use pg_dump / the existing flows.

https://claude.ai/code/session_016o7U7hiut6dkkhfbSJgKv4

Initial pulls under --diff-engine pg-delta were dumping the remote
schema via pg_dump before running pg-delta. The dump-then-restore
round-trip strips ownership information for objects the local postgres
role cannot assume, so platform-managed objects (FDWs, wasm wrappers,
system-owned ACLs) leak into the migration file and break
\`supabase db reset\` (see CLI-1469, CLI-1470).

pg-delta speaks pg_catalog directly via extractCatalog and the supabase
integration filters platform objects by owner. Diffing against an
empty shadow on initial pull yields a clean initial migration on its
own, so dumpRemoteSchema is unnecessary on this path.
@avallete avallete requested a review from a team as a code owner May 18, 2026 06:33
@coveralls
Copy link
Copy Markdown

coveralls commented May 18, 2026

Coverage Report for CI Build 27024156346

Warning

No base build found for commit dd8e1b7 on develop.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 64.279%

Details

  • Patch coverage: No coverable lines changed in this PR.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 16136
Covered Lines: 10372
Line Coverage: 64.28%
Coverage Strength: 7.05 hits per line

💛 - Coveralls

jgoux and others added 5 commits May 18, 2026 10:53
Added documentation for testing local pg-delta builds in CONTRIBUTING.md, detailing the steps to publish local changes and configure the CLI to use a local npm registry.

Updated the RunEdgeRuntimeScript function to accept additional options for handling npm registry configurations, allowing for scoped .npmrc files and environment variable forwarding.

Introduced PgDeltaNpmRegistryOption to manage npm registry settings and added tests to ensure correct behavior.

Refactored related functions to integrate these enhancements, improving the overall local development experience for pg-delta.
@avallete avallete marked this pull request as draft May 21, 2026 19:25
avallete and others added 9 commits June 4, 2026 10:54
Resolve edgeruntime conflicts by combining EdgeRuntimeOption support
with dynamic port allocation from develop (#5407).
- Renamed test cases for clarity and updated assertions to reflect new behavior regarding the use of the pg-delta flag and experimental config.
- Modified `shouldUseDeclarativePgDeltaPull` function to remove reliance on the `usePgDelta` variable.
- Enhanced debug capture logic in the diffing process to ensure proper handling of empty diffs.
- Updated TypeScript template to use the latest pg-delta version (1.0.0-alpha.25).
- Adjusted comments and formatting in various files for consistency and clarity.
- Updated the golangci-lint configuration to correct the exclusion format for the ST1003 rule.
- Removed the unused `diffWithStream` function from `internal/db/diff/diff.go` to clean up the codebase.
@avallete avallete marked this pull request as ready for review June 5, 2026 14:03
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 5, 2026

Supabase CLI preview

npx --yes https://pkg.pr.new/supabase@5255

Preview package for commit 2c92ca3.

claude and others added 3 commits June 5, 2026 14:39
Review follow-ups for PR #5255:

- Give SOURCE and TARGET distinct on-disk CA bundle filenames
  (pgdelta-source-ca.crt vs pgdelta-target-ca.crt) so a diff between
  two remote databases with different CAs cannot share one file.
  caBundleFilename derives the name from the env var.
- Replace strings.Contains(host, "pooler.supabase.com") with an
  exact match plus a ".pooler.supabase.com" suffix check so look-alike
  hostnames (e.g. pooler.supabase.com.example.org) do not match.
- Unexport the test-only EnsurePgDeltaVerifyCA helper; tests now
  exercise ensurePgDeltaSSL directly with an empty sslrootcert.
- Rename pull.exportCatalogPgDelta to pull.exportTargetCatalog to
  distinguish it from the diff package's identically named test seam
  and to reflect that it captures the remote (target) catalog for the
  empty-pull debug bundle.
The build:go-sidecar step runs first in the build chain, before any
bun --compile step has created dist/, so `cp ../cli-go/supabase-go
dist/supabase-go` failed with "No such file or directory" and broke
nx run supabase:build (and every e2e shard that depends on it).

Prefix the copy with `mkdir -p dist` so the destination exists
regardless of step order; it is idempotent when dist/ already exists.
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.

4 participants