Allow "in place" tablespaces.
authorThomas Munro <tmunro@postgresql.org>
Fri, 14 Jan 2022 08:27:44 +0000 (21:27 +1300)
committerThomas Munro <tmunro@postgresql.org>
Fri, 14 Jan 2022 11:09:24 +0000 (00:09 +1300)
Provide a developer-only GUC allow_in_place_tablespaces, disabled by
default.  When enabled, tablespaces can be created with an empty
LOCATION string, meaning that they should be created as a directory
directly beneath pg_tblspc.  This can be used for new testing scenarios,
in a follow-up patch.  Not intended for end-user usage, since it might
confuse backup tools that expect symlinks.

Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/CA%2BhUKGKpRWQ9SxdxxDmTBCJoR0YnFpMBe7kyzY8SUQk%2BHeskxg%40mail.gmail.com

doc/src/sgml/config.sgml
src/backend/commands/tablespace.c
src/backend/utils/misc/guc.c
src/include/commands/tablespace.h

index afbb6c35e3000d0e00babc4f0c7e7cc17c3ac9d0..d098a33a17f6b364a060520b1d82906295dcd6b9 100644 (file)
@@ -10433,6 +10433,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>
index 3e154ec10566383bcbd2d2a99cb2d620c3b5f825..7123b4069b4ff22cf2f6a882e5e35cc82f57e0c1 100644 (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 superuser */
    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")));
@@ -590,16 +595,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,
@@ -648,13 +673,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",
index 85752817f83aca1a5e8ab73cb1e4f791bbf2f5f2..effb9d03a037190e61e385ad3d27a1060a67916e 100644 (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"
@@ -1961,6 +1962,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."),
index 97b0be35e52a5110070a34e95c5e920570663c18..323528ebb83e1351116d99ff37b5723f8cc7fbd4 100644 (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