Allow "in place" tablespaces.

This is a backpatch to branches 10-14 of the following commits:

7170f2159f Allow "in place" tablespaces.
c6f2f01611 Fix pg_basebackup with in-place tablespaces.
f6f0db4d62 Fix pg_tablespace_location() with in-place tablespaces
7a7cd84893 doc: Remove mention to in-place tablespaces for pg_tablespace_location()
5344723755 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
This commit is contained in:
Alvaro Herrera 2022-07-27 07:55:13 +02:00
parent 3e1297a63f
commit 961cab0a5a
No known key found for this signature in database
GPG Key ID: 1C20ACB9D5C564AE
6 changed files with 103 additions and 7 deletions

View File

@ -10369,6 +10369,25 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
</para>
<variablelist>
<varlistentry id="guc-allow-in-place-tablespaces" xreflabel="allow_in_place_tablespaces">
<term><varname>allow_in_place_tablespaces</varname> (<type>boolean</type>)
<indexterm>
<primary><varname>allow_in_place_tablespaces</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Allows tablespaces to be created as directories inside
<filename>pg_tblspc</filename>, when an empty location string
is provided to the <command>CREATE TABLESPACE</command> command. This
is intended to allow testing replication scenarios where primary and
standby servers are running on the same machine. Such directories
are likely to confuse backup tools that expect to find only symbolic
links in that location. Only superusers can change this setting.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-allow-system-table-mods" xreflabel="allow_system_table_mods">
<term><varname>allow_system_table_mods</varname> (<type>boolean</type>)
<indexterm>

View File

@ -43,6 +43,7 @@
#include "commands/progress.h"
#include "commands/tablespace.h"
#include "common/controldata_utils.h"
#include "common/file_utils.h"
#include "executor/instrument.h"
#include "miscadmin.h"
#include "pg_trace.h"
@ -11168,6 +11169,14 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
snprintf(fullpath, sizeof(fullpath), "pg_tblspc/%s", de->d_name);
/*
* Skip anything that isn't a symlink/junction. For testing only,
* we sometimes use allow_in_place_tablespaces to create
* directories directly under pg_tblspc, which would fail below.
*/
if (get_dirent_type(fullpath, de, false, ERROR) != PGFILETYPE_LNK)
continue;
#if defined(HAVE_READLINK) || defined(WIN32)
rllen = readlink(fullpath, linkpath, sizeof(linkpath));
if (rllen < 0)

View File

@ -87,6 +87,7 @@
/* GUC variables */
char *default_tablespace = NULL;
char *temp_tablespaces = NULL;
bool allow_in_place_tablespaces = false;
static void create_tablespace_directories(const char *location,
@ -241,6 +242,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
char *location;
Oid ownerId;
Datum newOptions;
bool in_place;
/* Must be super user */
if (!superuser())
@ -266,12 +268,15 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
(errcode(ERRCODE_INVALID_NAME),
errmsg("tablespace location cannot contain single quotes")));
in_place = allow_in_place_tablespaces && strlen(location) == 0;
/*
* Allowing relative paths seems risky
*
* this also helps us ensure that location is not empty or whitespace
* This also helps us ensure that location is not empty or whitespace,
* unless specifying a developer-only in-place tablespace.
*/
if (!is_absolute_path(location))
if (!in_place && !is_absolute_path(location))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("tablespace location must be an absolute path")));
@ -592,16 +597,36 @@ create_tablespace_directories(const char *location, const Oid tablespaceoid)
char *linkloc;
char *location_with_version_dir;
struct stat st;
bool in_place;
linkloc = psprintf("pg_tblspc/%u", tablespaceoid);
location_with_version_dir = psprintf("%s/%s", location,
/*
* If we're asked to make an 'in place' tablespace, create the directory
* directly where the symlink would normally go. This is a developer-only
* option for now, to facilitate regression testing.
*/
in_place = strlen(location) == 0;
if (in_place)
{
if (MakePGDirectory(linkloc) < 0 && errno != EEXIST)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create directory \"%s\": %m",
linkloc)));
}
location_with_version_dir = psprintf("%s/%s", in_place ? linkloc : location,
TABLESPACE_VERSION_DIRECTORY);
/*
* Attempt to coerce target directory to safe permissions. If this fails,
* it doesn't exist or has the wrong owner.
* it doesn't exist or has the wrong owner. Not needed for in-place mode,
* because in that case we created the directory with the desired
* permissions.
*/
if (chmod(location, pg_dir_create_mode) != 0)
if (!in_place && chmod(location, pg_dir_create_mode) != 0)
{
if (errno == ENOENT)
ereport(ERROR,
@ -650,13 +675,13 @@ create_tablespace_directories(const char *location, const Oid tablespaceoid)
/*
* In recovery, remove old symlink, in case it points to the wrong place.
*/
if (InRecovery)
if (!in_place && InRecovery)
remove_tablespace_symlink(linkloc);
/*
* Create the symlink under PGDATA
*/
if (symlink(location, linkloc) < 0)
if (!in_place && symlink(location, linkloc) < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create symbolic link \"%s\": %m",

View File

@ -15,6 +15,7 @@
#include "postgres.h"
#include <sys/file.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <math.h>
@ -309,6 +310,9 @@ pg_tablespace_location(PG_FUNCTION_ARGS)
char sourcepath[MAXPGPATH];
char targetpath[MAXPGPATH];
int rllen;
#ifndef WIN32
struct stat st;
#endif
/*
* It's useful to apply this function to pg_class.reltablespace, wherein
@ -333,6 +337,31 @@ pg_tablespace_location(PG_FUNCTION_ARGS)
*/
snprintf(sourcepath, sizeof(sourcepath), "pg_tblspc/%u", tablespaceOid);
/*
* Before reading the link, check if the source path is a link or a
* junction point. Note that a directory is possible for a tablespace
* created with allow_in_place_tablespaces enabled. If a directory is
* found, a relative path to the data directory is returned.
*/
#ifdef WIN32
if (!pgwin32_is_junction(sourcepath))
PG_RETURN_TEXT_P(cstring_to_text(sourcepath));
#else
if (lstat(sourcepath, &st) < 0)
{
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m",
sourcepath)));
}
if (!S_ISLNK(st.st_mode))
PG_RETURN_TEXT_P(cstring_to_text(sourcepath));
#endif
/*
* In presence of a link or a junction point, return the path pointing to.
*/
rllen = readlink(sourcepath, targetpath, sizeof(targetpath));
if (rllen < 0)
ereport(ERROR,

View File

@ -46,6 +46,7 @@
#include "catalog/storage.h"
#include "commands/async.h"
#include "commands/prepare.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "commands/user.h"
#include "commands/vacuum.h"
@ -1952,6 +1953,17 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
{"allow_in_place_tablespaces", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allows tablespaces directly inside pg_tblspc, for testing."),
NULL,
GUC_NOT_IN_SAMPLE
},
&allow_in_place_tablespaces,
false,
NULL, NULL, NULL
},
{
{"lo_compat_privileges", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enables backward compatibility mode for privilege checks on large objects."),

View File

@ -19,6 +19,8 @@
#include "lib/stringinfo.h"
#include "nodes/parsenodes.h"
extern bool allow_in_place_tablespaces;
/* XLOG stuff */
#define XLOG_TBLSPC_CREATE 0x00
#define XLOG_TBLSPC_DROP 0x10