Hi,

On 29/08/2025 19:27, Sami Imseih wrote:
Thoughts on v8?

I tried v8 with the Java file in the original email and it works (I don't see the wrong query in the logs).

Small fix needed in the test descriptions: used to logged -> used to log.

Only question is if we should avoid the extra portal hashtable lookup as well, or leave that for a separate patch. I gave it a go anyways, see attached v9, let me know what you think.

Kind regards,

Mircea Cadariu
From b2bc429dd0fb4f01920a3bf70a9756cf912f0c56 Mon Sep 17 00:00:00 2001
From: "Sami Imseih (AWS)"
 <[email protected]>
Date: Fri, 29 Aug 2025 17:19:34 +0000
Subject: [PATCH v9] Drop unnamed portal before setting debug_query_string

Discussion: 
https://www.postgresql.org/message-id/flat/[email protected]
---
 src/backend/tcop/postgres.c                   |  48 +++++++-
 src/backend/utils/mmgr/portalmem.c            |  62 ++++++----
 src/include/utils/portal.h                    |   1 +
 src/test/modules/test_misc/meson.build        |   1 +
 .../modules/test_misc/t/009_log_temp_files.pl | 115 ++++++++++++++++++
 5 files changed, 199 insertions(+), 28 deletions(-)
 create mode 100644 src/test/modules/test_misc/t/009_log_temp_files.pl

diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 0cecd46490..bbfee7c0ce 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -148,6 +148,7 @@ static bool ignore_till_sync = false;
  * in order to reduce overhead for short-lived queries.
  */
 static CachedPlanSource *unnamed_stmt_psrc = NULL;
+static bool unnamed_portal = false;
 
 /* assorted command-line switches */
 static const char *userDoption = NULL; /* -D switch */
@@ -182,6 +183,7 @@ static bool IsTransactionExitStmt(Node *parsetree);
 static bool IsTransactionExitStmtList(List *pstmts);
 static bool IsTransactionStmtList(List *pstmts);
 static void drop_unnamed_stmt(void);
+static void drop_unnamed_portal(void);
 static void log_disconnections(int code, Datum arg);
 static void enable_statement_timeout(void);
 static void disable_statement_timeout(void);
@@ -1020,6 +1022,12 @@ exec_simple_query(const char *query_string)
        bool            use_implicit_block;
        char            msec_str[32];
 
+       /*
+        * Drop the unnamed portal before setting debug_query_string, to avoid
+        * attributing messages from the drop (e.g., temp usage) to the new 
query.
+        */
+       drop_unnamed_portal();
+
        /*
         * Report query to various monitoring facilities.
         */
@@ -1213,7 +1221,13 @@ exec_simple_query(const char *query_string)
                 * Create unnamed portal to run the query or queries in. If 
there
                 * already is one, silently drop it.
                 */
-               portal = CreatePortal("", true, true);
+               if (!unnamed_portal)
+                       portal = CreatePortalOnly("");
+               else
+                       portal = CreatePortal("", true, true);
+
+               unnamed_portal = true;
+
                /* Don't display the portal in pg_cursors */
                portal->visible = false;
 
@@ -1672,6 +1686,12 @@ exec_bind_message(StringInfo input_message)
                                         errmsg("unnamed prepared statement 
does not exist")));
        }
 
+       /*
+        * Same as exec_simple_query, drop the unnamed portal before setting
+        * debug_query_string.
+        */
+       drop_unnamed_portal();
+
        /*
         * Report query to various monitoring facilities.
         */
@@ -1753,7 +1773,14 @@ exec_bind_message(StringInfo input_message)
         * if the unnamed portal is specified.
         */
        if (portal_name[0] == '\0')
-               portal = CreatePortal(portal_name, true, true);
+       {
+               if (!unnamed_portal)
+                       portal = CreatePortalOnly(portal_name);
+               else
+                       portal = CreatePortal(portal_name, true, true);
+
+               unnamed_portal = true;
+       }
        else
                portal = CreatePortal(portal_name, false, false);
 
@@ -5229,3 +5256,20 @@ disable_statement_timeout(void)
        if (get_timeout_active(STATEMENT_TIMEOUT))
                disable_timeout(STATEMENT_TIMEOUT, false);
 }
