Skip to content

refactor(pretty_print): share the constructs several statements print as nodes/clauses - #999

Open
psteinroe wants to merge 8 commits into
refactor/pretty-print-argumentsfrom
refactor/pretty-print-clauses
Open

psteinroe wants to merge 8 commits into
refactor/pretty-print-argumentsfrom
refactor/pretty-print-clauses

Conversation

@psteinroe

@psteinroe psteinroe commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #996.

Many statements print the same constructs, and each had its own copy: 13 files mapped ObjectType to keywords, with about 250 match arms between them, the window frame was written twice, OPTIONS (...) eight times, the operator class name four times, and so on. Others lived in one node file and were called from others through super::<file>::<helper>. This PR moves them to nodes/clauses/, one file per construct, reached through the prelude like utils/. No snapshot changes.

What changed

nodes/clauses/:

  • object_type.rs: object_type_keywords(ObjectType), the one table of MATERIALIZED VIEW, TEXT SEARCH PARSER, ... used by DROP, ALTER ... OWNER, RENAME, SET SCHEMA, COMMENT, SECURITY LABEL, GRANT, ALTER TABLE and the rest. Each statement keeps its own fallback (TABLE, OBJECT, ...). COMMENT compared objtype against numbers (== 7); it uses ObjectType now.
  • modifiers.rs: IF EXISTS, IF NOT EXISTS, OR REPLACE (36 copies), and CASCADE / RESTRICT (emit_drop_behavior, 12 copies).
  • options.rs: WITH (...), OPTIONS (...), and the DefElem variants that were exported by def_elem.rs (generic, sequence, role and grant-role options).
  • window_frame.rs: the frame clause, with the frame option bits, from window_def.rs and window_clause.rs.
  • operators.rs and operator_class.rs: OPERATOR(schema.+) in expressions and ORDER BY ... USING, schema.+ in DDL, and name USING method.
  • function_options.rs: the option order and printing of CREATE / ALTER FUNCTION. normalize.rs sorts by the same order now.
  • set_clause.rs, filter.rs, grant.rs, case_result.rs, json.rs: the SET list, FILTER, the grant lists, the result of a CASE branch, and the SQL/JSON RETURNING / FORMAT / ON EMPTY / ON ERROR clauses.

The JSON nodes get a file each (json_output.rs, json_format.rs, json_returning.rs, json_behavior.rs, json_argument.rs, json_table_column.rs, ...). The node variants that other files called through super::<file>:: take an enum and go through nodes/mod.rs: emit_type_cast_as(.., CastSpelling::Call), emit_bool_expr_as(.., OperandGroup::Enclosing), emit_window_def_as(.., WindowForm::Named), emit_range_var_as(.., Only::Omitted), emit_case_expr_as(.., BranchPlacement::Indented). No node file calls super::<file>:: any more.

Where the copies disagreed

The output stays as it was:

  • Privileges: GRANT and REVOKE write all privileges on some columns as all (a), with a space before the columns (clauses/grant.rs). An AccessPriv reached through emit_node (ALTER DEFAULT PRIVILEGES) writes all privileges (a), may break before the column list, and has a group of its own (access_priv.rs).
  • A foreign server is SERVER everywhere except GRANT and SECURITY LABEL, which write FOREIGN SERVER.
  • Of the statements with a drop behavior, REVOKE ROLE and DROP SUBSCRIPTION also write RESTRICT. The others only write CASCADE (Restrict::Written / Restrict::Omitted).
  • A named window of the WINDOW clause may break between the mode and a single bound (ROWS / UNBOUNDED PRECEDING). The window of a call doesn't (SingleBoundBreak).
  • The JsonBehavior node writes coerce and has a group of its own. The behaviors of JSON_VALUE / JSON_QUERY don't. Both share the keywords.
  • x = ANY (...) keeps only the symbol of a qualified operator (sub_link.rs). The doc comment of operators.rs points there.
  • The two function option orders: the printer put TRANSFORM second, and normalize.rs put it last because it didn't know the option. Normalization sorts both ASTs by the same key, and TRANSFORM was the only option in its class, so sorting by the printer's order compares the same way. normalize.rs uses function_option_order now.

One difference that isn't a pure move: SECURITY LABEL and ALTER EXTENSION ADD/DROP listed fewer object types than the shared table, and wrote TABLE / OBJECT for the others (a materialized view, for instance). That output had a different AST, so format_statement refused it and left the statement as written. With the shared table they write the right keywords. Where the old copies of DROP, ALTER ... OWNER and RENAME fell back to TABLE, the types in question (a role, a cast, a transform) can't occur in those statements.

Tests

  • cargo test -p pgls_pretty_print: no snapshot changes.
  • cargo test -p pgls_pretty_print --release --test option_matrix -- --ignored
  • cargo clippy --workspace --all-targets -- -D warnings

This branch has not been deployed

No deployments
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