Skip to content

fix(deparser): PG18 regress round-trip sweep fixes - #354

Merged
pyramation merged 3 commits into
mainfrom
fix/deparser-pg18-regress-sweep
Sep 17, 2026
Merged

pyramation merged 3 commits into
mainfrom
fix/deparser-pg18-regress-sweep

Conversation

@pyramation

Copy link
Copy Markdown
Collaborator

Summary

Ran every statement from the PG18 src/test/regress/sql suite through parse → deparse → reparse and fixed everything the TS deparser got wrong (~9.1k parseable statements: 762 failures → 0 hard failures; 7 remaining are COPY ... FREEZE boolean-shape diffs that are semantically identical). Follow-up to #292/#346/#348/#349/#350 — same class of bugs, found by sweeping instead of waiting for reports.

Fixes, grouped by root cause:

WindowDef.frameOptions is a bitmask, not an enum (silent corruption — round-tripped but changed semantics)

  • Decode with FRAMEOPTION_* flags; only emit a frame when FRAMEOPTION_NONDEFAULT is set (previously default frames rendered as RANGE UNBOUNDED PRECEDING, and ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING lost its bounds/EXCLUDE).
  • Named windows: OVER w, OVER (w ORDER BY ...) and WINDOW w AS (...) preserved; FuncCall/JsonObjectAgg/JsonArrayAgg share formatOverClause.

Protobuf zero-default omission

  • Hash bounds: MODULUS n, REMAINDER 0remainder ?? 0 in both CreateStmt.partbound and PartitionBoundSpec.

Unhandled / mis-rendered statements (threw or produced unparseable SQL)

  • AlterPublicationStmt: ADD|DROP|SET TABLE ..., TABLES IN SCHEMA, SET (opts); PublicationObjSpec predicates wrapped WHERE (expr); CreatePublicationStmt emits FOR.
  • AlterSubscriptionStmt: all kinds (CONNECTION, SET/ADD/DROP PUBLICATION ... WITH, REFRESH PUBLICATION, ENABLE/DISABLE, SKIP).
  • DefElem args of type TypeName/Integer/Float/Boolean no longer stringified/quoted (publish_via_partition_root = 1, streaming = parallel).
  • CommonTableExpr.search_clause / cycle_clause (SEARCH DEPTH|BREADTH FIRST BY ... SET, CYCLE ... SET ... TO x DEFAULT y USING ...).
  • InsertStmt.overrideOVERRIDING {USER|SYSTEM} VALUE (also in MERGE INSERT actions).
  • AT_SetIdentity / AT_DropIdentity: SET GENERATED ALWAYS|BY DEFAULT, SET INCREMENT BY .., RESTART [WITH n], DROP IDENTITY [IF EXISTS] (was SET generated = 97, DROP IDENTITY RESTRICT).
  • ALTER SEQUENCE ... OWNED BY NONE (was OWNED BY "none").
  • ColumnDef.storage_name / .compressionSTORAGE x / COMPRESSION y; AT_AlterColumnType keeps collClause.
  • CREATE TABLE ... PARTITION OF parent (col WITH OPTIONS ...) FOR VALUES ... — column list now emitted after PARTITION OF, not before.
  • CREATE TABLE AS: WITH (...) moved before AS; reloption names quoted via QuoteUtils.quoteIdentifier ("Fillfactor").
  • CopyStmt: TO STDOUT vs FROM STDIN (was always STDIN); options rendered as FORMAT csv, DELIMITER ',', FORCE_QUOTE (a, b), bare FREEZE/HEADER.
  • GrantRoleStmt rewritten: WITH ADMIN|INHERIT|SET TRUE/FALSE, REVOKE ADMIN OPTION FOR, GRANTED BY, CASCADE; GrantStmt.grantorGRANTED BY.
  • SQL/JSON nodes added: JsonObjectConstructor, JsonArrayConstructor, JsonArrayQueryConstructor, JsonObjectAgg, JsonArrayAgg, JsonParseExpr, JsonScalarExpr, JsonSerializeExpr, JsonIsPredicate, JsonKeyValue, JsonValueExpr (with FORMAT JSON [ENCODING], RETURNING type, ABSENT|NULL ON NULL, WITH|WITHOUT UNIQUE KEYS).
  • MergeWhenClause UPDATE uses shared formatUpdateAssignmentsSET (b, c) = (SELECT ...) (was b = (...)[1], c = (...)[2]). Assignments now joined with , instead of , (one plpgsql-deparser snapshot updated).
  • CONSTR_EXCLUSION deferrability; INITIALLY DEFERRED without DEFERRABLE.
  • CONSTR_DEFAULT wraps non-b_expr expressions in parens (DEFAULT (1 IN (1, 2)), DEFAULT (a AND b)).
  • COMMENT ON CONSTRAINT c ON DOMAIN d.

