Skip to content

Commit ca347f5

Browse files
committed
Allow "in place" tablespaces.
This is a backpatch to branches 10-14 of the following commits: 7170f21 Allow "in place" tablespaces. c6f2f01 Fix pg_basebackup with in-place tablespaces. f6f0db4 Fix pg_tablespace_location() with in-place tablespaces 7a7cd84 doc: Remove mention to in-place tablespaces for pg_tablespace_location() 5344723 Remove unnecessary Windows-specific basebackup code. In-place tablespaces were introduced as a testing helper mechanism, but they are going to be used for a bugfix in WAL replay to be backpatched to all stable branches. I (Álvaro) had to adjust some code to account for lack of get_dirent_type() in branches prior to 14. Author: Thomas Munro <thomas.munro@gmail.com> Author: Michaël Paquier <michael@paquier.xyz> Author: Álvaro Herrera <alvherre@alvh.no-ip.org> Discussion: https://postgr.es/m/20220722081858.omhn2in5zt3g4nek@alvherre.pgsql
1 parent 968b892 commit ca347f5

File tree

6 files changed

+114
-7
lines changed

6 files changed

+114
-7
lines changed

doc/src/sgml/config.sgml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9610,6 +9610,25 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
96109610
</para>
96119611

96129612
<variablelist>
9613+
<varlistentry id="guc-allow-in-place-tablespaces" xreflabel="allow_in_place_tablespaces">
9614+
<term><varname>allow_in_place_tablespaces</varname> (<type>boolean</type>)
9615+
<indexterm>
9616+
<primary><varname>allow_in_place_tablespaces</varname> configuration parameter</primary>
9617+
</indexterm>
9618+
</term>
9619+
<listitem>
9620+
<para>
9621+
Allows tablespaces to be created as directories inside
9622+
<filename>pg_tblspc</filename>, when an empty location string
9623+
is provided to the <command>CREATE TABLESPACE</command> command. This
9624+
is intended to allow testing replication scenarios where primary and
9625+
standby servers are running on the same machine. Such directories
9626+
are likely to confuse backup tools that expect to find only symbolic
9627+
links in that location. Only superusers can change this setting.
9628+
</para>
9629+
</listitem>
9630+
</varlistentry>
9631+
96139632
<varlistentry id="guc-allow-system-table-mods" xreflabel="allow_system_table_mods">
96149633
<term><varname>allow_system_table_mods</varname> (<type>boolean</type>)
96159634
<indexterm>

src/backend/access/transam/xlog.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10674,13 +10674,33 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
1067410674
int rllen;
1067510675
StringInfoData buflinkpath;
1067610676
char *s = linkpath;
10677+
#ifndef WIN32
10678+
struct stat st;
10679+
#endif
1067710680

1067810681
/* Skip special stuff */
1067910682
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
1068010683
continue;
1068110684

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

10687+
/*
10688+
* Skip anything that isn't a symlink/junction. For testing only,
10689+
* we sometimes use allow_in_place_tablespaces to create
10690+
* directories directly under pg_tblspc, which would fail below.
10691+
*/
10692+
#ifndef WIN32
10693+
if (lstat(fullpath, &st) < 0)
10694+
ereport(LOG,
10695+
(errcode_for_file_access(),
10696+
errmsg("could not stat file \"%s\": %m",
10697+
fullpath)));
10698+
else if (!S_ISLNK(st.st_mode))
10699+
#else /* WIN32 */
10700+
if (!pgwin32_is_junction(fullpath))
10701+
#endif
10702+
continue;
10703+
1068410704
#if defined(HAVE_READLINK) || defined(WIN32)
1068510705
rllen = readlink(fullpath, linkpath, sizeof(linkpath));
1068610706
if (rllen < 0)

src/backend/commands/tablespace.c

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
/* GUC variables */
8989
char *default_tablespace = NULL;
9090
char *temp_tablespaces = NULL;
91+
bool allow_in_place_tablespaces = false;
9192

9293

9394
static void create_tablespace_directories(const char *location,
@@ -242,6 +243,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
242243
char *location;
243244
Oid ownerId;
244245
Datum newOptions;
246+
bool in_place;
245247

246248
/* Must be super user */
247249
if (!superuser())
@@ -267,12 +269,15 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
267269
(errcode(ERRCODE_INVALID_NAME),
268270
errmsg("tablespace location cannot contain single quotes")));
269271

272+
in_place = allow_in_place_tablespaces && strlen(location) == 0;
273+
270274
/*
271275
* Allowing relative paths seems risky
272276
*
273-
* this also helps us ensure that location is not empty or whitespace
277+
* This also helps us ensure that location is not empty or whitespace,
278+
* unless specifying a developer-only in-place tablespace.
274279
*/
275-
if (!is_absolute_path(location))
280+
if (!in_place && !is_absolute_path(location))
276281
ereport(ERROR,
277282
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
278283
errmsg("tablespace location must be an absolute path")));
@@ -593,16 +598,36 @@ create_tablespace_directories(const char *location, const Oid tablespaceoid)
593598
char *linkloc;
594599
char *location_with_version_dir;
595600
struct stat st;
601+
bool in_place;
596602

