Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vUKJR-006NQM-06 for pgsql-hackers@arkaria.postgresql.org; Sat, 13 Dec 2025 07:45:10 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vUKJO-00A6s9-19 for pgsql-hackers@arkaria.postgresql.org; Sat, 13 Dec 2025 07:45:07 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vUKJN-00A6rw-2h for pgsql-hackers@lists.postgresql.org; Sat, 13 Dec 2025 07:45:06 +0000 Received: from forwardcorp1d.mail.yandex.net ([178.154.239.200]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vUKJG-000UvD-33 for pgsql-hackers@lists.postgresql.org; Sat, 13 Dec 2025 07:45:01 +0000 Received: from mail-nwsmtp-smtp-corp-main-80.iva.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-80.iva.yp-c.yandex.net [IPv6:2a02:6b8:c0c:7888:0:640:a8fd:0]) by forwardcorp1d.mail.yandex.net (Yandex) with ESMTPS id 8934F80793 for ; Sat, 13 Dec 2025 10:44:57 +0300 (MSK) Received: from smtpclient.apple (unknown [2a02:6bf:803e:e00:d414:58c0:b5ca:4d8d]) by mail-nwsmtp-smtp-corp-main-80.iva.yp-c.yandex.net (smtpcorp/Yandex) with ESMTPSA id uiLYbO1FPqM0-MhRu2LQ8; Sat, 13 Dec 2025 10:44:57 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.ru; s=default; t=1765611897; bh=2Wj/8K3y/Tvkgn1BnVE/W8y/A4vntupfkIl2N0Hkdcc=; h=To:Date:Message-Id:From:Subject; b=Iv+BjrpOJ1VfUiEU9JBpnMIU5m0bz6S/JoFeVmSq/FyuZLcarMGzf3E+iW1nah5zo v/d/hPiM3BBDQ/bfgQI4nZ3v9jWXd8V8OohAj0C3ZbNch1DUIBPJ8W8Bd9OLtm0UrZ 3AfhyDWSWn0y3GZXLBHLhtEKc0enMbcY2NInSMUE= Authentication-Results: mail-nwsmtp-smtp-corp-main-80.iva.yp-c.yandex.net; dkim=pass header.i=@yandex-team.ru From: Roman Khapov Content-Type: multipart/mixed; boundary="Apple-Mail=_FE8A0B23-0BE6-4862-85F8-A4F09E8E948C" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3826.700.81\)) Subject: Additional message in pg_terminate_backend Message-Id: <064E214D-D86F-4917-A0B6-67EAD6BCB24A@yandex-team.ru> Date: Sat, 13 Dec 2025 12:44:46 +0500 To: pgsql-hackers@lists.postgresql.org X-Mailer: Apple Mail (2.3826.700.81) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Apple-Mail=_FE8A0B23-0BE6-4862-85F8-A4F09E8E948C Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 Hi hackers! Recently I started working on patch for adding additional message from = admin=20 in pg_terminate_backend for one of our greenplum fork. The main idea is = that there must be=20 done little investigation every time you see 'FATAL: terminating = connection due to administrator command=E2=80=99 to understand the = reason, especially in cases where connection was terminated by another user. So it was decided to create some new functions, that allows to = terminate connection with additional message. I did POC patches with the next main ideas: - lets add termReasonStr field in every PGPROC, that field can be used = in ProcessInterrupts()=20 - implementation of pg_terminate_backend/pg_cancel_backend should be = accessible from extensions, so lets move it in = pg_terminate_backend_impl/pg_cancel_backend_impl and add definitions for = it somewhere - write simple extensions, which defines functions like = pg_terminate_backend_msg, that sets termReasonStr and calls = pg_terminate_backend_impl After patch and added extension, it is possible to do smth like: postgres=3D# select pg_terminate_backend_msg(pg_backend_pid(), 0, =E2=80=99= the message'); FATAL: terminating connection due to administrator command: the message The general question I want to ask: can this patches be useful for = vanilla PostgreSQL? If so, there are some questions about patch improvements: - maybe the message can be delivered to backend by some other way than = the field in struct PGPROC? termReasonStr field consumes some shared memory and are used in rare = cases - names of all new funcs/fields/etc should be changed to some better = names - new file signalfuncs.h seems like too complicated solutions to define = *_impl functions, it can be done in some other files?=20 ----- Best regards, Roman Khapov --Apple-Mail=_FE8A0B23-0BE6-4862-85F8-A4F09E8E948C Content-Disposition: attachment; filename=0001-termination-msg-in-PGPROC.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="0001-termination-msg-in-PGPROC.patch" Content-Transfer-Encoding: quoted-printable =46rom=209ddd96f2da2332ef019c6da5825ae38cc820f2ac=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20roman=20khapov=20=0ADate:=20= Sat,=2013=20Dec=202025=2006:55:28=20+0000=0ASubject:=20[PATCH=201/2]=20= termination=20msg=20in=20PGPROC=0A=0AThis=20commit=20adds=20message=20in=20= PGPROC,=20that=20can=20be=20set=0Abefore=20terminating/cancelling=20= connection,=20in=20order=20to=20add=0Athis=20message=20when=20backend=20= is=20responding=20to=20client=20with=0A'FATAL:=20terminating=20= connection..'=20or=20'ERROR:=20cancelling=20statement..'=0A=0AFor=20now=20= there=20is=20no=20separated=20functions=20to=20perform=20termination=20= with=20msg,=0Abut=20that=20functions=20can=20be=20simply=20added=20in=20= extension.=20This=20requires=0Aimplementation=20of=20= pg_terminate_backend/pg_cancel_backend=20to=20be=20accessible=0Afrom=20= extenions,=20so=20pg_terminate_backend_impl=20and=20= pg_cancel_backend_impl=20was=0Aintroduced=20as=20public=20functions.=0A=0A= Signed-off-by:=20roman=20khapov=20=0A---=0A=20= src/backend/storage/ipc/signalfuncs.c=20|=2032=20= +++++++++++++++++++--------=0A=20src/backend/tcop/postgres.c=20=20=20=20=20= =20=20=20=20=20=20|=2026=20++++++++++++++++++----=0A=20= src/include/storage/proc.h=20=20=20=20=20=20=20=20=20=20=20=20|=20=206=20= +++++=0A=20src/include/storage/signalfuncs.h=20=20=20=20=20|=2013=20= +++++++++++=0A=204=20files=20changed,=2064=20insertions(+),=2013=20= deletions(-)=0A=20create=20mode=20100644=20= src/include/storage/signalfuncs.h=0A=0Adiff=20--git=20= a/src/backend/storage/ipc/signalfuncs.c=20= b/src/backend/storage/ipc/signalfuncs.c=0Aindex=20a3a670ba24..cd7eeeb9e3=20= 100644=0A---=20a/src/backend/storage/ipc/signalfuncs.c=0A+++=20= b/src/backend/storage/ipc/signalfuncs.c=0A@@=20-22,6=20+22,7=20@@=0A=20= #include=20"postmaster/syslogger.h"=0A=20#include=20"storage/pmsignal.h"=0A= =20#include=20"storage/proc.h"=0A+#include=20"storage/signalfuncs.h"=0A=20= #include=20"storage/procarray.h"=0A=20#include=20"utils/acl.h"=0A=20= #include=20"utils/fmgrprotos.h"=0A@@=20-133,9=20+134,9=20@@=20= pg_signal_backend(int=20pid,=20int=20sig)=0A=20=20*=20Note=20that=20only=20= superusers=20can=20signal=20superuser-owned=20processes.=0A=20=20*/=0A=20= Datum=0A-pg_cancel_backend(PG_FUNCTION_ARGS)=0A= +pg_cancel_backend_impl(int=20pid)=0A=20{=0A-=09int=09=09=09r=20=3D=20= pg_signal_backend(PG_GETARG_INT32(0),=20SIGINT);=0A+=09int=09=09=09r=20=3D= =20pg_signal_backend(pid,=20SIGINT);=0A=20=0A=20=09if=20(r=20=3D=3D=20= SIGNAL_BACKEND_NOSUPERUSER)=0A=20=09=09ereport(ERROR,=0A@@=20-161,6=20= +162,12=20@@=20pg_cancel_backend(PG_FUNCTION_ARGS)=0A=20=09= PG_RETURN_BOOL(r=20=3D=3D=20SIGNAL_BACKEND_SUCCESS);=0A=20}=0A=20=0A= +Datum=0A+pg_cancel_backend(PG_FUNCTION_ARGS)=0A+{=0A+=09return=20= pg_cancel_backend_impl(PG_GETARG_INT32(0));=0A+}=0A+=0A=20/*=0A=20=20*=20= Wait=20until=20there=20is=20no=20backend=20process=20with=20the=20given=20= PID=20and=20return=20true.=0A=20=20*=20On=20timeout,=20a=20warning=20is=20= emitted=20and=20false=20is=20returned.=0A@@=20-234,14=20+241,9=20@@=20= pg_wait_until_termination(int=20pid,=20int64=20timeout)=0A=20=20*=20Note=20= that=20only=20superusers=20can=20signal=20superuser-owned=20processes.=0A= =20=20*/=0A=20Datum=0A-pg_terminate_backend(PG_FUNCTION_ARGS)=0A= +pg_terminate_backend_impl(int=20pid,=20int=20timeout)=0A=20{=0A-=09int=09= =09=09pid;=0A-=09int=09=09=09r;=0A-=09int=09=09=09timeout;=09=09/*=20= milliseconds=20*/=0A-=0A-=09pid=20=3D=20PG_GETARG_INT32(0);=0A-=09= timeout=20=3D=20PG_GETARG_INT64(1);=0A+=09int=20r;=0A=20=0A=20=09if=20= (timeout=20<=200)=0A=20=09=09ereport(ERROR,=0A@@=20-278,6=20+280,18=20@@=20= pg_terminate_backend(PG_FUNCTION_ARGS)=0A=20=09=09PG_RETURN_BOOL(r=20=3D=3D= =20SIGNAL_BACKEND_SUCCESS);=0A=20}=0A=20=0A+Datum=0A= +pg_terminate_backend(PG_FUNCTION_ARGS)=0A+{=0A+=09int=09=09=09pid;=0A+=09= int=09=09=09timeout;=09=09/*=20milliseconds=20*/=0A+=0A+=09pid=20=3D=20= PG_GETARG_INT32(0);=0A+=09timeout=20=3D=20PG_GETARG_INT64(1);=0A+=0A+=09= return=20pg_terminate_backend_impl(pid,=20timeout);=0A+}=0A+=0A=20/*=0A=20= =20*=20Signal=20to=20reload=20the=20database=20configuration=0A=20=20*=0A= diff=20--git=20a/src/backend/tcop/postgres.c=20= b/src/backend/tcop/postgres.c=0Aindex=207dd75a490a..bb43d2db7e=20100644=0A= ---=20a/src/backend/tcop/postgres.c=0A+++=20= b/src/backend/tcop/postgres.c=0A@@=20-3356,9=20+3356,17=20@@=20= ProcessInterrupts(void)=0A=20=09=09=09proc_exit(0);=0A=20=09=09}=0A=20=09= =09else=0A-=09=09=09ereport(FATAL,=0A+=09=09{=0A+=09=09=09if=20= (MyProc->termReasonStr[0]=20=3D=3D=20'\0')=0A+=09=09=09=09ereport(FATAL,=0A= =20=09=09=09=09=09(errcode(ERRCODE_ADMIN_SHUTDOWN),=0A=20=09=09=09=09=09=20= errmsg("terminating=20connection=20due=20to=20administrator=20= command")));=0A+=09=09=09else=0A+=09=09=09=09ereport(FATAL,=0A+=09=09=09=09= =09(errcode(ERRCODE_ADMIN_SHUTDOWN),=0A+=09=09=09=09=09=20= errmsg("terminating=20connection=20due=20to=20administrator=20command:=20= %s",=0A+=09=09=09=09=09=09=09MyProc->termReasonStr)));=0A+=09=09}=0A=20=09= }=0A=20=0A=20=09if=20(CheckClientConnectionPending)=0A@@=20-3466,9=20= +3474,19=20@@=20ProcessInterrupts(void)=0A=20=09=09if=20= (!DoingCommandRead)=0A=20=09=09{=0A=20=09=09=09LockErrorCleanup();=0A-=09= =09=09ereport(ERROR,=0A-=09=09=09=09=09(errcode(ERRCODE_QUERY_CANCELED),=0A= -=09=09=09=09=09=20errmsg("canceling=20statement=20due=20to=20user=20= request")));=0A+=09=09=09if=20(MyProc->termReasonStr[0]=20=3D=3D=20'\0')=0A= +=09=09=09=09ereport(ERROR,=0A+=09=09=09=09=09=09= (errcode(ERRCODE_QUERY_CANCELED),=0A+=09=09=09=09=09=09=20= errmsg("canceling=20statement=20due=20to=20user=20request")));=0A+=09=09=09= else=0A+=09=09=09{=0A+=09=09=09=09ereport(ERROR,=0A+=09=09=09=09=09=09= (errcode(ERRCODE_QUERY_CANCELED),=0A+=09=09=09=09=09=09=20= errmsg("canceling=20statement=20due=20to=20user=20request:=20%s",=0A+=09=09= =09=09=09=09=09MyProc->termReasonStr)));=0A+=0A+=09=09=09=09= memset(MyProc->termReasonStr,=200,=20sizeof(MyProc->termReasonStr));=0A+=09= =09=09}=0A=20=09=09}=0A=20=09}=0A=20=0Adiff=20--git=20= a/src/include/storage/proc.h=20b/src/include/storage/proc.h=0Aindex=20= c6f5ebceef..f0f2bc4b93=20100644=0A---=20a/src/include/storage/proc.h=0A= +++=20b/src/include/storage/proc.h=0A@@=20-77,6=20+77,9=20@@=20struct=20= XidCache=0A=20=20*/=0A=20#define=09=09PROC_XMIN_FLAGS=20(PROC_IN_VACUUM=20= |=20PROC_IN_SAFE_IC)=0A=20=0A+/*=20including=20the=20termination=20null=20= byte=20*/=0A+#define=09=09PROC_TERM_REASON_MAX_LEN=2032=0A+=0A=20/*=0A=20= =20*=20We=20allow=20a=20limited=20number=20of=20"weak"=20relation=20= locks=20(AccessShareLock,=0A=20=20*=20RowShareLock,=20RowExclusiveLock)=20= to=20be=20recorded=20in=20the=20PGPROC=20structure=0A@@=20-301,6=20= +304,9=20@@=20struct=20PGPROC=0A=20=0A=20=09uint32=09=09wait_event_info;=09= /*=20proc's=20wait=20information=20*/=0A=20=0A+=09/*=20additional=20info=20= when=20termination=20signal=20sending=20*/=0A+=09char=09=09= termReasonStr[PROC_TERM_REASON_MAX_LEN];=0A+=0A=20=09/*=20Support=20for=20= group=20transaction=20status=20update.=20*/=0A=20=09bool=09=09= clogGroupMember;=09/*=20true,=20if=20member=20of=20clog=20group=20*/=0A=20= =09pg_atomic_uint32=20clogGroupNext;=20/*=20next=20clog=20group=20member=20= */=0Adiff=20--git=20a/src/include/storage/signalfuncs.h=20= b/src/include/storage/signalfuncs.h=0Anew=20file=20mode=20100644=0Aindex=20= 0000000000..359b3be67b=0A---=20/dev/null=0A+++=20= b/src/include/storage/signalfuncs.h=0A@@=20-0,0=20+1,13=20@@=0A+=0A= +#ifndef=20SIGNALFUNCS_H=0A+#define=20SIGNALFUNCS_H=0A+=0A+#include=20= =0A+=0A+Datum=0A+pg_terminate_backend_impl(int=20pid,=20int=20= timeout);=0A+=0A+Datum=0A+pg_cancel_backend_impl(int=20pid);=0A+=0A= +#endif=20/*=20SIGNALFUNCS_H=20*/=0A--=20=0A2.50.1=20(Apple=20Git-155)=0A= =0A= --Apple-Mail=_FE8A0B23-0BE6-4862-85F8-A4F09E8E948C Content-Disposition: attachment; filename=0002-pg_term_reason-POC-for-pg_terminate_backend_msg.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="0002-pg_term_reason-POC-for-pg_terminate_backend_msg.patch" Content-Transfer-Encoding: quoted-printable =46rom=205b15c5938e8641d0103d225246dbe03911e0ea3f=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20roman=20khapov=20=0ADate:=20= Sat,=2013=20Dec=202025=2007:06:00=20+0000=0ASubject:=20[PATCH=202/2]=20= pg_term_reason:=20POC=20for=20pg_terminate_backend_msg=0A=0AThis=20= commits=20adds=20proof-of-concept=20implementation=0Afor=20extensions,=20= that=20defines=20some=20function=20to=20terminate=0Aconnections=20with=20= additional=20message=20from=20admin.=0A=0AEx.:=0Apostgres=3D#=20create=20= extension=20pg_term_reason;=0ACREATE=20EXTENSION=0A=0Apostgres=3D#=20= select=20pg_terminate_backend_msg(pg_backend_pid(),=200,=20'Have=20you=20= seen=20my=20coffee=20cup?');=0AFATAL:=20=20terminating=20connection=20= due=20to=20administrator=20command:=20Have=20you=20seen=20my=20coffee=20= cup?=0A=0ASigned-off-by:=20roman=20khapov=20=0A---=0A=20= contrib/Makefile=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20|=20=201=20+=0A=20contrib/meson.build=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20|=20=201=20+=0A=20contrib/pg_term_reason/.gitignore=20=20=20=20=20=20=20= =20=20=20=20=20=20|=20=204=20+=0A=20contrib/pg_term_reason/Makefile=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20|=2022=20+++++=0A=20= contrib/pg_term_reason/meson.build=20=20=20=20=20=20=20=20=20=20=20=20|=20= 23=20+++++=0A=20.../pg_term_reason/pg_term_reason--1.0.sql=20=20=20=20|=20= 14=20+++=0A=20contrib/pg_term_reason/pg_term_reason.c=20=20=20=20=20=20=20= |=2089=20+++++++++++++++++++=0A=20= contrib/pg_term_reason/pg_term_reason.control=20|=20=205=20++=0A=20= contrib/pg_term_reason/t/001_basic.pl=20=20=20=20=20=20=20=20=20|=2028=20= ++++++=0A=209=20files=20changed,=20187=20insertions(+)=0A=20create=20= mode=20100644=20contrib/pg_term_reason/.gitignore=0A=20create=20mode=20= 100644=20contrib/pg_term_reason/Makefile=0A=20create=20mode=20100644=20= contrib/pg_term_reason/meson.build=0A=20create=20mode=20100644=20= contrib/pg_term_reason/pg_term_reason--1.0.sql=0A=20create=20mode=20= 100644=20contrib/pg_term_reason/pg_term_reason.c=0A=20create=20mode=20= 100644=20contrib/pg_term_reason/pg_term_reason.control=0A=20create=20= mode=20100644=20contrib/pg_term_reason/t/001_basic.pl=0A=0Adiff=20--git=20= a/contrib/Makefile=20b/contrib/Makefile=0Aindex=202f0a88d3f7..82eb2c0d12=20= 100644=0A---=20a/contrib/Makefile=0A+++=20b/contrib/Makefile=0A@@=20= -37,6=20+37,7=20@@=20SUBDIRS=20=3D=20\=0A=20=09=09pg_prewarm=09\=0A=20=09= =09pg_stat_statements=20\=0A=20=09=09pg_surgery=09\=0A+=09=09= pg_term_reason=20\=0A=20=09=09pg_trgm=09=09\=0A=20=09=09pgrowlocks=09\=0A= =20=09=09pgstattuple=09\=0Adiff=20--git=20a/contrib/meson.build=20= b/contrib/meson.build=0Aindex=20ed30ee7d63..398d58d71e=20100644=0A---=20= a/contrib/meson.build=0A+++=20b/contrib/meson.build=0A@@=20-53,6=20+53,7=20= @@=20subdir('pgrowlocks')=0A=20subdir('pg_stat_statements')=0A=20= subdir('pgstattuple')=0A=20subdir('pg_surgery')=0A= +subdir('pg_term_reason')=0A=20subdir('pg_trgm')=0A=20= subdir('pg_visibility')=0A=20subdir('pg_walinspect')=0Adiff=20--git=20= a/contrib/pg_term_reason/.gitignore=20= b/contrib/pg_term_reason/.gitignore=0Anew=20file=20mode=20100644=0Aindex=20= 0000000000..5dcb3ff972=0A---=20/dev/null=0A+++=20= b/contrib/pg_term_reason/.gitignore=0A@@=20-0,0=20+1,4=20@@=0A+#=20= Generated=20subdirectories=0A+/log/=0A+/results/=0A+/tmp_check/=0Adiff=20= --git=20a/contrib/pg_term_reason/Makefile=20= b/contrib/pg_term_reason/Makefile=0Anew=20file=20mode=20100644=0Aindex=20= 0000000000..fa751801f8=0A---=20/dev/null=0A+++=20= b/contrib/pg_term_reason/Makefile=0A@@=20-0,0=20+1,22=20@@=0A+#=20= contrib/pg_term_reason/Makefile=0A+=0A+MODULE_big=20=3D=20pg_term_reason=0A= +OBJS=20=3D=20\=0A+=09pg_term_reason.o=0A+=0A+EXTENSION=20=3D=20= pg_term_reason=0A+DATA=20=3D=20pg_term_reason--1.0.sql=0A+PGFILEDESC=20=3D= =20"pg_term_reason=20-=20add=20pg_terminate_backend_reasoned=20function"=0A= +=0A+TAP_TESTS=20=3D=201=0A+=0A+ifdef=20USE_PGXS=0A+PG_CONFIG=20=3D=20= pg_config=0A+PGXS=20:=3D=20$(shell=20$(PG_CONFIG)=20--pgxs)=0A+include=20= $(PGXS)=0A+else=0A+subdir=20=3D=20contrib/pg_term_reason=0A+top_builddir=20= =3D=20../..=0A+include=20$(top_builddir)/src/Makefile.global=0A+include=20= $(top_srcdir)/contrib/contrib-global.mk=0A+endif=0Adiff=20--git=20= a/contrib/pg_term_reason/meson.build=20= b/contrib/pg_term_reason/meson.build=0Anew=20file=20mode=20100644=0A= index=200000000000..ad93b4b5f2=0A---=20/dev/null=0A+++=20= b/contrib/pg_term_reason/meson.build=0A@@=20-0,0=20+1,23=20@@=0A= +pg_term_sources=20=3D=20files(=0A+=20=20'pg_term_reason.c',=0A+)=0A+=0A= +pg_term_reason=20=3D=20shared_module('pg_term_reason',=0A+=20=20= pg_term_sources,=0A+=20=20kwargs:=20contrib_mod_args,=0A+)=0A= +contrib_targets=20+=3D=20pg_term_reason=0A+=0A+install_data(=0A+=20=20= 'pg_term_reason--1.0.sql',=0A+=20=20'pg_term_reason.control',=0A+=20=20= kwargs:=20contrib_data_args,=0A+)=0A+=0A+tests=20+=3D=20{=0A+=20=20= 'tap':=20{=0A+=20=20=20=20'tests':=20[=0A+=20=20=20=20=20=20= 't/001_basic.pl',=0A+=20=20=20=20],=0A+=20=20},=0A+}=0Adiff=20--git=20= a/contrib/pg_term_reason/pg_term_reason--1.0.sql=20= b/contrib/pg_term_reason/pg_term_reason--1.0.sql=0Anew=20file=20mode=20= 100644=0Aindex=200000000000..e0f1bd5779=0A---=20/dev/null=0A+++=20= b/contrib/pg_term_reason/pg_term_reason--1.0.sql=0A@@=20-0,0=20+1,14=20= @@=0A+/*=20contrib/pg_term_reason/pg_term_reason--1.0.sql=20*/=0A+=0A+--=20= complain=20if=20script=20is=20sourced=20in=20psql,=20rather=20than=20via=20= CREATE=20EXTENSION=0A+\echo=20Use=20"CREATE=20EXTENSION=20= pg_term_reason"=20to=20load=20this=20file.=20\quit=0A+=0A+CREATE=20= FUNCTION=20pg_terminate_backend_msg(pid=20integer,=20timeout=20int8=20= DEFAULT=200,=20reason=20text=20DEFAULT=20'')=0A+RETURNS=20boolean=0A+AS=20= 'MODULE_PATHNAME',=20'pg_terminate_backend_msg'=0A+LANGUAGE=20C=20= STRICT;=0A+=0A+CREATE=20FUNCTION=20pg_cancel_backend_msg(pid=20integer,=20= reason=20text=20DEFAULT=20'')=0A+RETURNS=20boolean=0A+AS=20= 'MODULE_PATHNAME',=20'pg_cancel_backend_msg'=0A+LANGUAGE=20C=20STRICT;=0A= diff=20--git=20a/contrib/pg_term_reason/pg_term_reason.c=20= b/contrib/pg_term_reason/pg_term_reason.c=0Anew=20file=20mode=20100644=0A= index=200000000000..5c6ceb00fd=0A---=20/dev/null=0A+++=20= b/contrib/pg_term_reason/pg_term_reason.c=0A@@=20-0,0=20+1,89=20@@=0A= +/*-----------------------------------------------------------------------= --=0A+=20*=0A+=20*=20pg_term_reason.c=0A+=20*=09=20=20Functions=20to=20= terminate/cancel=20postgres=20backends=20with=20additional=20message=0A+=20= *=0A+=20*=20IDENTIFICATION=0A+=20*=09=20=20= contrib/pg_term_reason/pg_term_reason.c=0A+=20*=0A+=20= *-------------------------------------------------------------------------= =0A+=20*/=0A+=0A+#include=20"postgres.h"=0A+=0A+#include=20"fmgr.h"=0A= +#include=20"varatt.h"=0A+#include=20"storage/signalfuncs.h"=0A+#include=20= "storage/lock.h"=0A+#include=20"storage/procarray.h"=0A+#include=20= "storage/proc.h"=0A+=0A+PG_MODULE_MAGIC_EXT(=0A+=09=09=09=09=09.name=20=3D= =20"pg_term_reason",=0A+=09=09=09=09=09.version=20=3D=20PG_VERSION=0A+);=0A= +=0A+PG_FUNCTION_INFO_V1(pg_terminate_backend_msg);=0A= +PG_FUNCTION_INFO_V1(pg_cancel_backend_msg);=0A+=0A+static=20void=0A= +set_reason(int=20pid,=20const=20char=20*msg,=20int=20msglen)=0A+{=0A+=09= int=09=09=09len;=0A+=09PGPROC=09=09*proc;=0A+=0A+=09if=20(msglen=20<=3D=20= 0)=20{=0A+=09=09return;=0A+=09}=0A+=0A+=09LWLockAcquire(ProcArrayLock,=20= LW_SHARED);=0A+=0A+=09proc=20=3D=20BackendPidGetProcWithLock(pid);=0A+=0A= +=09if=20(proc=20!=3D=20NULL)=20{=0A+=09=09len=20=3D=20= Min(PROC_TERM_REASON_MAX_LEN=20-=201,=20msglen);=0A+=09=09= strncpy(proc->termReasonStr,=20msg,=20len);=0A+=09}=0A+=0A+=09= LWLockRelease(ProcArrayLock);=0A+}=0A+=0A+Datum=0A= +pg_cancel_backend_msg(PG_FUNCTION_ARGS)=0A+{=0A+=09int=09=09=09pid;=0A+=09= text=09=09*reason;=0A+=09char=09=09*reason_str;=0A+=09int=09=09=09= reason_len;=0A+=0A+=09pid=20=3D=20PG_GETARG_INT32(0);=0A+=09reason=20=3D=20= PG_GETARG_TEXT_P(1);=0A+=0A+=09reason_str=20=3D=20VARDATA(reason);=0A+=09= reason_len=20=3D=20VARSIZE(reason)=20-=20VARHDRSZ;=0A+=0A+=09= set_reason(pid,=20reason_str,=20reason_len);=0A+=0A+=09return=20= pg_cancel_backend_impl(pid);=0A+}=0A+=0A+Datum=0A= +pg_terminate_backend_msg(PG_FUNCTION_ARGS)=0A+{=0A+=09int=09=09=09pid;=0A= +=09int=09=09=09timeout;=09=09/*=20milliseconds=20*/=0A+=09text=09=09= *reason;=0A+=09char=09=09*reason_str;=0A+=09int=09=09=09reason_len;=0A+=0A= +=09pid=20=3D=20PG_GETARG_INT32(0);=0A+=09timeout=20=3D=20= PG_GETARG_INT64(1);=0A+=09reason=20=3D=20PG_GETARG_TEXT_P(2);=0A+=0A+=09= reason_str=20=3D=20VARDATA(reason);=0A+=09reason_len=20=3D=20= VARSIZE(reason)=20-=20VARHDRSZ;=0A+=0A+=09set_reason(pid,=20reason_str,=20= reason_len);=0A+=0A+=09return=20pg_terminate_backend_impl(pid,=20= timeout);=0A+}=0Adiff=20--git=20= a/contrib/pg_term_reason/pg_term_reason.control=20= b/contrib/pg_term_reason/pg_term_reason.control=0Anew=20file=20mode=20= 100644=0Aindex=200000000000..d3b04459aa=0A---=20/dev/null=0A+++=20= b/contrib/pg_term_reason/pg_term_reason.control=0A@@=20-0,0=20+1,5=20@@=0A= +#=20pg_term_reason=20extension=0A+comment=20=3D=20'extension=20to=20= terminate=20backends=20with=20additional=20reason=20string'=0A= +default_version=20=3D=20'1.0'=0A+module_pathname=20=3D=20= '$libdir/pg_term_reason'=0A+relocatable=20=3D=20true=0Adiff=20--git=20= a/contrib/pg_term_reason/t/001_basic.pl=20= b/contrib/pg_term_reason/t/001_basic.pl=0Anew=20file=20mode=20100644=0A= index=200000000000..5d9dadc462=0A---=20/dev/null=0A+++=20= b/contrib/pg_term_reason/t/001_basic.pl=0A@@=20-0,0=20+1,28=20@@=0A+use=20= strict;=0A+use=20warnings=20FATAL=20=3D>=20'all';=0A+use=20= PostgreSQL::Test::Cluster;=0A+use=20PostgreSQL::Test::Utils;=0A+use=20= Test::More;=0A+=0A+my=20$node=20=3D=20= PostgreSQL::Test::Cluster->new('primary');=0A+$node->init();=0A= +$node->start;=0A+=0A+$node->safe_psql('postgres',=20'create=20extension=20= pg_term_reason;');=0A+=0A+my=20($stdout,=20$stderr);=0A= +$node->psql('postgres',=0A+=09q[select=20= pg_terminate_backend_msg(pg_backend_pid(),=200,=20'hello=20from=20tap=20= tests!');],=0A+=09stdout=20=3D>=20\$stdout,=20stderr=20=3D>=20\$stderr);=0A= +like($stderr,=20qr/hello=20from=20tap=20tests\!/,=20"expected=20message=20= to=20be=20passed");=0A+=0A+$stdout=20=3D=20'';=0A+$stderr=20=3D=20'';=0A= +$node->psql('postgres',=0A+=09q[select=20= pg_cancel_backend_msg(pg_backend_pid(),=20'hello=20from=20tap=20tests=20= again!');],=0A+=09stdout=20=3D>=20\$stdout,=20stderr=20=3D>=20\$stderr);=0A= +like($stderr,=20qr/hello=20from=20tap=20tests=20again\!/,=20"expected=20= message=20to=20be=20passed");=0A+=0A+$node->stop;=0A+=0A+done_testing();=0A= --=20=0A2.50.1=20(Apple=20Git-155)=0A=0A= --Apple-Mail=_FE8A0B23-0BE6-4862-85F8-A4F09E8E948C--