2013-01-02 10:37 keltezéssel, Boszormenyi Zoltan írta:
2013-01-02 10:12 keltezéssel, Magnus Hagander írta:
On Wed, Jan 2, 2013 at 9:59 AM, Boszormenyi Zoltan <z...@cybertec.at <mailto:z...@cybertec.at>> wrote:

    2013-01-02 01:24 keltezéssel, Tom Lane írta:

        Boszormenyi Zoltan <z...@cybertec.at <mailto:z...@cybertec.at>> writes:

            2013-01-01 17:18 keltezéssel, Magnus Hagander írta:

                That way we can get around the whole need for changing memory
                allocation across all the
                frontends, no? Like the attached.

            Sure it's simpler but then the consistent look of the code is lost.
            What about the other patch to unify pg_malloc and friends?
            Basically all client code boils down to
                  fprintf(stderr, ...)
            in different disguise in their error reporting, so that patch can
            also be simplified but it seems that the atexit() - either 
explicitly
            or hidden behind InitPostgresFrontend() - cannot be avoided.

        Meh.  I find it seriously wrongheaded that something as minor as an
        escape_quotes() function should get to dictate both malloc wrappers
        and error recovery handling throughout every program that might use it.


    Actually, the unification of pg_malloc and friends wasn't dictated
    by this little code, it was just that pg_basebackup doesn't provide
    a pg_malloc implementation (only pg_malloc0) that is used by
    initdb's escape_quotes() function. Then I noticed how wide these
    almost identical functions have spread into client apps already.

    I would say this unification patch is completely orthogonal to
    the patch in $SUBJECT. I will post it in a different thread if it's
    wanted at all. The extra atexit() handler is not needed if a simple
    fprintf(stderr, ...) error reporting is enough in all clients.
    As far as I saw, all clients do exactly this but some of them hide
    this behind #define's.


Please do keep that one separate - let's avoid unnecessary feature-creep, whether it's good or bad features.

        I like Magnus' version a lot better than that idea.


    OK, I will post the core patch building on his code.


Thanks.

        A bigger issue that I notice with this code is that it's only correct in
        backend-safe encodings, as the comment mentions.  If we're going to be
        putting it into frontend programs, how safe is that going to be?

                                regards, tom lane


    The question in a different form is: does PostgreSQL support
    non-ASCII-safe encodings at all (or on the client side)? Forgive
    my ignorance and enlighten me: how many such encodings
    exist besides EBCDIC? Is UTF-16 non-ASCII-safe?


We do. See http://www.postgresql.org/docs/9.2/static/multibyte.html.

There are quite a few far-eastern encodings that aren't available as server encondings, and I believe it's all for this reason.

I see, thanks.

That said, do we need to care *in this specific case*? We use it in initdb to parse config strings, I believe. And we'd use it to parse a conninfo string in pg_basebackup, correct?

Correct.

Perhaps all we need to do is to clearly comment that it doesn't work with non-ascii safe encodings, or rename it to indicate that it's limited in this?

If you send a new patch with the function renamed accordingly, I will modify
my code to use it.

Attached is the quotes-v2 patch, the function is renamed and
the comment is modified plus the pg_basebackup v21 patch
that uses this function.

Best regards,
Zoltán Böszörményi

--
----------------------------------
Zoltán Böszörményi
Cybertec Schönig & Schönig GmbH
Gröhrmühlgasse 26
A-2700 Wiener Neustadt, Austria
Web: http://www.postgresql-support.de
     http://www.postgresql.at/

diff -durpN postgresql/src/bin/initdb/initdb.c postgresql.1/src/bin/initdb/initdb.c
--- postgresql/src/bin/initdb/initdb.c	2013-01-02 09:19:03.855521804 +0100
+++ postgresql.1/src/bin/initdb/initdb.c	2013-01-02 11:26:10.008494149 +0100
@@ -354,6 +354,18 @@ pg_strdup(const char *s)
 	return result;
 }
 
