summaryrefslogtreecommitdiffstats
path: root/debian/patches
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches')
-rw-r--r--debian/patches/50-per-version-dirs.patch29
-rw-r--r--debian/patches/51-default-sockets-in-var.patch20
-rw-r--r--debian/patches/52-tutorial-README.patch16
-rw-r--r--debian/patches/53-pg_service.conf_directory_doc.patch19
-rw-r--r--debian/patches/54-debian-alternatives-for-external-tools.patch28
-rw-r--r--debian/patches/70-history13
-rw-r--r--debian/patches/autoconf2.697
-rw-r--r--debian/patches/extension_destdir270
-rw-r--r--debian/patches/filter-debug-prefix-map44
-rw-r--r--debian/patches/focal-arm64-outline-atomics23
-rw-r--r--debian/patches/hurd-iovec26
-rw-r--r--debian/patches/jit-s390x96
-rw-r--r--debian/patches/libpgport-pkglibdir91
-rw-r--r--debian/patches/pgstat-report-conflicts-immediately.patch37
-rw-r--r--debian/patches/series15
-rw-r--r--debian/patches/v8-0001-Fix-recovery-conflict-SIGUSR1-handling.patch557
16 files changed, 1291 insertions, 0 deletions
diff --git a/debian/patches/50-per-version-dirs.patch b/debian/patches/50-per-version-dirs.patch
new file mode 100644
index 0000000..8277cdb
--- /dev/null
+++ b/debian/patches/50-per-version-dirs.patch
@@ -0,0 +1,29 @@
+Author: Martin Pitt <mpitt@debian.org>
+Description: Use version specific installation directories so that several major versions can be installed in parallel.
+Forwarded: No, Debian specific packaging with postgresql-common
+
+ * Install lib files into /usr/lib/postgresql/<version>/lib/
+ * Install server related header files into /usr/include/postgresql/<version>/server/
+
+Bug-Debian: http://bugs.debian.org/462037
+
+--- a/src/Makefile.global.in
++++ b/src/Makefile.global.in
+@@ -119,7 +119,7 @@ libdir := @libdir@
+ pkglibdir = $(libdir)
+ ifeq "$(findstring pgsql, $(pkglibdir))" ""
+ ifeq "$(findstring postgres, $(pkglibdir))" ""
+-override pkglibdir := $(pkglibdir)/postgresql
++override pkglibdir := /usr/lib/postgresql/@PG_MAJORVERSION@/lib
+ endif
+ endif
+
+@@ -167,7 +167,7 @@ endif # PGXS
+
+ # These derived path variables aren't separately configurable.
+
+-includedir_server = $(pkgincludedir)/server
++includedir_server = $(pkgincludedir)/@PG_MAJORVERSION@/server
+ includedir_internal = $(pkgincludedir)/internal
+ pgxsdir = $(pkglibdir)/pgxs
+ bitcodedir = $(pkglibdir)/bitcode
diff --git a/debian/patches/51-default-sockets-in-var.patch b/debian/patches/51-default-sockets-in-var.patch
new file mode 100644
index 0000000..9da49b8
--- /dev/null
+++ b/debian/patches/51-default-sockets-in-var.patch
@@ -0,0 +1,20 @@
+Author: Martin Pitt <mpitt@debian.org>
+Description: Put server Unix sockets into /var/run/postgresql/ by default
+Forwarded: No, Debian specific configuration with postgresql-common
+
+Using /tmp for sockets allows everyone to spoof a PostgreSQL server. Thus use
+/var/run/postgresql/ for "system" clusters which run as 'postgres' (user
+clusters will still use /tmp). Since system cluster are by far the common case,
+set it as default.
+
+--- a/src/include/pg_config_manual.h
++++ b/src/include/pg_config_manual.h
+@@ -206,7 +206,7 @@
+ * support them yet.
+ */
+ #ifndef WIN32
+-#define DEFAULT_PGSOCKET_DIR "/tmp"
++#define DEFAULT_PGSOCKET_DIR "/var/run/postgresql"
+ #else
+ #define DEFAULT_PGSOCKET_DIR ""
+ #endif
diff --git a/debian/patches/52-tutorial-README.patch b/debian/patches/52-tutorial-README.patch
new file mode 100644
index 0000000..9eb3263
--- /dev/null
+++ b/debian/patches/52-tutorial-README.patch
@@ -0,0 +1,16 @@
+Author: Martin Pitt <mpitt@debian.org>
+Description: Update tutorial README for required build dependencies.
+Forwarded: No, Debian specific
+
+--- a/src/tutorial/README
++++ b/src/tutorial/README
+@@ -6,8 +6,7 @@ tutorial
+ This directory contains SQL tutorial scripts. To look at them, first do a
+ % make
+ to compile all the scripts and C files for the user-defined functions
+-and types. (make needs to be GNU make --- it may be named something
+-different on your system, often 'gmake')
++and types. This requires a postgresql-server-dev-* package to be installed.
+
+ Then, run psql with the -s (single-step) flag:
+ % psql -s
diff --git a/debian/patches/53-pg_service.conf_directory_doc.patch b/debian/patches/53-pg_service.conf_directory_doc.patch
new file mode 100644
index 0000000..584b41c
--- /dev/null
+++ b/debian/patches/53-pg_service.conf_directory_doc.patch
@@ -0,0 +1,19 @@
+Author: Martin Pitt <mpitt@debian.org>
+Description: Update pg_service.conf example to tell the Debian specific file location.
+Forwarded: No, Debian specific
+
+Index: postgresql-9.2-9.2~beta1/src/interfaces/libpq/pg_service.conf.sample
+===================================================================
+--- postgresql-9.2-9.2~beta1.orig/src/interfaces/libpq/pg_service.conf.sample 2011-04-27 23:17:22.000000000 +0200
++++ postgresql-9.2-9.2~beta1/src/interfaces/libpq/pg_service.conf.sample 2011-05-10 11:25:42.151949794 +0200
+@@ -8,8 +8,8 @@
+ # to look up such parameters. A sample configuration for postgres is
+ # included in this file. Lines beginning with '#' are comments.
+ #
+-# Copy this to your sysconf directory (typically /usr/local/pgsql/etc) and
+-# rename it pg_service.conf.
++# Copy this to /etc/postgresql-common/ (or select its location with the
++# PGSYSCONFDIR environment variable) and rename it pg_service.conf.
+ #
+ #
+ #[postgres]
diff --git a/debian/patches/54-debian-alternatives-for-external-tools.patch b/debian/patches/54-debian-alternatives-for-external-tools.patch
new file mode 100644
index 0000000..0031989
--- /dev/null
+++ b/debian/patches/54-debian-alternatives-for-external-tools.patch
@@ -0,0 +1,28 @@
+Author: Martin Pitt <mpitt@debian.org>
+Description: Use Debian alternatives for external tools instead of hardcoded programs
+Forwarded: No, Debian specific
+
+--- a/src/bin/psql/settings.h
++++ b/src/bin/psql/settings.h
+@@ -19,8 +19,8 @@
+ #define DEFAULT_EDITOR "notepad.exe"
+ /* no DEFAULT_EDITOR_LINENUMBER_ARG for Notepad */
+ #else
+-#define DEFAULT_EDITOR "vi"
+-#define DEFAULT_EDITOR_LINENUMBER_ARG "+"
++#define DEFAULT_EDITOR "sensible-editor"
++/*#define DEFAULT_EDITOR_LINENUMBER_ARG "+"*/
+ #endif
+
+ #define DEFAULT_PROMPT1 "%/%R%x%# "
+--- a/src/include/fe_utils/print.h
++++ b/src/include/fe_utils/print.h
+@@ -20,7 +20,7 @@
+
+ /* This is not a particularly great place for this ... */
+ #ifndef __CYGWIN__
+-#define DEFAULT_PAGER "more"
++#define DEFAULT_PAGER "pager"
+ #else
+ #define DEFAULT_PAGER "less"
+ #endif
diff --git a/debian/patches/70-history b/debian/patches/70-history
new file mode 100644
index 0000000..34c8683
--- /dev/null
+++ b/debian/patches/70-history
@@ -0,0 +1,13 @@
+Author: Christoph Berg <myon@debian.org>
+Description: Document Debian location of release notes files.
+Forwarded: No, Debian specific
+
+--- a/HISTORY
++++ b/HISTORY
+@@ -3,3 +3,6 @@
+
+ Distribution file sets include release notes for their version and preceding
+ versions. Visit the file doc/src/sgml/html/release.html in an HTML browser.
++
++On Debian systems, the release notes are contained in the postgresql-doc-*
++packages, located in /usr/share/doc/postgresql-doc-*/html/release.html.
diff --git a/debian/patches/autoconf2.69 b/debian/patches/autoconf2.69
new file mode 100644
index 0000000..429044e
--- /dev/null
+++ b/debian/patches/autoconf2.69
@@ -0,0 +1,7 @@
+--- a/configure.ac
++++ b/configure.ac
+@@ -22,4 +21,0 @@ AC_INIT([PostgreSQL], [15devel], [pgsql-
+-m4_if(m4_defn([m4_PACKAGE_VERSION]), [2.69], [], [m4_fatal([Autoconf version 2.69 is required.
+-Untested combinations of 'autoconf' and PostgreSQL versions are not
+-recommended. You can remove the check from 'configure.ac' but it is then
+-your responsibility whether the result works or not.])])
diff --git a/debian/patches/extension_destdir b/debian/patches/extension_destdir
new file mode 100644
index 0000000..eedc81b
--- /dev/null
+++ b/debian/patches/extension_destdir
@@ -0,0 +1,270 @@
+--- a/src/backend/commands/extension.c
++++ b/src/backend/commands/extension.c
+@@ -132,6 +132,8 @@ static void ApplyExtensionUpdates(Oid ex
+ bool cascade,
+ bool is_create);
+ static char *read_whole_file(const char *filename, int *length);
++static bool file_exists(const char *name);
++static bool directory_exists(const char *dir);
+
+
+ /*
+@@ -392,6 +394,16 @@ get_extension_control_filename(const cha
+
+ get_share_path(my_exec_path, sharepath);
+ result = (char *) palloc(MAXPGPATH);
++ /*
++ * If extension_destdir is set, try to find the file there first
++ */
++ if (*extension_destdir != '\0')
++ {
++ snprintf(result, MAXPGPATH, "%s%s/extension/%s.control",
++ extension_destdir, sharepath, extname);
++ if (file_exists(result))
++ return result;
++ }
+ snprintf(result, MAXPGPATH, "%s/extension/%s.control",
+ sharepath, extname);
+
+@@ -431,6 +443,16 @@ get_extension_aux_control_filename(Exten
+ scriptdir = get_extension_script_directory(control);
+
+ result = (char *) palloc(MAXPGPATH);
++ /*
++ * If extension_destdir is set, try to find the file there first
++ */
++ if (*extension_destdir != '\0')
++ {
++ snprintf(result, MAXPGPATH, "%s%s/%s--%s.control",
++ extension_destdir, scriptdir, control->name, version);
++ if (file_exists(result))
++ return result;
++ }
+ snprintf(result, MAXPGPATH, "%s/%s--%s.control",
+ scriptdir, control->name, version);
+
+@@ -449,6 +471,23 @@ get_extension_script_filename(ExtensionC
+ scriptdir = get_extension_script_directory(control);
+
+ result = (char *) palloc(MAXPGPATH);
++ /*
++ * If extension_destdir is set, try to find the file there first
++ */
++ if (*extension_destdir != '\0')
++ {
++ if (from_version)
++ snprintf(result, MAXPGPATH, "%s%s/%s--%s--%s.sql",
++ extension_destdir, scriptdir, control->name, from_version, version);
++ else
++ snprintf(result, MAXPGPATH, "%s%s/%s--%s.sql",
++ extension_destdir, scriptdir, control->name, version);
++ if (file_exists(result))
++ {
++ pfree(scriptdir);
++ return result;
++ }
++ }
+ if (from_version)
+ snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql",
+ scriptdir, control->name, from_version, version);
+@@ -1186,6 +1225,59 @@ get_ext_ver_list(ExtensionControlFile *c
+ DIR *dir;
+ struct dirent *de;
+
++ /*
++ * If extension_destdir is set, try to find the files there first
++ */
++ if (*extension_destdir != '\0')
++ {
++ char location[MAXPGPATH];
++
++ snprintf(location, MAXPGPATH, "%s%s", extension_destdir,
++ get_extension_script_directory(control));
++ dir = AllocateDir(location);
++ while ((de = ReadDir(dir, location)) != NULL)
++ {
++ char *vername;
++ char *vername2;
++ ExtensionVersionInfo *evi;
++ ExtensionVersionInfo *evi2;
++
++ /* must be a .sql file ... */
++ if (!is_extension_script_filename(de->d_name))
++ continue;
++
++ /* ... matching extension name followed by separator */
++ if (strncmp(de->d_name, control->name, extnamelen) != 0 ||
++ de->d_name[extnamelen] != '-' ||
++ de->d_name[extnamelen + 1] != '-')
++ continue;
++
++ /* extract version name(s) from 'extname--something.sql' filename */
++ vername = pstrdup(de->d_name + extnamelen + 2);
++ *strrchr(vername, '.') = '\0';
++ vername2 = strstr(vername, "--");
++ if (!vername2)
++ {
++ /* It's an install, not update, script; record its version name */
++ evi = get_ext_ver_info(vername, &evi_list);
++ evi->installable = true;
++ continue;
++ }
++ *vername2 = '\0'; /* terminate first version */
++ vername2 += 2; /* and point to second */
++
++ /* if there's a third --, it's bogus, ignore it */
++ if (strstr(vername2, "--"))
++ continue;
++
++ /* Create ExtensionVersionInfos and link them together */
++ evi = get_ext_ver_info(vername, &evi_list);
++ evi2 = get_ext_ver_info(vername2, &evi_list);
++ evi->reachable = lappend(evi->reachable, evi2);
++ }
++ FreeDir(dir);
++ }
++
+ location = get_extension_script_directory(control);
+ dir = AllocateDir(location);
+ while ((de = ReadDir(dir, location)) != NULL)
+@@ -3470,3 +3562,32 @@ read_whole_file(const char *filename, in
+ buf[*length] = '\0';
+ return buf;
+ }
++
++static bool
++file_exists(const char *name)
++{
++ struct stat st;
++
++ Assert(name != NULL);
++
++ if (stat(name, &st) == 0)
++ return S_ISDIR(st.st_mode) ? false : true;
++ else if (!(errno == ENOENT || errno == ENOTDIR || errno == EACCES))
++ ereport(ERROR,
++ (errcode_for_file_access(),
++ errmsg("could not access file \"%s\": %m", name)));
++
++ return false;
++}
++
++static bool
++directory_exists(const char *dir)
++{
++ struct stat st;
++
++ if (stat(dir, &st) != 0)
++ return false;
++ if (S_ISDIR(st.st_mode))
++ return true;
++ return false;
++}
+--- a/src/include/utils/guc.h
++++ b/src/include/utils/guc.h
+@@ -274,6 +274,7 @@ extern PGDLLIMPORT char *ConfigFileName;
+ extern PGDLLIMPORT char *HbaFileName;
+ extern PGDLLIMPORT char *IdentFileName;
+ extern PGDLLIMPORT char *external_pid_file;
++extern PGDLLIMPORT char *extension_destdir;
+
+ extern PGDLLIMPORT char *application_name;
+
+--- a/src/backend/utils/fmgr/dfmgr.c
++++ b/src/backend/utils/fmgr/dfmgr.c
+@@ -34,6 +34,7 @@
+ #include "lib/stringinfo.h"
+ #include "miscadmin.h"
+ #include "storage/shmem.h"
++#include "utils/guc.h"
+ #include "utils/hsearch.h"
+
+
+@@ -432,7 +433,7 @@ expand_dynamic_library_name(const char *
+ {
+ bool have_slash;
+ char *new;
+- char *full;
++ char *full, *full2;
+
+ Assert(name);
+
+@@ -447,6 +448,19 @@ expand_dynamic_library_name(const char *
+ else
+ {
+ full = substitute_libpath_macro(name);
++ /*
++ * If extension_destdir is set, try to find the file there first
++ */
++ if (*extension_destdir != '\0')
++ {
++ full2 = psprintf("%s%s", extension_destdir, full);
++ if (file_exists(full2))
++ {
++ pfree(full);
++ return full2;
++ }
++ pfree(full2);
++ }
+ if (file_exists(full))
+ return full;
+ pfree(full);
+@@ -465,6 +479,19 @@ expand_dynamic_library_name(const char *
+ {
+ full = substitute_libpath_macro(new);
+ pfree(new);
++ /*
++ * If extension_destdir is set, try to find the file there first
++ */
++ if (*extension_destdir != '\0')
++ {
++ full2 = psprintf("%s%s", extension_destdir, full);
++ if (file_exists(full2))
++ {
++ pfree(full);
++ return full2;
++ }
++ pfree(full2);
++ }
+ if (file_exists(full))
+ return full;
+ pfree(full);
+--- a/src/backend/utils/misc/postgresql.conf.sample
++++ b/src/backend/utils/misc/postgresql.conf.sample
+@@ -750,6 +750,8 @@
+ # - Other Defaults -
+
+ #dynamic_library_path = '$libdir'
++#extension_destdir = '' # prepend path when loading extensions
++ # and shared objects (added by Debian)
+ #gin_fuzzy_search_limit = 0
+
+
+--- a/src/backend/utils/misc/guc_tables.c
++++ b/src/backend/utils/misc/guc_tables.c
+@@ -528,6 +528,7 @@ char *ConfigFileName;
+ char *HbaFileName;
+ char *IdentFileName;
+ char *external_pid_file;
++char *extension_destdir;
+
+ char *application_name;
+
+@@ -4346,6 +4347,17 @@ struct config_string ConfigureNamesStrin
+ },
+
+ {
++ {"extension_destdir", PGC_SUSET, FILE_LOCATIONS,
++ gettext_noop("Path to prepend for extension loading."),
++ gettext_noop("This directory is prepended to paths when loading extensions (control and SQL files), and to the '$libdir' directive when loading modules that back functions. The location is made configurable to allow build-time testing of extensions that do not have been installed to their proper location yet."),
++ GUC_SUPERUSER_ONLY
++ },
++ &extension_destdir,
++ "",
++ NULL, NULL, NULL
++ },
++
++ {
+ {"ssl_library", PGC_INTERNAL, PRESET_OPTIONS,
+ gettext_noop("Shows the name of the SSL library."),
+ NULL,
diff --git a/debian/patches/filter-debug-prefix-map b/debian/patches/filter-debug-prefix-map
new file mode 100644
index 0000000..dcfa9d8
--- /dev/null
+++ b/debian/patches/filter-debug-prefix-map
@@ -0,0 +1,44 @@
+To make the PostgreSQL server packages build reproducibly, we need to remove
+the build path from -fdebug-prefix-map and -ffile-prefix-map in CFLAGS.
+
+* The actual server build still uses the original CFLAGS so the build path is
+ correctly mapped in the object files.
+* The information printed by the pg_config binary and the system view is
+ filtered in src/common/Makefile and the configure script.
+* The build paths stored in Makefile.global are filtered in debian/rules.
+ (abs_top_builddir, abs_top_srcdir, configure_args, CFLAGS)
+* To make PGXS module builds reproducible, pg_buildext copies the environment
+ CFLAGS to COPT where Makefile.global picks them up, using the prefix maps
+ from dpkg-buildflags.
+
+--- a/src/common/Makefile
++++ b/src/common/Makefile
+@@ -33,7 +33,7 @@ STD_CPPFLAGS := $(filter-out -I$(top_src
+ STD_LDFLAGS := $(filter-out -L$(top_builddir)/src/common -L$(top_builddir)/src/port,$(LDFLAGS))
+ override CPPFLAGS += -DVAL_CC="\"$(CC)\""
+ override CPPFLAGS += -DVAL_CPPFLAGS="\"$(STD_CPPFLAGS)\""
+-override CPPFLAGS += -DVAL_CFLAGS="\"$(CFLAGS)\""
++override CPPFLAGS += -DVAL_CFLAGS="\"$(filter-out -fdebug-prefix-map=% -ffile-prefix-map=%,$(CFLAGS))\""
+ override CPPFLAGS += -DVAL_CFLAGS_SL="\"$(CFLAGS_SL)\""
+ override CPPFLAGS += -DVAL_LDFLAGS="\"$(STD_LDFLAGS)\""
+ override CPPFLAGS += -DVAL_LDFLAGS_EX="\"$(LDFLAGS_EX)\""
+--- a/configure.ac
++++ b/configure.ac
+@@ -27,6 +27,7 @@ AC_COPYRIGHT([Copyright (c) 1996-2023, P
+ AC_CONFIG_SRCDIR([src/backend/access/common/heaptuple.c])
+ AC_CONFIG_AUX_DIR(config)
+ AC_PREFIX_DEFAULT(/usr/local/pgsql)
++[ac_configure_args=$(echo "$ac_configure_args" | sed -e "s/ -f\(debug\|file\)-prefix-map=[^' ]*//g")]
+ AC_DEFINE_UNQUOTED(CONFIGURE_ARGS, ["$ac_configure_args"], [Saved arguments from configure])
+
+ [PG_MAJORVERSION=`expr "$PACKAGE_VERSION" : '\([0-9][0-9]*\)'`]
+--- a/configure
++++ b/configure
+@@ -2822,6 +2822,7 @@ ac_config_sub="$SHELL $ac_aux_dir/config
+ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
++ac_configure_args=$(echo "$ac_configure_args" | sed -e "s/ -f\(debug\|file\)-prefix-map=[^' ]*//g")
+
+
+ cat >>confdefs.h <<_ACEOF
diff --git a/debian/patches/focal-arm64-outline-atomics b/debian/patches/focal-arm64-outline-atomics
new file mode 100644
index 0000000..b87d8f5
--- /dev/null
+++ b/debian/patches/focal-arm64-outline-atomics
@@ -0,0 +1,23 @@
+Enable outline-atomics on arm64.
+
+The flag was added in focal's gcc, but is off by default there. It is enabled
+by default on all later distributions (hirsute, impish, bullseye, bookwork,
+sid).
+
+https://www.postgresql.org/message-id/flat/1635221042457.21654%40amazon.com
+
+This patch can be removed once focal is EOL.
+
+--- a/configure.ac
++++ b/configure.ac
+@@ -576,6 +576,10 @@ if test "$GCC" = yes -a "$ICC" = no; the
+ if test -n "$NOT_THE_CFLAGS"; then
+ CFLAGS="$CFLAGS -Wno-cast-function-type-strict"
+ fi
++ if test x"$host_cpu" == x"aarch64"; then
++ PGAC_PROG_CC_CFLAGS_OPT([-moutline-atomics])
++ PGAC_PROG_CXX_CFLAGS_OPT([-moutline-atomics])
++ fi
+ elif test "$ICC" = yes; then
+ # Intel's compiler has a bug/misoptimization in checking for
+ # division by NAN (NaN == 0), -mp1 fixes it, so add it to the CFLAGS.
diff --git a/debian/patches/hurd-iovec b/debian/patches/hurd-iovec
new file mode 100644
index 0000000..e5255f0
--- /dev/null
+++ b/debian/patches/hurd-iovec
@@ -0,0 +1,26 @@
+hurd-i386 does not define IOV_MAX
+
+--- a/src/include/port/pg_iovec.h
++++ b/src/include/port/pg_iovec.h
+@@ -20,9 +20,6 @@
+
+ #else
+
+-/* POSIX requires at least 16 as a maximum iovcnt. */
+-#define IOV_MAX 16
+-
+ /* Define our own POSIX-compatible iovec struct. */
+ struct iovec
+ {
+@@ -32,6 +29,11 @@ struct iovec
+
+ #endif
+
++/* POSIX requires at least 16 as a maximum iovcnt. */
++#ifndef IOV_MAX
++#define IOV_MAX 16
++#endif
++
+ /* Define a reasonable maximum that is safe to use on the stack. */
+ #define PG_IOV_MAX Min(IOV_MAX, 32)
+
diff --git a/debian/patches/jit-s390x b/debian/patches/jit-s390x
new file mode 100644
index 0000000..deb64e6
--- /dev/null
+++ b/debian/patches/jit-s390x
@@ -0,0 +1,96 @@
+From 0edaa982336823d4d7af8f10b91579fe0099ef3d Mon Sep 17 00:00:00 2001
+From: Tom Stellard <tstellar@redhat.com>
+Date: Tue, 20 Apr 2021 20:14:21 -0700
+Subject: [PATCH] jit: Workaround potential datalayout mismatch on s390x
+
+LLVM's s390x target uses a different datalayout for z13 and newer processors.
+If llvmjit_types.bc is compiled to target a processor older than z13, and
+then the JIT runs on a z13 or newer processor, then there will be a mismatch
+in datalayouts between llvmjit_types.bc and the JIT engine. This mismatch
+causes the JIT to fail at runtime.
+---
+ src/backend/jit/llvm/llvmjit.c | 46 ++++++++++++++++++++++++++++++++--
+ 1 file changed, 44 insertions(+), 2 deletions(-)
+
+--- a/src/backend/jit/llvm/llvmjit.c
++++ b/src/backend/jit/llvm/llvmjit.c
+@@ -777,6 +777,37 @@ llvm_compile_module(LLVMJitContext *cont
+ }
+
+ /*
++ * For the systemz target, LLVM uses a different datalayout for z13 and newer
++ * CPUs than it does for older CPUs. This can cause a mismatch in datalayouts
++ * in the case where the llvm_types_module is compiled with a pre-z13 CPU
++ * and the JIT is running on z13 or newer.
++ * See computeDataLayout() function in
++ * llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp for information on the
++ * datalayout differences.
++ */
++static bool
++needs_systemz_workaround(void)
++{
++ bool ret = false;
++#ifdef __s390x__
++ LLVMContextRef llvm_context;
++ LLVMTypeRef vec_type;
++ LLVMTargetDataRef llvm_layoutref;
++ if (strncmp(LLVMGetTargetName(llvm_targetref), "systemz", strlen("systemz")))
++ {
++ return false;
++ }
++
++ llvm_context = LLVMGetModuleContext(llvm_types_module);
++ vec_type = LLVMVectorType(LLVMIntTypeInContext(llvm_context, 32), 4);
++ llvm_layoutref = LLVMCreateTargetData(llvm_layout);
++ ret = (LLVMABIAlignmentOfType(llvm_layoutref, vec_type) == 16);
++ LLVMDisposeTargetData(llvm_layoutref);
++#endif
++ return ret;
++}
++
++/*
+ * Per session initialization.
+ */
+ static void
+@@ -785,6 +816,7 @@ llvm_session_initialize(void)
+ MemoryContext oldcontext;
+ char *error = NULL;
+ char *cpu = NULL;
++ char *host_features = NULL;
+ char *features = NULL;
+ LLVMTargetMachineRef opt0_tm;
+ LLVMTargetMachineRef opt3_tm;
+@@ -826,10 +858,17 @@ llvm_session_initialize(void)
+ * features not all CPUs have (weird, huh).
+ */
+ cpu = LLVMGetHostCPUName();
+- features = LLVMGetHostCPUFeatures();
++ features = host_features = LLVMGetHostCPUFeatures();
+ elog(DEBUG2, "LLVMJIT detected CPU \"%s\", with features \"%s\"",
+ cpu, features);
+
++ if (needs_systemz_workaround())
++ {
++ const char *no_vector =",-vector";
++ features = malloc(sizeof(char) * (strlen(host_features) + strlen(no_vector) + 1));
++ sprintf(features, "%s%s", host_features, no_vector);
++ }
++
+ opt0_tm =
+ LLVMCreateTargetMachine(llvm_targetref, llvm_triple, cpu, features,
+ LLVMCodeGenLevelNone,
+@@ -843,8 +882,13 @@ llvm_session_initialize(void)
+
+ LLVMDisposeMessage(cpu);
+ cpu = NULL;
+- LLVMDisposeMessage(features);
++ if (features != host_features)
++ {
++ free(features);
++ }
+ features = NULL;
++ LLVMDisposeMessage(host_features);
++ host_features = NULL;
+
+ /* force symbols in main binary to be loaded */
+ LLVMLoadLibraryPermanently(NULL);
diff --git a/debian/patches/libpgport-pkglibdir b/debian/patches/libpgport-pkglibdir
new file mode 100644
index 0000000..dc76bcc
--- /dev/null
+++ b/debian/patches/libpgport-pkglibdir
@@ -0,0 +1,91 @@
+Author: Christoph Berg <myon@debian.org>
+Description: Move libpgport/libpgcommon/libpgfeutils from libdir to pkglibdir
+ This allows client applications to link to version-specific libraries.
+ Used by pg-checksums.
+Forwarded: No, (somewhat) Debian specific
+
+--- a/src/common/Makefile
++++ b/src/common/Makefile
+@@ -125,15 +125,15 @@ distprep: kwlist_d.h
+
+ # libpgcommon is needed by some contrib
+ install: all installdirs
+- $(INSTALL_STLIB) libpgcommon.a '$(DESTDIR)$(libdir)/libpgcommon.a'
+- $(INSTALL_STLIB) libpgcommon_shlib.a '$(DESTDIR)$(libdir)/libpgcommon_shlib.a'
++ $(INSTALL_STLIB) libpgcommon.a '$(DESTDIR)$(pkglibdir)/libpgcommon.a'
++ $(INSTALL_STLIB) libpgcommon_shlib.a '$(DESTDIR)$(pkglibdir)/libpgcommon_shlib.a'
+
+ installdirs:
+- $(MKDIR_P) '$(DESTDIR)$(libdir)'
++ $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'
+
+ uninstall:
+- rm -f '$(DESTDIR)$(libdir)/libpgcommon.a'
+- rm -f '$(DESTDIR)$(libdir)/libpgcommon_shlib.a'
++ rm -f '$(DESTDIR)$(pkglibdir)/libpgcommon.a'
++ rm -f '$(DESTDIR)$(pkglibdir)/libpgcommon_shlib.a'
+
+ libpgcommon.a: $(OBJS_FRONTEND)
+ rm -f $@
+--- a/src/fe_utils/Makefile
++++ b/src/fe_utils/Makefile
+@@ -52,13 +52,13 @@ distprep: psqlscan.c
+
+ # libpgfeutils could be useful to contrib, so install it
+ install: all installdirs
+- $(INSTALL_STLIB) libpgfeutils.a '$(DESTDIR)$(libdir)/libpgfeutils.a'
++ $(INSTALL_STLIB) libpgfeutils.a '$(DESTDIR)$(pkglibdir)/libpgfeutils.a'
+
+ installdirs:
+- $(MKDIR_P) '$(DESTDIR)$(libdir)'
++ $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'
+
+ uninstall:
+- rm -f '$(DESTDIR)$(libdir)/libpgfeutils.a'
++ rm -f '$(DESTDIR)$(pkglibdir)/libpgfeutils.a'
+
+ clean distclean:
+ rm -f libpgfeutils.a $(OBJS) lex.backup
+--- a/src/port/Makefile
++++ b/src/port/Makefile
+@@ -70,15 +70,15 @@ all: libpgport.a libpgport_shlib.a libpg
+
+ # libpgport is needed by some contrib
+ install: all installdirs
+- $(INSTALL_STLIB) libpgport.a '$(DESTDIR)$(libdir)/libpgport.a'
+- $(INSTALL_STLIB) libpgport_shlib.a '$(DESTDIR)$(libdir)/libpgport_shlib.a'
++ $(INSTALL_STLIB) libpgport.a '$(DESTDIR)$(pkglibdir)/libpgport.a'
++ $(INSTALL_STLIB) libpgport_shlib.a '$(DESTDIR)$(pkglibdir)/libpgport_shlib.a'
+
+ installdirs:
+- $(MKDIR_P) '$(DESTDIR)$(libdir)'
++ $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'
+
+ uninstall:
+- rm -f '$(DESTDIR)$(libdir)/libpgport.a'
+- rm -f '$(DESTDIR)$(libdir)/libpgport_shlib.a'
++ rm -f '$(DESTDIR)$(pkglibdir)/libpgport.a'
++ rm -f '$(DESTDIR)$(pkglibdir)/libpgport_shlib.a'
+
+ libpgport.a: $(OBJS)
+ rm -f $@
+--- a/src/Makefile.global.in
++++ b/src/Makefile.global.in
+@@ -599,7 +599,7 @@ libpq = -L$(libpq_builddir) -lpq
+ ifeq ($(PORTNAME),darwin)
+ libpq_pgport = $(libpq)
+ else ifdef PGXS
+-libpq_pgport = -L$(libdir) -lpgcommon -lpgport $(libpq)
++libpq_pgport = -L$(pkglibdir) -lpgcommon -lpgport $(libpq)
+ else
+ libpq_pgport = -L$(top_builddir)/src/common -lpgcommon -L$(top_builddir)/src/port -lpgport $(libpq)
+ endif
+@@ -610,7 +610,7 @@ endif
+ # done if they don't, since they will have satisfied all their references
+ # from these libraries.)
+ ifdef PGXS
+-libpq_pgport_shlib = -L$(libdir) -lpgcommon_shlib -lpgport_shlib $(libpq)
++libpq_pgport_shlib = -L$(pkglibdir) -lpgcommon_shlib -lpgport_shlib $(libpq)
+ else
+ libpq_pgport_shlib = -L$(top_builddir)/src/common -lpgcommon_shlib -L$(top_builddir)/src/port -lpgport_shlib $(libpq)
+ endif
diff --git a/debian/patches/pgstat-report-conflicts-immediately.patch b/debian/patches/pgstat-report-conflicts-immediately.patch
new file mode 100644
index 0000000..5760310
--- /dev/null
+++ b/debian/patches/pgstat-report-conflicts-immediately.patch
@@ -0,0 +1,37 @@
+diff --git i/src/backend/utils/activity/pgstat_database.c w/src/backend/utils/activity/pgstat_database.c
+index 7149f22f729..bb36d73ec04 100644
+--- i/src/backend/utils/activity/pgstat_database.c
++++ w/src/backend/utils/activity/pgstat_database.c
+@@ -81,12 +81,22 @@ void
+ pgstat_report_recovery_conflict(int reason)
+ {
+ PgStat_StatDBEntry *dbentry;
++ PgStat_EntryRef *entry_ref;
++ PgStatShared_Database *sharedent;
+
+ Assert(IsUnderPostmaster);
+ if (!pgstat_track_counts)
+ return;
+
+- dbentry = pgstat_prep_database_pending(MyDatabaseId);
++ /*
++ * Update the shared stats directly - recovery conflicts should never be
++ * common enough for that to be a problem.
++ */
++ entry_ref =
++ pgstat_get_entry_ref_locked(PGSTAT_KIND_DATABASE, MyDatabaseId, InvalidOid, false);
++
++ sharedent = (PgStatShared_Database *) entry_ref->shared_stats;
++ dbentry = &sharedent->stats;
+
+ switch (reason)
+ {
+@@ -116,6 +126,8 @@ pgstat_report_recovery_conflict(int reason)
+ dbentry->conflict_startup_deadlock++;
+ break;
+ }
++
++ pgstat_unlock_entry(entry_ref);
+ }
+
+ /*
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..3271c55
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,15 @@
+50-per-version-dirs.patch
+51-default-sockets-in-var.patch
+52-tutorial-README.patch
+53-pg_service.conf_directory_doc.patch
+54-debian-alternatives-for-external-tools.patch
+70-history
+filter-debug-prefix-map
+libpgport-pkglibdir
+extension_destdir
+autoconf2.69
+focal-arm64-outline-atomics
+jit-s390x
+hurd-iovec
+v8-0001-Fix-recovery-conflict-SIGUSR1-handling.patch
+pgstat-report-conflicts-immediately.patch
diff --git a/debian/patches/v8-0001-Fix-recovery-conflict-SIGUSR1-handling.patch b/debian/patches/v8-0001-Fix-recovery-conflict-SIGUSR1-handling.patch
new file mode 100644
index 0000000..da4cb64
--- /dev/null
+++ b/debian/patches/v8-0001-Fix-recovery-conflict-SIGUSR1-handling.patch
@@ -0,0 +1,557 @@
+From 6544931e533aa015f39215f9c9d2df3e06700a96 Mon Sep 17 00:00:00 2001
+From: Thomas Munro <thomas.munro@gmail.com>
+Date: Tue, 10 May 2022 16:00:23 +1200
+Subject: [PATCH v8] Fix recovery conflict SIGUSR1 handling.
+
+We shouldn't be doing real work in a signal handler, to avoid reaching
+code that is not safe in that context. The previous coding also
+confused the 'reason' shown in error messages by clobbering global
+variables. Move all recovery conflict checking logic into the next CFI,
+and have the signal handler just set flags and the latch, following the
+standard pattern. Since there are several different reasons, use a
+separate flag for each.
+
+With this refactoring, the recovery conflict system no longer
+piggy-backs on top of the regular query cancelation mechanisms, but
+instead ereports directly if it decides that is necessary. It still
+needs to respect QueryCancelHoldoffCount, because otherwise the FEBE
+protocol might be corrupted (see commit 2b3a8b20c2d).
+
+Back-patch to 16. For now we have agreed not to back-patch this change
+any further than that, due to its complexity and the regex changes in
+commit bea3d7e that it depends on.
+
+Reviewed-by: Andres Freund <andres@anarazel.de>
+Reviewed-by: Michael Paquier <michael@paquier.xyz>
+Reviewed-by: Robert Haas <robertmhaas@gmail.com>
+Discussion: https://postgr.es/m/CA%2BhUKGK3PGKwcKqzoosamn36YW-fsuTdOPPF1i_rtEO%3DnEYKSg%40mail.gmail.com
+---
+ src/backend/storage/buffer/bufmgr.c | 4 +-
+ src/backend/storage/ipc/procsignal.c | 14 +-
+ src/backend/tcop/postgres.c | 333 ++++++++++++++-------------
+ src/include/storage/procsignal.h | 4 +-
+ src/include/tcop/tcopprot.h | 3 +-
+ 5 files changed, 188 insertions(+), 170 deletions(-)
+
+diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
+index df22aaa1c5..55c484a43e 100644
+--- a/src/backend/storage/buffer/bufmgr.c
++++ b/src/backend/storage/buffer/bufmgr.c
+@@ -4923,8 +4923,8 @@ LockBufferForCleanup(Buffer buffer)
+ }
+
+ /*
+- * Check called from RecoveryConflictInterrupt handler when Startup
+- * process requests cancellation of all pin holders that are blocking it.
++ * Check called from ProcessRecoveryConflictInterrupts() when Startup process
++ * requests cancellation of all pin holders that are blocking it.
+ */
+ bool
+ HoldingBufferPinThatDelaysRecovery(void)
+diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
+index c85cb5cc18..b7427906de 100644
+--- a/src/backend/storage/ipc/procsignal.c
++++ b/src/backend/storage/ipc/procsignal.c
+@@ -662,25 +662,25 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
+ HandleParallelApplyMessageInterrupt();
+
+ if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
+- RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
++ HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
+
+ if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_TABLESPACE))
+- RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE);
++ HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE);
+
+ if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOCK))
+- RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK);
++ HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK);
+
+ if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))
+- RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
++ HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
+
+ if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT))
+- RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT);
++ HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT);
+
+ if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK))
+- RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
++ HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+
+ if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
+- RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
++ HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+
+ SetLatch(MyLatch);
+
+diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
+index 36cc99ec9c..fab976227f 100644
+--- a/src/backend/tcop/postgres.c
++++ b/src/backend/tcop/postgres.c
+@@ -161,9 +161,8 @@ static bool EchoQuery = false; /* -E switch */
+ static bool UseSemiNewlineNewline = false; /* -j switch */
+
+ /* whether or not, and why, we were canceled by conflict with recovery */
+-static bool RecoveryConflictPending = false;
+-static bool RecoveryConflictRetryable = true;
+-static ProcSignalReason RecoveryConflictReason;
++static volatile sig_atomic_t RecoveryConflictPending = false;
++static volatile sig_atomic_t RecoveryConflictPendingReasons[NUM_PROCSIGNALS];
+
+ /* reused buffer to pass to SendRowDescriptionMessage() */
+ static MemoryContext row_description_context = NULL;
+@@ -182,7 +181,6 @@ static bool check_log_statement(List *stmt_list);
+ static int errdetail_execute(List *raw_parsetree_list);
+ static int errdetail_params(ParamListInfo params);
+ static int errdetail_abort(void);
+-static int errdetail_recovery_conflict(void);
+ static void bind_param_error_callback(void *arg);
+ static void start_xact_command(void);
+ static void finish_xact_command(void);
+@@ -2510,9 +2508,9 @@ errdetail_abort(void)
+ * Add an errdetail() line showing conflict source.
+ */
+ static int
+-errdetail_recovery_conflict(void)
++errdetail_recovery_conflict(ProcSignalReason reason)
+ {
+- switch (RecoveryConflictReason)
++ switch (reason)
+ {
+ case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+ errdetail("User was holding shared buffer pin for too long.");
+@@ -3040,143 +3038,205 @@ FloatExceptionHandler(SIGNAL_ARGS)
+ }
+
+ /*
+- * RecoveryConflictInterrupt: out-of-line portion of recovery conflict
+- * handling following receipt of SIGUSR1. Designed to be similar to die()
+- * and StatementCancelHandler(). Called only by a normal user backend
+- * that begins a transaction during recovery.
++ * Tell the next CHECK_FOR_INTERRUPTS() to check for a particular type of
++ * recovery conflict. Runs in a SIGUSR1 handler.
+ */
+ void
+-RecoveryConflictInterrupt(ProcSignalReason reason)
++HandleRecoveryConflictInterrupt(ProcSignalReason reason)
+ {
+- int save_errno = errno;
++ RecoveryConflictPendingReasons[reason] = true;
++ RecoveryConflictPending = true;
++ InterruptPending = true;
++ /* latch will be set by procsignal_sigusr1_handler */
++}
+
+- /*
+- * Don't joggle the elbow of proc_exit
+- */
+- if (!proc_exit_inprogress)
++/*
++ * Check one individual conflict reason.
++ */
++static void
++ProcessRecoveryConflictInterrupt(ProcSignalReason reason)
++{
++ switch (reason)
+ {
+- RecoveryConflictReason = reason;
+- switch (reason)
+- {
+- case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
++ case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+
+- /*
+- * If we aren't waiting for a lock we can never deadlock.
+- */
+- if (!IsWaitingForLock())
+- return;
++ /*
++ * If we aren't waiting for a lock we can never deadlock.
++ */
++ if (!IsWaitingForLock())
++ return;
+
+- /* Intentional fall through to check wait for pin */
+- /* FALLTHROUGH */
++ /* Intentional fall through to check wait for pin */
++ /* FALLTHROUGH */
+
+- case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
++ case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
+
+- /*
+- * If PROCSIG_RECOVERY_CONFLICT_BUFFERPIN is requested but we
+- * aren't blocking the Startup process there is nothing more
+- * to do.
+- *
+- * When PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK is
+- * requested, if we're waiting for locks and the startup
+- * process is not waiting for buffer pin (i.e., also waiting
+- * for locks), we set the flag so that ProcSleep() will check
+- * for deadlocks.
+- */
+- if (!HoldingBufferPinThatDelaysRecovery())
+- {
+- if (reason == PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK &&
+- GetStartupBufferPinWaitBufId() < 0)
+- CheckDeadLockAlert();
+- return;
+- }
++ /*
++ * If PROCSIG_RECOVERY_CONFLICT_BUFFERPIN is requested but we
++ * aren't blocking the Startup process there is nothing more to
++ * do.
++ *
++ * When PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK is requested,
++ * if we're waiting for locks and the startup process is not
++ * waiting for buffer pin (i.e., also waiting for locks), we set
++ * the flag so that ProcSleep() will check for deadlocks.
++ */
++ if (!HoldingBufferPinThatDelaysRecovery())
++ {
++ if (reason == PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK &&
++ GetStartupBufferPinWaitBufId() < 0)
++ CheckDeadLockAlert();
++ return;
++ }
+
+- MyProc->recoveryConflictPending = true;
++ MyProc->recoveryConflictPending = true;
+
+- /* Intentional fall through to error handling */
+- /* FALLTHROUGH */
++ /* Intentional fall through to error handling */
++ /* FALLTHROUGH */
+
+- case PROCSIG_RECOVERY_CONFLICT_LOCK:
+- case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
+- case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
++ case PROCSIG_RECOVERY_CONFLICT_LOCK:
++ case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
++ case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+
++ /*
++ * If we aren't in a transaction any longer then ignore.
++ */
++ if (!IsTransactionOrTransactionBlock())
++ return;
++
++ /* FALLTHROUGH */
++
++ case PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT:
++
++ /*
++ * If we're not in a subtransaction then we are OK to throw an
++ * ERROR to resolve the conflict. Otherwise drop through to the
++ * FATAL case.
++ *
++ * PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT is a special case that
++ * always throws an ERROR (ie never promotes to FATAL), though it
++ * still has to respect QueryCancelHoldoffCount, so it shares this
++ * code path. Logical decoding slots are only acquired while
++ * performing logical decoding. During logical decoding no user
++ * controlled code is run. During [sub]transaction abort, the
++ * slot is released. Therefore user controlled code cannot
++ * intercept an error before the replication slot is released.
++ *
++ * XXX other times that we can throw just an ERROR *may* be
++ * PROCSIG_RECOVERY_CONFLICT_LOCK if no locks are held in parent
++ * transactions
++ *
++ * PROCSIG_RECOVERY_CONFLICT_SNAPSHOT if no snapshots are held by
++ * parent transactions and the transaction is not
++ * transaction-snapshot mode
++ *
++ * PROCSIG_RECOVERY_CONFLICT_TABLESPACE if no temp files or
++ * cursors open in parent transactions
++ */
++ if (reason == PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT ||
++ !IsSubTransaction())
++ {
+ /*
+- * If we aren't in a transaction any longer then ignore.
++ * If we already aborted then we no longer need to cancel. We
++ * do this here since we do not wish to ignore aborted
++ * subtransactions, which must cause FATAL, currently.
+ */
+- if (!IsTransactionOrTransactionBlock())
++ if (IsAbortedTransactionBlockState())
+ return;
+
+ /*
+- * If we can abort just the current subtransaction then we are
+- * OK to throw an ERROR to resolve the conflict. Otherwise
+- * drop through to the FATAL case.
+- *
+- * XXX other times that we can throw just an ERROR *may* be
+- * PROCSIG_RECOVERY_CONFLICT_LOCK if no locks are held in
+- * parent transactions
+- *
+- * PROCSIG_RECOVERY_CONFLICT_SNAPSHOT if no snapshots are held
+- * by parent transactions and the transaction is not
+- * transaction-snapshot mode
+- *
+- * PROCSIG_RECOVERY_CONFLICT_TABLESPACE if no temp files or
+- * cursors open in parent transactions
++ * If a recovery conflict happens while we are waiting for
++ * input from the client, the client is presumably just
++ * sitting idle in a transaction, preventing recovery from
++ * making progress. We'll drop through to the FATAL case
++ * below to dislodge it, in that case.
+ */
+- if (!IsSubTransaction())
++ if (!DoingCommandRead)
+ {
+- /*
+- * If we already aborted then we no longer need to cancel.
+- * We do this here since we do not wish to ignore aborted
+- * subtransactions, which must cause FATAL, currently.
+- */
+- if (IsAbortedTransactionBlockState())
++ /* Avoid losing sync in the FE/BE protocol. */
++ if (QueryCancelHoldoffCount != 0)
++ {
++ /*
++ * Re-arm and defer this interrupt until later. See
++ * similar code in ProcessInterrupts().
++ */
++ RecoveryConflictPendingReasons[reason] = true;
++ RecoveryConflictPending = true;
++ InterruptPending = true;
+ return;
++ }
+
+- RecoveryConflictPending = true;
+- QueryCancelPending = true;
+- InterruptPending = true;
++ /*
++ * We are cleared to throw an ERROR. Either it's the
++ * logical slot case, or we have a top-level transaction
++ * that we can abort and a conflict that isn't inherently
++ * non-retryable.
++ */
++ LockErrorCleanup();
++ pgstat_report_recovery_conflict(reason);
++ ereport(ERROR,
++ (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
++ errmsg("canceling statement due to conflict with recovery"),
++ errdetail_recovery_conflict(reason)));
+ break;
+ }
++ }
+
+- /* Intentional fall through to session cancel */
+- /* FALLTHROUGH */
+-
+- case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+- RecoveryConflictPending = true;
+- ProcDiePending = true;
+- InterruptPending = true;
+- break;
+-
+- case PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT:
+- RecoveryConflictPending = true;
+- QueryCancelPending = true;
+- InterruptPending = true;
+- break;
++ /* Intentional fall through to session cancel */
++ /* FALLTHROUGH */
+
+- default:
+- elog(FATAL, "unrecognized conflict mode: %d",
+- (int) reason);
+- }
++ case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+
+- Assert(RecoveryConflictPending && (QueryCancelPending || ProcDiePending));
++ /*
++ * Retrying is not possible because the database is dropped, or we
++ * decided above that we couldn't resolve the conflict with an
++ * ERROR and fell through. Terminate the session.
++ */
++ pgstat_report_recovery_conflict(reason);
++ ereport(FATAL,
++ (errcode(reason == PROCSIG_RECOVERY_CONFLICT_DATABASE ?
++ ERRCODE_DATABASE_DROPPED :
++ ERRCODE_T_R_SERIALIZATION_FAILURE),
++ errmsg("terminating connection due to conflict with recovery"),
++ errdetail_recovery_conflict(reason),
++ errhint("In a moment you should be able to reconnect to the"
++ " database and repeat your command.")));
++ break;
+
+- /*
+- * All conflicts apart from database cause dynamic errors where the
+- * command or transaction can be retried at a later point with some
+- * potential for success. No need to reset this, since non-retryable
+- * conflict errors are currently FATAL.
+- */
+- if (reason == PROCSIG_RECOVERY_CONFLICT_DATABASE)
+- RecoveryConflictRetryable = false;
++ default:
++ elog(FATAL, "unrecognized conflict mode: %d", (int) reason);
+ }
++}
++
++/*
++ * Check each possible recovery conflict reason.
++ */
++static void
++ProcessRecoveryConflictInterrupts(void)
++{
++ ProcSignalReason reason;
+
+ /*
+- * Set the process latch. This function essentially emulates signal
+- * handlers like die() and StatementCancelHandler() and it seems prudent
+- * to behave similarly as they do.
++ * We don't need to worry about joggling the elbow of proc_exit, because
++ * proc_exit_prepare() holds interrupts, so ProcessInterrupts() won't call
++ * us.
+ */
+- SetLatch(MyLatch);
++ Assert(!proc_exit_inprogress);
++ Assert(InterruptHoldoffCount == 0);
++ Assert(RecoveryConflictPending);
+
+- errno = save_errno;
++ RecoveryConflictPending = false;
++
++ for (reason = PROCSIG_RECOVERY_CONFLICT_FIRST;
++ reason <= PROCSIG_RECOVERY_CONFLICT_LAST;
++ reason++)
++ {
++ if (RecoveryConflictPendingReasons[reason])
++ {
++ RecoveryConflictPendingReasons[reason] = false;
++ ProcessRecoveryConflictInterrupt(reason);
++ }
++ }
+ }
+
+ /*
+@@ -3231,24 +3291,6 @@ ProcessInterrupts(void)
+ */
+ proc_exit(1);
+ }
+- else if (RecoveryConflictPending && RecoveryConflictRetryable)
+- {
+- pgstat_report_recovery_conflict(RecoveryConflictReason);
+- ereport(FATAL,
+- (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+- errmsg("terminating connection due to conflict with recovery"),
+- errdetail_recovery_conflict()));
+- }
+- else if (RecoveryConflictPending)
+- {
+- /* Currently there is only one non-retryable recovery conflict */
+- Assert(RecoveryConflictReason == PROCSIG_RECOVERY_CONFLICT_DATABASE);
+- pgstat_report_recovery_conflict(RecoveryConflictReason);
+- ereport(FATAL,
+- (errcode(ERRCODE_DATABASE_DROPPED),
+- errmsg("terminating connection due to conflict with recovery"),
+- errdetail_recovery_conflict()));
+- }
+ else if (IsBackgroundWorker)
+ ereport(FATAL,
+ (errcode(ERRCODE_ADMIN_SHUTDOWN),
+@@ -3291,31 +3333,13 @@ ProcessInterrupts(void)
+ errmsg("connection to client lost")));
+ }
+
+- /*
+- * If a recovery conflict happens while we are waiting for input from the
+- * client, the client is presumably just sitting idle in a transaction,
+- * preventing recovery from making progress. Terminate the connection to
+- * dislodge it.
+- */
+- if (RecoveryConflictPending && DoingCommandRead)
+- {
+- QueryCancelPending = false; /* this trumps QueryCancel */
+- RecoveryConflictPending = false;
+- LockErrorCleanup();
+- pgstat_report_recovery_conflict(RecoveryConflictReason);
+- ereport(FATAL,
+- (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+- errmsg("terminating connection due to conflict with recovery"),
+- errdetail_recovery_conflict(),
+- errhint("In a moment you should be able to reconnect to the"
+- " database and repeat your command.")));
+- }
+-
+ /*
+ * Don't allow query cancel interrupts while reading input from the
+ * client, because we might lose sync in the FE/BE protocol. (Die
+ * interrupts are OK, because we won't read any further messages from the
+ * client in that case.)
++ *
++ * See similar logic in ProcessRecoveryConflictInterrupts().
+ */
+ if (QueryCancelPending && QueryCancelHoldoffCount != 0)
+ {
+@@ -3374,16 +3398,6 @@ ProcessInterrupts(void)
+ (errcode(ERRCODE_QUERY_CANCELED),
+ errmsg("canceling autovacuum task")));
+ }
+- if (RecoveryConflictPending)
+- {
+- RecoveryConflictPending = false;
+- LockErrorCleanup();
+- pgstat_report_recovery_conflict(RecoveryConflictReason);
+- ereport(ERROR,
+- (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+- errmsg("canceling statement due to conflict with recovery"),
+- errdetail_recovery_conflict()));
+- }
+
+ /*
+ * If we are reading a command from the client, just ignore the cancel
+@@ -3399,6 +3413,9 @@ ProcessInterrupts(void)
+ }
+ }
+
++ if (RecoveryConflictPending)
++ ProcessRecoveryConflictInterrupts();
++
+ if (IdleInTransactionSessionTimeoutPending)
+ {
+ /*
+diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
+index 2f52100b00..3a3a7eca77 100644
+--- a/src/include/storage/procsignal.h
++++ b/src/include/storage/procsignal.h
+@@ -38,13 +38,15 @@ typedef enum
+ PROCSIG_PARALLEL_APPLY_MESSAGE, /* Message from parallel apply workers */
+
+ /* Recovery conflict reasons */
+- PROCSIG_RECOVERY_CONFLICT_DATABASE,
++ PROCSIG_RECOVERY_CONFLICT_FIRST,
++ PROCSIG_RECOVERY_CONFLICT_DATABASE = PROCSIG_RECOVERY_CONFLICT_FIRST,
+ PROCSIG_RECOVERY_CONFLICT_TABLESPACE,
+ PROCSIG_RECOVERY_CONFLICT_LOCK,
+ PROCSIG_RECOVERY_CONFLICT_SNAPSHOT,
+ PROCSIG_RECOVERY_CONFLICT_LOGICALSLOT,
+ PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
+ PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
++ PROCSIG_RECOVERY_CONFLICT_LAST = PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
+
+ NUM_PROCSIGNALS /* Must be last! */
+ } ProcSignalReason;
+diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
+index abd7b4fff3..ab43b638ee 100644
+--- a/src/include/tcop/tcopprot.h
++++ b/src/include/tcop/tcopprot.h
+@@ -70,8 +70,7 @@ extern void die(SIGNAL_ARGS);
+ extern void quickdie(SIGNAL_ARGS) pg_attribute_noreturn();
+ extern void StatementCancelHandler(SIGNAL_ARGS);
+ extern void FloatExceptionHandler(SIGNAL_ARGS) pg_attribute_noreturn();
+-extern void RecoveryConflictInterrupt(ProcSignalReason reason); /* called from SIGUSR1
+- * handler */
++extern void HandleRecoveryConflictInterrupt(ProcSignalReason reason);
+ extern void ProcessClientReadInterrupt(bool blocked);
+ extern void ProcessClientWriteInterrupt(bool blocked);
+
+--
+2.41.0
+