v2-0001-pg_stat_statements-Fix-nested-tracking-for-implic.patch
application/octet-stream
Filename: v2-0001-pg_stat_statements-Fix-nested-tracking-for-implic.patch
Type: application/octet-stream
Part: 0
Patch
Format: format-patch
Series: patch v2-0001
Subject: pg_stat_statements: Fix nested tracking for implicitly closed cursors
| File | + | − |
|---|---|---|
| contrib/pg_stat_statements/expected/squashing.out | 15 | 1 |
| contrib/pg_stat_statements/sql/squashing.sql | 5 | 0 |
| src/backend/parser/parse_expr.c | 11 | 2 |
From 425a26fc3a48b21e0a4ad7a85321603cb915d704 Mon Sep 17 00:00:00 2001
From: Ubuntu <ubuntu@ip-172-31-46-230.ec2.internal>
Date: Wed, 14 Jan 2026 16:29:25 +0000
Subject: [PATCH v2 1/1] pg_stat_statements: Fix nested tracking for implicitly
closed cursors
When cursors are implicitly closed during COMMIT, pg_stat_statements
incorrectly marks the cursor's underlying query as toplevel=true because
PortalCleanup, which triggers ExecutorEnd, runs after ProcessUtility and
at which point the nesting_level is 0.
Fix by adding an is_txn_end flag to detect COMMIT statements and
temporarily adjust nesting_level in ExecutorEnd.
Add regression tests for both explicit and implicit cursor closure,
with track = 'all' and with and without track_planning.
Discussion: https://postgr.es/m/CAA5RZ0t2+GLnE_55L2cfCay+L8yPFpdPRVQo-JswUFgXy-EK5Q@mail.gmail.com
---
.../pg_stat_statements/expected/squashing.out | 16 +++++++++++++++-
contrib/pg_stat_statements/sql/squashing.sql | 5 +++++
src/backend/parser/parse_expr.c | 13 +++++++++++--
3 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/contrib/pg_stat_statements/expected/squashing.out b/contrib/pg_stat_statements/expected/squashing.out
index d5bb67c7222..13c184d5018 100644
--- a/contrib/pg_stat_statements/expected/squashing.out
+++ b/contrib/pg_stat_statements/expected/squashing.out
@@ -872,6 +872,18 @@ SELECT (ROW(ARRAY[1, 2], ARRAY[1, $1, 3])).*, 1 \bind 1
{1,2} | {1,1,3} | 1
(1 row)
+-- Mixed Const and non-Const expressions in the list will not be squashed
+SELECT * FROM test_squash a, test_squash b WHERE a.id IN (1, 2, 3, b.id, b.id + 1);
+ id | data | id | data
+----+------+----+------
+(0 rows)
+
+SELECT * FROM test_squash a, test_squash b WHERE a.id IN ($1, $2, $3, b.id, b.id + $4) \bind 1 2 3 1
+;
+ id | data | id | data
+----+------+----+------
+(0 rows)
+
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls
-------------------------------------------------------------------------------------------------------------+-------
@@ -884,8 +896,10 @@ SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT (ROW(ARRAY[$1 /*, ... */])).* | 1
SELECT (ROW(ARRAY[$1 /*, ... */], ARRAY[$2 /*, ... */])).* | 1
SELECT (ROW(ARRAY[$1 /*, ... */], ARRAY[$2 /*, ... */])).*, $3 | 1
+ SELECT * FROM test_squash a, test_squash b WHERE a.id IN ($1, $2, $3, b.id, b.id + $4) | 1
+ SELECT * FROM test_squash a, test_squash b WHERE a.id IN ($1, $2, $3, b.id, b.id + $4) | 1
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
-(8 rows)
+(10 rows)
--
-- cleanup
diff --git a/contrib/pg_stat_statements/sql/squashing.sql b/contrib/pg_stat_statements/sql/squashing.sql
index 03b0515f872..c3f2f506955 100644
--- a/contrib/pg_stat_statements/sql/squashing.sql
+++ b/contrib/pg_stat_statements/sql/squashing.sql
@@ -313,6 +313,11 @@ SELECT (ROW(ARRAY[1, 2], ARRAY[1, 2, 3])).*;
SELECT 1, 2, (ROW(ARRAY[1, 2], ARRAY[1, 2, 3])).*, 3, 4;
SELECT (ROW(ARRAY[1, 2], ARRAY[1, $1, 3])).*, 1 \bind 1
;
+
+-- Mixed Const and non-Const expressions in the list will not be squashed
+SELECT * FROM test_squash a, test_squash b WHERE a.id IN (1, 2, 3, b.id, b.id + 1);
+SELECT * FROM test_squash a, test_squash b WHERE a.id IN ($1, $2, $3, b.id, b.id + $4) \bind 1 2 3 1
+;
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
--
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 56826db4c26..c964386ccb2 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -1132,6 +1132,7 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
List *rnonvars;
bool useOr;
ListCell *l;
+ bool has_rvars = false;
/*
* If the operator is <>, combine with AND not OR.
@@ -1160,7 +1161,10 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
rexprs = lappend(rexprs, rexpr);
if (contain_vars_of_level(rexpr, 0))
+ {
rvars = lappend(rvars, rexpr);
+ has_rvars = true;
+ }
else
rnonvars = lappend(rnonvars, rexpr);
}
@@ -1225,10 +1229,15 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
newa->element_typeid = scalar_type;
newa->elements = aexprs;
newa->multidims = false;
- newa->list_start = a->rexpr_list_start;
- newa->list_end = a->rexpr_list_end;
newa->location = -1;
+ /*
+ * Mark the list as non-squashable if it contains Vars. Jumble
+ * should only squash lists containing non-Vars.
+ */
+ newa->list_start = has_rvars ? -1 : a->rexpr_list_start;
+ newa->list_end = has_rvars ? -1 : a->rexpr_list_end;
+
result = (Node *) make_scalar_array_op(pstate,
a->name,
useOr,
--
2.43.0