Fix pg_tablespace_location() with in-place tablespaces
authorMichael Paquier <michael@paquier.xyz>
Thu, 17 Mar 2022 02:25:02 +0000 (11:25 +0900)
committerMichael Paquier <michael@paquier.xyz>
Thu, 17 Mar 2022 02:25:02 +0000 (11:25 +0900)
Using this system function with an in-place tablespace (created when
allow_in_place_tablespaces is enabled by specifying an empty string as
location) caused a failure when using readlink(), as the tablespace is,
in this case, not a symbolic link in pg_tblspc/ but a directory.

Rather than getting a failure, the commit changes
pg_tablespace_location() so as a relative path to the data directory is
returned for in-place tablespaces, to make a difference between
tablespaces created when allow_in_place_tablespaces is enabled or not.
Getting a path rather than an empty string that would match the CREATE
TABLESPACE command in this case is more useful for tests that would like
to rely on this function.

While on it, a regression test is added for this case.  This is simple
to add in the main regression test suite thanks to regexp_replace() to
mask the part of the tablespace location dependent on its OID.

Author: Michael Paquier
Reviewed-by: Kyotaro Horiguchi, Thomas Munro
Discussion: https://postgr.es/m/YiG1RleON1WBcLnX@paquier.xyz

doc/src/sgml/func.sgml
src/backend/utils/adt/misc.c
src/test/regress/expected/tablespace.out
src/test/regress/sql/tablespace.sql

index 8a802fb22531313ae9965f73726e8c87c64cd444..89a5e17884c3ebd1f3cd6a6abf98c178db551e54 100644 (file)
@@ -23924,7 +23924,13 @@ SELECT currval(pg_get_serial_sequence('sometable', 'id'));
        </para>
        <para>
         Returns the file system path that this tablespace is located in.
-       </para></entry>
+       </para>
+       <para>
+        A relative path to the data directory is returned for tablespaces
+        created when <xref linkend="guc-allow-in-place-tablespaces"/> is
+        enabled.
+       </para>
+       </entry>
       </row>
 
       <row>
index 4568749d230bcb33215b8909c92068ebf9f18f62..89690be2ed1fef9f3817a2d50755e51e4cbbcdfd 100644 (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>
@@ -282,6 +283,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
@@ -306,6 +310,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,
index 2dfbcfdebe181b618c30364e924994806a4e18a1..473fe8c28e0797e03324b526bde16612c12c87b3 100644 (file)
@@ -24,6 +24,15 @@ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'regress_tblspacewith';
 DROP TABLESPACE regress_tblspacewith;
 -- create a tablespace we can use
 CREATE TABLESPACE regress_tblspace LOCATION '';
+-- This returns a relative path as of an effect of allow_in_place_tablespaces,
+-- masking the tablespace OID used in the path name.
+SELECT regexp_replace(pg_tablespace_location(oid), '(pg_tblspc)/(\d+)', '\1/NNN')
+  FROM pg_tablespace  WHERE spcname = 'regress_tblspace';
+ regexp_replace 
+----------------
+ pg_tblspc/NNN
+(1 row)
+
 -- try setting and resetting some properties for the new tablespace
 ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
 ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true);  -- fail
index 896f05cea3253562928290ad66686cd2e0dde87f..0949a28488d8b3b43b2c07be30760c87b74427ab 100644 (file)
@@ -22,6 +22,10 @@ DROP TABLESPACE regress_tblspacewith;
 
 -- create a tablespace we can use
 CREATE TABLESPACE regress_tblspace LOCATION '';
+-- This returns a relative path as of an effect of allow_in_place_tablespaces,
+-- masking the tablespace OID used in the path name.
+SELECT regexp_replace(pg_tablespace_location(oid), '(pg_tblspc)/(\d+)', '\1/NNN')
+  FROM pg_tablespace  WHERE spcname = 'regress_tblspace';
 
 -- try setting and resetting some properties for the new tablespace
 ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);