Skip to content

Move DSL operators to df schema and auto-manage search_path in grant_usage (#202)#226

Open
crprashant wants to merge 4 commits into
microsoft:mainfrom
crprashant:crprashant/operators-in-df-schema
Open

Move DSL operators to df schema and auto-manage search_path in grant_usage (#202)#226
crprashant wants to merge 4 commits into
microsoft:mainfrom
crprashant:crprashant/operators-in-df-schema

Conversation

@crprashant

@crprashant crprashant commented Jun 11, 2026

Copy link
Copy Markdown

Summary

Fixes #202.

pgspot flagged pg_durable's 7 DSL operators (~>, |=>, &, |, ?>, !>, @>) as being created in the public schema (PS017). This PR moves them into the df schema so the extension no longer pollutes public — and, so the unqualified operator syntax keeps working without every user hand-editing search_path, teaches df.grant_usage() to manage the role's search_path during onboarding.

Part 1 — Move operators into df

Operators inside df.start('a' ~> 'b') (and df.explain('...')) are resolved in the caller's session before df.start()/df.explain() run. With the operators now in df, the unqualified operator syntax requires df on the session search_path. This is a deliberate, pre-1.0 behavior change; fully-qualified DSL function calls (e.g. df.seq(...)) are unaffected.

  • src/lib.rs — qualify all 7 CREATE OPERATOR statements as df.<op> (fresh install); add search_path GUC to the unit-test postgresql_conf_options.
  • sql/pg_durable--0.2.2--0.2.3.sql — for existing installs, DROP the public operators and re-CREATE them in df (0.2.3 is unreleased, so its upgrade script is reused).
  • scripts/run-pgspot.sh — remove the now-obsolete PS017 allowlist entry.

Part 2 — df.grant_usage() manages search_path (no manual setup)

So users get the ergonomic operator syntax out of the box, onboarding now puts df on the role's search_path:

  • df.grant_usage(role) gains a fourth optional parameter, set_search_path boolean DEFAULT true. When true (the default) it adds df to the target role's search_path via ALTER ROLE — append-only and idempotent (appends , df to an existing per-role setting, or sets "$user", public, df when none exists). Pass set_search_path => false to opt out and manage it yourself.
  • df.revoke_usage(role) keeps its signature and now also removes the df entry again (idempotent; other entries preserved; RESET when nothing else remains).
  • Both ALTER ROLE blocks tolerate insufficient_privilege with a NOTICE, so a delegated admin who can't alter the target role still completes the grant/revoke.
  • Because the parameter list changes, the upgrade script DROPs and re-CREATEs grant_usage (re-REVOKEing PUBLIC EXECUTE) and CREATE OR REPLACEs revoke_usage. It includes a CREATE SCHEMA IF NOT EXISTS df; — a runtime no-op since the schema already exists — so the recreated function bodies stay byte-identical to src/lib.rs and pass the pgspot gate (the resulting PS010 is allowlisted in run-pgspot.sh). Rationale is documented in docs/upgrade-testing.md.

ℹ️ ALTER ROLE … SET search_path takes effect on the role's next connection, not the current session — so the new E2E test asserts the pg_db_role_setting catalog rather than effective resolution.

Backward compatibility

The new .so continues to work against a non-upgraded ≤0.2.2 schema: operators still live in public there, and the caller's default search_path includes public; the 3-arg grant_usage / old revoke_usage remain callable exactly as before. No Rust code hard-references the operator schema or these admin helpers. After ALTER EXTENSION UPDATE to 0.2.3, operators move to df and grant_usage/revoke_usage gain the search_path behavior.

Docs & tests

  • Docs/examplesUSER_GUIDE.md, README.md, docs/{grammar,api-reference,ARCHITECTURE,upgrade-testing}.md, .agents/skills/pg-durable-sql/SKILL.md, .github/copilot-instructions.md, CHANGELOG.md, and the example SQL files + operational-scenarios README document/apply the search_path story.
  • E2Escripts/test-e2e-{local,docker}.sh set df on the database search_path after CREATE EXTENSION so the suite's unqualified operator usage resolves; new tests/e2e/sql/50_grant_usage_search_path.sql asserts the grant/revoke search_path behavior (default adds df once, set_search_path => false opts out, existing paths are appended to, double-grant is idempotent, revoke removes df while preserving other entries) via the pg_db_role_setting catalog.

Testing

CI runs the format check, clippy, cargo pgrx test pg17, the E2E suite, and the upgrade tests (schema comparison + backward-compat) plus the pgspot gate. The modified upgrade script was verified against pgspot locally (passes with only the allowlisted PS010). The build and full E2E suite (including the new test) were also validated locally in Docker (linux/amd64).

pgspot flagged the 7 DSL operators (~>, |=>, &, |, ?>, !>, @>) as created in the public schema (PS017). Move them into the df schema to avoid polluting public.

Operators in df.start('a' ~> 'b') resolve in the caller's session before df.start()/df.explain() run, so df must be on the session search_path for the unqualified operator syntax. This is a documented, pre-1.0 behavior change.

- Qualify CREATE OPERATOR as df.<op> in src/lib.rs (fresh install)
- Move operators in the 0.2.2->0.2.3 upgrade script (DROP public, CREATE df)
- Add df to database search_path in E2E runners (local + docker)
- Add search_path GUC to unit-test config (postgresql_conf_options)
- Remove obsolete pgspot PS017 allowlist entry
- Update docs, examples, CHANGELOG, and upgrade-testing notes
The DSL operators now live in df (microsoft#202), so df must be on a role's search_path for the unqualified operator syntax to resolve. Add a fourth optional parameter to df.grant_usage(), set_search_path boolean DEFAULT true, that adds df to the target role's search_path via ALTER ROLE during onboarding (append-only and idempotent; opt out with set_search_path => false). df.revoke_usage(text) keeps its signature and now removes the df entry again. Both ALTER ROLE blocks tolerate insufficient_privilege with a NOTICE so the grant/revoke otherwise succeeds.

The 0.2.2->0.2.3 upgrade script drops and recreates grant_usage for the new signature and re-revokes PUBLIC EXECUTE. It includes CREATE SCHEMA IF NOT EXISTS df (a runtime no-op) so the recreated function bodies stay byte-identical to src/lib.rs and pass the pgspot gate; the resulting PS010 finding is allowlisted in run-pgspot.sh.

Add E2E test 50_grant_usage_search_path.sql (catalog-based assertions on pg_db_role_setting) and update USER_GUIDE, README, api-reference, upgrade-testing, and CHANGELOG.
@crprashant crprashant changed the title Move DSL operators from public to df schema (#202) Move DSL operators to df schema and auto-manage search_path in grant_usage (#202) Jun 11, 2026
Add df to the database-level search_path in 00_init so the unqualified
operator syntax (~>, |=>, &, |, ?>, !>, @>) resolves in the regression
tests after the operators moved from public to df. Use ALTER DATABASE
current_database() in a DO/format() block so it works for both the
contrib_regression and postgres databases used by the CI jobs. A
per-role setting is not sufficient because the tests use SET ROLE,
which does not reload role-level search_path.

Update expected/00_init.out for the new df.grant_usage search_path
NOTICE and the appended search_path setup block.
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.

Consider creating pg_durable operators in df schema instead of public

1 participant