2012-11-18 17:20 keltezéssel, Magnus Hagander írta:
On Tue, Oct 23, 2012 at 5:08 PM, Magnus Hagander <mag...@hagander.net> wrote:
On Oct 23, 2012 4:52 PM, "Alvaro Herrera" <alvhe...@2ndquadrant.com> wrote:
Boszormenyi Zoltan escribió:

Also, the check for conflict between -R and -x/-X is now removed.
The documentation for option -R has changed to reflect this and
there is no different error code 2 now: it would make the behaviour
inconsistent between -Fp and -Ft.

The PQconninfo patch is also attached but didn't change since the
last mail.
Magnus,

This patch is all yours to handle.  I'm guessing nothing will happen
until pgconf.eu is done and over, but hopefully you can share a few
beers with Zoltan over the whole subject (and maybe with Peter about the
PQconninfo stuff?)

I'm not closing this just yet, but if you're not able to handle this
soon, maybe it'd be better to punt it to the November commitfest.
It's on my to do list for when I get back, but correct, won't get to it
until after the conference.
I finally got around to looking at this patch now. Sorry about the way
too long delay.

A few thoughts:

First, on the libpq patch:

I'm not sure I like the for_replication flag to PQconninfo(). It seems
we're it's a quite limited API, and not very future proof. What's to
say that an app would only be interested in filtering based on
for_replication? One idea might be to have a bitmap field there, and
assign *all* conninfo options to a category. We could then have
categories for NORMAL and REPLICATION. But we could also for example
have a category for PASSWORD (or similar), so that you could get with
and without those. Passing in a 32-bit integer would allow us to have
32 different categories, and we could then use a bitmask to pick
things out.

It might sound a bit like overengineering, but it's also an API and
it's a PITA to change it in the future if more needs show up..

Check.

Second, I wonder if we really need to add the code for requiressl=1,
or if we should just remove it. The spelling requiressl=1 was
deprecated back in 7.4 - which has obviously been out of support for a
long time now.

I removed this option, the code is simpler, thanks to this.

Third, in fillPGconn. If mapping has both conninfoValue and connvalue,
it does a free() on the old value in memberaddr, but if it doesn't it
just overwrites memberaddr with strdup(). Shouldn't those two paths be
the same, meaning shouldn't the  if (*memberaddr) free(*memberaddr);
check be outside the if block?

This point is now moot, see above.

Fourth, I'm not sure I like the "memberaddr" term. It's not wrong, but
it threw me off a couple of times while reading it. It's not all that
important, and I'm not sure about another idea for it though - maybe
just "connmember"?

The variable is now "connmember".

Also, I noticed that there was already a conninfo_storeval(),
the new patch uses it and there's no need to introduce a
new conninfo_setval() function.

Then, about the pg_basebackup patch:

What's the reason for the () around 512 for TARCHUNKSZ?

Removed the () from around the value.

We have a lot of hardcoded entries of the 512 for tar in that file. We
should either keep using 512 as a constant, or change all those to
*also* use the #define. Right now, the code will obviously break if
you change the #define (for example, it compares to 512, but then uses
hdrleft = TARCHUNKSZ - tarhdrsz; to do calculation).

All 512 constants are now using the #define.

The name choice of "basebackup" for the bool in ReceiveTarFile() is
unfortunate, since we use the term base backup to refer to the
complete thing, not just the main tablespace. Probably
"basetablespcae" instead. And it should then be assigned at the top of
the function (to the result of PQgetisnull()), and used in the main
if() branch as well.

Done without your typo, so the variable is "basetablespace". ;-)

Should we really print the status message even if not in verbose mode?
We only print the "base backup complete" messages in verbose mode, for
example.

The message is written only in verbose mode now.

It seems wrong to free() recoveryconf in ReceiveTarFile(). It's
allocated globally at the beginning. While that loop should only be
called once (since only one tablespace can be the main one), it's a
confusing location for the free.

See below.

The whole tar writing part of the code needs a lot more comments. It's
entirely unclear what the code does there. Why does the recovery.conf
writing code need to be split up in multiple locations inside
ReceiveTarFile(), for example? It either needs to be simplified, or
explained why it can't be simplified in comments.

