*** doc/src/sgml/ref/pg_basebackup.sgml
--- doc/src/sgml/ref/pg_basebackup.sgml
***************
*** 138,143 **** PostgreSQL documentation
--- 138,155 ----
+
+
+
+
+ Specifies the location where tablespace with oid
+ is written, tablespacedir must be an absolute path.
+ This options can be specified multiple times for multiple tablespaces.
+
+
+
+
+
***************
*** 530,536 **** PostgreSQL documentation
The way PostgreSQL manages tablespaces, the path
for all additional tablespaces must be identical whenever a backup is
! restored. The main data directory, however, is relocatable to any location.
--- 542,548 ----
The way PostgreSQL manages tablespaces, the path
for all additional tablespaces must be identical whenever a backup is
! restored, if --tablespace isn't specified.
***************
*** 570,575 **** PostgreSQL documentation
--- 582,595 ----
(This command will fail if there are multiple tablespaces in the
database.)
+
+
+ To create a backup of a two-tablespace local database where tablespace
+ archive is written to ./backup/archive
+
+ $ pg_basebackup -D $(pwd)/backup/data -T archive:$(pwd)/backup/archive
+
+
*** src/bin/pg_basebackup/pg_basebackup.c
--- src/bin/pg_basebackup/pg_basebackup.c
***************
*** 33,40 ****
--- 33,54 ----
#include "streamutil.h"
+ #define atooid(x) ((Oid) strtoul((x), NULL, 10))
+
+ typedef struct TablespaceListCell {
+ struct TablespaceListCell *next;
+ Oid oid;
+ char path[1];
+ } TablespaceListCell;
+
+ typedef struct TablespaceList {
+ TablespaceListCell *head;
+ TablespaceListCell *tail;
+ } TablespaceList;
+
/* Global options */
static char *basedir = NULL;
+ static TablespaceList tablespace_locations = {NULL, NULL};
static char *xlog_dir = "";
static char format = 'p'; /* p(lain)/t(ar) */
static char *label = "pg_basebackup base backup";
***************
*** 86,91 **** static void BaseBackup(void);
--- 100,140 ----
static bool reached_end_position(XLogRecPtr segendpos, uint32 timeline,
bool segment_finished);
+ static const char *get_tablespace_location(Oid oid, const char *location);
+ static void update_tablespace_symlink(Oid oid, const char *location);
+ static bool tablespace_list_append(TablespaceList *list, const char *tablespace);
+
+ static bool
+ tablespace_list_append(TablespaceList *list, const char *tablespace)
+ {
+ TablespaceListCell *cell;
+ char *path;
+ Oid oid = atooid(tablespace);
+
+ if (!oid)
+ return false;
+
+ path = strstr(tablespace, ":/");
+ if (!path)
+ return false;
+ path++;
+
+ cell = (TablespaceListCell *) pg_malloc(sizeof(TablespaceListCell) + strlen(path));
+
+ cell->next = NULL;
+ cell->oid = oid;
+ strcpy(cell->path, path);
+
+ if (list->tail)
+ list->tail->next = cell;
+ else
+ list->head = cell;
+ list->tail = cell;
+
+ return true;
+ }
+
+
#ifdef HAVE_LIBZ
static const char *
get_gz_error(gzFile gzf)
***************
*** 110,115 **** usage(void)
--- 159,166 ----
printf(_(" %s [OPTION]...\n"), progname);
printf(_("\nOptions controlling the output:\n"));
printf(_(" -D, --pgdata=DIRECTORY receive base backup into directory\n"));
+ printf(_(" -T, --tablespace=OID:LOCATION\n"
+ " Specify tablespace oid and its absolute location\n"));
printf(_(" -F, --format=p|t output format (plain (default), tar)\n"));
printf(_(" -R, --write-recovery-conf\n"
" write recovery.conf after backup\n"));
***************
*** 861,874 **** ReceiveTarFile(PGconn *conn, PGresult *res, int rownum)
}
/*
* Receive a tar format stream from the connection to the server, and unpack
* the contents of it into a directory. Only files, directories and
* symlinks are supported, no other kinds of special files.
*
* If the data is for the main data directory, it will be restored in the
* specified directory. If it's for another tablespace, it will be restored
! * in the original directory, since relocation of tablespaces is not
! * supported.
*/
static void
ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum)
--- 912,967 ----
}
/*
+ * Retrieve tablespace location, either relocated or original depending on
+ * whether -T oid:tablespacedir was passed or not.
+ */
+ static const char *
+ get_tablespace_location(Oid oid, const char *location)
+ {
+ TablespaceListCell *cell;
+
+ for (cell = tablespace_locations.head; cell; cell = cell->next)
+ if (oid == cell->oid)
+ return cell->path;
+
+ return location;
+ }
+
+ /*
+ * Update symlinks to reflect relocated tablespace, only applied if
+ * tablespace isn't in its original location.
+ */
+ static void
+ update_tablespace_symlink(Oid oid, const char *location)
+ {
+ const char *new_location = get_tablespace_location(oid, location);
+ if (strcmp(new_location, location) != 0)
+ {
+ char linkloc[MAXPGPATH];
+ snprintf(linkloc, sizeof(linkloc), "%s/pg_tblspc/%d", basedir, oid);
+ if (unlink(linkloc) < 0 && errno != ENOENT)
+ {
+ fprintf(stderr, _("%s: unable to remove \"%s\": %s"),
+ progname, linkloc, strerror(errno));
+ disconnect_and_exit(1);
+ }
+ if (symlink(new_location, linkloc) < 0)
+ {
+ fprintf(stderr, _("%s: unable to create symlink \"%s\": %s"),
+ progname, linkloc, strerror(errno));
+ disconnect_and_exit(1);
+ }
+ }
+ }
+
+ /*
* Receive a tar format stream from the connection to the server, and unpack
* the contents of it into a directory. Only files, directories and
* symlinks are supported, no other kinds of special files.
*
* If the data is for the main data directory, it will be restored in the
* specified directory. If it's for another tablespace, it will be restored
! * in the original directory, if tablespace relocation is not enabled.
*/
static void
ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum)
***************
*** 884,890 **** ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum)
if (basetablespace)
strcpy(current_path, basedir);
else
! strcpy(current_path, PQgetvalue(res, rownum, 1));
/*
* Get the COPY data
--- 977,987 ----
if (basetablespace)
strcpy(current_path, basedir);
else
! {
! strcpy(current_path,
! get_tablespace_location(atooid(PQgetvalue(res, rownum, 0)),
! PQgetvalue(res, rownum, 1)));
! }
/*
* Get the COPY data
***************
*** 1465,1471 **** BaseBackup(void)
* we do anything anyway.
*/
if (format == 'p' && !PQgetisnull(res, i, 1))
! verify_dir_is_empty_or_create(PQgetvalue(res, i, 1));
}
/*
--- 1562,1572 ----
* we do anything anyway.
*/
if (format == 'p' && !PQgetisnull(res, i, 1))
! {
! char *path = (char *) get_tablespace_location(atooid(PQgetvalue(res, i, 0)),
! PQgetvalue(res, i, 1));
! verify_dir_is_empty_or_create(path);
! }
}
/*
***************
*** 1507,1512 **** BaseBackup(void)
--- 1608,1629 ----
progress_report(PQntuples(res), NULL);
fprintf(stderr, "\n"); /* Need to move to next line */
}
+
+ if (format == 'p' && tablespace_locations.head != NULL)
+ {
+ #ifdef HAVE_SYMLINK
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ int tblspc_oid = atooid(PQgetvalue(res, i, 0));
+ if (tblspc_oid)
+ update_tablespace_symlink(tblspc_oid, PQgetvalue(res, i, 1));
+ }
+ #else
+ fprintf(stderr, _("%s: not updating pg_tblspc with new tablespace relocation\n"),
+ progname);
+ #endif
+ }
+
PQclear(res);
/*
***************
*** 1655,1660 **** main(int argc, char **argv)
--- 1772,1778 ----
{"help", no_argument, NULL, '?'},
{"version", no_argument, NULL, 'V'},
{"pgdata", required_argument, NULL, 'D'},
+ {"tablespace", required_argument, NULL, 'T'},
{"format", required_argument, NULL, 'F'},
{"checkpoint", required_argument, NULL, 'c'},
{"write-recovery-conf", no_argument, NULL, 'R'},
***************
*** 1697,1703 **** main(int argc, char **argv)
}
}
! while ((c = getopt_long(argc, argv, "D:F:RxX:l:zZ:d:c:h:p:U:s:wWvP",
long_options, &option_index)) != -1)
{
switch (c)
--- 1815,1821 ----
}
}
! while ((c = getopt_long(argc, argv, "D:T:F:RxX:l:zZ:d:c:h:p:U:s:wWvP",
long_options, &option_index)) != -1)
{
switch (c)
***************
*** 1705,1710 **** main(int argc, char **argv)
--- 1823,1837 ----
case 'D':
basedir = pg_strdup(optarg);
break;
+ case 'T':
+ if (!tablespace_list_append(&tablespace_locations, optarg))
+ {
+ fprintf(stderr,
+ _("%s: invalid tablespace format \"%s\", must be \"oid:/absolute-path\"\n"),
+ progname, optarg);
+ exit(1);
+ }
+ break;
case 'F':
if (strcmp(optarg, "p") == 0 || strcmp(optarg, "plain") == 0)
format = 'p';