Skip to content

fix: support schema privilege diffs#287

Draft
dilame wants to merge 2 commits into
stripe:mainfrom
dilame:fix/schema-privileges
Draft

fix: support schema privilege diffs#287
dilame wants to merge 2 commits into
stripe:mainfrom
dilame:fix/schema-privileges

Conversation

@dilame
Copy link
Copy Markdown

@dilame dilame commented May 25, 2026

What

Plan and apply schema-level privilege changes (GRANT / REVOKE on schemas), including WITH GRANT OPTION changes.

This adds schema ACLs to the introspected NamedSchema model and emits authz-update migration statements when schema privileges drift between source and target. It also tracks schema owner names so owner-granted USAGE / CREATE ACL entries are treated as implicit owner privileges, not ordinary grant drift.

Why

pg-schema-diff already tracks table privileges, but schema privileges were invisible: NamedSchema only stored the schema name, GetSchemas only read nspname, and schema Alter emitted no SQL. That allowed a migration plan to converge to zero statements even when the target had GRANT USAGE ON SCHEMA ... TO ... and the source did not.

After the first schema-privilege fix, one production-shaped no-op remained: schemas owned by a role can expose ACL rows like service=UC/service. PostgreSQL owners already have implicit schema privileges, but the diff could still compare the target's explicit owner grant from the temp database as ordinary drift and repeatedly emit a no-op GRANT USAGE ON SCHEMA ... TO service.

How

  • internal/queries/queries.sql: add GetSchemaPrivileges from pg_namespace.nspacl; make GetSchemas return schema owner names.
  • internal/schema/schema.go: add SchemaPrivilege, attach sorted privileges to NamedSchema, and keep schema owner for implicit-owner classification.
  • pkg/diff/sql_generator.go: diff schema privileges, recreate when WITH GRANT OPTION changes, and filter current-owner USAGE / CREATE grants during comparison.
  • pkg/diff/schema_privilege_sql_generator.go: emit GRANT ... ON SCHEMA / REVOKE ... ON SCHEMA with AUTHZ_UPDATE hazards.
  • pkg/diff/plan_generator.go: clear schema privileges during validation for skipped privilege statements, matching table privilege handling.
  • internal/migration_acceptance_tests/named_schema_cases_test.go: cover grant, revoke, grant-option drift, non-owner CREATE revocation, and the owner-grant no-op case.

Test

PATH="/Applications/Postgres.app/Contents/Versions/16/bin:$PATH" go test ./internal/migration_acceptance_tests -run TestNamedSchemaTestCases/'Do not grant usage|Revoke create|Grant usage|Revoke usage|Change schema grant option' -count=1
PATH="/Applications/Postgres.app/Contents/Versions/16/bin:$PATH" go test ./internal/migration_acceptance_tests -run 'Test(NamedSchema|Sequence|LocalPartitionIndex|IndexNoConcurrent|Index|Function)TestCases' -count=1 -parallel 1
PATH="/Applications/Postgres.app/Contents/Versions/16/bin:$PATH" go test ./internal/schema -count=1
PATH="/Applications/Postgres.app/Contents/Versions/16/bin:$PATH" go test ./pkg/diff ./pkg/tempdb -count=1
PATH="/Applications/Postgres.app/Contents/Versions/16/bin:$PATH" go test $(go list ./cmd/... ./internal/... ./pkg/... | grep -v '/internal/migration_acceptance_tests$')

go test ./... was also attempted, but this worktree currently has an untracked scratch test_diff.go at the repository root, and the parallel acceptance package hit local temp-database statement-timeout noise. The same failed acceptance groups passed when rerun with -parallel 1.

dilame added 2 commits May 25, 2026 20:24
Schema-level grants were invisible to the schema model, so migrations could converge while a role was missing USAGE on a schema.

Constraint: pg-schema-diff skips privilege DDL during plan validation because temp roles are absent.

Rejected: Treat public schema default USAGE as implicit | would keep schema ACL introspection incomplete.

Confidence: high

Scope-risk: moderate

Directive: Keep schema privilege validation clearing in sync with every SkipValidation privilege generator.

Tested: PATH="/Applications/Postgres.app/Contents/Versions/16/bin:$PATH" go test ./...

Not-tested: Cross-version PostgreSQL matrix beyond local PostgreSQL 16.12.
Schema owners receive implicit USAGE and CREATE privileges, so ACL rows granted to the current owner should not be diffed as ordinary schema grants. Track schema owner during fetch and filter owner USAGE/CREATE while comparing schema privileges.

Constraint: PostgreSQL schema owners have implicit schema privileges, and pg-schema-diff does not generate schema ownership migrations.

Rejected: Treat owner-granted USAGE/CREATE as regular ACL drift | this repeats no-op GRANT/REVOKE statements when the desired schema grants the current owner.

Confidence: high

Scope-risk: narrow

Directive: Keep owner-aware privilege filtering local to schema privilege diffing until schema ownership migration is supported.

Tested: targeted named-schema acceptance; failed acceptance groups with -parallel 1; internal/schema; pkg/diff; pkg/tempdb; all non-acceptance cmd/internal/pkg packages.

Not-tested: Production migrate-plan against Cloud SQL; full go test ./... is blocked by untracked scratch test_diff.go in this worktree and parallel temp database statement-timeout noise in acceptance tests.
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