diff options
Diffstat (limited to 'debian/patches')
-rw-r--r-- | debian/patches/50-per-version-dirs.patch | 29 | ||||
-rw-r--r-- | debian/patches/51-default-sockets-in-var.patch | 20 | ||||
-rw-r--r-- | debian/patches/52-tutorial-README.patch | 16 | ||||
-rw-r--r-- | debian/patches/53-pg_service.conf_directory_doc.patch | 19 | ||||
-rw-r--r-- | debian/patches/54-debian-alternatives-for-external-tools.patch | 28 | ||||
-rw-r--r-- | debian/patches/70-history | 13 | ||||
-rw-r--r-- | debian/patches/autoconf2.69 | 7 | ||||
-rw-r--r-- | debian/patches/extension_destdir | 270 | ||||
-rw-r--r-- | debian/patches/filter-debug-prefix-map | 44 | ||||
-rw-r--r-- | debian/patches/focal-arm64-outline-atomics | 23 | ||||
-rw-r--r-- | debian/patches/hurd-iovec | 26 | ||||
-rw-r--r-- | debian/patches/jit-s390x | 96 | ||||
-rw-r--r-- | debian/patches/libpgport-pkglibdir | 91 | ||||
-rw-r--r-- | debian/patches/pgstat-report-conflicts-immediately.patch | 37 | ||||
-rw-r--r-- | debian/patches/series | 15 | ||||
-rw-r--r-- | debian/patches/v8-0001-Fix-recovery-conflict-SIGUSR1-handling.patch | 557 |
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 + |