提交 0e0153bd 编写于 作者: J Jimmy Yih

Autogenerate recovery.conf file at the end of pg_rewind

Like pg_basebackup, we should generate recovery.conf file at the end of
pg_rewind so that utilities do not have to take care of that step. This is
required in Greenplum mainly because users will not use pg_rewind manually. Note
that we only autogenerate recovery.conf file if pg_rewind is called with source
server because we utilize the libpq connection information. We expect pg_rewind
usage to only be through gprecoverseg.

Added TODO message to create common library between pg_basebackup and pg_rewind
to create the recovery.conf file since most of this code addition is copied from
pg_basebackup.c file (there are a couple diffs to make it work for pg_rewind).
Co-authored-by: NPaul Guo <pguo@pivotal.io>
上级 9c66b2b1
......@@ -43,4 +43,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 */
......@@ -30,9 +30,13 @@
#include "catalog/catalog.h"
#include "catalog/pg_type.h"
#include "pqexpbuffer.h"
static PGconn *conn = NULL;
/* Contents of recovery.conf to be generated */
static PQExpBuffer recoveryconfcontents = NULL;
/*
* Files are fetched max CHUNKSIZE bytes at a time.
*
......@@ -554,3 +558,213 @@ execute_pagemap(datapagemap_t *pagemap, const char *path)
}
pg_free(iter);
}
/*
* TODO: Most of the below code is copied directly from pg_basebackup.c. There
* are only a couple subtle differences if you diff the two (e.g. removal of
* recovery.done file, excluding "options" to avoid utility mode, etc.). We
* should create a common library between the two to create the recovery.conf
* file.
*/
static void
disconnect_and_exit(int code)
{
if (conn != NULL)
PQfinish(conn);
exit(code);
}
/*
* Escape a parameter value so that it can be used as part of a libpq
* connection string, e.g. in:
*
* application_name=<value>
*
* The returned string is malloc'd. Return NULL on out-of-memory.
*/
static char *
escapeConnectionParameter(const char *src)
{
bool need_quotes = false;
bool need_escaping = false;
const char *p;
char *dstbuf;
char *dst;
/*
* First check if quoting is needed. Any quote (') or backslash (\)
* characters need to be escaped. Parameters are separated by whitespace,
* so any string containing whitespace characters need to be quoted. An
* empty string is represented by ''.
*/
if (strchr(src, '\'') != NULL || strchr(src, '\\') != NULL)
need_escaping = true;
for (p = src; *p; p++)
{
if (isspace((unsigned char) *p))
{
need_quotes = true;
break;
}
}
if (*src == '\0')
return pg_strdup("''");
if (!need_quotes && !need_escaping)
return pg_strdup(src); /* no quoting or escaping needed */
/*
* Allocate a buffer large enough for the worst case that all the source
* characters need to be escaped, plus quotes.
*/
dstbuf = pg_malloc(strlen(src) * 2 + 2 + 1);
dst = dstbuf;
if (need_quotes)
*(dst++) = '\'';
for (; *src; src++)
{
if (*src == '\'' || *src == '\\')
*(dst++) = '\\';
*(dst++) = *src;
}
if (need_quotes)
*(dst++) = '\'';
*dst = '\0';
return dstbuf;
}
/*
* 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 recovery.conf 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);
disconnect_and_exit(1);
}
connOptions = PQconninfo(conn);
if (connOptions == NULL)
{
fprintf(stderr, _("%s: out of memory\n"), progname);
disconnect_and_exit(1);
}
appendPQExpBufferStr(recoveryconfcontents, "standby_mode = 'on'\n");
appendPQExpBufferStr(recoveryconfcontents, "recovery_target_timeline = 'latest'\n");
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 ||
strcmp(option->keyword, "options") == 0 ||
(option->val == NULL) ||
(option->val[0] == '\0'))
continue;
/* Separate key-value pairs with spaces */
if (conninfo_buf.len != 0)
appendPQExpBufferStr(&conninfo_buf, " ");
/*
* Write "keyword=value" pieces, the value string is escaped and/or
* quoted if necessary.
*/
escaped = escapeConnectionParameter(option->val);
appendPQExpBuffer(&conninfo_buf, "%s=%s", option->keyword, escaped);
free(escaped);
}
/*
* 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);
disconnect_and_exit(1);
}
termPQExpBuffer(&conninfo_buf);
PQconninfoFree(connOptions);
}
/*
* Write a recovery.conf file into the directory specified in basedir,
* with the contents already collected in memory.
*/
void
WriteRecoveryConf(void)
{
char filename[MAXPGPATH];
FILE *cf;
/* Remove recovery.done file that was most likely copied from source instance */
snprintf(filename, sizeof(filename), "%s/recovery.done", datadir_target);
unlink(filename);
snprintf(filename, sizeof(filename), "%s/recovery.conf", datadir_target);
cf = fopen(filename, "w");
if (cf == NULL)
{
fprintf(stderr, _("%s: could not create file \"%s\": %s\n"), progname, filename, strerror(errno));
disconnect_and_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));
disconnect_and_exit(1);
}
fclose(cf);
}
......@@ -43,6 +43,8 @@ static void ensureCleanShutdown(const char *argv0);
static ControlFileData ControlFile_target;
static ControlFileData ControlFile_source;
static bool writerecoveryconf = false;
const char *progname;
/* Configuration options */
......@@ -63,6 +65,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 recovery.conf after backup\n"));
printf(_(" -n, --dry-run stop before modifying anything\n"));
printf(_(" -P, --progress write progress messages\n"));
printf(_(" --debug write a lot of debug messages\n"));
......@@ -78,6 +81,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'},
......@@ -118,7 +122,7 @@ main(int argc, char **argv)
}
}
while ((c = getopt_long(argc, argv, "D:nP", long_options, &option_index)) != -1)
while ((c = getopt_long(argc, argv, "D:nPR", long_options, &option_index)) != -1)
{
switch (c)
{
......@@ -134,6 +138,10 @@ main(int argc, char **argv)
dry_run = true;
break;
case 'R':
writerecoveryconf = true;
break;
case 3:
debug = true;
break;
......@@ -277,6 +285,13 @@ main(int argc, char **argv)
if (!rewind_needed)
{
printf(_("no rewind required\n"));
if (writerecoveryconf && connstr_source)
{
GenerateRecoveryConf();
WriteRecoveryConf();
}
exit(0);
}
......@@ -367,6 +382,12 @@ main(int argc, char **argv)
pg_log(PG_PROGRESS, "syncing target data directory\n");
syncTargetDirectory(argv[0]);
if (writerecoveryconf && connstr_source)
{
GenerateRecoveryConf();
WriteRecoveryConf();
}
printf(_("Done!\n"));
return 0;
......
......@@ -27,6 +27,8 @@ extern bool debug;
extern bool showprogress;
extern bool dry_run;
extern const char *progname;
/* in parsexlog.c */
extern void extractPageMap(const char *datadir, XLogRecPtr startpoint,
TimeLineID tli, XLogRecPtr endpoint);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册