From 3e166b0fb0cc9800a9a898240adb4750e4c6c705 Mon Sep 17 00:00:00 2001 From: Warda Bibi Date: Sat, 4 Apr 2026 20:12:58 +0500 Subject: [PATCH v6] message in pg_terminate_backend and pg_cancel_backend Sometimes it is useful to terminate or cancel a backend with an additional message explaining the reason, so the client sees something more informative than the generic error text. This patch adds an optional message argument to pg_terminate_backend() and pg_cancel_backend(), folded into the existing functions via DEFAULT parameters so no new catalog entries are needed: pg_cancel_backend(pid integer, message text DEFAULT '') pg_terminate_backend(pid integer, timeout int8 DEFAULT 0, message text DEFAULT '') The message is stored in a dedicated shared memory region (BackendMsgSlots, one slot per MaxBackends indexed by ProcNumber) and read by the target backend in ProcessInterrupts() before the ereport(FATAL/ERROR). Unicode-safe truncation is applied via pg_mbcliplen() if the message exceeds BACKEND_MSG_MAX_LEN bytes, with a NOTICE emitted to the caller. Author: Daniel Gustafsson Author: Roman Khapov Reviewed-by: Kirill Reshke Reviewed-by: Jim Jones Reviewed-by: Andrey Borodin --- src/backend/catalog/system_functions.sql | 10 ++++++ src/backend/storage/ipc/signalfuncs.c | 32 ++---------------- src/backend/tcop/postgres.c | 8 ++--- src/backend/utils/misc/backend_msg.c | 41 +++++++++--------------- src/include/catalog/pg_proc.dat | 15 +++------ src/include/utils/backend_msg.h | 4 ++- 6 files changed, 41 insertions(+), 69 deletions(-) diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql index 69699f8830a..7dbad6a279b 100644 --- a/src/backend/catalog/system_functions.sql +++ b/src/backend/catalog/system_functions.sql @@ -378,6 +378,16 @@ BEGIN ATOMIC END; +CREATE OR REPLACE FUNCTION + pg_cancel_backend(pid integer, message text DEFAULT '') + RETURNS boolean STRICT VOLATILE LANGUAGE INTERNAL AS 'pg_cancel_backend' + PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION + pg_terminate_backend(pid integer, timeout int8 DEFAULT 0, message text DEFAULT '') + RETURNS boolean STRICT VOLATILE LANGUAGE INTERNAL AS 'pg_terminate_backend' + PARALLEL SAFE; + -- -- The default permissions for functions mean that anyone can execute them. -- A number of functions shouldn't be executable by just anyone, but rather diff --git a/src/backend/storage/ipc/signalfuncs.c b/src/backend/storage/ipc/signalfuncs.c index 2501ec58658..eabb341a2ba 100644 --- a/src/backend/storage/ipc/signalfuncs.c +++ b/src/backend/storage/ipc/signalfuncs.c @@ -112,7 +112,7 @@ pg_signal_backend(int pid, int sig, const char *msg) if (msg != NULL && msg[0] != '\0') { - int r = BackendMsgSet(pid, msg); + int r = BackendMsgSet(GetNumberFromPGProc(proc), msg); if (r != -1 && r != strlen(msg)) ereport(NOTICE, @@ -172,23 +172,11 @@ pg_cancel_backend_internal(pid_t pid, const char *msg) Datum pg_cancel_backend(PG_FUNCTION_ARGS) { int pid; - - pid = PG_GETARG_INT32(0); - - return pg_cancel_backend_internal(pid, NULL); -} - -Datum pg_cancel_backend_msg(PG_FUNCTION_ARGS) -{ - int pid; - char *msg; + char *msg; Datum result; pid = PG_GETARG_INT32(0); msg = text_to_cstring(PG_GETARG_TEXT_PP(1)); - if (msg == NULL) { - PG_RETURN_BOOL(false); - } result = pg_cancel_backend_internal(pid, msg); @@ -313,26 +301,12 @@ Datum pg_terminate_backend(PG_FUNCTION_ARGS) { int pid; int timeout_ms; - - pid = PG_GETARG_INT32(0); - timeout_ms = PG_GETARG_INT64(1); - - return pg_terminate_backend_internal(pid, timeout_ms, NULL); -} - -Datum pg_terminate_backend_msg(PG_FUNCTION_ARGS) -{ - int pid; - int timeout_ms; - char *msg; + char *msg; Datum result; pid = PG_GETARG_INT32(0); timeout_ms = PG_GETARG_INT64(1); msg = text_to_cstring(PG_GETARG_TEXT_PP(2)); - if (msg == NULL) { - PG_RETURN_BOOL(false); - } result = pg_terminate_backend_internal(pid, timeout_ms, msg); diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 92040d748f4..25d2857f69f 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -3402,10 +3402,10 @@ ProcessInterrupts(void) (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("terminating connection due to administrator command: %s", msg))); } - - 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"))); } } diff --git a/src/backend/utils/misc/backend_msg.c b/src/backend/utils/misc/backend_msg.c index 34d44069056..560aee148be 100644 --- a/src/backend/utils/misc/backend_msg.c +++ b/src/backend/utils/misc/backend_msg.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * src/include/utils/misc/backend_msg.c + * src/backend/utils/misc/backend_msg.c * *-------------------------------------------------------------------- */ @@ -86,7 +86,7 @@ void BackendMsgInit(int id) on_shmem_exit(backend_msg_slot_clean, Int32GetDatum(0) /* not used */); } -int BackendMsgSet(pid_t pid, const char *msg) +int BackendMsgSet(ProcNumber procno, const char *msg) { BackendMsgSlot *slot; int len; @@ -94,35 +94,26 @@ int BackendMsgSet(pid_t pid, const char *msg) if (msg == NULL || msg[0] == '\0') return 0; - for (int i = 0; i < MaxBackends; ++i) - { - slot = &BackendMsgSlots[i]; - - if (slot->pid == 0 || slot->pid != pid) - continue; - - SpinLockAcquire(&slot->lock); - - if (slot->pid != pid) - { - SpinLockRelease(&slot->lock); - break; - } + slot = &BackendMsgSlots[procno]; - len = pg_mbcliplen(msg, strlen(msg), sizeof(slot->msg) - 1); - memcpy(slot->msg, msg, len); - slot->msg[len] = '\0'; + SpinLockAcquire(&slot->lock); + if (slot->pid == 0) + { SpinLockRelease(&slot->lock); - - return len; + ereport(LOG, + (errmsg("can't set message for missing backend, requested by %ld", + (long) MyProcPid))); + return -1; } - ereport(LOG, - (errmsg("can't set message for missing backend %ld, requested by %ld", - (long) pid, (long) MyProcPid))); + len = pg_mbcliplen(msg, strlen(msg), sizeof(slot->msg) - 1); + memcpy(slot->msg, msg, len); + slot->msg[len] = '\0'; - return -1; + SpinLockRelease(&slot->lock); + + return len; } int BackendMsgGet(char *buf, int max_len) diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 2732dcb6867..3833fc4ba2c 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6727,19 +6727,14 @@ { oid => '2171', descr => 'cancel a server process\' current query', proname => 'pg_cancel_backend', provolatile => 'v', prorettype => 'bool', - proargtypes => 'int4', prosrc => 'pg_cancel_backend' }, -{ oid => '8223', descr => 'cancel a server process\' current query', - proname => 'pg_cancel_backend', provolatile => 'v', prorettype => 'bool', - proargtypes => 'int4 text', prosrc => 'pg_cancel_backend_msg' }, + proargtypes => 'int4 text', proargnames => '{pid,message}', + proargdefaults => '{""}', + prosrc => 'pg_cancel_backend' }, { oid => '2096', descr => 'terminate a server process', - proname => 'pg_terminate_backend', provolatile => 'v', prorettype => 'bool', - proargtypes => 'int4 int8', proargnames => '{pid,timeout}', - proargdefaults => '{0}', - prosrc => 'pg_terminate_backend' }, -{ oid => '8222', descr => 'terminate a server process', proname => 'pg_terminate_backend', provolatile => 'v', prorettype => 'bool', proargtypes => 'int4 int8 text', proargnames => '{pid,timeout,message}', - prosrc => 'pg_terminate_backend_msg' }, + proargdefaults => '{0,""}', + prosrc => 'pg_terminate_backend' }, { oid => '2172', descr => 'prepare for taking an online backup', proname => 'pg_backup_start', provolatile => 'v', proparallel => 'r', prorettype => 'pg_lsn', proargtypes => 'text bool', diff --git a/src/include/utils/backend_msg.h b/src/include/utils/backend_msg.h index 7e53c59845f..5ccad83af11 100644 --- a/src/include/utils/backend_msg.h +++ b/src/include/utils/backend_msg.h @@ -15,12 +15,14 @@ #ifndef BACKEND_MSG_H #define BACKEND_MSG_H +#include "storage/proc.h" + #define BACKEND_MSG_MAX_LEN 128 extern void BackendMsgShmemInit(void); extern Size BackendMsgShmemSize(void); extern void BackendMsgInit(int id); -extern int BackendMsgSet(pid_t pid, const char *msg); +extern int BackendMsgSet(ProcNumber procno, const char *msg); extern int BackendMsgGet(char *buf, int max_len); extern bool BackendMsgIsSet(void); -- 2.51.0