_tarCreateHeader() is really badly named, since it specifically
creates a tar header for recovery.conf only. Either that needs to be a
parameter, or it needs to have a name that indicates this is the only
thing it does. The former is probably better.

_tarCreateHeader() now accepts the file name and the file size arguments.

Much of the tar stuff is very similar (I haven't looked to see if it's
identical) to the stuff in backend/replication/basebackup.c. Perhaps
we should have a src/port/tarutil.c?

I will implement it as a separate patch.

escape_string() - already exists as escape_quotes() in initdb, AFAICT.
We should either move it to src/port/, or at least copy it so it's
exactly the same.

A copy of escape_quotes() is now in pg_basebackup.c and is used.

I will also unify the copies of it in a separate patch.

CreateRecoveryConf() should just use PQExpBuffer (in libpq), I think -
that does away with a lot of code. We already use this from e.g.
pg_dump, so there's a precedent for using internal code from libpq in
frontends.

PQexpBuffer is used now and it's created and destroyed inside BaseBackup().

Again, my apologies for this review taking so long. I will try to be
more attentive to the next round :S

Please, review the new patches.

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

--
  Magnus Hagander
  Me: http://www.hagander.net/
  Work: http://www.redpill-linpro.com/




--
----------------------------------
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/doc/src/sgml/libpq.sgml postgresql.1/doc/src/sgml/libpq.sgml
--- postgresql/doc/src/sgml/libpq.sgml	2012-08-03 09:39:30.114266570 +0200
+++ postgresql.1/doc/src/sgml/libpq.sgml	2012-11-20 11:57:41.359859195 +0100
@@ -496,6 +496,49 @@ typedef struct
      </listitem>
     </varlistentry>
 
+    <varlistentry id="libpq-pqconninfo">
+     <term><function>PQconninfo</function><indexterm><primary>PQconninfo</></></term>
+     <listitem>
+      <para>
+       Returns the connection options used by a live connection.
+<synopsis>
+/*
+ * Option flags for PQconninfo
+ */
+#define PG_CONNINFO_NORMAL              0x01
+#define PG_CONNINFO_PASSWORD            0x02
+#define PG_CONNINFO_REPLICATION         0x04
+
+PQconninfoOption *PQconninfo(PGconn *conn, int flags);
+</synopsis>
+      </para>
+
+      <para>
+       Returns a connection options array.  This can be used to determine
+       all possible <function>PQconnectdb</function> options and their
+       current values that were used to connect to the server. The return
+       value points to an array of <structname>PQconninfoOption</structname>
+       structures, which ends with an entry having a null <structfield>keyword</>
+       pointer.  Every notes above for <function>PQconndefaults</function> also apply.
+       An application may present a dialog using the previous settings by:
+<programlisting>
+        PQconninfoOption *options = PQconninfo(conn, PG_CONNINFO_NORMAL | PG_CONNINFO_PASSWORD);
+</programlisting>
+      </para>
+
+      <para>
+       The array returned by the <literal>PG_CONNINFO_REPLICATION</> flag is
+       a subset of <literal>PG_CONNINFO_NORMAL | PG_CONNINFO_PASSWORD</>.
+       This subset excludes some options from the array which are used by the
+       walreceiver module. <application>pg_basebackup</application> uses this
+       pre-filtered list of options to construct <literal>primary_conninfo</>
+       in the automatically generated recovery.conf file.
+      </para>
+
+     </listitem>
+    </varlistentry>
+
+
     <varlistentry id="libpq-pqconninfoparse">
      <term><function>PQconninfoParse</function><indexterm><primary>PQconninfoParse</></></term>
      <listitem>
diff -durpN postgresql/src/interfaces/libpq/exports.txt postgresql.1/src/interfaces/libpq/exports.txt
--- postgresql/src/interfaces/libpq/exports.txt	2012-10-09 09:58:14.342782974 +0200
+++ postgresql.1/src/interfaces/libpq/exports.txt	2012-11-20 10:55:03.396145749 +0100
@@ -164,3 +164,4 @@ PQsetSingleRowMode        161
 lo_lseek64                162
 lo_tell64                 163
 lo_truncate64             164
