From ddeb645a00084335e42ea13b1c91406160b749fd Mon Sep 17 00:00:00 2001
From: Sami Imseih <simseih@amazon.com>
Date: Wed, 30 Apr 2025 14:25:28 -0500
Subject: [PATCH v2 1/2] Normalize cursor utility statements

---
 .../pg_stat_statements/expected/cursors.out   | 24 +++++++++----------
 .../expected/level_tracking.out               | 24 +++++++++----------
 .../pg_stat_statements/expected/utility.out   |  8 +++----
 src/backend/parser/gram.y                     | 18 ++++++++++++++
 src/include/nodes/parsenodes.h                | 12 +++++++---
 5 files changed, 53 insertions(+), 33 deletions(-)

diff --git a/contrib/pg_stat_statements/expected/cursors.out b/contrib/pg_stat_statements/expected/cursors.out
index 0fc4b2c098d..1f4de404707 100644
--- a/contrib/pg_stat_statements/expected/cursors.out
+++ b/contrib/pg_stat_statements/expected/cursors.out
@@ -16,10 +16,10 @@ CLOSE cursor_stats_1;
 DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 2;
 CLOSE cursor_stats_1;
 SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows |                         query                         
--------+------+-------------------------------------------------------
-     2 |    0 | CLOSE cursor_stats_1
-     2 |    0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT $1
+ calls | rows |                       query                        
+-------+------+----------------------------------------------------
+     2 |    0 | CLOSE $1
+     2 |    0 | DECLARE $1 CURSOR WITH HOLD FOR SELECT $2
      1 |    1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
 (3 rows)
 
@@ -49,18 +49,16 @@ CLOSE cursor_stats_1;
 CLOSE cursor_stats_2;
 COMMIT;
 SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows |                         query                         
--------+------+-------------------------------------------------------
+ calls | rows |                       query                        
+-------+------+----------------------------------------------------
      1 |    0 | BEGIN
-     1 |    0 | CLOSE cursor_stats_1
-     1 |    0 | CLOSE cursor_stats_2
+     2 |    0 | CLOSE $1
      1 |    0 | COMMIT
-     1 |    0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT $1
-     1 |    0 | DECLARE cursor_stats_2 CURSOR WITH HOLD FOR SELECT $1
-     1 |    1 | FETCH 1 IN cursor_stats_1
-     1 |    1 | FETCH 1 IN cursor_stats_2
+     2 |    0 | DECLARE $1 CURSOR WITH HOLD FOR SELECT $2
+     1 |    1 | FETCH $1 IN cursor_stats_1
+     1 |    1 | FETCH $1 IN cursor_stats_2
      1 |    1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
-(9 rows)
+(7 rows)
 
 SELECT pg_stat_statements_reset() IS NOT NULL AS t;
  t 
diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index 03bea14d5da..2b2f1d5ee42 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -921,7 +921,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
  toplevel | calls |                                    query                                     
 ----------+-------+------------------------------------------------------------------------------
  t        |     1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)          +
-          |       |   DECLARE foocur CURSOR FOR SELECT * FROM stats_track_tab
+          |       |   DECLARE $1 CURSOR FOR SELECT * FROM stats_track_tab
  t        |     1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) SELECT $1
  f        |     1 | SELECT $1
  f        |     1 | SELECT * FROM stats_track_tab
@@ -954,7 +954,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
  toplevel | calls |                                    query                                     
 ----------+-------+------------------------------------------------------------------------------
  t        |     1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF)          +
-          |       |   DECLARE foocur CURSOR FOR SELECT * FROM stats_track_tab
+          |       |   DECLARE $1 CURSOR FOR SELECT * FROM stats_track_tab
  t        |     1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) SELECT $1
  t        |     1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
 (3 rows)
@@ -1136,13 +1136,13 @@ CLOSE foocur;
 COMMIT;
 SELECT toplevel, calls, query FROM pg_stat_statements
   ORDER BY query COLLATE "C";
- toplevel | calls |                          query                          
-----------+-------+---------------------------------------------------------
+ toplevel | calls |                        query                        
+----------+-------+-----------------------------------------------------
  t        |     1 | BEGIN
- t        |     1 | CLOSE foocur
+ t        |     1 | CLOSE $1
  t        |     1 | COMMIT
- t        |     1 | DECLARE FOOCUR CURSOR FOR SELECT * from stats_track_tab
- t        |     1 | FETCH FORWARD 1 FROM foocur
+ t        |     1 | DECLARE $1 CURSOR FOR SELECT * from stats_track_tab
+ t        |     1 | FETCH FORWARD $1 FROM foocur
  f        |     1 | SELECT * from stats_track_tab
  t        |     1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
 (7 rows)
