On 13/11/2025 06:27, Michael Paquier wrote:

This is new, attaching the information to a Vfd in fd.c.  Not sure
that adding this information to this structure is a good concept.
This layer of the code has no idea of query strings currently, so that
feels a bit like a layer violation.

Thanks for having a look! FWIW I found a way for Plan C to work without including tcoprot into fd, see attached.

There's a new field in that structure indeed, but maybe not that far fetched, it's the query that triggered the creation of the file.

Kind regards,

Mircea Cadariu
From 08e5e884a2d86e0510d29c94fdf17405a25d822c Mon Sep 17 00:00:00 2001
From: Mircea Cadariu <[email protected]>
Date: Wed, 12 Nov 2025 10:45:21 +0000
Subject: [PATCH v1] show temp file creating query

---
 src/backend/storage/file/fd.c                 | 39 ++++++++++++++++---
 src/backend/tcop/postgres.c                   | 10 -----
 src/include/miscadmin.h                       |  2 +
 src/include/tcop/tcopprot.h                   |  1 -
 .../modules/test_misc/t/009_log_temp_files.pl | 16 ++++----
 5 files changed, 43 insertions(+), 25 deletions(-)

diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index a4ec7959f3..f03d31fbd1 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -206,6 +206,7 @@ typedef struct vfd
        /* NB: fileName is malloc'd, and must be free'd when closing the VFD */
        int                     fileFlags;              /* open(2) flags for 
(re)opening the file */
        mode_t          fileMode;               /* mode to pass to open(2) */
+       char       *temp_file_creator_query;    /* creator query for this temp 
file, if any */
 } Vfd;
 
 /*
@@ -1482,6 +1483,11 @@ FreeVfd(File file)
                free(vfdP->fileName);
                vfdP->fileName = NULL;
        }
+       if (vfdP->temp_file_creator_query != NULL)
+       {
+               free(vfdP->temp_file_creator_query);
+               vfdP->temp_file_creator_query = NULL;
+       }
        vfdP->fdstate = 0x0;
 
        vfdP->nextFree = VfdCache[0].nextFree;
@@ -1524,18 +1530,27 @@ FileAccess(File file)
 
 /*
  * Called whenever a temporary file is deleted to report its size.
+ * If temp_file_creator_query is non-NULL, it represents the query that 
created this
+ * temp file and will be logged.
  */
 static void
-ReportTemporaryFileUsage(const char *path, off_t size)
+ReportTemporaryFileUsage(const char *path, off_t size, const char 
*temp_file_creator_query)
 {
        pgstat_report_tempfile(size);
 
        if (log_temp_files >= 0)
        {
                if ((size / 1024) >= log_temp_files)
-                       ereport(LOG,
-                                       (errmsg("temporary file: path \"%s\", 
size %lu",
-                                                       path, (unsigned long) 
size)));
+               {
+                       if (temp_file_creator_query != NULL)
+                               ereport(LOG,
+                                               (errmsg("temporary file: path 
\"%s\", size %lu, created due to: %s",
+                                                               path, (unsigned 
long) size, temp_file_creator_query)));
+                       else
+                               ereport(LOG,
+                                               (errmsg("temporary file: path 
\"%s\", size %lu",
+                                                               path, (unsigned 
long) size)));
+               }
        }
 }
 
@@ -1842,6 +1857,12 @@ OpenTemporaryFileInTablespace(Oid tblspcOid, bool 
rejectError)
                                 tempfilepath);
        }
 
+       /*
+        * Remember the creator query for this temp file.
+        */
+       if (file > 0 && debug_query_string != NULL)
+               VfdCache[file].temp_file_creator_query = 
strdup(debug_query_string);
+
        return file;
 }
 
@@ -1889,6 +1910,12 @@ PathNameCreateTemporaryFile(const char *path, bool 
error_on_failure)
        /* Register it for automatic close. */
        RegisterTemporaryFile(file);
 
+       /*
+        * Remember the creator query for this temp file.
+        */
+       if (debug_query_string != NULL)
+               VfdCache[file].temp_file_creator_query = 
strdup(debug_query_string);
+
        return file;
 }
 
@@ -1960,7 +1987,7 @@ PathNameDeleteTemporaryFile(const char *path, bool 
error_on_failure)
        }
 
        if (stat_errno == 0)
