0001-Auto-generate-recovery-conf-at-the-end-of-pg_re.patch

application/octet-stream

Filename: 0001-Auto-generate-recovery-conf-at-the-end-of-pg_re.patch
Type: application/octet-stream
Part: 1
Message: Two pg_rewind patches (auto generate recovery conf and ensure clean shutdown)

Patch

Format: format-patch
Series: patch 0001
Subject: Auto generate recovery conf at the end of pg_rewind
File+
src/bin/pg_rewind/fetch.h 3 0
src/bin/pg_rewind/libpq_fetch.c 138 0
src/bin/pg_rewind/Makefile 1 1
src/bin/pg_rewind/pg_rewind.c 22 1
src/bin/pg_rewind/pg_rewind.h 2 0
From abb1068b10a5f558623d6fc8f214c29d09f6bb28 Mon Sep 17 00:00:00 2001
From: Jimmy Yih <jyih@pivotal.io>
Date: Sun, 4 Nov 2018 22:54:53 -0800
Subject: [PATCH] Auto generate recovery conf at the end of pg_rewind

---
 src/bin/pg_rewind/Makefile      |   2 +-
 src/bin/pg_rewind/fetch.h       |   3 +
 src/bin/pg_rewind/libpq_fetch.c | 138 ++++++++++++++++++++++++++++++++
 src/bin/pg_rewind/pg_rewind.c   |  23 +++++-
 src/bin/pg_rewind/pg_rewind.h   |   2 +
 6 files changed, 166 insertions(+), 3 deletions(-)

diff --git a/src/bin/pg_rewind/Makefile b/src/bin/pg_rewind/Makefile
index 04f3b8f520..3b7f1ce177 100644
--- a/src/bin/pg_rewind/Makefile
+++ b/src/bin/pg_rewind/Makefile
@@ -16,7 +16,7 @@ top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
 override CPPFLAGS := -I$(libpq_srcdir) -DFRONTEND $(CPPFLAGS)
-LDFLAGS_INTERNAL += $(libpq_pgport)
+LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport)
 
 OBJS	= pg_rewind.o parsexlog.o xlogreader.o datapagemap.o timeline.o \
 	fetch.o file_ops.o copy_fetch.o libpq_fetch.o filemap.o logging.o \
diff --git a/src/bin/pg_rewind/fetch.h b/src/bin/pg_rewind/fetch.h
index a694e8b157..7db9337ced 100644
--- a/src/bin/pg_rewind/fetch.h
+++ b/src/bin/pg_rewind/fetch.h
@@ -41,4 +41,7 @@ extern void copy_executeFileMap(filemap_t *map);
 typedef void (*process_file_callback_t) (const char *path, file_type_t type, size_t size, const char *link_target);
 extern void traverse_datadir(const char *datadir, process_file_callback_t callback);
 
+extern void GenerateRecoveryConf(void);
+extern void WriteRecoveryConf(void);
+
 #endif							/* FETCH_H */
diff --git a/src/bin/pg_rewind/libpq_fetch.c b/src/bin/pg_rewind/libpq_fetch.c
index d06e277432..47a3f68c93 100644
--- a/src/bin/pg_rewind/libpq_fetch.c
+++ b/src/bin/pg_rewind/libpq_fetch.c
@@ -24,10 +24,15 @@
 #include "libpq-fe.h"
 #include "catalog/pg_type_d.h"
 #include "fe_utils/connect.h"
+#include "fe_utils/string_utils.h"
 #include "port/pg_bswap.h"
+#include "pqexpbuffer.h"
 
 static PGconn *conn = NULL;
 