+PQconninfo                165
diff -durpN postgresql/src/interfaces/libpq/fe-connect.c postgresql.1/src/interfaces/libpq/fe-connect.c
--- postgresql/src/interfaces/libpq/fe-connect.c	2012-09-09 08:11:09.470401480 +0200
+++ postgresql.1/src/interfaces/libpq/fe-connect.c	2012-11-20 12:53:49.728643547 +0100
@@ -137,6 +137,9 @@ static int ldapServiceLookup(const char
  * PQconninfoOptions[] *must* be NULL.	In a working copy, non-null "val"
  * fields point to malloc'd strings that should be freed when the working
  * array is freed (see PQconninfoFree).
+ *
+ * If you add a new connection option to this list, remember to add it to
+ * PQconninfoMappings[] below.
  * ----------
  */
 static const PQconninfoOption PQconninfoOptions[] = {
@@ -203,16 +206,6 @@ static const PQconninfoOption PQconninfo
 	{"keepalives_count", NULL, NULL, NULL,
 	"TCP-Keepalives-Count", "", 10},	/* strlen(INT32_MAX) == 10 */
 
-#ifdef USE_SSL
-
-	/*
-	 * "requiressl" is deprecated, its purpose having been taken over by
-	 * "sslmode". It remains for backwards compatibility.
-	 */
-	{"requiressl", "PGREQUIRESSL", "0", NULL,
-	"Require-SSL", "D", 1},
-#endif
-
 	/*
 	 * ssl options are allowed even without client SSL support because the
 	 * client can still handle SSL modes "disable" and "allow". Other
@@ -264,6 +257,83 @@ static const PQconninfoOption PQconninfo
 	NULL, NULL, 0}
 };
 
+/*
+ * We need a mapping between the PQconninfoOptions[] array
+ * and PGconn members. We have to keep it separate from
+ * PQconninfoOptions[] to not leak info about PGconn members
+ * to clients.
+ */
+typedef struct PQconninfoMapping {
+	char		*keyword;
+	size_t		member_offset;
+	int		flags;
+} PQconninfoMapping;
+#define PGCONNMEMBERADDR(conn, mapping) ((char **)((char *)conn + mapping->member_offset))
+
+static const PQconninfoMapping PQconninfoMappings[] =
+{
+	/* "authtype" is not used anymore, there is no mapping to PGconn */
+	/* there is no mapping of "service" to PGconn */
+	{ "user", offsetof(struct pg_conn, pguser),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "password", offsetof(struct pg_conn, pgpass),
+	  PG_CONNINFO_PASSWORD | PG_CONNINFO_REPLICATION },
+	{ "connect_timeout", offsetof(struct pg_conn, connect_timeout),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "dbname", offsetof(struct pg_conn, dbName),
+	  PG_CONNINFO_NORMAL },
+	{ "host", offsetof(struct pg_conn, pghost),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "hostaddr", offsetof(struct pg_conn, pghostaddr),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "port", offsetof(struct pg_conn, pgport),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "client_encoding", offsetof(struct pg_conn, client_encoding_initial),
+	  PG_CONNINFO_NORMAL },
+	{ "tty", offsetof(struct pg_conn, pgtty),
+	  PG_CONNINFO_NORMAL },
+	{ "options", offsetof(struct pg_conn, pgoptions),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "application_name", offsetof(struct pg_conn, appname),
+	  PG_CONNINFO_NORMAL },
+	{ "fallback_application_name", offsetof(struct pg_conn, fbappname),
+	  PG_CONNINFO_NORMAL },
+	{ "keepalives", offsetof(struct pg_conn, keepalives),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "keepalives_idle", offsetof(struct pg_conn, keepalives_idle),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "keepalives_interval", offsetof(struct pg_conn, keepalives_interval),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "keepalives_count", offsetof(struct pg_conn, keepalives_count),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "sslmode", offsetof(struct pg_conn, sslmode),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "sslcompression", offsetof(struct pg_conn, sslcompression),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "sslcert", offsetof(struct pg_conn, sslcert),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "sslkey", offsetof(struct pg_conn, sslkey),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "sslrootcert", offsetof(struct pg_conn, sslrootcert),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "sslcrl", offsetof(struct pg_conn, sslcrl),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+	{ "requirepeer", offsetof(struct pg_conn, requirepeer),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
+	{ "krbsrvname", offsetof(struct pg_conn, krbsrvname),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+#endif
+#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
+	{ "gsslib", offsetof(struct pg_conn, requirepeer),
+	  PG_CONNINFO_NORMAL | PG_CONNINFO_REPLICATION },
+#endif
+	{ "replication", offsetof(struct pg_conn, replication),
+	  PG_CONNINFO_NORMAL },
+	/* Terminating entry --- MUST BE LAST */
+	{ NULL, 0, 0 }
+};
+
 static const PQEnvironmentOption EnvironmentOptions[] =
 {
 	/* common user-interface settings */
@@ -295,7 +365,8 @@ static PGconn *makeEmptyPGconn(void);
 static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
 static void freePGconn(PGconn *conn);
 static void closePGconn(PGconn *conn);
-static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage);
+static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage,
+						int flags);
 static PQconninfoOption *parse_connection_string(const char *conninfo,
 						PQExpBuffer errorMessage, bool use_defaults);
 static int	uri_prefix_length(const char *connstr);
@@ -627,7 +698,7 @@ PQconnectStart(const char *conninfo)
 static void
 fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
 {
-	const char *tmp;
+	const PQconninfoMapping *mapping;
 
 	/*
 	 * Move option values into conn structure
@@ -637,72 +708,19 @@ fillPGconn(PGconn *conn, PQconninfoOptio
 	 *
 	 * XXX: probably worth checking strdup() return value here...
 	 */
-	tmp = conninfo_getval(connOptions, "hostaddr");
-	conn->pghostaddr = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "host");
-	conn->pghost = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "port");
-	conn->pgport = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "tty");
-	conn->pgtty = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "options");
-	conn->pgoptions = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "application_name");
-	conn->appname = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "fallback_application_name");
-	conn->fbappname = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "dbname");
-	conn->dbName = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "user");
-	conn->pguser = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "password");
-	conn->pgpass = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "connect_timeout");
-	conn->connect_timeout = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "client_encoding");
-	conn->client_encoding_initial = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "keepalives");
-	conn->keepalives = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "keepalives_idle");
-	conn->keepalives_idle = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "keepalives_interval");
-	conn->keepalives_interval = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "keepalives_count");
-	conn->keepalives_count = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "sslmode");
-	conn->sslmode = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "sslcompression");
-	conn->sslcompression = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "sslkey");
-	conn->sslkey = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "sslcert");
-	conn->sslcert = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "sslrootcert");
-	conn->sslrootcert = tmp ? strdup(tmp) : NULL;
-	tmp = conninfo_getval(connOptions, "sslcrl");
-	conn->sslcrl = tmp ? strdup(tmp) : NULL;
-#ifdef USE_SSL
-	tmp = conninfo_getval(connOptions, "requiressl");
-	if (tmp && tmp[0] == '1')
+	for (mapping = PQconninfoMappings; mapping->keyword; mapping++)
 	{
-		/* here warn that the requiressl option is deprecated? */
-		if (conn->sslmode)
-			free(conn->sslmode);
-		conn->sslmode = strdup("require");
+		const char *tmp = conninfo_getval(connOptions, mapping->keyword);
+
+		if (tmp)
+		{
+			char  **connmember = PGCONNMEMBERADDR(conn, mapping);
+
+			if (*connmember)
+				free(*connmember);
+			*connmember = tmp ? strdup(tmp) : NULL;
+		}
 	}
