]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Split out recovery confing-writing code from pg_basebackup
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 25 Sep 2019 17:35:24 +0000 (14:35 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 25 Sep 2019 17:35:24 +0000 (14:35 -0300)
... into a new file, fe_utils/recovery_gen.c.

This can later be used by pg_rewind.

Authors: Paul Guo, Jimmy Yih, Ashwin Agrawal.  A few tweaks by Álvaro Herrera
Reviewed-by: Michaël Paquier
Discussion: https://postgr.es/m/CAEET0ZEffUkXc48pg2iqARQgGRYDiiVxDu+yYek_bTwJF+q=Uw@mail.gmail.com

src/bin/pg_basebackup/pg_basebackup.c
src/fe_utils/Makefile
src/fe_utils/recovery_gen.c [new file with mode: 0644]
src/include/fe_utils/recovery_gen.h [new file with mode: 0644]
src/tools/msvc/Mkvcbuild.pm

index 7986872f106c1232a3010767d586709e10d783fe..55ef13926da67620d5e1aa6844921e33297bf5ce 100644 (file)
@@ -31,6 +31,7 @@
 #include "common/file_utils.h"
 #include "common/logging.h"
 #include "common/string.h"
+#include "fe_utils/recovery_gen.h"
 #include "fe_utils/string_utils.h"
 #include "getopt_long.h"
 #include "libpq-fe.h"
@@ -67,11 +68,6 @@ 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
  */
@@ -147,8 +143,6 @@ static void progress_report(int tablespacenum, const char *filename, bool force)
 
 static void ReceiveTarFile(PGconn *conn, PGresult *res, int rownum);
 static void ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum);
-static void GenerateRecoveryConf(PGconn *conn);
-static void WriteRecoveryConf(void);
 static void BaseBackup(void);
 
 static bool reached_end_position(XLogRecPtr segendpos, uint32 timeline,
@@ -1629,7 +1623,7 @@ ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum)
                PQfreemem(copybuf);
 
        if (basetablespace && writerecoveryconf)
-               WriteRecoveryConf();
+               WriteRecoveryConfig(conn, basedir, recoveryconfcontents);
 
        /*
         * No data is synced here, everything is done for all tablespaces at the
@@ -1637,156 +1631,6 @@ ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum)
         */
 }
 
-/*
- * Escape a string so that it can be used as a value in a key-value pair
- * a configuration file.
- */
-static char *
-escape_quotes(const char *src)
-{
-       char       *result = escape_single_quotes_ascii(src);
-
-       if (!result)
-       {
-               pg_log_error("out of memory");
-               exit(1);
-       }
-       return result;
-}
-
-/*
- * Create a configuration file in memory using a PQExpBuffer
- */
-static void
-GenerateRecoveryConf(PGconn *conn)
-{
-       PQconninfoOption *connOptions;
-       PQconninfoOption *option;
-       PQExpBufferData conninfo_buf;
-       char       *escaped;
-
-       recoveryconfcontents = createPQExpBuffer();
-       if (!recoveryconfcontents)
-       {
-               pg_log_error("out of memory");
-               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)
-       {
-               pg_log_error("out of memory");
-               exit(1);
-       }
-
-       initPQExpBuffer(&conninfo_buf);
-       for (option = connOptions; option && option->keyword; option++)
-       {
-               /* Omit empty settings and those libpqwalreceiver overrides. */
-               if (strcmp(option->keyword, "replication") == 0 ||
-                       strcmp(option->keyword, "dbname") == 0 ||
-                       strcmp(option->keyword, "fallback_application_name") == 0 ||
-                       (option->val == NULL) ||
-                       (option->val != NULL && option->val[0] == '\0'))
-                       continue;
-
-               /* Separate key-value pairs with spaces */
-               if (conninfo_buf.len != 0)
-                       appendPQExpBufferChar(&conninfo_buf, ' ');
-
-               /*
-                * Write "keyword=value" pieces, the value string is escaped and/or
-                * quoted if necessary.
-                */
-               appendPQExpBuffer(&conninfo_buf, "%s=", option->keyword);
-               appendConnStrVal(&conninfo_buf, option->val);
-       }
-
-       /*
-        * Escape the connection string, so that it can be put in the config file.
-        * Note that this is different from the escaping of individual connection
-        * options above!
-        */
-       escaped = escape_quotes(conninfo_buf.data);
-       appendPQExpBuffer(recoveryconfcontents, "primary_conninfo = '%s'\n", escaped);
-       free(escaped);
-
-       if (replication_slot)
-       {
-               /* unescaped: ReplicationSlotValidateName allows [a-z0-9_] only */
-               appendPQExpBuffer(recoveryconfcontents, "primary_slot_name = '%s'\n",
-                                                 replication_slot);
-       }
-
-       if (PQExpBufferBroken(recoveryconfcontents) ||
-               PQExpBufferDataBroken(conninfo_buf))
-       {
-               pg_log_error("out of memory");
-               exit(1);
-       }
-
-       termPQExpBuffer(&conninfo_buf);
-
-       PQconninfoFree(connOptions);
-}
-
-
-/*
- * Write the configuration file into the directory specified in basedir,
- * 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;
-
-       if (PQserverVersion(conn) < MINIMUM_VERSION_FOR_RECOVERY_GUC)
-               is_recovery_guc_supported = false;
-
-       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)
-       {
-               pg_log_error("could not open file \"%s\": %m", filename);
-               exit(1);
-       }
-
-       if (fwrite(recoveryconfcontents->data, recoveryconfcontents->len, 1, cf) != 1)
-       {
-               pg_log_error("could not write to file \"%s\": %m", filename);
-               exit(1);
-       }
-
-       fclose(cf);
-
-       if (is_recovery_guc_supported)
-       {
-               snprintf(filename, MAXPGPATH, "%s/%s", basedir, "standby.signal");
-               cf = fopen(filename, "w");
-               if (cf == NULL)
-               {
-                       pg_log_error("could not create file \"%s\": %m", filename);
-                       exit(1);
-               }
-
-               fclose(cf);
-       }
-}
-
 
 static void
 BaseBackup(void)
@@ -1843,7 +1687,7 @@ BaseBackup(void)
         * Build contents of configuration file if requested
         */
        if (writerecoveryconf)
