From 9ddd96f2da2332ef019c6da5825ae38cc820f2ac Mon Sep 17 00:00:00 2001
From: roman khapov <r.khapov@ya.ru>
Date: Sat, 13 Dec 2025 06:55:28 +0000
Subject: [PATCH 1/2] termination msg in PGPROC

This commit adds message in PGPROC, that can be set
before terminating/cancelling connection, in order to add
this message when backend is responding to client with
'FATAL: terminating connection..' or 'ERROR: cancelling statement..'

For now there is no separated functions to perform termination with msg,
but that functions can be simply added in extension. This requires
implementation of pg_terminate_backend/pg_cancel_backend to be accessible
from extenions, so pg_terminate_backend_impl and pg_cancel_backend_impl was
introduced as public functions.

Signed-off-by: roman khapov <r.khapov@ya.ru>
---
 src/backend/storage/ipc/signalfuncs.c | 32 +++++++++++++++++++--------
 src/backend/tcop/postgres.c           | 26 ++++++++++++++++++----
 src/include/storage/proc.h            |  6 +++++
 src/include/storage/signalfuncs.h     | 13 +++++++++++
 4 files changed, 64 insertions(+), 13 deletions(-)
 create mode 100644 src/include/storage/signalfuncs.h

diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c
index a3a670ba24..cd7eeeb9e3 100644
--- a/src/backend/storage/ipc/signalfuncs.c
+++ b/src/backend/storage/ipc/signalfuncs.c
@@ -22,6 +22,7 @@
 #include "postmaster/syslogger.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
+#include "storage/signalfuncs.h"
 #include "storage/procarray.h"
 #include "utils/acl.h"
 #include "utils/fmgrprotos.h"
@@ -133,9 +134,9 @@ pg_signal_backend(int pid, int sig)
  * Note that only superusers can signal superuser-owned processes.
  */
 Datum
-pg_cancel_backend(PG_FUNCTION_ARGS)
+pg_cancel_backend_impl(int pid)
 {
-	int			r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
+	int			r = pg_signal_backend(pid, SIGINT);
 
 	if (r == SIGNAL_BACKEND_NOSUPERUSER)
 		ereport(ERROR,
@@ -161,6 +162,12 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
 
+Datum
+pg_cancel_backend(PG_FUNCTION_ARGS)
+{
+	return pg_cancel_backend_impl(PG_GETARG_INT32(0));
+}
+
 /*
  * Wait until there is no backend process with the given PID and return true.
  * On timeout, a warning is emitted and false is returned.
@@ -234,14 +241,9 @@ pg_wait_until_termination(int pid, int64 timeout)
  * Note that only superusers can signal superuser-owned processes.
  */
 Datum
-pg_terminate_backend(PG_FUNCTION_ARGS)
+pg_terminate_backend_impl(int pid, int timeout)
 {
-	int			pid;
-	int			r;
-	int			timeout;		/* milliseconds */
-
-	pid = PG_GETARG_INT32(0);
-	timeout = PG_GETARG_INT64(1);
+	int r;
 
 	if (timeout < 0)
 		ereport(ERROR,
@@ -278,6 +280,18 @@ pg_terminate_backend(PG_FUNCTION_ARGS)
 		PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
 }
 
+Datum
+pg_terminate_backend(PG_FUNCTION_ARGS)
+{
+	int			pid;
+	int			timeout;		/* milliseconds */
+
+	pid = PG_GETARG_INT32(0);
+	timeout = PG_GETARG_INT64(1);
+
+	return pg_terminate_backend_impl(pid, timeout);
+}
+
 /*
  * Signal to reload the database configuration
  *
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7dd75a490a..bb43d2db7e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3356,9 +3356,17 @@ ProcessInterrupts(void)
 			proc_exit(0);
 		}
 		else
-			ereport(FATAL,
+		{
+			if (MyProc->termReasonStr[0] == '\0')
+				ereport(FATAL,
 					(errcode(ERRCODE_ADMIN_SHUTDOWN),
 					 errmsg("terminating connection due to administrator command")));
+			else
+				ereport(FATAL,
+					(errcode(ERRCODE_ADMIN_SHUTDOWN),
+					 errmsg("terminating connection due to administrator command: %s",
+							MyProc->termReasonStr)));
+		}
 	}
 
 	if (CheckClientConnectionPending)
@@ -3466,9 +3474,19 @@ ProcessInterrupts(void)
 		if (!DoingCommandRead)
 		{
 			LockErrorCleanup();
-			ereport(ERROR,
-					(errcode(ERRCODE_QUERY_CANCELED),
-					 errmsg("canceling statement due to user request")));
+			if (MyProc->termReasonStr[0] == '\0')
+				ereport(ERROR,
+						(errcode(ERRCODE_QUERY_CANCELED),
+						 errmsg("canceling statement due to user request")));
+			else
+			{
+				ereport(ERROR,
+						(errcode(ERRCODE_QUERY_CANCELED),
+						 errmsg("canceling statement due to user request: %s",
+							MyProc->termReasonStr)));
+
+				memset(MyProc->termReasonStr, 0, sizeof(MyProc->termReasonStr));
+			}
 		}
 	}
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index c6f5ebceef..f0f2bc4b93 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -77,6 +77,9 @@ struct XidCache
  */
 #define		PROC_XMIN_FLAGS (PROC_IN_VACUUM | PROC_IN_SAFE_IC)
 
+/* including the termination null byte */
+#define		PROC_TERM_REASON_MAX_LEN 32
+
 /*
  * We allow a limited number of "weak" relation locks (AccessShareLock,
  * RowShareLock, RowExclusiveLock) to be recorded in the PGPROC structure
@@ -301,6 +304,9 @@ struct PGPROC
 
 	uint32		wait_event_info;	/* proc's wait information */
 
+	/* additional info when termination signal sending */
+	char		termReasonStr[PROC_TERM_REASON_MAX_LEN];
+
 	/* Support for group transaction status update. */
 	bool		clogGroupMember;	/* true, if member of clog group */
 	pg_atomic_uint32 clogGroupNext; /* next clog group member */
diff --git a/src/include/storage/signalfuncs.h b/src/include/storage/signalfuncs.h
new file mode 100644
index 0000000000..359b3be67b
--- /dev/null
+++ b/src/include/storage/signalfuncs.h
@@ -0,0 +1,13 @@
+
+#ifndef SIGNALFUNCS_H
+#define SIGNALFUNCS_H
+
+#include <postgres.h>
+
+Datum
+pg_terminate_backend_impl(int pid, int timeout);
+
+Datum
+pg_cancel_backend_impl(int pid);
+
+#endif /* SIGNALFUNCS_H */
-- 
2.50.1 (Apple Git-155)