-#endif
-	tmp = conninfo_getval(connOptions, "requirepeer");
-	conn->requirepeer = tmp ? strdup(tmp) : NULL;
-#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
-	tmp = conninfo_getval(connOptions, "krbsrvname");
-	conn->krbsrvname = tmp ? strdup(tmp) : NULL;
-#endif
-#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
-	tmp = conninfo_getval(connOptions, "gsslib");
-	conn->gsslib = tmp ? strdup(tmp) : NULL;
-#endif
-	tmp = conninfo_getval(connOptions, "replication");
-	conn->replication = tmp ? strdup(tmp) : NULL;
 }
 
 /*
@@ -884,7 +902,7 @@ PQconndefaults(void)
 	if (PQExpBufferDataBroken(errorBuf))
 		return NULL;			/* out of memory already :-( */
 
-	connOptions = conninfo_init(&errorBuf);
+	connOptions = conninfo_init(&errorBuf, PG_CONNINFO_NORMAL | PG_CONNINFO_PASSWORD);
 	if (connOptions != NULL)
 	{
 		if (!conninfo_add_defaults(connOptions, &errorBuf))
@@ -4008,9 +4026,11 @@ PQconninfoParse(const char *conninfo, ch
  * Build a working copy of the constant PQconninfoOptions array.
  */
 static PQconninfoOption *
-conninfo_init(PQExpBuffer errorMessage)
+conninfo_init(PQExpBuffer errorMessage, int flags)
 {
+	const PQconninfoMapping	*mapping;
 	PQconninfoOption *options;
+	PQconninfoOption *opt_dest;
 
 	options = (PQconninfoOption *) malloc(sizeof(PQconninfoOptions));
 	if (options == NULL)
@@ -4019,7 +4039,23 @@ conninfo_init(PQExpBuffer errorMessage)
 						  libpq_gettext("out of memory\n"));
 		return NULL;
 	}
-	memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
+
+	opt_dest = options;
+	for (mapping = PQconninfoMappings; mapping->keyword; mapping++)
+	{
+		PQconninfoOption *opt_src;
+
+		if (!(mapping->flags & flags))
+			continue;
+
+		opt_src = conninfo_find((PQconninfoOption *)PQconninfoOptions, mapping->keyword);
+		if (opt_src)
+		{
+			memcpy(opt_dest, opt_src, sizeof(PQconninfoOption));
+			opt_dest++;
+		}
+	}
+	MemSet(opt_dest, 0, sizeof(PQconninfoOption));
 
 	return options;
 }
