From 8b325c572b63859781879cf645e7eb87521e1118 Mon Sep 17 00:00:00 2001 From: Henson Choi Date: Fri, 5 Jun 2026 16:52:38 +0900 Subject: [PATCH 52/68] Handle row pattern navigation nodes in exprTypmod and isSimpleNode RPRNavExpr, the PREV/NEXT/FIRST/LAST navigation node used in a row pattern DEFINE clause, was missing from two type-helper switches and fell through to their defaults. In exprTypmod() the default returns -1, so the argument's typmod was dropped. A navigation expression evaluates its argument on another row without changing its type, so its result has the same type and typmod as the argument; return the argument's typmod, matching how exprType() already reports its result type. In isSimpleNode() the default returns false, which made pretty-printing wrap a navigation operand in redundant parentheses, for example val > (PREV(val)). A navigation expression deparses as a function-like FUNC(..) call that already delimits itself, so it is simple and needs no extra parentheses; add it to the function-like group. This affects display and node completeness only; execution and re-parsing were already correct, as the extra parentheses were still valid SQL. Add a pretty-mode regression using the two-argument pg_get_viewdef in rpr_base, a path the existing single-argument navigation tests did not exercise. --- src/backend/nodes/nodeFuncs.c | 3 +++ src/backend/utils/adt/ruleutils.c | 1 + src/test/regress/expected/rpr_base.out | 27 ++++++++++++++++++++++++++ src/test/regress/sql/rpr_base.sql | 13 +++++++++++++ 4 files changed, 44 insertions(+) diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 101c03b6ae8..aecf4e4da0a 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -392,6 +392,9 @@ exprTypmod(const Node *expr) return ((const ArrayCoerceExpr *) expr)->resulttypmod; case T_CollateExpr: return exprTypmod((Node *) ((const CollateExpr *) expr)->arg); + case T_RPRNavExpr: + /* result has the same type/typmod as the argument expression */ + return exprTypmod((Node *) ((const RPRNavExpr *) expr)->arg); case T_CaseExpr: { /* diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 6237080fb36..2b8439e452e 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9616,6 +9616,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_FuncExpr: case T_JsonConstructorExpr: case T_JsonExpr: + case T_RPRNavExpr: /* function-like: name(..) or name[..] */ return true; diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out index 4fe7360114f..fa6d101f874 100644 --- a/src/test/regress/expected/rpr_base.out +++ b/src/test/regress/expected/rpr_base.out @@ -2488,6 +2488,33 @@ SELECT pg_get_viewdef('rpr_serial_nav6'::regclass); b AS (NEXT(FIRST(val), (3)::bigint) > 0) ); (1 row) +-- Pretty deparse: navigation calls are function-like and take no extra parens +CREATE VIEW rpr_nav_pretty_v AS +SELECT id, val, count(*) OVER w +FROM rpr_serial +WINDOW w AS (ORDER BY id + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A B+) + DEFINE A AS TRUE, + B AS val > PREV(val) AND PREV(val) IS NOT NULL + AND NEXT(val) > FIRST(val) + AND PREV(FIRST(val)) > 0); +SELECT pg_get_viewdef('rpr_nav_pretty_v'::regclass, true); + pg_get_viewdef +--------------------------------------------------------------------------------------------------------- + SELECT id, + + val, + + count(*) OVER w AS count + + FROM rpr_serial + + WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + + AFTER MATCH SKIP PAST LAST ROW + + INITIAL + + PATTERN (a b+) + + DEFINE + + a AS true, + + b AS val > PREV(val) AND PREV(val) IS NOT NULL AND NEXT(val) > FIRST(val) AND PREV(FIRST(val)) > 0 ); +(1 row) + -- Reluctant {1}? quantifier deparse through ruleutils CREATE VIEW rpr_quant_reluctant_v AS SELECT id, val, count(*) OVER w diff --git a/src/test/regress/sql/rpr_base.sql b/src/test/regress/sql/rpr_base.sql index c6fcfa3e9ff..e0af7199629 100644 --- a/src/test/regress/sql/rpr_base.sql +++ b/src/test/regress/sql/rpr_base.sql @@ -1693,6 +1693,19 @@ WINDOW w AS (ORDER BY id DEFINE A AS TRUE, B AS NEXT(FIRST(val), 3) > 0); SELECT pg_get_viewdef('rpr_serial_nav6'::regclass); +-- Pretty deparse: navigation calls are function-like and take no extra parens +CREATE VIEW rpr_nav_pretty_v AS +SELECT id, val, count(*) OVER w +FROM rpr_serial +WINDOW w AS (ORDER BY id + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A B+) + DEFINE A AS TRUE, + B AS val > PREV(val) AND PREV(val) IS NOT NULL + AND NEXT(val) > FIRST(val) + AND PREV(FIRST(val)) > 0); +SELECT pg_get_viewdef('rpr_nav_pretty_v'::regclass, true); + -- Reluctant {1}? quantifier deparse through ruleutils CREATE VIEW rpr_quant_reluctant_v AS SELECT id, val, count(*) OVER w -- 2.50.1 (Apple Git-155)