+
+/* Drop the unnamed portal if one exists */
+static void
+drop_unnamed_portal(void)
+{
+       Portal          portal;
+
+       if (!unnamed_portal)
+               return;
+
+       /* Get the portal and drop it */
+       portal = GetPortalByName("");
+       if (PortalIsValid(portal))
+               PortalDrop(portal, false);
+
+       unnamed_portal = false;
+}
diff --git a/src/backend/utils/mmgr/portalmem.c 
b/src/backend/utils/mmgr/portalmem.c
index 0be1c2b0ff..facb475fd9 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -162,37 +162,13 @@ PortalGetPrimaryStmt(Portal portal)
        return NULL;
 }
 
-/*
- * CreatePortal
- *             Returns a new portal given a name.
- *
- * allowDup: if true, automatically drop any pre-existing portal of the
- * same name (if false, an error is raised).
- *
- * dupSilent: if true, don't even emit a WARNING.
- */
 Portal
-CreatePortal(const char *name, bool allowDup, bool dupSilent)
+CreatePortalOnly(const char *name)
 {
-       Portal          portal;
+       Portal portal;
 
        Assert(PointerIsValid(name));
 
-       portal = GetPortalByName(name);
-       if (PortalIsValid(portal))
-       {
-               if (!allowDup)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_DUPLICATE_CURSOR),
-                                        errmsg("cursor \"%s\" already exists", 
name)));
-               if (!dupSilent)
-                       ereport(WARNING,
-                                       (errcode(ERRCODE_DUPLICATE_CURSOR),
-                                        errmsg("closing existing cursor 
\"%s\"",
-                                                       name)));
-               PortalDrop(portal, false);
-       }
-
        /* make new portal structure */
        portal = (Portal) MemoryContextAllocZero(TopPortalContext, sizeof 
*portal);
 
@@ -227,6 +203,40 @@ CreatePortal(const char *name, bool allowDup, bool 
dupSilent)
        return portal;
 }
 