@@ -4095,7 +4131,7 @@ conninfo_parse(const char *conninfo, PQE
 	PQconninfoOption *options;
 
 	/* Make a working copy of PQconninfoOptions */
-	options = conninfo_init(errorMessage);
+	options = conninfo_init(errorMessage, PG_CONNINFO_NORMAL | PG_CONNINFO_PASSWORD);
 	if (options == NULL)
 		return NULL;
 
@@ -4295,7 +4331,7 @@ conninfo_array_parse(const char *const *
 	}
 
 	/* Make a working copy of PQconninfoOptions */
-	options = conninfo_init(errorMessage);
+	options = conninfo_init(errorMessage, PG_CONNINFO_NORMAL | PG_CONNINFO_PASSWORD);
 	if (options == NULL)
 	{
 		PQconninfoFree(dbname_options);
@@ -4485,7 +4521,7 @@ conninfo_uri_parse(const char *uri, PQEx
 	PQconninfoOption *options;
 
 	/* Make a working copy of PQconninfoOptions */
-	options = conninfo_init(errorMessage);
+	options = conninfo_init(errorMessage, PG_CONNINFO_NORMAL | PG_CONNINFO_PASSWORD);
 	if (options == NULL)
 		return NULL;
 
@@ -5066,6 +5102,44 @@ conninfo_find(PQconninfoOption *connOpti
 }
 
 
+/*
+ * Return the connection options used for the connections
+ */
+PQconninfoOption *
+PQconninfo(PGconn *conn, int flags)
+{
+	PQExpBufferData errorBuf;
+	PQconninfoOption *connOptions;
+
+	if (conn == NULL)
+		return NULL;
+
+	/* We don't actually report any errors here, but callees want a buffer */
+	initPQExpBuffer(&errorBuf);
+	if (PQExpBufferDataBroken(errorBuf))
+		return NULL;		/* out of memory already :-( */
+
+	connOptions = conninfo_init(&errorBuf, flags);
+
+	if (connOptions != NULL)
+	{
+		const PQconninfoMapping *mapping;
+
+		for (mapping = PQconninfoMappings; mapping->keyword; mapping++)
+		{
+			char **connmember = PGCONNMEMBERADDR(conn, mapping);
+
+			conninfo_storeval(connOptions, mapping->keyword, *connmember,
+					&errorBuf, false, false);
+		}
+	}
+
+	termPQExpBuffer(&errorBuf);
+
+	return connOptions;
+}
+
+
 void
 PQconninfoFree(PQconninfoOption *connOptions)
 {
diff -durpN postgresql/src/interfaces/libpq/libpq-fe.h postgresql.1/src/interfaces/libpq/libpq-fe.h
--- postgresql/src/interfaces/libpq/libpq-fe.h	2012-10-09 09:58:14.343782980 +0200
+++ postgresql.1/src/interfaces/libpq/libpq-fe.h	2012-11-20 11:49:36.747692399 +0100
@@ -36,6 +36,13 @@ extern		"C"
 #define PG_COPYRES_EVENTS		  0x04
 #define PG_COPYRES_NOTICEHOOKS	  0x08
 
+/*
+ * Option flags for PQconninfo
+ */
+#define PG_CONNINFO_NORMAL		0x01
+#define PG_CONNINFO_PASSWORD		0x02
+#define PG_CONNINFO_REPLICATION		0x04
+
 /* Application-visible enum types */
 
 /*
@@ -262,6 +269,9 @@ extern PQconninfoOption *PQconndefaults(
 /* parse connection options in same way as PQconnectdb */
 extern PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg);
 
+/* return the connection options used by a live connection */
+extern PQconninfoOption *PQconninfo(PGconn *conn, int flags);
+
 /* free the data structure returned by PQconndefaults() or PQconninfoParse() */
 extern void PQconninfoFree(PQconninfoOption *connOptions);
 
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	2012-11-20 12:56:32.891843707 +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	2012-10-03 10:40:48.297207389 +0200
+++ postgresql.2/src/bin/pg_basebackup/pg_basebackup.c	2012-11-20 16:40:52.329063335 +0100
@@ -19,12 +19,14 @@
 #define FRONTEND 1
 #include "postgres.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>
@@ -46,6 +48,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 */
@@ -70,6 +73,10 @@ static int	has_xlogendptr = 0;
 static volatile LONG has_xlogendptr = 0;
 #endif
 
+/* Don't ever change this value, the TAR file format requires it. */
+#define TARCHUNKSZ	512
+PQExpBuffer rcExpBuf = NULL;
+
 /* Function headers */
 static void usage(void);
 static void verify_dir_is_empty_or_create(char *dirname);
@@ -77,6 +84,13 @@ static void progress_report(int tablespa
 
 static void ReceiveTarFile(PGconn *conn, PGresult *res, int rownum);
 static void ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum);
+static char *escape_quotes(const char *src);
+static void CreateRecoveryConf(PGconn *conn);
+static void WriteRecoveryConf(void);
+static int  _tarChecksum(char *header);
+static void print_val(char *s, uint64 val, unsigned int base, size_t len);
+static void scan_val(char *s, uint64 *val, unsigned int base, size_t len);
+static void _tarCreateHeader(char *header, char *filename, size_t filesize);
 static void BaseBackup(void);
 
 static bool reached_end_position(XLogRecPtr segendpos, uint32 timeline,
@@ -107,6 +121,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"));
@@ -452,6 +468,45 @@ progress_report(int tablespacenum, const
 
 
 /*
+ * 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.
@@ -467,12 +522,17 @@ ReceiveTarFile(PGconn *conn, PGresult *r
 	char		filename[MAXPGPATH];
 	char	   *copybuf = NULL;
 	FILE	   *tarfile = NULL;
+	char		tarhdr[TARCHUNKSZ];
+	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
@@ -584,6 +644,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;
@@ -598,38 +668,36 @@ 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.
 			 */
-			char		zerobuf[1024];
+			char		zerobuf[2*TARCHUNKSZ];
 
 			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[TARCHUNKSZ];
+				int		padding;
+
+				_tarCreateHeader(header, "recovery.conf", rcExpBuf->len);
+
+				padding = ((rcExpBuf->len + (TARCHUNKSZ-1)) & ~(TARCHUNKSZ-1)) - 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)
 			{
@@ -665,25 +733,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 < TARCHUNKSZ)
+					{
+						/*
+						 * 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 = TARCHUNKSZ - 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 + (TARCHUNKSZ-1)) & ~(TARCHUNKSZ-1)) - filesz;
+						filesz += padding;
+
+						/* Indicate that the subsequent data is the file content. */
+						in_tarhdr = false;
+
+						if (!skip_file)
+							WRITE_TAR_DATA(tarhdr, TARCHUNKSZ);
+					}
+				}
+				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;
@@ -712,10 +879,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));
@@ -767,13 +935,13 @@ ReceiveAndUnpackTarFile(PGconn *conn, PG
 			/*
 			 * No current file, so this must be the header for a new file
 			 */
-			if (r != 512)
+			if (r != TARCHUNKSZ)
 			{
 				fprintf(stderr, _("%s: invalid tar block header size: %d\n"),
 						progname, r);
 				disconnect_and_exit(1);
 			}
-			totaldone += 512;
+			totaldone += TARCHUNKSZ;
 
 			if (sscanf(copybuf + 124, "%11o", &current_len_left) != 1)
 			{
@@ -794,7 +962,7 @@ ReceiveAndUnpackTarFile(PGconn *conn, PG
 			 * All files are padded up to 512 bytes
 			 */
 			current_padding =
-				((current_len_left + 511) & ~511) - current_len_left;
+				((current_len_left + (TARCHUNKSZ-1)) & ~(TARCHUNKSZ-1)) - current_len_left;
 
 			/*
 			 * First part of header is zero terminated filename
@@ -937,6 +1105,258 @@ ReceiveAndUnpackTarFile(PGconn *conn, PG
 
 	if (copybuf != NULL)
 		PQfreemem(copybuf);
+
+	if (basetablespace)
+		WriteRecoveryConf();
+}
+
+static int
+_tarChecksum(char *header)
+{
+	int			i,
+				sum;
+
+	/*
+	 * Per POSIX, the checksum is the simple sum of all bytes in the header,
+	 * treating the bytes as unsigned, and treating the checksum field (at
+	 * offset 148) as though it contained 8 spaces.
+	 */
+	sum = 8 * ' ';				/* presumed value for checksum field */
+	for (i = 0; i < TARCHUNKSZ; i++)
+		if (i < 148 || i >= 156)
+			sum += 0xFF & header[i];
+	return sum;
+}
+
+
+/*
+ * Utility routine to print possibly larger than 32 bit integers in a
+ * portable fashion.  Filled with zeros.
+ */
+static void
+print_val(char *s, uint64 val, unsigned int base, size_t len)
+{
+	int			i;
+
+	for (i = len; i > 0; i--)
+	{
+		int			digit = val % base;
+
+		s[i - 1] = '0' + digit;
+		val = val / base;
+	}
+}
+
+
+/*
+ * Inverse for 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;
+}
+
+
+static void
+_tarCreateHeader(char *header, char *filename, size_t filesize)
+{
+	/*
+	 * Note: most of the fields in a tar header are not supposed to be
+	 * null-terminated.  We use sprintf, which will write a null after the
+	 * required bytes; that null goes into the first byte of the next field.
+	 * This is okay as long as we fill the fields in order.
+	 */
+	memset(header, 0, TARCHUNKSZ /* sizeof the tar header */);
+
+	/* Name 100 */
+	sprintf(&header[0], "%.99s", filename);
+
+	/* Mode 8 */
+	sprintf(&header[100], "0000600 ");
+
+	/* User ID 8 */
+	sprintf(&header[108], "0004000 ");
+
+	/* Group 8 */
+	sprintf(&header[116], "0002000 ");
+
+	/* File size 12 - 11 digits, 1 space; use print_val for 64 bit support */
+	print_val(&header[124], filesize, 8, 11);
+	sprintf(&header[135], " ");
+
+	/* Mod Time 12 */
+	sprintf(&header[136], "%011o ", (int) time(NULL));
+
+	/* Checksum 8 cannot be calculated until we've filled all other fields */
+
+	/* Type - regular file */
+	sprintf(&header[156], "0");
+
+	/* Link Name 100 (leave as nulls) */
+
+	/* Magic 6 */
+	sprintf(&header[257], "ustar");
+
+	/* Version 2 */
+	sprintf(&header[263], "00");
+
+	/* User 32 */
+	/* XXX: Do we need to care about setting correct username? */
+	sprintf(&header[265], "%.31s", "postgres");
+
+	/* Group 32 */
+	/* XXX: Do we need to care about setting correct group name? */
+	sprintf(&header[297], "%.31s", "postgres");
+
+	/* Major Dev 8 */
+	sprintf(&header[329], "%07o ", 0);
+
+	/* Minor Dev 8 */
+	sprintf(&header[337], "%07o ", 0);
+
+	/* Prefix 155 - not used, leave as nulls */
+
+	/*
+	 * We mustn't overwrite the next field while inserting the checksum.
+	 * Fortunately, the checksum can't exceed 6 octal digits, so we just write
+	 * 6 digits, a space, and a null, which is legal per POSIX.
+	 */
+	sprintf(&header[148], "%06o ", _tarChecksum(header));
+}
+
+/*
+ * Escape single quotes in a string
+ */
+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;
+}
+
+/*
+ * Try to create recovery.conf in memory and set the length to write later.
+ */
+static void
+CreateRecoveryConf(PGconn *conn)
+{
+	PQconninfoOption   *connOptions;
+	PQconninfoOption   *option;
+
+	if (!writerecoveryconf)
+		return;
+
+	connOptions = PQconninfo(conn, PG_CONNINFO_REPLICATION);
+	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 not set or empty.
+		 */
+		if ((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);
 }
 
 
@@ -960,6 +1380,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
 	 */
@@ -1223,6 +1652,9 @@ BaseBackup(void)
 #endif
 	}
 
+	/* Free the recovery.conf contents */
+	destroyPQExpBuffer(rcExpBuf);
+
 	/*
 	 * End of copy data. Final result is already checked inside the loop.
 	 */
@@ -1243,6 +1675,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'},
@@ -1280,7 +1713,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)
@@ -1301,6 +1734,9 @@ main(int argc, char **argv)
 					exit(1);
 				}
 				break;
