0002-pg_term_reason-POC-for-pg_terminate_backend_msg.patch
application/octet-stream
Filename: 0002-pg_term_reason-POC-for-pg_terminate_backend_msg.patch
Type: application/octet-stream
Part: 1
From 5b15c5938e8641d0103d225246dbe03911e0ea3f Mon Sep 17 00:00:00 2001
From: roman khapov <r.khapov@ya.ru>
Date: Sat, 13 Dec 2025 07:06:00 +0000
Subject: [PATCH 2/2] pg_term_reason: POC for pg_terminate_backend_msg
This commits adds proof-of-concept implementation
for extensions, that defines some function to terminate
connections with additional message from admin.
Ex.:
postgres=# create extension pg_term_reason;
CREATE EXTENSION
postgres=# select pg_terminate_backend_msg(pg_backend_pid(), 0, 'Have you seen my coffee cup?');
FATAL: terminating connection due to administrator command: Have you seen my coffee cup?
Signed-off-by: roman khapov <r.khapov@ya.ru>
---
contrib/Makefile | 1 +
contrib/meson.build | 1 +
contrib/pg_term_reason/.gitignore | 4 +
contrib/pg_term_reason/Makefile | 22 +++++
contrib/pg_term_reason/meson.build | 23 +++++
.../pg_term_reason/pg_term_reason--1.0.sql | 14 +++
contrib/pg_term_reason/pg_term_reason.c | 89 +++++++++++++++++++
contrib/pg_term_reason/pg_term_reason.control | 5 ++
contrib/pg_term_reason/t/001_basic.pl | 28 ++++++
9 files changed, 187 insertions(+)
create mode 100644 contrib/pg_term_reason/.gitignore
create mode 100644 contrib/pg_term_reason/Makefile
create mode 100644 contrib/pg_term_reason/meson.build
create mode 100644 contrib/pg_term_reason/pg_term_reason--1.0.sql
create mode 100644 contrib/pg_term_reason/pg_term_reason.c
create mode 100644 contrib/pg_term_reason/pg_term_reason.control
create mode 100644 contrib/pg_term_reason/t/001_basic.pl
diff --git a/contrib/Makefile b/contrib/Makefile
index 2f0a88d3f7..82eb2c0d12 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -37,6 +37,7 @@ SUBDIRS = \
pg_prewarm \
pg_stat_statements \
pg_surgery \
+ pg_term_reason \
pg_trgm \
pgrowlocks \
pgstattuple \
diff --git a/contrib/meson.build b/contrib/meson.build
index ed30ee7d63..398d58d71e 100644
--- a/contrib/meson.build
+++ b/contrib/meson.build
@@ -53,6 +53,7 @@ subdir('pgrowlocks')
subdir('pg_stat_statements')
subdir('pgstattuple')
subdir('pg_surgery')
+subdir('pg_term_reason')
subdir('pg_trgm')
subdir('pg_visibility')
subdir('pg_walinspect')
diff --git a/contrib/pg_term_reason/.gitignore b/contrib/pg_term_reason/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/contrib/pg_term_reason/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/contrib/pg_term_reason/Makefile b/contrib/pg_term_reason/Makefile
new file mode 100644
index 0000000000..fa751801f8
--- /dev/null
+++ b/contrib/pg_term_reason/Makefile
@@ -0,0 +1,22 @@
+# contrib/pg_term_reason/Makefile
+
+MODULE_big = pg_term_reason
+OBJS = \
+ pg_term_reason.o
+
+EXTENSION = pg_term_reason
+DATA = pg_term_reason--1.0.sql
+PGFILEDESC = "pg_term_reason - add pg_terminate_backend_reasoned function"
+
+TAP_TESTS = 1
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/pg_term_reason
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/pg_term_reason/meson.build b/contrib/pg_term_reason/meson.build
new file mode 100644
index 0000000000..ad93b4b5f2
--- /dev/null
+++ b/contrib/pg_term_reason/meson.build
@@ -0,0 +1,23 @@
+pg_term_sources = files(
+ 'pg_term_reason.c',
+)
+
+pg_term_reason = shared_module('pg_term_reason',
+ pg_term_sources,
+ kwargs: contrib_mod_args,
+)
+contrib_targets += pg_term_reason
+
+install_data(
+ 'pg_term_reason--1.0.sql',
+ 'pg_term_reason.control',
+ kwargs: contrib_data_args,
+)
+
+tests += {
+ 'tap': {
+ 'tests': [
+ 't/001_basic.pl',
+ ],
+ },
+}
diff --git a/contrib/pg_term_reason/pg_term_reason--1.0.sql b/contrib/pg_term_reason/pg_term_reason--1.0.sql
new file mode 100644
index 0000000000..e0f1bd5779
--- /dev/null
+++ b/contrib/pg_term_reason/pg_term_reason--1.0.sql
@@ -0,0 +1,14 @@
+/* contrib/pg_term_reason/pg_term_reason--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pg_term_reason" to load this file. \quit
+
+CREATE FUNCTION pg_terminate_backend_msg(pid integer, timeout int8 DEFAULT 0, reason text DEFAULT '')
+RETURNS boolean
+AS 'MODULE_PATHNAME', 'pg_terminate_backend_msg'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION pg_cancel_backend_msg(pid integer, reason text DEFAULT '')
+RETURNS boolean
+AS 'MODULE_PATHNAME', 'pg_cancel_backend_msg'
+LANGUAGE C STRICT;
diff --git a/contrib/pg_term_reason/pg_term_reason.c b/contrib/pg_term_reason/pg_term_reason.c
new file mode 100644
index 0000000000..5c6ceb00fd
--- /dev/null
+++ b/contrib/pg_term_reason/pg_term_reason.c
@@ -0,0 +1,89 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_term_reason.c
+ * Functions to terminate/cancel postgres backends with additional message
+ *
+ * IDENTIFICATION
+ * contrib/pg_term_reason/pg_term_reason.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "varatt.h"
+#include "storage/signalfuncs.h"
+#include "storage/lock.h"
+#include "storage/procarray.h"
+#include "storage/proc.h"
+
+PG_MODULE_MAGIC_EXT(
+ .name = "pg_term_reason",
+ .version = PG_VERSION
+);
+
+PG_FUNCTION_INFO_V1(pg_terminate_backend_msg);
+PG_FUNCTION_INFO_V1(pg_cancel_backend_msg);
+
+static void
+set_reason(int pid, const char *msg, int msglen)
+{
+ int len;
+ PGPROC *proc;
+
+ if (msglen <= 0) {
+ return;
+ }
+
+ LWLockAcquire(ProcArrayLock, LW_SHARED);
+
+ proc = BackendPidGetProcWithLock(pid);
+
+ if (proc != NULL) {
+ len = Min(PROC_TERM_REASON_MAX_LEN - 1, msglen);
+ strncpy(proc->termReasonStr, msg, len);
+ }
+
+ LWLockRelease(ProcArrayLock);
+}
+
+Datum
+pg_cancel_backend_msg(PG_FUNCTION_ARGS)
+{
+ int pid;
+ text *reason;
+ char *reason_str;
+ int reason_len;
+
+ pid = PG_GETARG_INT32(0);
+ reason = PG_GETARG_TEXT_P(1);
+
+ reason_str = VARDATA(reason);
+ reason_len = VARSIZE(reason) - VARHDRSZ;
+
+ set_reason(pid, reason_str, reason_len);
+
+ return pg_cancel_backend_impl(pid);
+}
+
+Datum
+pg_terminate_backend_msg(PG_FUNCTION_ARGS)
+{
+ int pid;
+ int timeout; /* milliseconds */
+ text *reason;
+ char *reason_str;
+ int reason_len;
+
+ pid = PG_GETARG_INT32(0);
+ timeout = PG_GETARG_INT64(1);
+ reason = PG_GETARG_TEXT_P(2);
+
+ reason_str = VARDATA(reason);
+ reason_len = VARSIZE(reason) - VARHDRSZ;
+
+ set_reason(pid, reason_str, reason_len);
+
+ return pg_terminate_backend_impl(pid, timeout);
+}
diff --git a/contrib/pg_term_reason/pg_term_reason.control b/contrib/pg_term_reason/pg_term_reason.control
new file mode 100644
index 0000000000..d3b04459aa
--- /dev/null
+++ b/contrib/pg_term_reason/pg_term_reason.control
@@ -0,0 +1,5 @@
+# pg_term_reason extension
+comment = 'extension to terminate backends with additional reason string'
+default_version = '1.0'
+module_pathname = '$libdir/pg_term_reason'
+relocatable = true
diff --git a/contrib/pg_term_reason/t/001_basic.pl b/contrib/pg_term_reason/t/001_basic.pl
new file mode 100644
index 0000000000..5d9dadc462
--- /dev/null
+++ b/contrib/pg_term_reason/t/001_basic.pl
@@ -0,0 +1,28 @@
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+my $node = PostgreSQL::Test::Cluster->new('primary');
+$node->init();
+$node->start;
+
+$node->safe_psql('postgres', 'create extension pg_term_reason;');
+
+my ($stdout, $stderr);
+$node->psql('postgres',
+ q[select pg_terminate_backend_msg(pg_backend_pid(), 0, 'hello from tap tests!');],
+ stdout => \$stdout, stderr => \$stderr);
+like($stderr, qr/hello from tap tests\!/, "expected message to be passed");
+
+$stdout = '';
+$stderr = '';
+$node->psql('postgres',
+ q[select pg_cancel_backend_msg(pg_backend_pid(), 'hello from tap tests again!');],
+ stdout => \$stdout, stderr => \$stderr);
+like($stderr, qr/hello from tap tests again\!/, "expected message to be passed");
+
+$node->stop;
+
+done_testing();
--
2.50.1 (Apple Git-155)