+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;
+}
+
 /*
  * make a copy of the array of lines, with token replaced by replacement
  * the first time it occurs on each line.
@@ -2415,35 +2427,6 @@ check_ok(void)
 	}
 }
 
-/*
- * Escape (by doubling) any single quotes or backslashes in given string
- *
- * Note: this is used to process both postgresql.conf entries and SQL
- * string literals.  Since postgresql.conf strings are defined to treat
- * backslashes as escapes, we have to double backslashes here.	Hence,
- * when using this for a SQL string literal, use E'' syntax.
- *
- * We do not need to worry about encoding considerations because all
- * valid backend encodings are ASCII-safe.
- */
-static char *
-escape_quotes(const char *src)
-{
-	int			len = strlen(src),
-				i,
-				j;
-	char	   *result = pg_malloc(len * 2 + 1);
-
-	for (i = 0, j = 0; i < len; i++)
-	{
-		if (SQL_STR_DOUBLE(src[i], true))
-			result[j++] = src[i];
-		result[j++] = src[i];
-	}
-	result[j] = '\0';
-	return result;
-}
-
 /* Hack to suppress a warning about %x from some versions of gcc */
 static inline size_t
 my_strftime(char *s, size_t max, const char *fmt, const struct tm * tm)
diff -durpN postgresql/src/include/port.h postgresql.1/src/include/port.h
--- postgresql/src/include/port.h	2013-01-02 09:19:03.902522322 +0100
+++ postgresql.1/src/include/port.h	2013-01-02 11:26:32.695654454 +0100
@@ -469,4 +469,7 @@ extern int	pg_mkdir_p(char *path, int om
 void		tarCreateHeader(char *h, const char *filename, const char *linktarget, size_t size, mode_t mode, uid_t uid, gid_t gid, time_t mtime);
 int			tarChecksum(char *header);
 
+/* port/quotes.c */
+extern char *escape_single_quotes_ascii(const char *src);
+
 #endif   /* PG_PORT_H */
diff -durpN postgresql/src/port/Makefile postgresql.1/src/port/Makefile
--- postgresql/src/port/Makefile	2013-01-02 09:19:03.935522680 +0100
+++ postgresql.1/src/port/Makefile	2013-01-02 10:43:57.358410221 +0100
@@ -32,7 +32,7 @@ LIBS += $(PTHREAD_LIBS)
 
 OBJS = $(LIBOBJS) chklocale.o dirmod.o erand48.o exec.o fls.o inet_net_ntop.o \
 	noblock.o path.o pgcheckdir.o pg_crc.o pgmkdirp.o pgsleep.o \
-	pgstrcasecmp.o qsort.o qsort_arg.o sprompt.o tar.o thread.o
+	pgstrcasecmp.o quotes.o qsort.o qsort_arg.o sprompt.o tar.o thread.o
 
 # foo_srv.o and foo.o are both built from foo.c, but only foo.o has -DFRONTEND
 OBJS_SRV = $(OBJS:%.o=%_srv.o)
diff -durpN postgresql/src/port/quotes.c postgresql.1/src/port/quotes.c
--- postgresql/src/port/quotes.c	1970-01-01 01:00:00.000000000 +0100
+++ postgresql.1/src/port/quotes.c	2013-01-02 11:25:46.616328180 +0100
@@ -0,0 +1,37 @@
+#include "c.h"
+
+/*
+ * Escape (by doubling) any single quotes or backslashes in given string
+ *
+ * Note: this is used to process both postgresql.conf entries and SQL
+ * string literals.  Since postgresql.conf strings are defined to treat
+ * backslashes as escapes, we have to double backslashes here.	Hence,
+ * when using this for a SQL string literal, use E'' syntax.
+ *
+ * We do not care about encoding considerations because this function
+ * is only used for parsing or creating configuration files.
+ *
+ * Returns a malloced() string that it's the responsibility of the caller
+ * to free.
+ */
+char *
+escape_single_quotes_ascii(const char *src)
+{
+	int			len = strlen(src),
+				i,
+				j;
+	char	   *result = malloc(len * 2 + 1);
+
+	if (!result)
+		return NULL;
+
+	for (i = 0, j = 0; i < len; i++)
+	{
+		if (SQL_STR_DOUBLE(src[i], true))
+			result[j++] = src[i];
+		result[j++] = src[i];
+	}
+	result[j] = '\0';
+	return result;
+}
+
diff -durpN postgresql.1/doc/src/sgml/ref/pg_basebackup.sgml postgresql.2/doc/src/sgml/ref/pg_basebackup.sgml
--- postgresql.1/doc/src/sgml/ref/pg_basebackup.sgml	2012-11-08 13:13:04.151630632 +0100
+++ postgresql.2/doc/src/sgml/ref/pg_basebackup.sgml	2013-01-02 10:46:41.642608888 +0100
@@ -189,6 +189,21 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>-R</option></term>
+      <term><option>--write-recovery-conf</option></term>
+      <listitem>
+
+       <para>
+        Write a minimal recovery.conf into the output directory (or into
+        the base archive file if <option>--format=tar</option> was specified)
+        using the connection parameters from the command line to ease
+        setting up the standby.
+       </para>
+
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-x</option></term>
       <term><option>--xlog</option></term>
       <listitem>
diff -durpN postgresql.1/src/bin/pg_basebackup/pg_basebackup.c postgresql.2/src/bin/pg_basebackup/pg_basebackup.c
--- postgresql.1/src/bin/pg_basebackup/pg_basebackup.c	2013-01-02 09:19:03.856521815 +0100
+++ postgresql.2/src/bin/pg_basebackup/pg_basebackup.c	2013-01-02 11:29:05.353701197 +0100
@@ -13,12 +13,14 @@
 
 #include "postgres_fe.h"
 #include "libpq-fe.h"
+#include "pqexpbuffer.h"
 
 #include <unistd.h>
 #include <dirent.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <time.h>
 
 #ifdef HAVE_LIBZ
 #include <zlib.h>
@@ -40,6 +42,7 @@ int			compresslevel = 0;
 bool		includewal = false;
 bool		streamwal = false;
 bool		fastcheckpoint = false;
+bool		writerecoveryconf = false;
 int			standby_message_timeout = 10 * 1000;		/* 10 sec = default */
 
 /* Progress counters */
@@ -64,13 +67,18 @@ static int	has_xlogendptr = 0;
 static volatile LONG has_xlogendptr = 0;
 #endif
 
+PQExpBuffer rcExpBuf = NULL;
+
 /* Function headers */
 static void usage(void);
 static void verify_dir_is_empty_or_create(char *dirname);
 static void progress_report(int tablespacenum, const char *filename);
 
+static void scan_val(char *s, uint64 *val, unsigned int base, size_t len);
 static void ReceiveTarFile(PGconn *conn, PGresult *res, int rownum);
 static void ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum);