+/* Contents of configuration to be generated */
+static PQExpBuffer recoveryconfcontents = NULL;
+
 /*
  * Files are fetched max CHUNKSIZE bytes at a time.
  *
@@ -526,3 +531,136 @@ execute_pagemap(datapagemap_t *pagemap, const char *path)
 	}
 	pg_free(iter);
 }
+
+/*
+ * 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)
+	{
+		fprintf(stderr, _("%s: out of memory\n"), progname);
+		exit(1);
+	}
+	return result;
+}
+
+/*
+ * Create a configuration file in memory using a PQExpBuffer
+ */
+void
+GenerateRecoveryConf(void)
+{
+	PQconninfoOption *connOptions;
+	PQconninfoOption *option;
+	PQExpBufferData conninfo_buf;
+	char	   *escaped;
+
+	recoveryconfcontents = createPQExpBuffer();
+	if (!recoveryconfcontents)
+	{
+		fprintf(stderr, _("%s: out of memory\n"), progname);
+		exit(1);
+	}
+
+	connOptions = PQconninfo(conn);
+	if (connOptions == NULL)
+	{
+		fprintf(stderr, _("%s: out of memory\n"), progname);
+		exit(1);
+	}
+
+	initPQExpBuffer(&conninfo_buf);
+	for (option = connOptions; option && option->keyword; option++)
+	{
+		/*
+		 * Do not emit this setting if: - the setting is "replication",
+		 * "dbname" or "fallback_application_name", since these would be
+		 * overridden by the libpqwalreceiver module anyway. - not set or
+		 * empty.
+		 */
+		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 (PQExpBufferBroken(recoveryconfcontents) ||
+		PQExpBufferDataBroken(conninfo_buf))
+	{
+		fprintf(stderr, _("%s: out of memory\n"), progname);
+		exit(1);
+	}
+
+	termPQExpBuffer(&conninfo_buf);
+
+	PQconninfoFree(connOptions);
+}
+
+
+/*
+ * Write the configuration file into the directory specified in datadir_target,
+ * with the contents already collected in memory.
+ * Then write the signal file into the datadir_target also.
+ */
+void
+WriteRecoveryConf(void)
+{
+	char		filename[MAXPGPATH];
+	FILE	   *cf;
+
+	snprintf(filename, MAXPGPATH, "%s/%s", datadir_target, "postgresql.auto.conf");
+
+	cf = fopen(filename, "a");
+	if (cf == NULL)
+	{
+		fprintf(stderr, _("%s: could not open file \"%s\": %s\n"), progname, filename, strerror(errno));
+		exit(1);
+	}
+
+	if (fwrite(recoveryconfcontents->data, recoveryconfcontents->len, 1, cf) != 1)
+	{
+		fprintf(stderr,
+				_("%s: could not write to file \"%s\": %s\n"),
+				progname, filename, strerror(errno));
+		exit(1);
+	}
+
+	fclose(cf);
+
+	snprintf(filename, MAXPGPATH, "%s/%s", datadir_target, "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);
+}
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index aa753bb315..b6b288bbfe 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -45,6 +45,8 @@ static void findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex);
 static ControlFileData ControlFile_target;
 static ControlFileData ControlFile_source;
 
+static bool writerecoveryconf = false;
+
 const char *progname;
 int			WalSegSz;
 
@@ -71,6 +73,7 @@ usage(const char *progname)
 	printf(_("  -D, --target-pgdata=DIRECTORY  existing data directory to modify\n"));
 	printf(_("      --source-pgdata=DIRECTORY  source data directory to synchronize with\n"));
 	printf(_("      --source-server=CONNSTR    source server to synchronize with\n"));
+	printf(_("  -R, --write-recovery-conf      write configuration for replication\n"));
 	printf(_("  -n, --dry-run                  stop before modifying anything\n"));
 	printf(_("  -N, --no-sync                  do not wait for changes to be written\n"));
 	printf(_("                                 safely to disk\n"));
@@ -88,6 +91,7 @@ main(int argc, char **argv)
 	static struct option long_options[] = {
 		{"help", no_argument, NULL, '?'},
 		{"target-pgdata", required_argument, NULL, 'D'},
+		{"write-recovery-conf", no_argument, NULL, 'R'},
 		{"source-pgdata", required_argument, NULL, 1},
 		{"source-server", required_argument, NULL, 2},
 		{"version", no_argument, NULL, 'V'},
@@ -129,7 +133,7 @@ main(int argc, char **argv)
 		}
 	}
 
-	while ((c = getopt_long(argc, argv, "D:nNP", long_options, &option_index)) != -1)
+	while ((c = getopt_long(argc, argv, "D:nNPR", long_options, &option_index)) != -1)
 	{
 		switch (c)
 		{
@@ -149,6 +153,10 @@ main(int argc, char **argv)
 				do_sync = false;
 				break;
 
+			case 'R':
+				writerecoveryconf = true;
+				break;
+
 			case 3:
 				debug = true;
 				break;
@@ -291,6 +299,13 @@ main(int argc, char **argv)
 	if (!rewind_needed)
 	{
 		printf(_("no rewind required\n"));
+
+		if (writerecoveryconf && connstr_source)
+		{
+			GenerateRecoveryConf();
+			WriteRecoveryConf();
+		}
+
 		exit(0);
 	}
 
@@ -382,6 +397,12 @@ main(int argc, char **argv)
 	pg_log(PG_PROGRESS, "syncing target data directory\n");
 	syncTargetDirectory();
 
+	if (writerecoveryconf && connstr_source)
+	{
+		GenerateRecoveryConf();
+		WriteRecoveryConf();
+	}
+
 	printf(_("Done!\n"));
 
 	return 0;
diff --git a/src/bin/pg_rewind/pg_rewind.h b/src/bin/pg_rewind/pg_rewind.h
index 83b2898b8b..0ce9ba0426 100644
--- a/src/bin/pg_rewind/pg_rewind.h
+++ b/src/bin/pg_rewind/pg_rewind.h
@@ -30,6 +30,8 @@ extern int	WalSegSz;
 extern TimeLineHistoryEntry *targetHistory;
 extern int	targetNentries;
 
+extern const char *progname;
+
 /* in parsexlog.c */
 extern void extractPageMap(const char *datadir, XLogRecPtr startpoint,
 			   int tliIndex, XLogRecPtr endpoint);
-- 
2.17.2