diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_basebackup.sgml index eb0c1d6..4c4d06d 100644 --- a/doc/src/sgml/ref/pg_basebackup.sgml +++ b/doc/src/sgml/ref/pg_basebackup.sgml @@ -203,6 +203,18 @@ PostgreSQL documentation + + + + Specify the location for the transaction log directory. + xlogdir should be an absolute path. + User can specify the xlog directory, only if the format is plain. + Data directory and xlog directory shouldn't be same. + + + + + diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c index a1e12a8..bce9f45 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -35,6 +35,7 @@ /* Global options */ char *basedir = NULL; +static char *xlog_dir = ""; char format = 'p'; /* p(lain)/t(ar) */ char *label = "pg_basebackup base backup"; bool showprogress = false; @@ -73,6 +74,7 @@ static PQExpBuffer recoveryconfcontents = NULL; /* Function headers */ static void usage(void); +static void verify_data_and_xlog_dir_same(); static void verify_dir_is_empty_or_create(char *dirname); static void progress_report(int tablespacenum, const char *filename); @@ -115,6 +117,7 @@ usage(void) 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")); + printf(_(" --xlogdir=XLOGDIR location for the transaction log directory\n")); printf(_(" -z, --gzip compress tar output\n")); printf(_(" -Z, --compress=0-9 compress tar output with given compression level\n")); printf(_("\nGeneral options:\n")); @@ -343,6 +346,70 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier) } /* + * Verify the given data directory and transaction log directory are + * same or not. + */ +static void +verify_data_and_xlog_dir_same() +{ + char datadir[MAXPGPATH]; + char pwd[MAXPGPATH]; + char xlogdir[MAXPGPATH]; + + if (getcwd(pwd, MAXPGPATH) < 0) + { + fprintf(stderr, + _("%s: could not get current working directory: %s\n"), + progname, strerror(errno)); + exit(1); + } + + if (chdir(basedir) < 0) + { + fprintf(stderr, _("%s: could not change directory to \"%s\": %s\n"), + progname, basedir, strerror(errno)); + exit(1); + } + + if (getcwd(datadir, MAXPGPATH) < 0) + { + fprintf(stderr, + _("%s: could not get current working directory: %s\n"), + progname, strerror(errno)); + exit(1); + } + + if (chdir(xlog_dir) < 0) + { + fprintf(stderr, _("%s: could not change directory to \"%s\": %s\n"), + progname, xlog_dir, strerror(errno)); + exit(1); + } + + if (getcwd(xlogdir, MAXPGPATH) < 0) + { + fprintf(stderr, + _("%s: could not get current working directory: %s\n"), + progname, strerror(errno)); + exit(1); + } + + if (chdir(pwd) < 0) + { + fprintf(stderr, _("%s: could not change directory to \"%s\": %s\n"), + progname, pwd, strerror(errno)); + exit(1); + } + + if (strcmp(datadir, xlogdir) == 0) + { + fprintf(stderr, _("%s: Data directory and transaction log " + "directory are same\n"), progname); + exit(1); + } +} + +/* * Verify that the given directory exists and is empty. If it does not * exist, it is created. If it exists but is not empty, an error will * be give and the process ended. @@ -979,11 +1046,12 @@ ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum) if (mkdir(filename, S_IRWXU) != 0) { /* - * When streaming WAL, pg_xlog will have been created - * by the wal receiver process, so just ignore failure - * on that. + * If user specified xlog_dir or when streaming WAL, + * pg_xlog will have been created. So just ignore + * failure on that. */ - if (!streamwal || strcmp(filename + strlen(filename) - 8, "/pg_xlog") != 0) + if ((!streamwal && (strcmp(xlog_dir, "") == 0)) + || strcmp(filename + strlen(filename) - 8, "/pg_xlog") != 0) { fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"), @@ -1666,6 +1734,7 @@ main(int argc, char **argv) {"status-interval", required_argument, NULL, 's'}, {"verbose", no_argument, NULL, 'v'}, {"progress", no_argument, NULL, 'P'}, + {"xlogdir", required_argument, NULL, 1}, {NULL, 0, NULL, 0} }; int c; @@ -1750,6 +1819,9 @@ main(int argc, char **argv) exit(1); } break; + case 1: + xlog_dir = pg_strdup(optarg); + break; case 'l': label = pg_strdup(optarg); break; @@ -1862,10 +1934,11 @@ main(int argc, char **argv) exit(1); } - if (format != 'p' && streamwal) + if (format != 'p' && (streamwal || (strcmp(xlog_dir, "") != 0))) { fprintf(stderr, - _("%s: WAL streaming can only be used in plain mode\n"), + _("%s: Providing user specified xlog directory or " + "WAL streaming can only be used in plain mode\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); @@ -1890,6 +1963,42 @@ main(int argc, char **argv) if (format == 'p' || strcmp(basedir, "-") != 0) verify_dir_is_empty_or_create(basedir); + /* Create transaction log symlink, if required */ + if (strcmp(xlog_dir, "") != 0) + { + char *linkloc; + + /* clean up xlog directory name, check it's absolute */ + canonicalize_path(xlog_dir); + if (!is_absolute_path(xlog_dir)) + { + fprintf(stderr, _("%s: transaction log directory location must be " + "an absolute path\n"), progname); + exit(1); + } + + verify_data_and_xlog_dir_same(); + verify_dir_is_empty_or_create(xlog_dir); + + /* form name of the place where the symlink must go */ + linkloc = (char *) pg_malloc0(strlen(basedir) + 8 + 1); + sprintf(linkloc, "%s/pg_xlog", basedir); + +#ifdef HAVE_SYMLINK + if (symlink(xlog_dir, linkloc) != 0) + { + fprintf(stderr, _("%s: could not create symbolic link \"%s\": %s\n"), + progname, linkloc, strerror(errno)); + exit(1); + } +#else + fprintf(stderr, _("%s: symlinks are not supported on this platform " + "cannot use xlogdir")); + exit(1); +#endif + pg_free(linkloc); + } + BaseBackup(); return 0;