From beeb8e2e0717065296dc7b32daba2d66f0f931dd Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Fri, 8 Mar 2019 10:14:03 +0900 Subject: [PATCH] Fix compatibility of pg_basebackup -R with 11 and older versions MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit When 2dedf4d9 has integrated recovery.conf into postgresql.conf, it also changed pg_basebackup -R in the way recovery configuration is generated. However this implementation forgot the fact that pg_basebackup needs to keep compatibility with older server versions as well. Reported-by: Devrim Gündüz Author: Sergei Kornilov, Michael Paquier Discussion: https://postgr.es/m/3458f7cd12d74acd90180a671c8d5a081d60e162.camel@gunduz.org --- src/bin/pg_basebackup/pg_basebackup.c | 109 +++++++++++++++++++------- 1 file changed, 82 insertions(+), 27 deletions(-) diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c index 3d2d4cd0b9..916371232b 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -66,6 +66,11 @@ typedef struct TablespaceList */ #define MINIMUM_VERSION_FOR_TEMP_SLOTS 100000 +/* + * recovery.conf is integrated into postgresql.conf from version 12. + */ +#define MINIMUM_VERSION_FOR_RECOVERY_GUC 120000 + /* * Different ways to include WAL */ @@ -974,6 +979,7 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum) bool basetablespace = PQgetisnull(res, rownum, 0); bool in_tarhdr = true; bool skip_file = false; + bool is_recovery_guc_supported = true; bool is_postgresql_auto_conf = false; bool found_postgresql_auto_conf = false; int file_padding_len = 0; @@ -984,6 +990,10 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum) gzFile ztarfile = NULL; #endif + /* recovery.conf is integrated into postgresql.conf in 12 and newer */ + if (PQserverVersion(conn) < MINIMUM_VERSION_FOR_RECOVERY_GUC) + is_recovery_guc_supported = false; + if (basetablespace) { /* @@ -1130,11 +1140,20 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum) { char header[512]; - if (!found_postgresql_auto_conf) + /* + * If postgresql.auto.conf has not been found in the streamed + * data, add recovery configuration to postgresql.auto.conf if + * recovery parameters are GUCs. If the instance connected to + * is older than 12, create recovery.conf with this data + * otherwise. + */ + if (!found_postgresql_auto_conf || !is_recovery_guc_supported) { int padding; - tarCreateHeader(header, "postgresql.auto.conf", NULL, + tarCreateHeader(header, + is_recovery_guc_supported ? "postgresql.auto.conf" : "recovery.conf", + NULL, recoveryconfcontents->len, pg_file_create_mode, 04000, 02000, time(NULL)); @@ -1142,18 +1161,26 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum) padding = ((recoveryconfcontents->len + 511) & ~511) - recoveryconfcontents->len; WRITE_TAR_DATA(header, sizeof(header)); - WRITE_TAR_DATA(recoveryconfcontents->data, recoveryconfcontents->len); + WRITE_TAR_DATA(recoveryconfcontents->data, + recoveryconfcontents->len); if (padding) WRITE_TAR_DATA(zerobuf, padding); } - tarCreateHeader(header, "standby.signal", NULL, - 0, /* zero-length file */ - pg_file_create_mode, 04000, 02000, - time(NULL)); + /* + * standby.signal is supported only if recovery parameters are + * GUCs. + */ + if (is_recovery_guc_supported) + { + tarCreateHeader(header, "standby.signal", NULL, + 0, /* zero-length file */ + pg_file_create_mode, 04000, 02000, + time(NULL)); - WRITE_TAR_DATA(header, sizeof(header)); - WRITE_TAR_DATA(zerobuf, 511); + WRITE_TAR_DATA(header, sizeof(header)); + WRITE_TAR_DATA(zerobuf, 511); + } } /* 2 * 512 bytes empty data at end of file */ @@ -1252,16 +1279,24 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum) * We have the complete header structure in tarhdr, * look at the file metadata: we may want append * recovery info into postgresql.auto.conf and skip - * standby.signal file. In both cases we must - * calculate tar padding + * standby.signal file if recovery parameters are + * integrated as GUCs, and recovery.conf otherwise. In + * both cases we must calculate tar padding. */ - skip_file = (strcmp(&tarhdr[0], "standby.signal") == 0); - is_postgresql_auto_conf = (strcmp(&tarhdr[0], "postgresql.auto.conf") == 0); + if (is_recovery_guc_supported) + { + skip_file = (strcmp(&tarhdr[0], "standby.signal") == 0); + is_postgresql_auto_conf = (strcmp(&tarhdr[0], "postgresql.auto.conf") == 0); + } + else + skip_file = (strcmp(&tarhdr[0], "recovery.conf") == 0); filesz = read_tar_number(&tarhdr[124], 12); file_padding_len = ((filesz + 511) & ~511) - filesz; - if (is_postgresql_auto_conf && writerecoveryconf) + if (is_recovery_guc_supported && + is_postgresql_auto_conf && + writerecoveryconf) { /* replace tar header */ char header[512]; @@ -1313,7 +1348,9 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum) pos += bytes2write; filesz -= bytes2write; } - else if (is_postgresql_auto_conf && writerecoveryconf) + else if (is_recovery_guc_supported && + is_postgresql_auto_conf && + writerecoveryconf) { /* append recovery config to postgresql.auto.conf */ int padding; @@ -1690,6 +1727,13 @@ GenerateRecoveryConf(PGconn *conn) exit(1); } + /* + * In PostgreSQL 12 and newer versions, standby_mode is gone, replaced by + * standby.signal to trigger a standby state at recovery. + */ + if (PQserverVersion(conn) < MINIMUM_VERSION_FOR_RECOVERY_GUC) + appendPQExpBufferStr(recoveryconfcontents, "standby_mode = 'on'\n"); + connOptions = PQconninfo(conn); if (connOptions == NULL) { @@ -1756,21 +1800,29 @@ GenerateRecoveryConf(PGconn *conn) /* * Write the configuration file into the directory specified in basedir, - * with the contents already collected in memory. - * Then write the signal file into the basedir also. + * with the contents already collected in memory appended. Then write + * the signal file into the basedir. If the server does not support + * recovery parameters as GUCs, the signal file is not necessary, and + * configuration is written to recovery.conf. */ static void WriteRecoveryConf(void) { char filename[MAXPGPATH]; FILE *cf; + bool is_recovery_guc_supported = true; - snprintf(filename, MAXPGPATH, "%s/%s", basedir, "postgresql.auto.conf"); + if (PQserverVersion(conn) < MINIMUM_VERSION_FOR_RECOVERY_GUC) + is_recovery_guc_supported = false; - cf = fopen(filename, "a"); + snprintf(filename, MAXPGPATH, "%s/%s", basedir, + is_recovery_guc_supported ? "postgresql.auto.conf" : "recovery.conf"); + + cf = fopen(filename, is_recovery_guc_supported ? "a" : "w"); if (cf == NULL) { - fprintf(stderr, _("%s: could not open file \"%s\": %s\n"), progname, filename, strerror(errno)); + fprintf(stderr, _("%s: could not open file \"%s\": %s\n"), + progname, filename, strerror(errno)); exit(1); } @@ -1784,15 +1836,18 @@ WriteRecoveryConf(void) fclose(cf); - snprintf(filename, MAXPGPATH, "%s/%s", basedir, "standby.signal"); - cf = fopen(filename, "w"); - if (cf == NULL) + if (is_recovery_guc_supported) { - fprintf(stderr, _("%s: could not create file \"%s\": %s\n"), progname, filename, strerror(errno)); - exit(1); - } + snprintf(filename, MAXPGPATH, "%s/%s", basedir, "standby.signal"); + cf = fopen(filename, "w"); + if (cf == NULL) + { + fprintf(stderr, _("%s: could not create file \"%s\": %s\n"), progname, filename, strerror(errno)); + exit(1); + } - fclose(cf); + fclose(cf); + } } -- 2.40.0