+static void CreateRecoveryConf(PGconn *conn);
+static void WriteRecoveryConf(void);
 static void BaseBackup(void);
 
 static bool reached_end_position(XLogRecPtr segendpos, uint32 timeline,
@@ -101,6 +109,8 @@ usage(void)
 	printf(_("\nOptions controlling the output:\n"));
 	printf(_("  -D, --pgdata=DIRECTORY receive base backup into directory\n"));
 	printf(_("  -F, --format=p|t       output format (plain (default), tar)\n"));
+	printf(_("  -R, --write-recovery-conf\n"
+			 "                         write recovery.conf after backup\n"));
 	printf(_("  -x, --xlog             include required WAL files in backup (fetch mode)\n"));
 	printf(_("  -X, --xlog-method=fetch|stream\n"
 			 "                         include required WAL files with specified method\n"));
@@ -446,6 +456,65 @@ progress_report(int tablespacenum, const
 
 
 /*
+ * Inverse of print_val()
+ */
+static void
+scan_val(char *s, uint64 *val, unsigned int base, size_t len)
+{
+	uint64			tmp = 0;
+	int			i;
+
+	for (i = 0; i < len; i++)
+	{
+		int		digit = s[i] - '0';
+
+		tmp = tmp * base + digit;
+	}
+
+	*val = tmp;
+}
+
+
+/*
+ * Write a piece of tar data
+ */
+static void
+writeTarData(
+#ifdef HAVE_LIBZ
+	gzFile ztarfile,
+#endif
+	FILE *tarfile, char *buf, int r, char *current_file)
+{
+#ifdef HAVE_LIBZ
+	if (ztarfile != NULL)
+	{
+		if (gzwrite(ztarfile, buf, r) != r)
+		{
+			fprintf(stderr,
+				_("%s: could not write to compressed file \"%s\": %s\n"),
+					progname, current_file, get_gz_error(ztarfile));
+			disconnect_and_exit(1);
+		}
+	}
+	else
+#endif
+	{
+		if (fwrite(buf, r, 1, tarfile) != 1)
+		{
+			fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"),
+					progname, current_file, strerror(errno));
+			disconnect_and_exit(1);
+		}
+	}
+}
+
+#ifdef HAVE_LIBZ
+#define WRITE_TAR_DATA(buf, sz)	writeTarData(ztarfile, tarfile, buf, sz, filename)
+#else
+#define WRITE_TAR_DATA(buf, sz)	writeTarData(tarfile, buf, sz, filename)
+#endif
+
+/*
  * Receive a tar format file from the connection to the server, and write
  * the data from this file directly into a tar file. If compression is
  * enabled, the data will be compressed while written to the file.
@@ -461,12 +530,17 @@ ReceiveTarFile(PGconn *conn, PGresult *r
 	char		filename[MAXPGPATH];
 	char	   *copybuf = NULL;
 	FILE	   *tarfile = NULL;
+	char		tarhdr[512];
+	bool		basetablespace = PQgetisnull(res, rownum, 0);
+	bool		in_tarhdr, skip_file;
+	size_t		tarhdrsz;
+	uint64		filesz;
 
 #ifdef HAVE_LIBZ
 	gzFile		ztarfile = NULL;
 #endif
 
-	if (PQgetisnull(res, rownum, 0))
+	if (basetablespace)
 	{
 		/*
 		 * Base tablespaces
@@ -578,6 +652,16 @@ ReceiveTarFile(PGconn *conn, PGresult *r
 		disconnect_and_exit(1);
 	}
 
+	/*
+	 * Initialize our variables for tracking
+	 * individual files inside the TAR stream.
+	 * For more detailed explanation, see below.
+	 */
+	in_tarhdr = true;
+	skip_file = false;
+	tarhdrsz = 0;
+	filesz = 0;
+
 	while (1)
 	{
 		int			r;
@@ -592,7 +676,8 @@ ReceiveTarFile(PGconn *conn, PGresult *r
 		if (r == -1)
 		{
 			/*
-			 * End of chunk. Close file (but not stdout).
+			 * End of chunk. Write recovery.conf into the tar file (if it
+			 * was requested) and close file (but not stdout).
 			 *
 			 * Also, write two completely empty blocks at the end of the tar
 			 * file, as required by some tar programs.
@@ -600,30 +685,30 @@ ReceiveTarFile(PGconn *conn, PGresult *r
 			char		zerobuf[1024];
 
 			MemSet(zerobuf, 0, sizeof(zerobuf));
-#ifdef HAVE_LIBZ
-			if (ztarfile != NULL)
-			{
-				if (gzwrite(ztarfile, zerobuf, sizeof(zerobuf)) !=
-					sizeof(zerobuf))
-				{
-					fprintf(stderr,
-					_("%s: could not write to compressed file \"%s\": %s\n"),
-							progname, filename, get_gz_error(ztarfile));
-					disconnect_and_exit(1);
-				}
-			}
-			else
-#endif
+
+			if (basetablespace && writerecoveryconf)
 			{
-				if (fwrite(zerobuf, sizeof(zerobuf), 1, tarfile) != 1)
-				{
-					fprintf(stderr,
-							_("%s: could not write to file \"%s\": %s\n"),
-							progname, filename, strerror(errno));
-					disconnect_and_exit(1);
-				}
+				char		header[512];
+				int		padding;
+
+				tarCreateHeader(header, "recovery.conf", NULL,
+									rcExpBuf->len,
+									0600, 04000, 02000,
+									time(NULL));
+
+				padding = ((rcExpBuf->len + 511) & ~511) - rcExpBuf->len;
+
+				WRITE_TAR_DATA(header, sizeof(header));
+				WRITE_TAR_DATA(rcExpBuf->data, rcExpBuf->len);
+				if (padding)
+					WRITE_TAR_DATA(zerobuf, padding);
+
+				if (verbose)
+					fprintf(stderr, _("%s: recovery.conf written into '%s'\n"), progname, filename);
 			}
 
+			WRITE_TAR_DATA(zerobuf, sizeof(zerobuf));
+
 #ifdef HAVE_LIBZ
 			if (ztarfile != NULL)
 			{
@@ -659,25 +744,124 @@ ReceiveTarFile(PGconn *conn, PGresult *r
 			disconnect_and_exit(1);
 		}
 
-#ifdef HAVE_LIBZ
-		if (ztarfile != NULL)
+		if (!writerecoveryconf || !basetablespace)
 		{
-			if (gzwrite(ztarfile, copybuf, r) != r)
-			{
-				fprintf(stderr,
-					_("%s: could not write to compressed file \"%s\": %s\n"),
-						progname, filename, get_gz_error(ztarfile));
-				disconnect_and_exit(1);
-			}
+			/*
+			 * If --write-recovery-conf was not requested or this
+			 * is not the base tablespace, simply pass the received
+			 * data into the TAR file, either compressed or not.
+			 */
+		
+			WRITE_TAR_DATA(copybuf, r);
 		}
 		else
-#endif
 		{
-			if (fwrite(copybuf, r, 1, tarfile) != 1)
+			/*
+			 * If --write-recovery-conf was requested AND this
+			 * is the base tablespace, the TAR stream may contain
+			 * a recovery.conf file if the backup is coming from
+			 * a standby server. We have to skip this file in
+			 * the stream and add a new one constructed by
+			 * CreateRecoveryConf() at the end of the stream.
+			 *
+			 * To do this, we have to process the individual files
+			 * inside the TAR stream. The stream consists of a header
+			 * and zero or more chunks, all 512 bytes long. The stream
+			 * from the server is broken up into smaller pieces, so
+			 * we have to track the size of the files to find the next
+			 * header structure.
+			 */
+			int	rr = r; /* Save the value returned by PQgetCopyData */
+			int	pos = 0;
+
+			while (rr > 0)
 			{
-				fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"),
-						progname, filename, strerror(errno));
-				disconnect_and_exit(1);
+				if (in_tarhdr)
+				{
+					/*
+					 * We're currently reading a header structure
+					 * inside the TAR stream, i.e. the file metadata.
+					 */
+					if (tarhdrsz < 512)
+					{
+						/*
+						 * Copy the header structure into tarhdr[]
+						 * in case the header is not aligned to 512 bytes
+						 * or it's not returned in whole by the last
+						 * PQgetCopyData call.
+						 */
+						int	hdrleft, bytes2copy;
+
+						hdrleft = 512 - tarhdrsz;
+						bytes2copy = (rr > hdrleft ? hdrleft : rr);
+
+						memcpy(&tarhdr[tarhdrsz], copybuf + pos, bytes2copy);
+
+						rr -= bytes2copy;
+						pos += bytes2copy;
+						tarhdrsz += bytes2copy;
+					}
+					else
+					{
+						/*
+						 * We have the whole header structure in tarhdr[],
+						 * look at the file metadata:
+						 * - the subsequent file contents have to be skipped
+						 *   if the filename is recovery.conf
+						 * - find out the size of the file padded to the next
+						 *   multiple of 512
+						 */
+						int64	padding;
+
+						skip_file = (strcmp(&tarhdr[0], "recovery.conf") == 0);
+
+						scan_val(&tarhdr[124], &filesz, 8, 11);
+
+						padding = ((filesz + 511) & ~511) - filesz;
+						filesz += padding;
+
+						/* Indicate that the subsequent data is the file content. */
+						in_tarhdr = false;
+
+						if (!skip_file)
+							WRITE_TAR_DATA(tarhdr, 512);
+					}
+				}
+				else
+				{
+					/*
+					 * We're processing a file's contents.
+					 */
+					if (filesz > 0)
+					{
+						/*
+						 * We still have data to read (and possibly write).
+						 */
+						int	bytes2write;
+
+						bytes2write = (filesz > rr ? rr : filesz);
+
+						if (!skip_file)
+							WRITE_TAR_DATA(copybuf + pos, bytes2write);
+
+						rr -= bytes2write;
+						pos += bytes2write;
+						filesz -= bytes2write;
+					}
+					else
+					{
+						/*
+						 * No more data in the current file,
+						 * the next piece of data (if any) will
+						 * be a new file header structure.
+						 * Reinitialize all our variables.
+						 */
+						in_tarhdr = true;
+						skip_file = false;
+						tarhdrsz = 0;
+						filesz = 0;
+					}
+				}
 			}
 		}
 		totaldone += r;
@@ -706,10 +890,11 @@ ReceiveAndUnpackTarFile(PGconn *conn, PG
 	char		filename[MAXPGPATH];
 	int			current_len_left;
 	int			current_padding = 0;
+	bool			basetablespace = PQgetisnull(res, rownum, 0);
 	char	   *copybuf = NULL;
 	FILE	   *file = NULL;
 
-	if (PQgetisnull(res, rownum, 0))
+	if (basetablespace)
 		strcpy(current_path, basedir);
 	else
 		strcpy(current_path, PQgetvalue(res, rownum, 1));
@@ -931,6 +1116,131 @@ ReceiveAndUnpackTarFile(PGconn *conn, PG
 
 	if (copybuf != NULL)
 		PQfreemem(copybuf);
+
+	if (basetablespace)
+		WriteRecoveryConf();
+}
+
+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 the new recovery.conf in memory
+ */
+static void
+CreateRecoveryConf(PGconn *conn)
+{
+	PQconninfoOption   *connOptions;
+	PQconninfoOption   *option;
+
+	if (!writerecoveryconf)
+		return;
+
+	connOptions = PQconninfo(conn);
+	if (connOptions == NULL)
+	{
+		fprintf(stderr, _("%s: out of memory"), progname);
+		disconnect_and_exit(1);
+	}
+
+	appendPQExpBufferStr(rcExpBuf, "standby_mode = 'on'\n");
+	if (PQExpBufferBroken(rcExpBuf))
+	{
+		fprintf(stderr, _("%s: out of memory"), progname);
+		disconnect_and_exit(1);
+	}
+
+	appendPQExpBufferStr(rcExpBuf, "primary_conninfo = '");
+	if (PQExpBufferBroken(rcExpBuf))
+	{
+		fprintf(stderr, _("%s: out of memory"), progname);
+		disconnect_and_exit(1);
+	}
+
+	for (option = connOptions; option && option->keyword; option++)
+	{
+		char	   *escaped;
+
+		/*
+		 * 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;
+
+		/*
+		 * Write "keyword='value'" pieces, the value string is escaped
+		 * if necessary and doubled single quotes around the value string.
+		 */
+		escaped = escape_quotes(option->val);
+
+		appendPQExpBuffer(rcExpBuf, "%s=''%s'' ", option->keyword, escaped);
+		if (PQExpBufferBroken(rcExpBuf))
+		{
+			fprintf(stderr, _("%s: out of memory"), progname);
+			disconnect_and_exit(1);
+		}
+
+		free(escaped);
+	}
+
+	appendPQExpBufferStr(rcExpBuf, "'\n");
+	if (PQExpBufferBroken(rcExpBuf))
+	{
+		fprintf(stderr, _("%s: out of memory"), progname);
+		disconnect_and_exit(1);
+	}
+
+	PQconninfoFree(connOptions);
+}
+
+
+static void
+WriteRecoveryConf(void)
+{
+	char		filename[MAXPGPATH];
+	FILE	   *cf;
+
+	if (!writerecoveryconf)
+		return;
+
+	sprintf(filename, "%s/recovery.conf", basedir);
+
+	cf = fopen(filename, "w");
+	if (cf == NULL)
+	{
+		fprintf(stderr, _("%s: cannot create %s: %s"), progname, filename, strerror(errno));
+		disconnect_and_exit(1);
+	}
+
+	if (fwrite(rcExpBuf->data, rcExpBuf->len, 1, cf) != 1)
+	{
+		fprintf(stderr,
+				_("%s: could not write to file \"%s\": %s\n"),
+				progname, filename, strerror(errno));
+		disconnect_and_exit(1);
+	}
+
+	fclose(cf);
+
+	if (verbose)
+		fprintf(stderr, _("%s: recovery.conf written.\n"), progname);
 }
 
 
@@ -954,6 +1264,15 @@ BaseBackup(void)
 		/* Error message already written in GetConnection() */
 		exit(1);
 
+	rcExpBuf = createPQExpBuffer();
+	if (!rcExpBuf)
+	{
+		fprintf(stderr, _("%s: out of memory"), progname);
+		disconnect_and_exit(1);
+	}
+
+	CreateRecoveryConf(conn);
+
 	/*
 	 * Run IDENTIFY_SYSTEM so we can get the timeline
 	 */
@@ -1217,6 +1536,9 @@ BaseBackup(void)
 #endif
 	}
 
+	/* Free the recovery.conf contents */
+	destroyPQExpBuffer(rcExpBuf);
+
 	/*
 	 * End of copy data. Final result is already checked inside the loop.
 	 */
@@ -1237,6 +1559,7 @@ main(int argc, char **argv)
 		{"pgdata", required_argument, NULL, 'D'},
 		{"format", required_argument, NULL, 'F'},
 		{"checkpoint", required_argument, NULL, 'c'},
+		{"write-recovery-conf", no_argument, NULL, 'R'},
 		{"xlog", no_argument, NULL, 'x'},
 		{"xlog-method", required_argument, NULL, 'X'},
 		{"gzip", no_argument, NULL, 'z'},
@@ -1274,7 +1597,7 @@ main(int argc, char **argv)
 		}
 	}
 
-	while ((c = getopt_long(argc, argv, "D:F:xX:l:zZ:c:h:p:U:s:wWvP",
+	while ((c = getopt_long(argc, argv, "D:F:RxX:l:zZ:c:h:p:U:s:wWvP",
 							long_options, &option_index)) != -1)
 	{
 		switch (c)
@@ -1295,6 +1618,9 @@ main(int argc, char **argv)
 					exit(1);
 				}
 				break;
+			case 'R':
+				writerecoveryconf = true;
+				break;
 			case 'x':
 				if (includewal)
 				{
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to