Skip to content

fix(deparser): keep AND CHAIN, trigger names, XML INDENT, SHOW quoting, COPY values, tablespaces and WITH TIES - #368

Open
pyramation wants to merge 1 commit into
mainfrom
devin/1792900946-deparser-silent-drops
Open

pyramation wants to merge 1 commit into
mainfrom
devin/1792900946-deparser-silent-drops

Conversation

@pyramation

Copy link
Copy Markdown
Collaborator

Summary

Fixes deparser paths that silently emitted different SQL (or invalid SQL) for valid PG18 input.

Input Before After
COMMIT AND CHAIN / ROLLBACK AND CHAIN COMMIT COMMIT AND CHAIN (TransactionStmt.chain)
ALTER TRIGGER p ON grandp RENAME TO q ALTER TRIGGER grandp RENAME TO q (syntax error) ALTER TRIGGER p ON grandp RENAME TO q
XMLSERIALIZE(DOCUMENT x AS text INDENT) INDENT dropped kept (XmlSerialize.indent)
SHOW "TimeZone" SHOW TimeZone (→ timezone) each dotted part quoted via QuoteUtils.quoteDottedName; SHOW ALL keyed on name === 'all' so SHOW "ALL" stays quoted
COPY ... (FORMAT "CSV") FORMAT CSV (→ csv) FORMAT 'CSV'
COPY option with Boolean false arg (JSON AST omits boolval) HEADER (= true) HEADER false
CREATE TABLE ... TABLESPACE ts (incl. partitioned / PARTITION OF / USING heap) TABLESPACE dropped kept (CreateStmt.tablespacename)
PRIMARY KEY/UNIQUE/EXCLUDE ... INCLUDE (b) WITH (fillfactor=10) USING INDEX TABLESPACE ts all three dropped kept (including, options, indexspace)
ADD PRIMARY KEY USING INDEX "My Idx", CREATE/DROP/ALTER TABLESPACE "My TS" unquoted (syntax error) quoted
ALTER TABLESPACE ts SET (random_page_cost = 1) SET (RANDOM_PAGE_COST 1) (syntax error) SET (random_page_cost = 1)
ORDER BY a FETCH FIRST 2 ROWS WITH TIES (also in views) LIMIT 2 FETCH FIRST 2 ROWS WITH TIES; with an offset, OFFSET n ROWS comes first; a count that isn't a plain non-negative constant gets parentheses

Tests

Each fix has a kitchen-sink fixture in __fixtures__/kitchen-sink/misc/ (mostly statements from the PG18 regression suite: transactions, triggers, xml, tablespace, limit), with generated tests from npm run kitchen-sink:
transaction-chain, alter-trigger-rename, xmlserialize-indent, show-quoting, copy-option-values, tablespace-clauses, fetch-with-ties.
The parser never produces a false Boolean COPY arg, so that case is a hand-built AST test in __tests__/misc/copy-boolean-options.test.ts.

All 8 new suites fail with only src/deparser.ts reverted:

misc/show-quoting-1         AST_MISMATCH         SHOW TimeZone
misc/tablespace-clauses-1   AST_MISMATCH         CREATE TABLE testschema.foo (i int)
misc/alter-trigger-rename-1 INVALID_DEPARSED_SQL ALTER TRIGGER grandp RENAME TO q
misc/fetch-with-ties-1      AST_MISMATCH         ... ORDER BY thousand LIMIT 2
misc/transaction-chain-2    AST_MISMATCH         COMMIT
misc/xmlserialize-indent-1  AST_MISMATCH         XMLSERIALIZE ( DOCUMENT ... AS text )
misc/copy-option-values-3   AST_MISMATCH         COPY t FROM STDIN WITH (FORMAT CSV)
copy-boolean-options        expected HEADER false, got HEADER

With the fix: deparser 758/758 tests and 424 snapshots pass (no existing snapshot changed). transform 250/250, plpgsql-deparser 115/115 and transform-ast 1443/1443 also pass.

Not changed: COPY's legacy CSV HEADER / FREEZE produce Boolean(true) args, which are still emitted as a bare HEADER inside WITH (...). That means the same thing, but the reparsed AST has no arg.

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

@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 Oct 25, 2026 •

Copy link
Copy Markdown

Review complete. 🟠 1 high · 🟡 1 medium

📍 Findings outside the diff (2) — 🟠 1 high · 🟡 1 medium — defects on lines GitHub can't attach comments to

🟠 High — EXCLUDE constraint emits WITH before WHERE · deparser.ts:3244–3248 · unchanged line

// packages/deparser/src/deparser.ts
3244	      this.pushIndexConstraintOptions(node, output, context);
3245	      if (node.where_clause) {
3246	        output.push('WHERE');
3247	        output.push(context.parens(this.visit(node.where_clause as any, context)));
3248	      }

The new pushIndexConstraintOptions call emits INCLUDE (...) and WITH (...) clauses before the caller appends the WHERE (predicate) clause (packages/deparser/src/deparser.ts:3244-3248). PostgreSQL's grammar for exclusion constraints requires the order EXCLUDE (elems) INCLUDE (...) WHERE (...) WITH (...). An EXCLUDE constraint with both a predicate and WITH options now deparses to SQL PostgreSQL cannot re-parse.


🟡 Medium — Trigger rename emits name before IF EXISTS · deparser.ts:8524–8564 · unchanged line

// packages/deparser/src/deparser.ts
8524	    case 'OBJECT_TRIGGER':
8525	      output.push('TRIGGER');
8526	      if (node.subname) {
8527	        output.push(QuoteUtils.quoteIdentifier(node.subname));
8528	      }
8529	      break;
8530	    case 'OBJECT_TSCONFIGURATION':
8531	      output.push('TEXT SEARCH CONFIGURATION');
8532	      break;
8533	    case 'OBJECT_TSDICTIONARY':
8534	      output.push('TEXT SEARCH DICTIONARY');
8535	      break;

The new OBJECT_TRIGGER branch pushes the trigger name right after the TRIGGER keyword (packages/deparser/src/deparser.ts:8524-8529), but IF EXISTS from node.missing_ok is pushed later at line 8562. With missing_ok set the output is ALTER TRIGGER mytrig IF EXISTS ON mytable RENAME TO ..., which PostgreSQL rejects. The PR's trigger-rename fixtures do not set missing_ok, so tests miss this.


This PR extends pgsql-deparser with correct SQL text emission for several statement types and clauses previously dropped or mangled, each backed by a new kitchen-sink fixture and round-trip test. Two defects remain in the new emission paths that should be fixed before merge.

Files Change
packages/deparser/src/deparser.ts Adds WITH TIES limit emission, COPY option value rendering (including explicit boolean false and FORMAT quoting), SHOW name quoting, tablespace/index options on constraints, AND [NO] CHAIN on transaction statements, XMLSERIALIZE INDENT, and trigger rename support
packages/deparser/__tests__/misc/copy-boolean-options.test.ts + kitchen-sink fixture tests New unit and round-trip tests covering each new syntax path
__fixtures__/kitchen-sink/misc/*.sql + __fixtures__/generated/generated.json New SQL fixtures for the seven statement groups and regenerated fixture manifest

Reviewed commit: 8f032e0

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Folded into #371

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