-               GenerateRecoveryConf(conn);
+               recoveryconfcontents = GenerateRecoveryConfig(conn, replication_slot);
 
        /*
         * Run IDENTIFY_SYSTEM so we can get the timeline
index 7d738003237161b097d8d8ea83d41f2e1c43feb0..f2e516a2aa32c3b0ed9482927c0f3dd651eec783 100644 (file)
@@ -19,7 +19,8 @@ include $(top_builddir)/src/Makefile.global
 
 override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS)
 
-OBJS = mbprint.o print.o psqlscan.o simple_list.o string_utils.o conditional.o
+OBJS = conditional.o mbprint.o print.o psqlscan.o recovery_gen.o \
+       simple_list.o string_utils.o
 
 all: libpgfeutils.a
 
diff --git a/src/fe_utils/recovery_gen.c b/src/fe_utils/recovery_gen.c
new file mode 100644 (file)
index 0000000..6641f95
--- /dev/null
@@ -0,0 +1,176 @@
+/*-------------------------------------------------------------------------
+ *
+ * recovery_gen.c
+ *             Generator for recovery configuration
+ *
+ * Portions Copyright (c) 2011-2019, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include "common/logging.h"
+#include "fe_utils/string_utils.h"
+#include "fe_utils/recovery_gen.h"
+
+
+static char *escape_quotes(const char *src);
+
+/*
+ * Write recovery configuration contents into a fresh PQExpBuffer, and
+ * return it.
+ */
+PQExpBuffer
+GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
+{
+       PQconninfoOption *connOptions;
+       PQExpBufferData conninfo_buf;
+       char       *escaped;
+       PQExpBuffer contents;
+
+       Assert(pgconn != NULL);
+
+       contents = createPQExpBuffer();
+       if (!contents)
+       {
+               pg_log_error("out of memory");
+               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(pgconn) < MINIMUM_VERSION_FOR_RECOVERY_GUC)
+               appendPQExpBufferStr(contents, "standby_mode = 'on'\n");
+
+       connOptions = PQconninfo(pgconn);
+       if (connOptions == NULL)
+       {
+               pg_log_error("out of memory");
+               exit(1);
+       }
+
+       initPQExpBuffer(&conninfo_buf);
+       for (PQconninfoOption *opt = connOptions; opt && opt->keyword; opt++)
+       {
+               /* Omit empty settings and those libpqwalreceiver overrides. */
+               if (strcmp(opt->keyword, "replication") == 0 ||
+                       strcmp(opt->keyword, "dbname") == 0 ||
+                       strcmp(opt->keyword, "fallback_application_name") == 0 ||
+                       (opt->val == NULL) ||
+                       (opt->val != NULL && opt->val[0] == '\0'))
+                       continue;
+
+               /* Separate key-value pairs with spaces */
+               if (conninfo_buf.len != 0)
+                       appendPQExpBufferChar(&conninfo_buf, ' ');
+
+               /*
+                * Write "keyword=value" pieces, the value string is escaped and/or
+                * quoted if necessary.
+                */
+               appendPQExpBuffer(&conninfo_buf, "%s=", opt->keyword);
+               appendConnStrVal(&conninfo_buf, opt->val);
+       }
+       if (PQExpBufferDataBroken(conninfo_buf))
+       {
+               pg_log_error("out of memory");
+               exit(1);
+       }
+
+       /*
+        * Escape the connection string, so that it can be put in the config file.
+        * Note that this is different from the escaping of individual connection
+        * options above!
+        */
+       escaped = escape_quotes(conninfo_buf.data);
+       termPQExpBuffer(&conninfo_buf);
+       appendPQExpBuffer(contents, "primary_conninfo = '%s'\n", escaped);
+       free(escaped);
+
+       if (replication_slot)
+       {
+               /* unescaped: ReplicationSlotValidateName allows [a-z0-9_] only */
+               appendPQExpBuffer(contents, "primary_slot_name = '%s'\n",
+                                                 replication_slot);
+       }
+
+       if (PQExpBufferBroken(contents))
+       {
+               pg_log_error("out of memory");
+               exit(1);
+       }
+
+       PQconninfoFree(connOptions);
+
+       return contents;
+}
+
+/*
+ * Write the configuration file in the directory specified in target_dir,
+ * with the contents already collected in memory appended.  Then write
+ * the signal file into the target_dir.  If the server does not support
+ * recovery parameters as GUCs, the signal file is not necessary, and
+ * configuration is written to recovery.conf.
+ */
+void
+WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)
+{
+       char            filename[MAXPGPATH];
+       FILE       *cf;
+       bool            use_recovery_conf;
+
+       Assert(pgconn != NULL);
+
+       use_recovery_conf =
+               PQserverVersion(pgconn) < MINIMUM_VERSION_FOR_RECOVERY_GUC;
+
+       snprintf(filename, MAXPGPATH, "%s/%s", target_dir,
+                        use_recovery_conf ? "recovery.conf" : "postgresql.auto.conf");
+
+       cf = fopen(filename, use_recovery_conf ? "a" : "w");
+       if (cf == NULL)
+       {
+               pg_log_error("could not open file \"%s\": %m", filename);
+               exit(1);
+       }
+
+       if (fwrite(contents->data, contents->len, 1, cf) != 1)
+       {
+               pg_log_error("could not write to file \"%s\": %m", filename);
+               exit(1);
+       }
+
+       fclose(cf);
+
+       if (!use_recovery_conf)
+       {
+               snprintf(filename, MAXPGPATH, "%s/%s", target_dir, "standby.signal");
+               cf = fopen(filename, "w");
+               if (cf == NULL)
+               {
+                       pg_log_error("could not create file \"%s\": %m", filename);
+                       exit(1);
+               }
+
+               fclose(cf);
+       }
+}
+
+/*
+ * Escape a string so that it can be used as a value in a key-value pair
+ * a configuration file.
+ */
+static char *
+escape_quotes(const char *src)
+{
+       char       *result = escape_single_quotes_ascii(src);
+
+       if (!result)
+       {
+               pg_log_error("out of memory");
+               exit(1);
+       }
+       return result;
+}
diff --git a/src/include/fe_utils/recovery_gen.h b/src/include/fe_utils/recovery_gen.h
new file mode 100644 (file)
index 0000000..8b15307
--- /dev/null
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * Generator for recovery configuration
+ *
+ * Portions Copyright (c) 2011-2019, PostgreSQL Global Development Group
+ *
+ * src/include/fe_utils/recovery_gen.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef RECOVERY_GEN_H
+#define RECOVERY_GEN_H
+
+#include "libpq-fe.h"
+#include "pqexpbuffer.h"
+
+/*
+ * recovery configuration is part of postgresql.conf in version 12 and up, and
+ * in recovery.conf before that.
+ */
+#define MINIMUM_VERSION_FOR_RECOVERY_GUC 120000
+
+extern PQExpBuffer GenerateRecoveryConfig(PGconn *pgconn,
+                                                                                 char *pg_replication_slot);
+extern void WriteRecoveryConfig(PGconn *pgconn, char *target_dir,
+                                                               PQExpBuffer contents);
+
+#endif                                                 /* RECOVERY_GEN_H */
index 00b2bc25e50524bc483a9dedf7afc9b57843fcee..7a103e61406e53e691ffea2dea760e59be5257d6 100644 (file)
@@ -142,7 +142,8 @@ sub mkvcbuild
        our @pgcommonbkndfiles = @pgcommonallfiles;
 
        our @pgfeutilsfiles = qw(
-         conditional.c mbprint.c print.c psqlscan.l psqlscan.c simple_list.c string_utils.c);
+         conditional.c mbprint.c print.c psqlscan.l psqlscan.c
+         simple_list.c string_utils.c recovery_gen.c);
 
        $libpgport = $solution->AddProject('libpgport', 'lib', 'misc');
        $libpgport->AddDefine('FRONTEND');