Skip to content

Commit 363e8f9

Browse files
committed
Fix pg_basebackup with in-place tablespaces some more.
Commit c6f2f01 purported to make this work, but problems remained. In a plain-format backup, the files from an in-place tablespace got included in the tar file for the main tablespace, which is wrong but it's not clear that it has any user-visible consequences. In a tar-format backup, the TABLESPACE_MAP option is used, and so we never iterated over pg_tblspc and thus never backed up the in-place tablespaces anywhere at all. To fix this, reverse the changes in that commit, so that when we scan pg_tblspc during a backup, we create tablespaceinfo objects even for in-place tablespaces. We set the field that would normally contain the absolute pathname to the relative path pg_tblspc/${TSOID}, and that's good enough to make basebackup.c happy without any further changes. However, pg_basebackup needs a couple of adjustments to make it work. First, it needs to understand that a relative path for a tablespace means it's an in-place tablespace. Second, it needs to tolerate the situation where restoring the main tablespace tries to create pg_tblspc or a subdirectory and finds that it already exists, because we restore user-defined tablespaces before the main tablespace. Since in-place tablespaces are only intended for use in development and testing, no back-patch. Patch by me, reviewed by Thomas Munro and Michael Paquier. Discussion: http://postgr.es/m/CA+TgmobwvbEp+fLq2PykMYzizcvuNv0a7gPMJtxOTMOuuRLMHg@mail.gmail.com
1 parent f180290 commit 363e8f9

File tree

4 files changed

+159
-66
lines changed

4 files changed

+159
-66
lines changed

src/backend/access/transam/xlog.c

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8454,76 +8454,92 @@ do_pg_backup_start(const char *backupidstr, bool fast, List **tablespaces,
84548454
char fullpath[MAXPGPATH + 10];
84558455
char linkpath[MAXPGPATH];
84568456
char *relpath = NULL;
8457-
int rllen;
8458-
StringInfoData escapedpath;
84598457
char *s;
8458+
PGFileType de_type;
84608459

84618460
/* Skip anything that doesn't look like a tablespace */
84628461
if (strspn(de->d_name, "0123456789") != strlen(de->d_name))
84638462
continue;
84648463

84658464
snprintf(fullpath, sizeof(fullpath), "pg_tblspc/%s", de->d_name);
84668465

8467-
/*
8468-
* Skip anything that isn't a symlink/junction. For testing only,
8469-
* we sometimes use allow_in_place_tablespaces to create
8470-
* directories directly under pg_tblspc, which would fail below.
8471-
*/
8472-
if (get_dirent_type(fullpath, de, false, ERROR) != PGFILETYPE_LNK)
8473-
continue;
8466+
de_type = get_dirent_type(fullpath, de, false, ERROR);
84748467

8475-
rllen = readlink(fullpath, linkpath, sizeof(linkpath));
8476-
if (rllen < 0)
8468+
if (de_type == PGFILETYPE_LNK)
84778469
{
8478-
ereport(WARNING,
8479-
(errmsg("could not read symbolic link \"%s\": %m",
8480-
fullpath)));
8481-
continue;
8470+
StringInfoData escapedpath;
8471+
int rllen;
8472+
8473+
rllen = readlink(fullpath, linkpath, sizeof(linkpath));
8474+
if (rllen < 0)
8475+
{
8476+
ereport(WARNING,
8477+
(errmsg("could not read symbolic link \"%s\": %m",
8478+
fullpath)));
8479+
continue;
8480+
}
8481+
else if (rllen >= sizeof(linkpath))
8482+
{
8483+
ereport(WARNING,
8484+
(errmsg("symbolic link \"%s\" target is too long",
8485+
fullpath)));
8486+
continue;
8487+
}
8488+
linkpath[rllen] = '\0';
8489+
8490+
/*
8491+
* Relpath holds the relative path of the tablespace directory
8492+
* when it's located within PGDATA, or NULL if it's located
8493+
* elsewhere.
8494+
*/
8495+
if (rllen > datadirpathlen &&
8496+
strncmp(linkpath, DataDir, datadirpathlen) == 0 &&
8497+
IS_DIR_SEP(linkpath[datadirpathlen]))
8498+
relpath = pstrdup(linkpath + datadirpathlen + 1);
8499+
8500+
/*
8501+
* Add a backslash-escaped version of the link path to the
8502+
* tablespace map file.
8503+
*/
8504+
initStringInfo(&escapedpath);
8505+
for (s = linkpath; *s; s++)
8506+
{
8507+
if (*s == '\n' || *s == '\r' || *s == '\\')
8508+
appendStringInfoChar(&escapedpath, '\\');
8509+
appendStringInfoChar(&escapedpath, *s);
8510+
}
8511+
appendStringInfo(tblspcmapfile, "%s %s\n",
8512+
de->d_name, escapedpath.data);
8513+
pfree(escapedpath.data);
84828514
}
8483-
else if (rllen >= sizeof(linkpath))
8515+
else if (de_type == PGFILETYPE_DIR)
84848516
{
8485-
ereport(WARNING,
8486-
(errmsg("symbolic link \"%s\" target is too long",
8487-
fullpath)));
8488-
continue;
8517+
/*
8518+
* It's possible to use allow_in_place_tablespaces to create
8519+
* directories directly under pg_tblspc, for testing purposes
8520+
* only.
8521+
*
8522+
* In this case, we store a relative path rather than an
8523+
* absolute path into the tablespaceinfo.
8524+
*/
8525+
snprintf(linkpath, sizeof(linkpath), "pg_tblspc/%s",
8526+
de->d_name);
8527+
relpath = pstrdup(linkpath);
84898528
}
8490-
linkpath[rllen] = '\0';
8491-
8492-
/*
8493-
* Build a backslash-escaped version of the link path to include
8494-
* in the tablespace map file.
8495-
*/
8496-
initStringInfo(&escapedpath);
8497-
for (s = linkpath; *s; s++)
8529+
else
84988530
{
8499-
if (*s == '\n' || *s == '\r' || *s == '\\')
8500-
appendStringInfoChar(&escapedpath, '\\');
8501-
appendStringInfoChar(&escapedpath, *s);
8531+
/* Skip any other file type that appears here. */
8532+
continue;
85028533
}
85038534

8504-
/*
8505-
* Relpath holds the relative path of the tablespace directory
8506-
* when it's located within PGDATA, or NULL if it's located
8507-
* elsewhere.
8508-
*/
8509-
if (rllen > datadirpathlen &&
8510-
strncmp(linkpath, DataDir, datadirpathlen) == 0 &&
8511-
IS_DIR_SEP(linkpath[datadirpathlen]))
8512-
relpath = linkpath + datadirpathlen + 1;
8513-
85148535
ti = palloc(sizeof(tablespaceinfo));
85158536
ti->oid = pstrdup(de->d_name);
85168537
ti->path = pstrdup(linkpath);
8517-
ti->rpath = relpath ? pstrdup(relpath) : NULL;
8538+
ti->rpath = relpath;
85188539
ti->size = -1;
85198540

85208541
if (tablespaces)
85218542
*tablespaces = lappend(*tablespaces, ti);
8522-
8523-
appendStringInfo(tblspcmapfile, "%s %s\n",
8524-
ti->oid, escapedpath.data);
8525-
8526-
pfree(escapedpath.data);
85278543
}
85288544
FreeDir(tblspcdir);
85298545