@@ -1166,13 +1166,13 @@ CLOSE foocur;
 COMMIT;
 SELECT toplevel, calls, query FROM pg_stat_statements
   ORDER BY query COLLATE "C";
- toplevel | calls |                          query                          
-----------+-------+---------------------------------------------------------
+ toplevel | calls |                        query                        
+----------+-------+-----------------------------------------------------
  t        |     1 | BEGIN
- t        |     1 | CLOSE foocur
+ t        |     1 | CLOSE $1
  t        |     1 | COMMIT
- t        |     1 | DECLARE FOOCUR CURSOR FOR SELECT * FROM stats_track_tab
- t        |     1 | FETCH FORWARD 1 FROM foocur
+ t        |     1 | DECLARE $1 CURSOR FOR SELECT * FROM stats_track_tab
+ t        |     1 | FETCH FORWARD $1 FROM foocur
  t        |     1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
 (6 rows)
 
diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out
index aa4f0f7e628..e8b27d8304b 100644
--- a/contrib/pg_stat_statements/expected/utility.out
+++ b/contrib/pg_stat_statements/expected/utility.out
@@ -701,14 +701,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
      1 |    3 | COPY pgss_ctas (a, b) FROM STDIN
      1 |   13 | CREATE MATERIALIZED VIEW pgss_matv AS SELECT * FROM pgss_ctas
      1 |   10 | CREATE TABLE pgss_ctas AS SELECT a, $1 b FROM generate_series($2, $3) a
-     1 |    0 | DECLARE pgss_cursor CURSOR FOR SELECT * FROM pgss_matv
-     1 |    5 | FETCH FORWARD 5 pgss_cursor
-     1 |    7 | FETCH FORWARD ALL pgss_cursor
-     1 |    1 | FETCH NEXT pgss_cursor
+     1 |    0 | DECLARE $1 CURSOR FOR SELECT * FROM pgss_matv
+     3 |   13 | FETCH NEXT pgss_cursor
      1 |   13 | REFRESH MATERIALIZED VIEW pgss_matv
      1 |   10 | SELECT generate_series($1, $2) c INTO pgss_select_into
      1 |    1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
-(12 rows)
+(10 rows)
 
 DROP MATERIALIZED VIEW pgss_matv;
 DROP TABLE pgss_ctas;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3c4268b271a..c9be19643ac 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3341,6 +3341,7 @@ ClosePortalStmt:
 					ClosePortalStmt *n = makeNode(ClosePortalStmt);
 
 					n->portalname = $2;
+					n->location = @2;
 					$$ = (Node *) n;
 				}
 			| CLOSE ALL
@@ -3348,6 +3349,7 @@ ClosePortalStmt:
 					ClosePortalStmt *n = makeNode(ClosePortalStmt);
 
 					n->portalname = NULL;
+					n->location = -1;
 					$$ = (Node *) n;
 				}
 		;
@@ -7479,6 +7481,7 @@ fetch_args:	cursor_name
 					n->portalname = $1;
 					n->direction = FETCH_FORWARD;
 					n->howMany = 1;
+					n->location = -1;
 					$$ = (Node *) n;
 				}
 			| from_in cursor_name
@@ -7488,6 +7491,7 @@ fetch_args:	cursor_name
 					n->portalname = $2;
 					n->direction = FETCH_FORWARD;
 					n->howMany = 1;
+					n->location = -1;
 					$$ = (Node *) n;
 				}
 			| NEXT opt_from_in cursor_name
@@ -7497,6 +7501,7 @@ fetch_args:	cursor_name
 					n->portalname = $3;
 					n->direction = FETCH_FORWARD;
 					n->howMany = 1;
+					n->location = -1;
 					$$ = (Node *) n;
 				}
 			| PRIOR opt_from_in cursor_name
@@ -7506,6 +7511,7 @@ fetch_args:	cursor_name
 					n->portalname = $3;
 					n->direction = FETCH_BACKWARD;
 					n->howMany = 1;
+					n->location = -1;
 					$$ = (Node *) n;
 				}
 			| FIRST_P opt_from_in cursor_name
@@ -7515,6 +7521,7 @@ fetch_args:	cursor_name
 					n->portalname = $3;
 					n->direction = FETCH_ABSOLUTE;
 					n->howMany = 1;
+					n->location = -1;
 					$$ = (Node *) n;
 				}
 			| LAST_P opt_from_in cursor_name
@@ -7524,6 +7531,7 @@ fetch_args:	cursor_name
 					n->portalname = $3;
 					n->direction = FETCH_ABSOLUTE;
 					n->howMany = -1;
+					n->location = -1;
 					$$ = (Node *) n;
 				}
 			| ABSOLUTE_P SignedIconst opt_from_in cursor_name
@@ -7533,6 +7541,7 @@ fetch_args:	cursor_name
 					n->portalname = $4;
 					n->direction = FETCH_ABSOLUTE;
 					n->howMany = $2;