Tests

  • 9 new fixture files __fixtures__/kitchen-sink/misc/pg18-*.sql (~230 statements), regenerated via npm run kitchen-sink.
  • pnpm build, pnpm lint, and all package test suites pass locally (pnpm -r test first-fail abort was only the updated snapshot).

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

Window frame bitmask decoding, publication/subscription stmts, CTE SEARCH/CYCLE,
INSERT OVERRIDING, COPY options, GRANT/REVOKE role options + GRANTED BY,
identity column alterations, STORAGE/COMPRESSION, PARTITION OF column options,
hash MODULUS/REMAINDER 0, CTAS WITH placement, SQL/JSON constructors/aggregates,
multi-column MERGE UPDATE, deferred EXCLUDE, DEFAULT b_expr parens,
COMMENT ON CONSTRAINT ... ON DOMAIN.
@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

@tenki-reviewer

tenki-reviewer Bot commented Sep 17, 2026

Copy link
Copy Markdown

Review complete. 🟠 1 high

💬 Inline comments (1)

🧹 Nitpicks (1) — 🟢 1 low
  • 🟢 Regenerate pgsql-types after metadata edit (field-metadata.json:490) — The PR's packages/pgsql-types/src/field-metadata.json edit adds A_Const to BoolExpr.args (line 490) and JsonIsPredicate to CaseWhen.expr (line 589), but the committed auto-generated packages/pgsql-types/src/types.ts was not regenerated: BoolExpr_args (line 63) still omits A_Const and CaseWhen_expr (line 68) still omits JsonIsPredicate.

This PR expands kitchen-sink coverage to PostgreSQL 18 syntax and extends the deparser and type-inference metadata to support it. It adds nine new PG18 SQL fixtures (pg18-copy, cte-insert, grant-role, identity-storage, misc-ddl, partition-ctas, publication-subscription, sqljson, window-frames) with matching round-trip test files, updates the generated fixture JSON, and adds the corresponding deparser handlers (new SQL/JSON constructors and IS JSON predicates, GRANTED BY in GRANT/REVOKE, COPY options, CTE SEARCH/CYCLE, publication/subscription options) plus pgsql-types field-metadata tags.

Files Change
__fixtures__/kitchen-sink/misc/pg18-*.sql, matching packages/deparser/__tests__/kitchen-sink/misc-pg18-*.test.ts, __fixtures__/generated/generated.json Add nine PG18 fixture sets and their AST round-trip tests.
packages/deparser/src/deparser.ts Add handlers for PG18 syntax: SQL/JSON constructors and IS JSON, GRANTED BY / REVOKE OPTION FOR, COPY options, CTE SEARCH/CYCLE, publication/subscription and DDL options.
packages/pgsql-types/src/field-metadata.json Add inference tags (A_Const in BoolExpr.args, JsonIsPredicate in CaseWhen.expr) for the new expressions.
packages/plpgsql-deparser/__tests__/__snapshots__/schema-rename-mapped.test.ts.snap Snapshot update reflecting shared type changes.

Reviewed commit: 20649be

@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.

Adds PG18 kitchen-sink fixtures (SQL/JSON, COPY, CTE INSERT, GRANT/REVOKE, publication/subscription, DDL) plus the deparser changes and pgsql-types metadata needed to round-trip them.

Key findings

Comment thread packages/deparser/src/deparser.ts
@pyramation
pyramation merged commit 2c03a6a into main Sep 17, 2026
15 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