+			case 'R':
+				writerecoveryconf = true;
+				break;
 			case 'x':
 				if (includewal)
 				{
diff -durpN postgresql.1/src/bin/pg_basebackup/streamutil.c postgresql.2/src/bin/pg_basebackup/streamutil.c
--- postgresql.1/src/bin/pg_basebackup/streamutil.c	2012-10-03 10:40:48.298207395 +0200
+++ postgresql.2/src/bin/pg_basebackup/streamutil.c	2012-11-20 14:29:11.815346687 +0100
@@ -44,13 +44,13 @@ pg_strdup(const char *s)
 	if (!result)
 	{
 		fprintf(stderr, _("%s: out of memory\n"), progname);
-		exit(1);
+		disconnect_and_exit(1);
 	}
 	return result;
 }
 
 void *
-pg_malloc0(size_t size)
+pg_malloc(size_t size)
 {
 	void	   *result;
 
@@ -61,13 +61,24 @@ pg_malloc0(size_t size)
 	if (!result)
 	{
 		fprintf(stderr, _("%s: out of memory\n"), progname);
-		exit(1);
+		disconnect_and_exit(1);
 	}
 	MemSet(result, 0, size);
 	return result;
 }
 
 
+void *
+pg_malloc0(size_t size)
+{
+	void	   *tmp;
+
+	tmp = pg_malloc(size);
+	MemSet(tmp, 0, size);
+	return tmp;
+}
+
+
 /*
  * Connect to the server. Returns a valid PGconn pointer if connected,
  * or NULL on non-permanent error. On permanent error, the function will
diff -durpN postgresql.1/src/bin/pg_basebackup/streamutil.h postgresql.2/src/bin/pg_basebackup/streamutil.h
--- postgresql.1/src/bin/pg_basebackup/streamutil.h	2012-10-03 10:40:48.299207401 +0200
+++ postgresql.2/src/bin/pg_basebackup/streamutil.h	2012-11-20 14:24:29.517573163 +0100
@@ -17,6 +17,7 @@ extern PGconn *conn;
 
 
 extern char *pg_strdup(const char *s);
+extern void *pg_malloc(size_t size);
 extern void *pg_malloc0(size_t size);
 
 extern PGconn *GetConnection(void);
-- 
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