+					n->location = @2;
 					$$ = (Node *) n;
 				}
 			| RELATIVE_P SignedIconst opt_from_in cursor_name
@@ -7542,6 +7551,7 @@ fetch_args:	cursor_name
 					n->portalname = $4;
 					n->direction = FETCH_RELATIVE;
 					n->howMany = $2;
+					n->location = @2;
 					$$ = (Node *) n;
 				}
 			| SignedIconst opt_from_in cursor_name
@@ -7551,6 +7561,7 @@ fetch_args:	cursor_name
 					n->portalname = $3;
 					n->direction = FETCH_FORWARD;
 					n->howMany = $1;
+					n->location = @1;
 					$$ = (Node *) n;
 				}
 			| ALL opt_from_in cursor_name
@@ -7560,6 +7571,7 @@ fetch_args:	cursor_name
 					n->portalname = $3;
 					n->direction = FETCH_FORWARD;
 					n->howMany = FETCH_ALL;
+					n->location = -1;
 					$$ = (Node *) n;
 				}
 			| FORWARD opt_from_in cursor_name
@@ -7569,6 +7581,7 @@ fetch_args:	cursor_name
 					n->portalname = $3;
 					n->direction = FETCH_FORWARD;
 					n->howMany = 1;
+					n->location = -1;
 					$$ = (Node *) n;
 				}
 			| FORWARD SignedIconst opt_from_in cursor_name
@@ -7578,6 +7591,7 @@ fetch_args:	cursor_name
 					n->portalname = $4;
 					n->direction = FETCH_FORWARD;
 					n->howMany = $2;
+					n->location = @2;
 					$$ = (Node *) n;
 				}
 			| FORWARD ALL opt_from_in cursor_name
@@ -7596,6 +7610,7 @@ fetch_args:	cursor_name
 					n->portalname = $3;
 					n->direction = FETCH_BACKWARD;
 					n->howMany = 1;
+					n->location = -1;
 					$$ = (Node *) n;
 				}
 			| BACKWARD SignedIconst opt_from_in cursor_name
@@ -7605,6 +7620,7 @@ fetch_args:	cursor_name
 					n->portalname = $4;
 					n->direction = FETCH_BACKWARD;
 					n->howMany = $2;
+					n->location = @2;
 					$$ = (Node *) n;
 				}
 			| BACKWARD ALL opt_from_in cursor_name
@@ -7614,6 +7630,7 @@ fetch_args:	cursor_name
 					n->portalname = $4;
 					n->direction = FETCH_BACKWARD;
 					n->howMany = FETCH_ALL;
+					n->location = -1;
 					$$ = (Node *) n;
 				}
 		;
@@ -12752,6 +12769,7 @@ DeclareCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR Select
 					DeclareCursorStmt *n = makeNode(DeclareCursorStmt);
 
 					n->portalname = $2;
+					n->location = @2;
 					/* currently we always set FAST_PLAN option */
 					n->options = $3 | $5 | CURSOR_OPT_FAST_PLAN;
 					n->query = $7;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4610fc61293..14181a48924 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3387,9 +3387,11 @@ typedef struct SecLabelStmt
 typedef struct DeclareCursorStmt
 {
 	NodeTag		type;
-	char	   *portalname;		/* name of the portal (cursor) */
+	/* name of the portal (cursor) */
+	char	   *portalname pg_node_attr(query_jumble_ignore);
 	int			options;		/* bitmask of options (see above) */
 	Node	   *query;			/* the query (see comments above) */
+	ParseLoc	location pg_node_attr(query_jumble_location);
 } DeclareCursorStmt;
 
 /* ----------------------
@@ -3399,7 +3401,9 @@ typedef struct DeclareCursorStmt
 typedef struct ClosePortalStmt
 {
 	NodeTag		type;
-	char	   *portalname;		/* name of the portal (cursor) */
+	/* name of the portal (cursor) */
+	char	   *portalname pg_node_attr(query_jumble_ignore);
+	ParseLoc	location pg_node_attr(query_jumble_location);
 	/* NULL means CLOSE ALL */
 } ClosePortalStmt;
 
@@ -3423,9 +3427,11 @@ typedef struct FetchStmt
 {
 	NodeTag		type;
 	FetchDirection direction;	/* see above */
-	long		howMany;		/* number of rows, or position argument */
+	/* number of rows, or position argument */
+	long		howMany pg_node_attr(query_jumble_ignore);
 	char	   *portalname;		/* name of portal (cursor) */
 	bool		ismove;			/* true if MOVE */
+	ParseLoc	location pg_node_attr(query_jumble_location);
 } FetchStmt;
 
 /* ----------------------
-- 
2.39.5 (Apple Git-154)