+/*
+ * CreatePortal
+ *             Returns a new portal given a name.
+ *
+ * allowDup: if true, automatically drop any pre-existing portal of the
+ * same name (if false, an error is raised).
+ *
+ * dupSilent: if true, don't even emit a WARNING.
+ */
+Portal
+CreatePortal(const char *name, bool allowDup, bool dupSilent)
+{
+       Portal          portal;
+
+       Assert(PointerIsValid(name));
+
+       portal = GetPortalByName(name);
+       if (PortalIsValid(portal))
+       {
+               if (!allowDup)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DUPLICATE_CURSOR),
+                                        errmsg("cursor \"%s\" already exists", 
name)));
+               if (!dupSilent)
+                       ereport(WARNING,
+                                       (errcode(ERRCODE_DUPLICATE_CURSOR),
+                                        errmsg("closing existing cursor 
\"%s\"",
+                                                       name)));
+               PortalDrop(portal, false);
+       }
+
+       return CreatePortalOnly(name);
+}
+
 /*
  * CreateNewPortal
  *             Create a new portal, assigning it a random nonconflicting name.
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 0b62143af8..fb80bf3945 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -226,6 +226,7 @@ extern void AtSubAbort_Portals(SubTransactionId mySubid,
                                                           ResourceOwner 
myXactOwner,
                                                           ResourceOwner 
parentXactOwner);
 extern void AtSubCleanup_Portals(SubTransactionId mySubid);
+extern Portal CreatePortalOnly(const char *name);
 extern Portal CreatePortal(const char *name, bool allowDup, bool dupSilent);
 extern Portal CreateNewPortal(void);
 extern void PinPortal(Portal portal);
diff --git a/src/test/modules/test_misc/meson.build 
b/src/test/modules/test_misc/meson.build
index 6b1e730bf4..f258bf1ccd 100644
--- a/src/test/modules/test_misc/meson.build
+++ b/src/test/modules/test_misc/meson.build
@@ -17,6 +17,7 @@ tests += {
       't/006_signal_autovacuum.pl',
       't/007_catcache_inval.pl',
       't/008_replslot_single_user.pl',
+      't/009_log_temp_files.pl',
     ],
   },
 }
diff --git a/src/test/modules/test_misc/t/009_log_temp_files.pl 
b/src/test/modules/test_misc/t/009_log_temp_files.pl
new file mode 100644
index 0000000000..d16eb82a54
--- /dev/null
+++ b/src/test/modules/test_misc/t/009_log_temp_files.pl
@@ -0,0 +1,115 @@
+# Copyright (c) 2025, PostgreSQL Global Development Group
+
+# Verify that temp files are logged with the right statement.
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+sub check_log
+{
+       my $node = shift;
+       my $offset = shift;
+
+       if ($node->log_contains('STATEMENT:  SELECT 1', $offset))
+       {
+               ok(0, "The wrong query has been logged");
+       }
+       elsif ($node->log_contains('STATEMENT:  SELECT a FROM foo ORDER BY a', 
$offset))
+       {
+               ok(1, "The right query has been logged");
+       }
+       elsif ($node->log_contains('STATEMENT:  CLOSE', $offset))
+       {
+               ok(1, "Cursor CLOSE has been logged, OK");
+       }
+       else
+       {
+               ok(1, "No query has been logged");
+       }
+}
+
+my $offset = 0;
+my $node = PostgreSQL::Test::Cluster->new('primary');
+$node->init();
+$node->append_conf(
+       'postgresql.conf', qq(
+work_mem = 64kB
+log_temp_files = 0
+log_statement = all
+log_line_prefix='%t [%p]: [%l-1] user=%u,db=%d,host=%h,app=%a,cilent=%h'
+log_min_duration_statement = 0
+));
+$node->start;
+
+$node->safe_psql("postgres", qq{
+CREATE UNLOGGED TABLE foo(a int);
+INSERT INTO foo(a) SELECT * FROM generate_series(1, 5000);
+VACUUM ANALYZE foo;
+});
+
+$offset = -s $node->logfile;
+# The following sequence used to log the second query instead of the first one
+# Now it should log no query at all
+$node->safe_psql("postgres", qq{
+BEGIN;
+SELECT a FROM foo ORDER BY a OFFSET \$1 \\bind 4999 \\g
+SELECT 1;
+COMMIT;
+});
+check_log($node, $offset);
+
+$offset = -s $node->logfile;
+# The following sequence should log no query at all
+$node->safe_psql("postgres", qq{
+SELECT a FROM foo ORDER BY a OFFSET \$1 \\bind 4999 \\g
+SELECT 1;
+});
+check_log($node, $offset);
+
+$offset = -s $node->logfile;
+# The following pipelined sequence used to log the second query instead of the 
first one
+# Now it should log no query at all
+$node->safe_psql("postgres", qq{
+\\startpipeline
+SELECT a FROM foo ORDER BY a OFFSET \$1 \\bind 4999 \\sendpipeline
+SELECT 1;
+\\endpipeline
+});
+check_log($node, $offset);
+
+$offset = -s $node->logfile;
+# The following sequence should log the right query
+$node->safe_psql("postgres", qq{
+SELECT a FROM foo ORDER BY a OFFSET 4999;
+});
+check_log($node, $offset);
+
+$offset = -s $node->logfile;
+# The following sequence should log no query at all (named statement, unnamed 
portal)
+$node->safe_psql("postgres", qq{
+BEGIN;
+SELECT a FROM foo ORDER BY a OFFSET \$1 \\parse stmt
+\\bind_named stmt 4999 \\g
+SELECT 1;
+COMMIT;
+});
+check_log($node, $offset);
+
+$offset = -s $node->logfile;
+# The following sequence should log the CLOSE query
+$node->safe_psql("postgres", qq{
+BEGIN;
+DECLARE mycur CURSOR FOR SELECT a FROM foo ORDER BY a OFFSET 4999;
+FETCH 10 FROM mycur;
+SELECT 1;
+CLOSE mycur;
+COMMIT;
+});
+check_log($node, $offset);
+
+
+$node->stop('fast');
+done_testing();

base-commit: 09119238a18191dea3deed635a2b2a6ffe904932
-- 
2.39.5 (Apple Git-154)

Reply via email to