nocfbot-0052-Handle-row-pattern-navigation-nodes-in-exprTypmod.txt

text/plain

Filename: nocfbot-0052-Handle-row-pattern-navigation-nodes-in-exprTypmod.txt
Type: text/plain
Part: 50
Message: Re: Row pattern recognition
From 8b325c572b63859781879cf645e7eb87521e1118 Mon Sep 17 00:00:00 2001
From: Henson Choi <assam258@gmail.com>
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)