-               ReportTemporaryFileUsage(path, filestats.st_size);
+               ReportTemporaryFileUsage(path, filestats.st_size, NULL);
        else
        {
                errno = stat_errno;
@@ -2048,7 +2075,7 @@ FileClose(File file)
 
                /* and last report the stat results */
                if (stat_errno == 0)
-                       ReportTemporaryFileUsage(vfdP->fileName, 
filestats.st_size);
+                       ReportTemporaryFileUsage(vfdP->fileName, 
filestats.st_size, vfdP->temp_file_creator_query);
                else
                {
                        errno = stat_errno;
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 2bd8910268..7dd75a490a 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -2327,16 +2327,6 @@ exec_execute_message(const char *portal_name, long 
max_rows)
                         * message.  The next protocol message will start a 
fresh timeout.
                         */
                        disable_statement_timeout();
-
-                       /*
-                        * We completed fetching from an unnamed portal.  There 
is no need
-                        * for it beyond this point, so drop it now rather than 
wait for
-                        * the next Bind message to do this cleanup.  This 
ensures that
-                        * the correct statement is logged when cleaning up 
temporary file
-                        * usage.
-                        */
-                       if (portal->name[0] == '\0')
-                               PortalDrop(portal, false);
                }
 
                /* Send appropriate CommandComplete to client */
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 9a7d733dde..030ad593a8 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -208,6 +208,8 @@ extern PGDLLIMPORT Oid MyDatabaseId;
 
 extern PGDLLIMPORT Oid MyDatabaseTableSpace;
 
+extern PGDLLIMPORT const char *debug_query_string;
+
 extern PGDLLIMPORT bool MyDatabaseHasLoginEventTriggers;
 
 /*
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index c1bcfdec67..962eec7f9b 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -23,7 +23,6 @@
 typedef struct ExplainState ExplainState;      /* defined in explain_state.h */
 
 extern PGDLLIMPORT CommandDest whereToSendOutput;
-extern PGDLLIMPORT const char *debug_query_string;
 extern PGDLLIMPORT int PostAuthDelay;
 extern PGDLLIMPORT int client_connection_check_interval;
 
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
index 7ecd301ae2..4d88577e98 100644
--- a/src/test/modules/test_misc/t/009_log_temp_files.pl
+++ b/src/test/modules/test_misc/t/009_log_temp_files.pl
@@ -39,7 +39,7 @@ SELECT 'unnamed portal';
 END;
 });
 ok( $node->log_contains(
-               qr/LOG:\s+temporary file: path.*\n.*\ STATEMENT:\s+SELECT a 
FROM foo ORDER BY a OFFSET \$1/s,
+               qr/LOG:\s+temporary file: path.*created due to: SELECT a FROM 
foo ORDER BY a OFFSET \$1/s,
                $log_offset),
        "unnamed portal");
 
@@ -51,7 +51,7 @@ $node->safe_psql(
 SELECT a FROM foo ORDER BY a OFFSET \$1 \\bind 4991 \\g
 });
 ok( $node->log_contains(
-               qr/LOG:\s+temporary file: path.*\n.*\ STATEMENT:\s+SELECT a 
FROM foo ORDER BY a OFFSET \$1/s,
+               qr/LOG:\s+temporary file: path.*created due to: SELECT a FROM 
foo ORDER BY a OFFSET \$1/s,
                $log_offset),
        "bind and implicit transaction");
 
@@ -65,7 +65,7 @@ SELECT 'named portal';
 END;
 });
 ok( $node->log_contains(
-               qr/LOG:\s+temporary file: path.*\n.*\ STATEMENT:\s+SELECT a 
FROM foo ORDER BY a OFFSET \$1/s,
+               qr/LOG:\s+temporary file: path.*created due to: SELECT a FROM 
foo ORDER BY a OFFSET \$1/s,
                $log_offset),
        "named portal");
 
@@ -79,7 +79,7 @@ SELECT 'pipelined query';
 \\endpipeline
 });
 ok( $node->log_contains(
-               qr/LOG:\s+temporary file: path.*\n.*\ STATEMENT:\s+SELECT a 
FROM foo ORDER BY a OFFSET \$1/s,
+               qr/LOG:\s+temporary file: path.*created due to: SELECT a FROM 
foo ORDER BY a OFFSET \$1/s,
                $log_offset),
        "pipelined query");
 
@@ -91,7 +91,7 @@ SELECT a, a, a FROM foo ORDER BY a OFFSET \$1 \\parse p1
 \\bind_named p1 4993 \\g
 });
 ok( $node->log_contains(
-               qr/LOG:\s+temporary file: path.*\n.*\ STATEMENT:\s+SELECT a, a, 
a FROM foo ORDER BY a OFFSET \$1/s,
+               qr/LOG:\s+temporary file: path.*created due to: SELECT a, a, a 
FROM foo ORDER BY a OFFSET \$1/s,
                $log_offset),
        "parse and bind");
 
@@ -104,7 +104,7 @@ SELECT a FROM foo ORDER BY a OFFSET 4994;
 END;
 });
 ok( $node->log_contains(
-               qr/LOG:\s+temporary file: path.*\n.*\ STATEMENT:\s+SELECT a 
FROM foo ORDER BY a OFFSET 4994;/s,
+               qr/LOG:\s+temporary file: path.*created due to: SELECT a FROM 
foo ORDER BY a OFFSET 4994;/s,
                $log_offset),
        "simple query");
 
@@ -120,7 +120,7 @@ CLOSE mycur;
 END;
 });
 ok( $node->log_contains(
-               qr/LOG:\s+temporary file: path.*\n.*\ STATEMENT:\s+CLOSE 
mycur;/s,
+               qr/LOG:\s+temporary file: path.*created due to: FETCH 10 FROM 
mycur;/s,
                $log_offset),
        "cursor");
 
@@ -135,7 +135,7 @@ DEALLOCATE p1;
 END;
 });
 ok( $node->log_contains(
-               qr/LOG:\s+temporary file: path.*\n.*\ STATEMENT:\s+EXECUTE 
p1;/s,
+               qr/LOG:\s+temporary file: path.*created due to: EXECUTE p1;/s,
                $log_offset),
        "prepare/execute");
 
-- 
2.39.5 (Apple Git-154)

Reply via email to