Skip to content

fix(deparser): keep schema qualification on GRANT ON TYPE/DOMAIN, COMMENT ON TRIGGER/RULE/OPERATOR/CAST and CREATE OPERATOR commutator - #375

Merged
pyramation merged 4 commits into
mainfrom
fix/deparser-qualified-object-refs
Nov 5, 2026
Merged

pyramation merged 4 commits into
mainfrom
fix/deparser-qualified-object-refs

Conversation

@pyramation

Copy link
Copy Markdown
Collaborator

Summary

Found while round-tripping pgpm diff migrations (constructive) through the deparser: several schema-qualified object references deparsed to SQL that Postgres rejects. Each case is a name list that was either joined as a List (fx, e) or had only its first element read.

Input Before Now
GRANT USAGE ON TYPE fx.e TO r1 (also DOMAIN, REVOKE) GRANT USAGE ON TYPE fx, e TO r1 GRANT USAGE ON TYPE fx.e TO r1
COMMENT ON TRIGGER t_tg ON fx.t IS 'x' COMMENT ON TRIGGER fx.t.t_tg IS 'x' unchanged
COMMENT ON RULE r ON fx.t IS 'x' COMMENT ON RULE fx.t.r IS 'x' unchanged
COMMENT ON OPERATOR fx.### (int, int) IS 'x' COMMENT ON OPERATOR fx(int, int) IS 'x' unchanged
COMMENT ON CAST (int AS text) IS 'x' COMMENT ON CAST int.text IS 'x' unchanged
CREATE OPERATOR fx.### (..., COMMUTATOR = OPERATOR(fx.###)) Commutator = fx, "###" Commutator = OPERATOR(fx.###)

Changes in deparser.ts:

  • GrantStmt (both object-list branches): for OBJECT_TYPE/OBJECT_DOMAIN, a List object is joined with . instead of visited as a list.
  • CommentStmt: OBJECT_TRIGGER/OBJECT_RULE take the last name as the trigger/rule and the rest as the (possibly schema-qualified) table; OBJECT_CAST renders (source AS target); operator names use all objname parts via a new qualifiedOperatorName helper (schema parts quoted, operator symbol verbatim).
  • DefineStmt (CREATE OPERATOR): multi-part COMMUTATOR/NEGATOR lists render as OPERATOR(schema.op).

Fixture __fixtures__/kitchen-sink/misc/qualified-object-refs.sql + regenerated generated.json/test file, committed first so the test fails on main. Full deparser suite: 750/750.

The constructive side (pgpm diff e2e cases for comments/grants on every object kind) depends on this being published and bumped there.

Link to Devin session: https://app.devin.ai/sessions/5cfb0f837b8849969154e1b17a38803d
Open in Devin Desktop: https://app.devin.ai/desktop/session/5cfb0f837b8849969154e1b17a38803d?variant=devin
Requested by: @pyramation

…OMAIN, COMMENT ON TRIGGER/RULE/OPERATOR/CAST and CREATE OPERATOR commutator
…MENT ON TRIGGER/RULE/OPERATOR/CAST and CREATE OPERATOR commutator/negator
@devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@tenki-reviewer

tenki-reviewer Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Review complete. 🟡 1 medium

💬 Inline comments (1)

🧹 Nitpicks (2) — 🟢 2 low
  • 🟢 Extract duplicated qualified-object name logic (deparser.ts:8730) — The identical qualifiedKinds mapping and List-flattening join were added in both the ACL_TARGET_OBJECT branch and the default branch of the GRANT/REVOKE object handling (packages/deparser/src/deparser.ts:8730).
  • 🟢 Hand-inserted generated.json entries will reorder on regeneration (generated.json) — The new misc/qualified-object-refs-* entries were hand-inserted into the generated fixture index between misc/quotes_etc-30.sql and misc/policy-quoted-names-1.sql, but npm run fixtures rewrites the whole file from an alphabetical glob walk, which would order policy- before qualified- before quotes_etc-.

The deparser gains a qualifiedOperatorName helper and extends CommentStmt, GrantStmt/RevokeStmt, and DefineStmt handling so schema-qualified names (e.g. OPERATOR(fx.###), COMMENT ON TRIGGER, GRANT ON TYPE/DOMAIN) are emitted with per-component identifier quoting and correct dot-joining. Ten new misc/qualified-object-refs kitchen-sink fixtures cover operators, triggers, rules, casts, types, domains, and grant/revoke variants, all of which round-trip in the project's own harness.

Files Change
packages/deparser/src/deparser.ts Adds qualified-object name emission for COMMENT ON, GRANT/REVOKE, and CREATE/ALTER OPERATOR, including a qualifiedOperatorName helper that quotes schema parts while leaving the operator symbol raw
__fixtures__/kitchen-sink/misc/qualified-object-refs.sql New fixture file with ten statements exercising qualified operators, triggers, rules, casts, types, and domains
__fixtures__/generated/generated.json Adds the ten fixture entries to the generated fixture index (hand-inserted rather than regenerated)
packages/deparser/__tests__/kitchen-sink/misc-qualified-object-refs.test.ts New kitchen-sink test driving the ten fixtures through parse/deparse round-trip verification

Reviewed commit: b030b3a

@tenki-reviewer tenki-reviewer Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds schema-qualified object reference support (operators, triggers, rules, types, domains) to the deparser with ten new kitchen-sink fixtures, but qualified OPERATOR() commutators still round-trip incorrectly in the ALTER OPERATOR path.

Key findings

Comment on lines +10142 to +10144
if (listItems.length > 1 && listItems.every(item => item.String)) {
return `${preservedDefName} = OPERATOR(${this.qualifiedOperatorName(listItems)})`;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 bug · medium

Qualified OPERATOR() commutator support is incomplete

The new multi-item OPERATOR(schema.op) emission for COMMUTATOR/NEGATOR was added only to the DefineStmt OBJECT_OPERATOR inline handler (packages/deparser/src/deparser.ts:10142). Sibling handlers were not extended, so ALTER OPERATOR fx.### (int, int) SET (COMMUTATOR = OPERATOR(fx.!!!)) round-trips to the invalid SQL commutator = 'fx, !!!' (verified by running the deparser). Users deparsing qualified commutator changes get non-reparseable output.

📋 Prompt for AI Agents

In packages/deparser/src/deparser.ts, the AlterOperatorStmt branch of the DefElem handler (around line 6045) and the DefineStmt-context branches in DefElem (~line 5941) and formatOptionValue (~line 6421) only handle single-item commutator/negator lists; a schema-qualified list such as OPERATOR(fx.!!!) falls through and renders as the invalid SQL commutator = 'fx, !!!'. Extend those branches to emit OPERATOR(<qualified name>) for multi-item all-String lists, mirroring the handling added in the DefineStmt OBJECT_OPERATOR inline path at lines 10142-10144. Verified reproduction: ALTER OPERATOR fx.### (int, int) SET (COMMUTATOR = OPERATOR(fx.!!!));.

@pyramation
pyramation merged commit eaeb8ec into main Nov 5, 2026
14 checks passed
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