597603
linkloc = psprintf("pg_tblspc/%u", tablespaceoid);
598-
location_with_version_dir = psprintf("%s/%s", location,
604+
605+
/*
606+
* If we're asked to make an 'in place' tablespace, create the directory
607+
* directly where the symlink would normally go. This is a developer-only
608+
* option for now, to facilitate regression testing.
609+
*/
610+
in_place = strlen(location) == 0;
611+
612+
if (in_place)
613+
{
614+
if (MakePGDirectory(linkloc) < 0 && errno != EEXIST)
615+
ereport(ERROR,
616+
(errcode_for_file_access(),
617+
errmsg("could not create directory \"%s\": %m",
618+
linkloc)));
619+
}
620+
621+
location_with_version_dir = psprintf("%s/%s", in_place ? linkloc : location,
599622
TABLESPACE_VERSION_DIRECTORY);
600623

601624
/*
602625
* Attempt to coerce target directory to safe permissions. If this fails,
603-
* it doesn't exist or has the wrong owner.
626+
* it doesn't exist or has the wrong owner. Not needed for in-place mode,
627+
* because in that case we created the directory with the desired
628+
* permissions.
604629
*/
605-
if (chmod(location, pg_dir_create_mode) != 0)
630+
if (!in_place && chmod(location, pg_dir_create_mode) != 0)
606631
{
607632
if (errno == ENOENT)
608633
ereport(ERROR,
@@ -651,13 +676,13 @@ create_tablespace_directories(const char *location, const Oid tablespaceoid)
651676
/*
652677
* In recovery, remove old symlink, in case it points to the wrong place.
653678
*/
654-
if (InRecovery)
679+
if (!in_place && InRecovery)
655680
remove_tablespace_symlink(linkloc);
656681

657682
/*
658683
* Create the symlink under PGDATA
659684
*/
660-
if (symlink(location, linkloc) < 0)
685+
if (!in_place && symlink(location, linkloc) < 0)
661686
ereport(ERROR,
662687
(errcode_for_file_access(),
663688
errmsg("could not create symbolic link \"%s\": %m",

src/backend/utils/adt/misc.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "postgres.h"
1616

1717
#include <sys/file.h>
18+
#include <sys/stat.h>
1819
#include <dirent.h>
1920
#include <fcntl.h>
2021
#include <math.h>
@@ -307,6 +308,9 @@ pg_tablespace_location(PG_FUNCTION_ARGS)
307308
char sourcepath[MAXPGPATH];
308309
char targetpath[MAXPGPATH];
309310
int rllen;
311+
#ifndef WIN32
312+
struct stat st;
313+
#endif
310314

311315
/*
312316
* It's useful to apply this function to pg_class.reltablespace, wherein
@@ -331,6 +335,31 @@ pg_tablespace_location(PG_FUNCTION_ARGS)
331335
*/
332336
snprintf(sourcepath, sizeof(sourcepath), "pg_tblspc/%u", tablespaceOid);
333337

338+
/*
339+
* Before reading the link, check if the source path is a link or a
340+
* junction point. Note that a directory is possible for a tablespace
341+
* created with allow_in_place_tablespaces enabled. If a directory is
342+
* found, a relative path to the data directory is returned.
343+
*/
344+
#ifdef WIN32
345+
if (!pgwin32_is_junction(sourcepath))
346+
PG_RETURN_TEXT_P(cstring_to_text(sourcepath));
347+
#else
348+
if (lstat(sourcepath, &st) < 0)
349+
{
350+
ereport(ERROR,
351+
(errcode_for_file_access(),
352+
errmsg("could not stat file \"%s\": %m",
353+
sourcepath)));
354+
}
355+
356+
if (!S_ISLNK(st.st_mode))
357+
PG_RETURN_TEXT_P(cstring_to_text(sourcepath));
358+
#endif
359+
360+
/*
361+
* In presence of a link or a junction point, return the path pointing to.
362+
*/
334363
rllen = readlink(sourcepath, targetpath, sizeof(targetpath));
335364
if (rllen < 0)
336365
ereport(ERROR,

src/backend/utils/misc/guc.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "catalog/pg_authid.h"
3939
#include "commands/async.h"
4040
#include "commands/prepare.h"
41+
#include "commands/tablespace.h"
4142
#include "commands/user.h"
4243
#include "commands/vacuum.h"
4344
#include "commands/variable.h"
@@ -1792,6 +1793,17 @@ static struct config_bool ConfigureNamesBool[] =
17921793
NULL, NULL, NULL
17931794
},
17941795

1796+
{
1797+
{"allow_in_place_tablespaces", PGC_SUSET, DEVELOPER_OPTIONS,
1798+
gettext_noop("Allows tablespaces directly inside pg_tblspc, for testing."),
1799+
NULL,
1800+
GUC_NOT_IN_SAMPLE
1801+
},
1802+
&allow_in_place_tablespaces,
1803+
false,
1804+
NULL, NULL, NULL
1805+
},
1806+
17951807
{
17961808
{"lo_compat_privileges", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
17971809
gettext_noop("Enables backward compatibility mode for privilege checks on large objects."),

src/include/commands/tablespace.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include "lib/stringinfo.h"
2020
#include "nodes/parsenodes.h"
2121

22+
extern bool allow_in_place_tablespaces;
23+
2224
/* XLOG stuff */
2325
#define XLOG_TBLSPC_CREATE 0x00
2426
#define XLOG_TBLSPC_DROP 0x10

0 commit comments

Comments
 (0)