src/bin/pg_basebackup/bbstreamer_file.c

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -276,28 +276,49 @@ bbstreamer_extractor_content(bbstreamer *streamer, bbstreamer_member *member,
276276
}
277277
}
278278

279+
/*
280+
* Should we tolerate an already-existing directory?
281+
*
282+
* When streaming WAL, pg_wal (or pg_xlog for pre-9.6 clusters) will have been
283+
* created by the wal receiver process. Also, when the WAL directory location
284+
* was specified, pg_wal (or pg_xlog) has already been created as a symbolic
285+
* link before starting the actual backup. So just ignore creation failures
286+
* on related directories.
287+
*
288+
* If in-place tablespaces are used, pg_tblspc and subdirectories may already
289+
* exist when we get here. So tolerate that case, too.
290+
*/
291+
static bool
292+
should_allow_existing_directory(const char *pathname)
293+
{
294+
const char *filename = last_dir_separator(pathname) + 1;
295+
296+
if (strcmp(filename, "pg_wal") == 0 ||
297+
strcmp(filename, "pg_xlog") == 0 ||
298+
strcmp(filename, "archive_status") == 0 ||
299+
strcmp(filename, "pg_tblspc") == 0)
300+
return true;
301+
302+
if (strspn(filename, "0123456789") == strlen(filename))
303+
{
304+
const char *pg_tblspc = strstr(pathname, "/pg_tblspc/");
305+
306+
return pg_tblspc != NULL && pg_tblspc + 11 == filename;
307+
}
308+
309+
return false;
310+
}
311+
279312
/*
280313
* Create a directory.
281314
*/
282315
static void
283316
extract_directory(const char *filename, mode_t mode)
284317
{
285-
if (mkdir(filename, pg_dir_create_mode) != 0)
286-
{
287-
/*
288-
* When streaming WAL, pg_wal (or pg_xlog for pre-9.6 clusters) will
289-
* have been created by the wal receiver process. Also, when the WAL
290-
* directory location was specified, pg_wal (or pg_xlog) has already
291-
* been created as a symbolic link before starting the actual backup.
292-
* So just ignore creation failures on related directories.
293-
*/
294-
if (!((pg_str_endswith(filename, "/pg_wal") ||
295-
pg_str_endswith(filename, "/pg_xlog") ||
296-
pg_str_endswith(filename, "/archive_status")) &&
297-
errno == EEXIST))
298-
pg_fatal("could not create directory \"%s\": %m",
299-
filename);
300-
}
318+
if (mkdir(filename, pg_dir_create_mode) != 0 &&
319+
(errno != EEXIST || !should_allow_existing_directory(filename)))
320+
pg_fatal("could not create directory \"%s\": %m",
321+
filename);
301322

302323
#ifndef WIN32
303324
if (chmod(filename, mode))

src/bin/pg_basebackup/pg_basebackup.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,9 +1122,17 @@ CreateBackupStreamer(char *archive_name, char *spclocation,
11221122
* other tablespaces will be written to the directory where they're
11231123
* located on the server, after applying any user-specified tablespace
11241124
* mappings.
1125+
*
1126+
* In the case of an in-place tablespace, spclocation will be a
1127+
* relative path. We just convert it to an absolute path by prepending
1128+
* basedir.
11251129
*/
1126-
directory = spclocation == NULL ? basedir
1127-
: get_tablespace_mapping(spclocation);
1130+
if (spclocation == NULL)
1131+
directory = basedir;
1132+
else if (!is_absolute_path(spclocation))
1133+
directory = psprintf("%s/%s", basedir, spclocation);
1134+
else
1135+
directory = get_tablespace_mapping(spclocation);
11281136
streamer = bbstreamer_extractor_new(directory,
11291137
get_tablespace_mapping,
11301138
progress_update_filename);
@@ -1955,7 +1963,15 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
19551963
*/
19561964
if (backup_target == NULL && format == 'p' && !PQgetisnull(res, i, 1))
19571965
{
1958-
char *path = unconstify(char *, get_tablespace_mapping(PQgetvalue(res, i, 1)));
1966+
char *path = PQgetvalue(res, i, 1);
1967+
1968+
if (is_absolute_path(path))
1969+
path = unconstify(char *, get_tablespace_mapping(path));
1970+
else
1971+
{
1972+
/* This is an in-place tablespace, so prepend basedir. */
1973+
path = psprintf("%s/%s", basedir, path);
1974+
}
19591975

19601976
verify_dir_is_empty_or_create(path, &made_tablespace_dirs, &found_tablespace_dirs);
19611977
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright (c) 2021-2023, PostgreSQL Global Development Group
2+
3+
use strict;
4+
use warnings;
5+
use PostgreSQL::Test::Cluster;
6+
use PostgreSQL::Test::Utils;
7+
use Test::More;
8+
9+
my $tempdir = PostgreSQL::Test::Utils::tempdir;
10+
11+
# For nearly all pg_basebackup invocations some options should be specified,
12+
# to keep test times reasonable. Using @pg_basebackup_defs as the first
13+
# element of the array passed to IPC::Run interpolate the array (as it is
14+
# not a reference to an array)...
15+
my @pg_basebackup_defs = ('pg_basebackup', '--no-sync', '-cfast');
16+
17+
# Set up an instance.
18+
my $node = PostgreSQL::Test::Cluster->new('main');
19+
$node->init('allows_streaming' => 1);
20+
$node->start();
21+
22+
# Create an in-place tablespace.
23+
$node->safe_psql('postgres', <<EOM);
24+
SET allow_in_place_tablespaces = on;
25+
CREATE TABLESPACE inplace LOCATION '';
26+
EOM
27+
28+
# Back it up.
29+
my $backupdir = $tempdir . '/backup';
30+
$node->command_ok(
31+
[ @pg_basebackup_defs, '-D', $backupdir, '-Ft', '-X', 'none' ],
32+
'pg_basebackup runs');
33+
34+
# Make sure we got base.tar and one tablespace.
35+
ok(-f "$backupdir/base.tar", 'backup tar was created');
36+
my @tblspc_tars = glob "$backupdir/[0-9]*.tar";
37+
is(scalar(@tblspc_tars), 1, 'one tablespace tar was created');
38+
39+
# All good.
40+
done_testing();

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy