2012-10-04 06:47 keltezéssel, Boszormenyi Zoltan írta:
2012-10-04 05:24 keltezéssel, Peter Eisentraut írta:
On Wed, 2012-10-03 at 18:15 +0200, Boszormenyi Zoltan wrote:
The second generation of this work is now attached and contains a new
feature as was discussed and suggested by Magnus Hagander, Fujii Masao
and Peter Eisentraut. So libpq has grown a new function:
+/* return the connection options used by a live connection */
+extern PQconninfoOption *PQconninfo(PGconn *conn);
This copies all the connection parameters back from the live PGconn
itself
so everything that's needed to connect is already validated.
I don't like that this code maintains a second list of all possible
libpq connection parameters.
Where does it do that? In PQconninfo() itself? Why is it a problem?
Or to put it bluntly: the same problem is in fillPGconn() too, as it also
has the same set of parameters listed. So there is already code
that you don't like. :-)
How about a static mapping between option names and
offsetof(struct pg_conn, member) values? This way both fillPGconn()
and PQconninfo() can avoid maintaining the list of parameter names.
Did you think about something like the attached code?
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/interfaces/libpq/exports.txt postgresql.1/src/interfaces/libpq/exports.txt
--- postgresql/src/interfaces/libpq/exports.txt 2012-08-03 09:39:30.118266598 +0200
+++ postgresql.1/src/interfaces/libpq/exports.txt 2012-10-04 11:29:24.864309200 +0200
@@ -161,3 +161,5 @@ PQping 158
PQpingParams 159
PQlibVersion 160
PQsetSingleRowMode 161
+PQconninfo 162
+PQconninfoForReplication 163
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-10-04 12:30:56.705605588 +0200
@@ -131,12 +131,17 @@ static int ldapServiceLookup(const char
* "" Normal input field
* "*" Password field - hide value
* "D" Debug option - don't show by default
+ * A special "usable for replication connections" ("R") flag is
+ * added to Disp-Char in a backwards compatible fashion.
*
* PQconninfoOptions[] is a constant static array that we use to initialize
* a dynamically allocated working copy. All the "val" fields in
* 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[] = {
@@ -146,62 +151,62 @@ static const PQconninfoOption PQconninfo
* still try to set it.
*/
{"authtype", "PGAUTHTYPE", DefaultAuthtype, NULL,
- "Database-Authtype", "D", 20},
+ "Database-Authtype", "D\0", 20},
{"service", "PGSERVICE", NULL, NULL,
- "Database-Service", "", 20},
+ "Database-Service", "\0", 20},
{"user", "PGUSER", NULL, NULL,
- "Database-User", "", 20},
+ "Database-User", "\0R", 20},
{"password", "PGPASSWORD", NULL, NULL,
- "Database-Password", "*", 20},
+ "Database-Password", "*\0R", 20},
{"connect_timeout", "PGCONNECT_TIMEOUT", NULL, NULL,
- "Connect-timeout", "", 10}, /* strlen(INT32_MAX) == 10 */
+ "Connect-timeout", "\0R", 10}, /* strlen(INT32_MAX) == 10 */
{"dbname", "PGDATABASE", NULL, NULL,
- "Database-Name", "", 20},
+ "Database-Name", "\0", 20},
{"host", "PGHOST", NULL, NULL,
- "Database-Host", "", 40},
+ "Database-Host", "\0R", 40},
{"hostaddr", "PGHOSTADDR", NULL, NULL,
- "Database-Host-IP-Address", "", 45},
+ "Database-Host-IP-Address", "\0R", 45},
{"port", "PGPORT", DEF_PGPORT_STR, NULL,
- "Database-Port", "", 6},
+ "Database-Port", "\0R", 6},
{"client_encoding", "PGCLIENTENCODING", NULL, NULL,
- "Client-Encoding", "", 10},
+ "Client-Encoding", "\0", 10},
/*
* "tty" is no longer used either, but keep it present for backwards
* compatibility.
*/
{"tty", "PGTTY", DefaultTty, NULL,
- "Backend-Debug-TTY", "D", 40},
+ "Backend-Debug-TTY", "D\0", 40},
{"options", "PGOPTIONS", DefaultOption, NULL,
- "Backend-Debug-Options", "D", 40},
+ "Backend-Debug-Options", "D\0R", 40},
{"application_name", "PGAPPNAME", NULL, NULL,
- "Application-Name", "", 64},
+ "Application-Name", "\0", 64},
{"fallback_application_name", NULL, NULL, NULL,
- "Fallback-Application-Name", "", 64},
+ "Fallback-Application-Name", "\0", 64},
{"keepalives", NULL, NULL, NULL,
- "TCP-Keepalives", "", 1}, /* should be just '0' or '1' */
+ "TCP-Keepalives", "\0R", 1}, /* should be just '0' or '1' */
{"keepalives_idle", NULL, NULL, NULL,
- "TCP-Keepalives-Idle", "", 10}, /* strlen(INT32_MAX) == 10 */
+ "TCP-Keepalives-Idle", "\0R", 10}, /* strlen(INT32_MAX) == 10 */
{"keepalives_interval", NULL, NULL, NULL,
- "TCP-Keepalives-Interval", "", 10}, /* strlen(INT32_MAX) == 10 */
+ "TCP-Keepalives-Interval", "\0R", 10}, /* strlen(INT32_MAX) == 10 */
{"keepalives_count", NULL, NULL, NULL,
- "TCP-Keepalives-Count", "", 10}, /* strlen(INT32_MAX) == 10 */
+ "TCP-Keepalives-Count", "\0R", 10}, /* strlen(INT32_MAX) == 10 */
#ifdef USE_SSL
@@ -210,7 +215,7 @@ static const PQconninfoOption PQconninfo
* "sslmode". It remains for backwards compatibility.
*/
{"requiressl", "PGREQUIRESSL", "0", NULL,
- "Require-SSL", "D", 1},
+ "Require-SSL", "D\0", 1},
#endif
/*
@@ -220,30 +225,30 @@ static const PQconninfoOption PQconninfo
* to exclude them since none of them are mandatory.
*/
{"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
- "SSL-Mode", "", 8}, /* sizeof("disable") == 8 */
+ "SSL-Mode", "\0R", 8}, /* sizeof("disable") == 8 */
{"sslcompression", "PGSSLCOMPRESSION", "1", NULL,
- "SSL-Compression", "", 1},
+ "SSL-Compression", "\0R", 1},
{"sslcert", "PGSSLCERT", NULL, NULL,
- "SSL-Client-Cert", "", 64},
+ "SSL-Client-Cert", "\0R", 64},
{"sslkey", "PGSSLKEY", NULL, NULL,
- "SSL-Client-Key", "", 64},
+ "SSL-Client-Key", "\0R", 64},
{"sslrootcert", "PGSSLROOTCERT", NULL, NULL,
- "SSL-Root-Certificate", "", 64},
+ "SSL-Root-Certificate", "\0R", 64},
{"sslcrl", "PGSSLCRL", NULL, NULL,
- "SSL-Revocation-List", "", 64},
+ "SSL-Revocation-List", "\0R", 64},
{"requirepeer", "PGREQUIREPEER", NULL, NULL,
- "Require-Peer", "", 10},
+ "Require-Peer", "\0R", 10},
#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
/* Kerberos and GSSAPI authentication support specifying the service name */
{"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
- "Kerberos-service-name", "", 20},
+ "Kerberos-service-name", "\0R", 20},
#endif
#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
@@ -253,17 +258,72 @@ static const PQconninfoOption PQconninfo
* default
*/
{"gsslib", "PGGSSLIB", NULL, NULL,
- "GSS-library", "", 7}, /* sizeof("gssapi") = 7 */
+ "GSS-library", "\0R", 7}, /* sizeof("gssapi") = 7 */
#endif
{"replication", NULL, NULL, NULL,
- "Replication", "D", 5},
+ "Replication", "D\0", 5},
/* Terminating entry --- MUST BE LAST */
{NULL, NULL, NULL, NULL,
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;
+ char *conninfoValue; /* Special value mapping */
+ char *connValue;
+} 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), NULL, NULL },
+ { "password", offsetof(struct pg_conn, pgpass), NULL, NULL },
+ { "connect_timeout", offsetof(struct pg_conn, connect_timeout), NULL, NULL },
+ { "dbname", offsetof(struct pg_conn, dbName), NULL, NULL },
+ { "host", offsetof(struct pg_conn, pghost), NULL, NULL },
+ { "hostaddr", offsetof(struct pg_conn, pghostaddr), NULL, NULL },
+ { "port", offsetof(struct pg_conn, pgport), NULL, NULL },
+ { "client_encoding", offsetof(struct pg_conn, client_encoding_initial), NULL, NULL },
+ { "tty", offsetof(struct pg_conn, pgtty), NULL, NULL },
+ { "options", offsetof(struct pg_conn, pgoptions), NULL, NULL },
+ { "application_name", offsetof(struct pg_conn, appname), NULL, NULL },
+ { "fallback_application_name", offsetof(struct pg_conn, fbappname), NULL, NULL },
+ { "keepalives", offsetof(struct pg_conn, keepalives), NULL, NULL },
+ { "keepalives_idle", offsetof(struct pg_conn, keepalives_idle), NULL, NULL },
+ { "keepalives_interval", offsetof(struct pg_conn, keepalives_interval), NULL, NULL },
+ { "keepalives_count", offsetof(struct pg_conn, keepalives_count), NULL, NULL },
+#ifdef USE_SSL
+ { "requiressl", offsetof(struct pg_conn, sslmode), "1", "require" },
+#endif
+ { "sslmode", offsetof(struct pg_conn, sslmode), NULL, NULL },
+ { "sslcompression", offsetof(struct pg_conn, sslcompression), NULL, NULL },
+ { "sslcert", offsetof(struct pg_conn, sslcert), NULL, NULL },
+ { "sslkey", offsetof(struct pg_conn, sslkey), NULL, NULL },
+ { "sslrootcert", offsetof(struct pg_conn, sslrootcert), NULL, NULL },
+ { "sslcrl", offsetof(struct pg_conn, sslcrl), NULL, NULL },
+ { "requirepeer", offsetof(struct pg_conn, requirepeer), NULL, NULL },
+#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
+ { "krbsrvname", offsetof(struct pg_conn, krbsrvname), NULL, NULL },
+#endif
+#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
+ { "gsslib", offsetof(struct pg_conn, requirepeer), NULL, NULL },
+#endif
+ { "replication", offsetof(struct pg_conn, replication), NULL, NULL },
+ /* Terminating entry --- MUST BE LAST */
+ { NULL, 0, NULL, NULL }
+};
+
static const PQEnvironmentOption EnvironmentOptions[] =
{
/* common user-interface settings */
@@ -318,6 +378,8 @@ static char *conninfo_uri_decode(const c
static bool get_hexdigit(char digit, int *value);
static const char *conninfo_getval(PQconninfoOption *connOptions,
const char *keyword);
+static void conninfo_setval(PQconninfoOption *connOptions,
+ const char *keyword, const char *val);
static PQconninfoOption *conninfo_storeval(PQconninfoOption *connOptions,
const char *keyword, const char *value,
PQExpBuffer errorMessage, bool ignoreMissing, bool uri_decode);
@@ -627,7 +689,9 @@ PQconnectStart(const char *conninfo)
static void
fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
{
+ const PQconninfoMapping *mapping;
const char *tmp;
+ char **memberaddr;
/*
* Move option values into conn structure
@@ -637,72 +701,24 @@ 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");
+ tmp = conninfo_getval(connOptions, mapping->keyword);
+ memberaddr = PGCONNMEMBERADDR(conn, mapping);
+
+ if (mapping->conninfoValue && mapping->connValue)
+ {
+ size_t len = strlen(mapping->conninfoValue);
+ if (tmp && strncmp(tmp, mapping->conninfoValue, len) == 0)
+ {
+ if (*memberaddr)
+ free(*memberaddr);
+ *memberaddr = strdup(mapping->connValue);
+ }
+ }
+ else
+ *memberaddr = 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;
}
/*
@@ -4985,6 +5001,24 @@ conninfo_getval(PQconninfoOption *connOp
}
/*
+ * Set an option value corresponding to the keyword in the connOptions array.
+ */
+static void
+conninfo_setval(PQconninfoOption *connOptions, const char *keyword,
+ const char *val)
+{
+ PQconninfoOption *option;
+
+ option = conninfo_find(connOptions, keyword);
+ if (option)
+ {
+ if (option->val)
+ free(option->val);
+ option->val = val ? strdup(val) : NULL;
+ }
+}
+
+/*
* Store a (new) value for an option corresponding to the keyword in
* connOptions array.
*
@@ -5066,6 +5100,56 @@ conninfo_find(PQconninfoOption *connOpti
}
+/*
+ * Return the connection options used for the connections
+ */
+PQconninfoOption *
+PQconninfo(PGconn *conn)
+{
+ 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);
+
+ termPQExpBuffer(&errorBuf);
+
+ if (connOptions != NULL)
+ {
+ const PQconninfoMapping *mapping;
+
+ for (mapping = PQconninfoMappings; mapping->keyword; mapping++)
+ {
+ char **memberaddr = PGCONNMEMBERADDR(conn, mapping);
+
+ conninfo_setval(connOptions, mapping->keyword, *memberaddr);
+ }
+ }
+
+ return connOptions;
+}
+
+
+bool
+PQconninfoForReplication(PQconninfoOption *option)
+{
+ char *tmp;
+
+ if (option == NULL)
+ return false;
+
+ tmp = option->dispchar + strlen(option->dispchar) + 1;
+ return (*tmp == 'R');
+}
+
+
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-08-03 09:39:30.122266626 +0200
+++ postgresql.1/src/interfaces/libpq/libpq-fe.h 2012-10-04 11:29:16.112252964 +0200
@@ -193,7 +193,8 @@ typedef struct _PQconninfoOption
* connect dialog. Values are: "" Display
* entered value as is "*" Password field -
* hide value "D" Debug option - don't show
- * by default */
+ * by default. Optionally a hidden "R" character
+ * after the first '\0' character. */
int dispsize; /* Field size in characters for dialog */
} PQconninfoOption;
@@ -262,6 +263,12 @@ 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);
+
+/* return whether the connection option is valid for a replication connection */
+extern bool PQconninfoForReplication(PQconninfoOption *option);
+
/* 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-08-24 09:49:22.960530329 +0200
+++ postgresql.2/doc/src/sgml/ref/pg_basebackup.sgml 2012-10-04 11:32:21.004441824 +0200
@@ -189,6 +189,27 @@ 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 using
+ the connection parameters from the command line to ease
+ setting up the standby. Since creating a backup for a standalone
+ server with <option>-x</option> or <option>-X</option> and adding
+ a recovery.conf to it wouldn't make a working standby, these options
+ naturally conflict.
+ </para>
+ <para>
+ When this option is specified and taking the base backup succeeded,
+ failing to write the <filename>recovery.conf</filename> results
+ in the error code 2. This way, scripts can distinguish between different
+ failure cases of <application>pg_basebackup</application>.
+ </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-10-04 11:34:19.137201718 +0200
@@ -46,6 +46,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,14 +71,18 @@ static int has_xlogendptr = 0;
static volatile LONG has_xlogendptr = 0;
#endif
+static PQconninfoOption *connOptions = 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 stderr_write_error(FILE *cf, char *filename);
static void ReceiveTarFile(PGconn *conn, PGresult *res, int rownum);
static void ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum);
static void BaseBackup(void);
+static void WriteRecoveryConf(void);
static bool reached_end_position(XLogRecPtr segendpos, uint32 timeline,
bool segment_finished);
@@ -107,6 +112,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"));
@@ -960,6 +967,10 @@ BaseBackup(void)
/* Error message already written in GetConnection() */
exit(1);
+ /* If recovery.conf is to be written, keep the connection parameters for later usage */
+ if (writerecoveryconf)
+ connOptions = PQconninfo(conn);
+
/*
* Run IDENTIFY_SYSTEM so we can get the timeline
*/
@@ -1234,6 +1245,71 @@ BaseBackup(void)
}
+static void
+stderr_write_error(FILE *cf, char *filename)
+{
+ fprintf(stderr, _("cannot write to %s: %s"), filename, strerror(errno));
+ fclose(cf);
+ unlink(filename);
+ exit(2);
+}
+
+
+/*
+ * Attempt to create recovery.conf and write the expected contents to it.
+ */
+static void
+WriteRecoveryConf(void)
+{
+ char filename[MAXPGPATH];
+ PQconninfoOption *option;
+ FILE *cf;
+
+ if (!writerecoveryconf)
+ return;
+
+ sprintf(filename, "%s/recovery.conf", basedir);
+
+ cf = fopen(filename, "w");
+ if (cf == NULL)
+ {
+ fprintf(stderr, _("cannot create %s: %s"), filename, strerror(errno));
+ exit(2);
+ }
+
+ if (fprintf(cf, "standby_mode = 'on'\n") < 0)
+ stderr_write_error(cf, filename);
+
+ if (fprintf(cf, "primary_conninfo = '") < 0)
+ stderr_write_error(cf, filename);
+
+ for (option = connOptions; option && option->keyword; option++)
+ {
+ /*
+ * Do not emit this setting if not for replication,
+ * not set, empty or default.
+ */
+ if (!PQconninfoForReplication(option) ||
+ option->val == NULL ||
+ (option->val != NULL && option->val[0] == '\0') ||
+ (option->val &&
+ option->compiled &&
+ strcmp(option->val, option->compiled) == 0))
+ continue;
+
+ /* write "keyword='value'" pieces, single quotes doubled */
+ if (fprintf(cf, "%s=''%s'' ", option->keyword, option->val) < 0)
+ stderr_write_error(cf, filename);
+ }
+
+ if (fprintf(cf, "'\n") < 0)
+ stderr_write_error(cf, filename);
+
+ PQconninfoFree(connOptions);
+
+ fclose(cf);
+}
+
int
main(int argc, char **argv)
{
@@ -1243,6 +1319,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 +1357,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 +1378,9 @@ main(int argc, char **argv)
exit(1);
}
break;
+ case 'R':
+ writerecoveryconf = true;
+ break;
case 'x':
if (includewal)
{
@@ -1466,6 +1546,13 @@ main(int argc, char **argv)
}
#endif
+ if (writerecoveryconf && includewal)
+ {
+ fprintf(stderr,
+ _("--xlog and --writerecoveryconf are mutually exclusive\n"));
+ exit(1);
+ }
+
/*
* Verify that the target directory exists, or create it. For plaintext
* backups, always require the directory. For tar backups, require it
@@ -1476,5 +1563,7 @@ main(int argc, char **argv)
BaseBackup();
+ WriteRecoveryConf();
+
return 0;
}
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers