LCOV - code coverage report
Current view: top level - src/backend/commands - tablespace.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 68.8 % 544 374
Test Date: 2026-01-26 10:56:24 Functions: 88.2 % 17 15
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 25.9 % 533 138

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * tablespace.c
       4                 :             :  *        Commands to manipulate table spaces
       5                 :             :  *
       6                 :             :  * Tablespaces in PostgreSQL are designed to allow users to determine
       7                 :             :  * where the data file(s) for a given database object reside on the file
       8                 :             :  * system.
       9                 :             :  *
      10                 :             :  * A tablespace represents a directory on the file system. At tablespace
      11                 :             :  * creation time, the directory must be empty. To simplify things and
      12                 :             :  * remove the possibility of having file name conflicts, we isolate
      13                 :             :  * files within a tablespace into database-specific subdirectories.
      14                 :             :  *
      15                 :             :  * To support file access via the information given in RelFileLocator, we
      16                 :             :  * maintain a symbolic-link map in $PGDATA/pg_tblspc. The symlinks are
      17                 :             :  * named by tablespace OIDs and point to the actual tablespace directories.
      18                 :             :  * There is also a per-cluster version directory in each tablespace.
      19                 :             :  * Thus the full path to an arbitrary file is
      20                 :             :  *                      $PGDATA/pg_tblspc/spcoid/PG_MAJORVER_CATVER/dboid/relfilenumber
      21                 :             :  * e.g.
      22                 :             :  *                      $PGDATA/pg_tblspc/20981/PG_9.0_201002161/719849/83292814
      23                 :             :  *
      24                 :             :  * There are two tablespaces created at initdb time: pg_global (for shared
      25                 :             :  * tables) and pg_default (for everything else).  For backwards compatibility
      26                 :             :  * and to remain functional on platforms without symlinks, these tablespaces
      27                 :             :  * are accessed specially: they are respectively
      28                 :             :  *                      $PGDATA/global/relfilenumber
      29                 :             :  *                      $PGDATA/base/dboid/relfilenumber
      30                 :             :  *
      31                 :             :  * To allow CREATE DATABASE to give a new database a default tablespace
      32                 :             :  * that's different from the template database's default, we make the
      33                 :             :  * provision that a zero in pg_class.reltablespace means the database's
      34                 :             :  * default tablespace.  Without this, CREATE DATABASE would have to go in
      35                 :             :  * and munge the system catalogs of the new database.
      36                 :             :  *
      37                 :             :  *
      38                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      39                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
      40                 :             :  *
      41                 :             :  *
      42                 :             :  * IDENTIFICATION
      43                 :             :  *        src/backend/commands/tablespace.c
      44                 :             :  *
      45                 :             :  *-------------------------------------------------------------------------
      46                 :             :  */
      47                 :             : #include "postgres.h"
      48                 :             : 
      49                 :             : #include <unistd.h>
      50                 :             : #include <dirent.h>
      51                 :             : #include <sys/stat.h>
      52                 :             : 
      53                 :             : #include "access/heapam.h"
      54                 :             : #include "access/htup_details.h"
      55                 :             : #include "access/reloptions.h"
      56                 :             : #include "access/tableam.h"
      57                 :             : #include "access/xact.h"
      58                 :             : #include "access/xloginsert.h"
      59                 :             : #include "access/xlogutils.h"
      60                 :             : #include "catalog/binary_upgrade.h"
      61                 :             : #include "catalog/catalog.h"
      62                 :             : #include "catalog/dependency.h"
      63                 :             : #include "catalog/indexing.h"
      64                 :             : #include "catalog/objectaccess.h"
      65                 :             : #include "catalog/pg_tablespace.h"
      66                 :             : #include "commands/comment.h"
      67                 :             : #include "commands/seclabel.h"
      68                 :             : #include "commands/tablespace.h"
      69                 :             : #include "common/file_perm.h"
      70                 :             : #include "miscadmin.h"
      71                 :             : #include "postmaster/bgwriter.h"
      72                 :             : #include "storage/fd.h"
      73                 :             : #include "storage/standby.h"
      74                 :             : #include "utils/acl.h"
      75                 :             : #include "utils/builtins.h"
      76                 :             : #include "utils/fmgroids.h"
      77                 :             : #include "utils/guc_hooks.h"
      78                 :             : #include "utils/memutils.h"
      79                 :             : #include "utils/rel.h"
      80                 :             : #include "utils/varlena.h"
      81                 :             : 
      82                 :             : /* GUC variables */
      83                 :             : char       *default_tablespace = NULL;
      84                 :             : char       *temp_tablespaces = NULL;
      85                 :             : bool            allow_in_place_tablespaces = false;
      86                 :             : 
      87                 :             : Oid                     binary_upgrade_next_pg_tablespace_oid = InvalidOid;
      88                 :             : 
      89                 :             : static void create_tablespace_directories(const char *location,
      90                 :             :                                                                                   const Oid tablespaceoid);
      91                 :             : static bool destroy_tablespace_directories(Oid tablespaceoid, bool redo);
      92                 :             : 
      93                 :             : 
      94                 :             : /*
      95                 :             :  * Each database using a table space is isolated into its own name space
      96                 :             :  * by a subdirectory named for the database OID.  On first creation of an
      97                 :             :  * object in the tablespace, create the subdirectory.  If the subdirectory
      98                 :             :  * already exists, fall through quietly.
      99                 :             :  *
     100                 :             :  * isRedo indicates that we are creating an object during WAL replay.
     101                 :             :  * In this case we will cope with the possibility of the tablespace
     102                 :             :  * directory not being there either --- this could happen if we are
     103                 :             :  * replaying an operation on a table in a subsequently-dropped tablespace.
     104                 :             :  * We handle this by making a directory in the place where the tablespace
     105                 :             :  * symlink would normally be.  This isn't an exact replay of course, but
     106                 :             :  * it's the best we can do given the available information.
     107                 :             :  *
     108                 :             :  * If tablespaces are not supported, we still need it in case we have to
     109                 :             :  * re-create a database subdirectory (of $PGDATA/base) during WAL replay.
     110                 :             :  */
     111                 :             : void
     112                 :       12496 : TablespaceCreateDbspace(Oid spcOid, Oid dbOid, bool isRedo)
     113                 :             : {
     114                 :       12496 :         struct stat st;
     115                 :       12496 :         char       *dir;
     116                 :             : 
     117                 :             :         /*
     118                 :             :          * The global tablespace doesn't have per-database subdirectories, so
     119                 :             :          * nothing to do for it.
     120                 :             :          */
     121         [ +  + ]:       12496 :         if (spcOid == GLOBALTABLESPACE_OID)
     122                 :          68 :                 return;
     123                 :             : 
     124         [ +  - ]:       12428 :         Assert(OidIsValid(spcOid));
     125         [ +  - ]:       12428 :         Assert(OidIsValid(dbOid));
     126                 :             : 
     127                 :       12428 :         dir = GetDatabasePath(dbOid, spcOid);
     128                 :             : 
     129         [ +  + ]:       12428 :         if (stat(dir, &st) < 0)
     130                 :             :         {
     131                 :             :                 /* Directory does not exist? */
     132         [ +  - ]:           1 :                 if (errno == ENOENT)
     133                 :             :                 {
     134                 :             :                         /*
     135                 :             :                          * Acquire TablespaceCreateLock to ensure that no DROP TABLESPACE
     136                 :             :                          * or TablespaceCreateDbspace is running concurrently.
     137                 :             :                          */
     138                 :           1 :                         LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
     139                 :             : 
     140                 :             :                         /*
     141                 :             :                          * Recheck to see if someone created the directory while we were
     142                 :             :                          * waiting for lock.
     143                 :             :                          */
     144   [ -  +  #  # ]:           1 :                         if (stat(dir, &st) == 0 && S_ISDIR(st.st_mode))
     145                 :             :                         {
     146                 :             :                                 /* Directory was created */
     147                 :           0 :                         }
     148                 :             :                         else
     149                 :             :                         {
     150                 :             :                                 /* Directory creation failed? */
     151         [ +  - ]:           1 :                                 if (MakePGDirectory(dir) < 0)
     152                 :             :                                 {
     153                 :             :                                         /* Failure other than not exists or not in WAL replay? */
     154         [ #  # ]:           0 :                                         if (errno != ENOENT || !isRedo)
     155   [ #  #  #  # ]:           0 :                                                 ereport(ERROR,
     156                 :             :                                                                 (errcode_for_file_access(),
     157                 :             :                                                                  errmsg("could not create directory \"%s\": %m",
     158                 :             :                                                                                 dir)));
     159                 :             : 
     160                 :             :                                         /*
     161                 :             :                                          * During WAL replay, it's conceivable that several levels
     162                 :             :                                          * of directories are missing if tablespaces are dropped
     163                 :             :                                          * further ahead of the WAL stream than we're currently
     164                 :             :                                          * replaying.  An easy way forward is to create them as
     165                 :             :                                          * plain directories and hope they are removed by further
     166                 :             :                                          * WAL replay if necessary.  If this also fails, there is
     167                 :             :                                          * trouble we cannot get out of, so just report that and
     168                 :             :                                          * bail out.
     169                 :             :                                          */
     170         [ #  # ]:           0 :                                         if (pg_mkdir_p(dir, pg_dir_create_mode) < 0)
     171   [ #  #  #  # ]:           0 :                                                 ereport(ERROR,
     172                 :             :                                                                 (errcode_for_file_access(),
     173                 :             :                                                                  errmsg("could not create directory \"%s\": %m",
     174                 :             :                                                                                 dir)));
     175                 :           0 :                                 }
     176                 :             :                         }
     177                 :             : 
     178                 :           1 :                         LWLockRelease(TablespaceCreateLock);
     179                 :           1 :                 }
     180                 :             :                 else
     181                 :             :                 {
     182   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     183                 :             :                                         (errcode_for_file_access(),
     184                 :             :                                          errmsg("could not stat directory \"%s\": %m", dir)));
     185                 :             :                 }
     186                 :           1 :         }
     187                 :             :         else
     188                 :             :         {
     189                 :             :                 /* Is it not a directory? */
     190         [ +  - ]:       12427 :                 if (!S_ISDIR(st.st_mode))
     191   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     192                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     193                 :             :                                          errmsg("\"%s\" exists but is not a directory",
     194                 :             :                                                         dir)));
     195                 :             :         }
     196                 :             : 
     197                 :       12428 :         pfree(dir);
     198         [ -  + ]:       12496 : }
     199                 :             : 
     200                 :             : /*
     201                 :             :  * Create a table space
     202                 :             :  *
     203                 :             :  * Only superusers can create a tablespace. This seems a reasonable restriction
     204                 :             :  * since we're determining the system layout and, anyway, we probably have
     205                 :             :  * root if we're doing this kind of activity
     206                 :             :  */
     207                 :             : Oid
     208                 :           6 : CreateTableSpace(CreateTableSpaceStmt *stmt)
     209                 :             : {
     210                 :           6 :         Relation        rel;
     211                 :           6 :         Datum           values[Natts_pg_tablespace];
     212                 :           6 :         bool            nulls[Natts_pg_tablespace] = {0};
     213                 :           6 :         HeapTuple       tuple;
     214                 :           6 :         Oid                     tablespaceoid;
     215                 :           6 :         char       *location;
     216                 :           6 :         Oid                     ownerId;
     217                 :           6 :         Datum           newOptions;
     218                 :           6 :         bool            in_place;
     219                 :             : 
     220                 :             :         /* Must be superuser */
     221         [ +  - ]:           6 :         if (!superuser())
     222   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     223                 :             :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     224                 :             :                                  errmsg("permission denied to create tablespace \"%s\"",
     225                 :             :                                                 stmt->tablespacename),
     226                 :             :                                  errhint("Must be superuser to create a tablespace.")));
     227                 :             : 
     228                 :             :         /* However, the eventual owner of the tablespace need not be */
     229         [ -  + ]:           6 :         if (stmt->owner)
     230                 :           0 :                 ownerId = get_rolespec_oid(stmt->owner, false);
     231                 :             :         else
     232                 :           6 :                 ownerId = GetUserId();
     233                 :             : 
     234                 :             :         /* Unix-ify the offered path, and strip any trailing slashes */
     235                 :           6 :         location = pstrdup(stmt->location);
     236                 :           6 :         canonicalize_path(location);
     237                 :             : 
     238                 :             :         /* disallow quotes, else CREATE DATABASE would be at risk */
     239         [ +  - ]:           6 :         if (strchr(location, '\''))
     240   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     241                 :             :                                 (errcode(ERRCODE_INVALID_NAME),
     242                 :             :                                  errmsg("tablespace location cannot contain single quotes")));
     243                 :             : 
     244         [ +  + ]:           6 :         in_place = allow_in_place_tablespaces && strlen(location) == 0;
     245                 :             : 
     246                 :             :         /*
     247                 :             :          * Allowing relative paths seems risky
     248                 :             :          *
     249                 :             :          * This also helps us ensure that location is not empty or whitespace,
     250                 :             :          * unless specifying a developer-only in-place tablespace.
     251                 :             :          */
     252   [ +  +  +  + ]:           6 :         if (!in_place && !is_absolute_path(location))
     253   [ +  -  +  - ]:           2 :                 ereport(ERROR,
     254                 :             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     255                 :             :                                  errmsg("tablespace location must be an absolute path")));
     256                 :             : 
     257                 :             :         /*
     258                 :             :          * Check that location isn't too long. Remember that we're going to append
     259                 :             :          * 'PG_XXX/<dboid>/<relid>_<fork>.<nnn>'.  FYI, we never actually
     260                 :             :          * reference the whole path here, but MakePGDirectory() uses the first two
     261                 :             :          * parts.
     262                 :             :          */
     263                 :           4 :         if (strlen(location) + 1 + strlen(TABLESPACE_VERSION_DIRECTORY) + 1 +
     264         [ +  - ]:           4 :                 OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1 + OIDCHARS > MAXPGPATH)
     265   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     266                 :             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     267                 :             :                                  errmsg("tablespace location \"%s\" is too long",
     268                 :             :                                                 location)));
     269                 :             : 
     270                 :             :         /* Warn if the tablespace is in the data directory. */
     271         [ +  - ]:           4 :         if (path_is_prefix_of_path(DataDir, location))
     272   [ #  #  #  # ]:           0 :                 ereport(WARNING,
     273                 :             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     274                 :             :                                  errmsg("tablespace location should not be inside the data directory")));
     275                 :             : 
     276                 :             :         /*
     277                 :             :          * Disallow creation of tablespaces named "pg_xxx"; we reserve this
     278                 :             :          * namespace for system purposes.
     279                 :             :          */
     280   [ +  -  +  - ]:           4 :         if (!allowSystemTableMods && IsReservedName(stmt->tablespacename))
     281   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     282                 :             :                                 (errcode(ERRCODE_RESERVED_NAME),
     283                 :             :                                  errmsg("unacceptable tablespace name \"%s\"",
     284                 :             :                                                 stmt->tablespacename),
     285                 :             :                                  errdetail("The prefix \"pg_\" is reserved for system tablespaces.")));
     286                 :             : 
     287                 :             :         /*
     288                 :             :          * If built with appropriate switch, whine when regression-testing
     289                 :             :          * conventions for tablespace names are violated.
     290                 :             :          */
     291                 :             : #ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
     292                 :             :         if (strncmp(stmt->tablespacename, "regress_", 8) != 0)
     293                 :             :                 elog(WARNING, "tablespaces created by regression test cases should have names starting with \"regress_\"");
     294                 :             : #endif
     295                 :             : 
     296                 :             :         /*
     297                 :             :          * Check that there is no other tablespace by this name.  (The unique
     298                 :             :          * index would catch this anyway, but might as well give a friendlier
     299                 :             :          * message.)
     300                 :             :          */
     301         [ +  - ]:           4 :         if (OidIsValid(get_tablespace_oid(stmt->tablespacename, true)))
     302   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     303                 :             :                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     304                 :             :                                  errmsg("tablespace \"%s\" already exists",
     305                 :             :                                                 stmt->tablespacename)));
     306                 :             : 
     307                 :             :         /*
     308                 :             :          * Insert tuple into pg_tablespace.  The purpose of doing this first is to
     309                 :             :          * lock the proposed tablename against other would-be creators. The
     310                 :             :          * insertion will roll back if we find problems below.
     311                 :             :          */
     312                 :           4 :         rel = table_open(TableSpaceRelationId, RowExclusiveLock);
     313                 :             : 
     314         [ -  + ]:           4 :         if (IsBinaryUpgrade)
     315                 :             :         {
     316                 :             :                 /* Use binary-upgrade override for tablespace oid */
     317         [ #  # ]:           0 :                 if (!OidIsValid(binary_upgrade_next_pg_tablespace_oid))
     318   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     319                 :             :                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     320                 :             :                                          errmsg("pg_tablespace OID value not set when in binary upgrade mode")));
     321                 :             : 
     322                 :           0 :                 tablespaceoid = binary_upgrade_next_pg_tablespace_oid;
     323                 :           0 :                 binary_upgrade_next_pg_tablespace_oid = InvalidOid;
     324                 :           0 :         }
     325                 :             :         else
     326                 :           4 :                 tablespaceoid = GetNewOidWithIndex(rel, TablespaceOidIndexId,
     327                 :             :                                                                                    Anum_pg_tablespace_oid);
     328                 :           4 :         values[Anum_pg_tablespace_oid - 1] = ObjectIdGetDatum(tablespaceoid);
     329                 :           4 :         values[Anum_pg_tablespace_spcname - 1] =
     330                 :           4 :                 DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename));
     331                 :           4 :         values[Anum_pg_tablespace_spcowner - 1] =
     332                 :           4 :                 ObjectIdGetDatum(ownerId);
     333                 :           4 :         nulls[Anum_pg_tablespace_spcacl - 1] = true;
     334                 :             : 
     335                 :             :         /* Generate new proposed spcoptions (text array) */
     336                 :           4 :         newOptions = transformRelOptions((Datum) 0,
     337                 :           4 :                                                                          stmt->options,
     338                 :             :                                                                          NULL, NULL, false, false);
     339                 :           4 :         (void) tablespace_reloptions(newOptions, true);
     340         [ +  + ]:           4 :         if (newOptions != (Datum) 0)
     341                 :           2 :                 values[Anum_pg_tablespace_spcoptions - 1] = newOptions;
     342                 :             :         else
     343                 :           2 :                 nulls[Anum_pg_tablespace_spcoptions - 1] = true;
     344                 :             : 
     345                 :           4 :         tuple = heap_form_tuple(rel->rd_att, values, nulls);
     346                 :             : 
     347                 :           4 :         CatalogTupleInsert(rel, tuple);
     348                 :             : 
     349                 :           4 :         heap_freetuple(tuple);
     350                 :             : 
     351                 :             :         /* Record dependency on owner */
     352                 :           4 :         recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId);
     353                 :             : 
     354                 :             :         /* Post creation hook for new tablespace */
     355         [ +  - ]:           4 :         InvokeObjectPostCreateHook(TableSpaceRelationId, tablespaceoid, 0);
     356                 :             : 
     357                 :           4 :         create_tablespace_directories(location, tablespaceoid);
     358                 :             : 
     359                 :             :         /* Record the filesystem change in XLOG */
     360                 :             :         {
     361                 :           4 :                 xl_tblspc_create_rec xlrec;
     362                 :             : 
     363                 :           4 :                 xlrec.ts_id = tablespaceoid;
     364                 :             : 
     365                 :           4 :                 XLogBeginInsert();
     366                 :           4 :                 XLogRegisterData(&xlrec,
     367                 :             :                                                  offsetof(xl_tblspc_create_rec, ts_path));
     368                 :           4 :                 XLogRegisterData(location, strlen(location) + 1);
     369                 :             : 
     370                 :           4 :                 (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_CREATE);
     371                 :           4 :         }
     372                 :             : 
     373                 :             :         /*
     374                 :             :          * Force synchronous commit, to minimize the window between creating the
     375                 :             :          * symlink on-disk and marking the transaction committed.  It's not great
     376                 :             :          * that there is any window at all, but definitely we don't want to make
     377                 :             :          * it larger than necessary.
     378                 :             :          */
     379                 :           4 :         ForceSyncCommit();
     380                 :             : 
     381                 :           4 :         pfree(location);
     382                 :             : 
     383                 :             :         /* We keep the lock on pg_tablespace until commit */
     384                 :           4 :         table_close(rel, NoLock);
     385                 :             : 
     386                 :           8 :         return tablespaceoid;
     387                 :           4 : }
     388                 :             : 
     389                 :             : /*
     390                 :             :  * Drop a table space
     391                 :             :  *
     392                 :             :  * Be careful to check that the tablespace is empty.
     393                 :             :  */
     394                 :             : void
     395                 :           4 : DropTableSpace(DropTableSpaceStmt *stmt)
     396                 :             : {
     397                 :           4 :         char       *tablespacename = stmt->tablespacename;
     398                 :           4 :         TableScanDesc scandesc;
     399                 :           4 :         Relation        rel;
     400                 :           4 :         HeapTuple       tuple;
     401                 :           4 :         Form_pg_tablespace spcform;
     402                 :           4 :         ScanKeyData entry[1];
     403                 :           4 :         Oid                     tablespaceoid;
     404                 :           4 :         char       *detail;
     405                 :           4 :         char       *detail_log;
     406                 :             : 
     407                 :             :         /*
     408                 :             :          * Find the target tuple
     409                 :             :          */
     410                 :           4 :         rel = table_open(TableSpaceRelationId, RowExclusiveLock);
     411                 :             : 
     412                 :           8 :         ScanKeyInit(&entry[0],
     413                 :             :                                 Anum_pg_tablespace_spcname,
     414                 :             :                                 BTEqualStrategyNumber, F_NAMEEQ,
     415                 :           4 :                                 CStringGetDatum(tablespacename));
     416                 :           4 :         scandesc = table_beginscan_catalog(rel, 1, entry);
     417                 :           4 :         tuple = heap_getnext(scandesc, ForwardScanDirection);
     418                 :             : 
     419         [ +  - ]:           4 :         if (!HeapTupleIsValid(tuple))
     420                 :             :         {
     421         [ #  # ]:           0 :                 if (!stmt->missing_ok)
     422                 :             :                 {
     423   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     424                 :             :                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
     425                 :             :                                          errmsg("tablespace \"%s\" does not exist",
     426                 :             :                                                         tablespacename)));
     427                 :           0 :                 }
     428                 :             :                 else
     429                 :             :                 {
     430   [ #  #  #  # ]:           0 :                         ereport(NOTICE,
     431                 :             :                                         (errmsg("tablespace \"%s\" does not exist, skipping",
     432                 :             :                                                         tablespacename)));
     433                 :           0 :                         table_endscan(scandesc);
     434                 :           0 :                         table_close(rel, NoLock);
     435                 :             :                 }
     436                 :           0 :                 return;
     437                 :             :         }
     438                 :             : 
     439                 :           4 :         spcform = (Form_pg_tablespace) GETSTRUCT(tuple);
     440                 :           4 :         tablespaceoid = spcform->oid;
     441                 :             : 
     442                 :             :         /* Must be tablespace owner */
     443         [ +  - ]:           4 :         if (!object_ownercheck(TableSpaceRelationId, tablespaceoid, GetUserId()))
     444                 :           0 :                 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TABLESPACE,
     445                 :           0 :                                            tablespacename);
     446                 :             : 
     447                 :             :         /* Disallow drop of the standard tablespaces, even by superuser */
     448         [ +  - ]:           4 :         if (IsPinnedObject(TableSpaceRelationId, tablespaceoid))
     449                 :           0 :                 aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_TABLESPACE,
     450                 :           0 :                                            tablespacename);
     451                 :             : 
     452                 :             :         /* Check for pg_shdepend entries depending on this tablespace */
     453         [ +  + ]:           4 :         if (checkSharedDependencies(TableSpaceRelationId, tablespaceoid,
     454                 :             :                                                                 &detail, &detail_log))
     455   [ +  -  +  - ]:           1 :                 ereport(ERROR,
     456                 :             :                                 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
     457                 :             :                                  errmsg("tablespace \"%s\" cannot be dropped because some objects depend on it",
     458                 :             :                                                 tablespacename),
     459                 :             :                                  errdetail_internal("%s", detail),
     460                 :             :                                  errdetail_log("%s", detail_log)));
     461                 :             : 
     462                 :             :         /* DROP hook for the tablespace being removed */
     463         [ +  - ]:           3 :         InvokeObjectDropHook(TableSpaceRelationId, tablespaceoid, 0);
     464                 :             : 
     465                 :             :         /*
     466                 :             :          * Remove the pg_tablespace tuple (this will roll back if we fail below)
     467                 :             :          */
     468                 :           3 :         CatalogTupleDelete(rel, &tuple->t_self);
     469                 :             : 
     470                 :           3 :         table_endscan(scandesc);
     471                 :             : 
     472                 :             :         /*
     473                 :             :          * Remove any comments or security labels on this tablespace.
     474                 :             :          */
     475                 :           3 :         DeleteSharedComments(tablespaceoid, TableSpaceRelationId);
     476                 :           3 :         DeleteSharedSecurityLabel(tablespaceoid, TableSpaceRelationId);
     477                 :             : 
     478                 :             :         /*
     479                 :             :          * Remove dependency on owner.
     480                 :             :          */
     481                 :           3 :         deleteSharedDependencyRecordsFor(TableSpaceRelationId, tablespaceoid, 0);
     482                 :             : 
     483                 :             :         /*
     484                 :             :          * Acquire TablespaceCreateLock to ensure that no TablespaceCreateDbspace
     485                 :             :          * is running concurrently.
     486                 :             :          */
     487                 :           3 :         LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
     488                 :             : 
     489                 :             :         /*
     490                 :             :          * Try to remove the physical infrastructure.
     491                 :             :          */
     492         [ +  + ]:           3 :         if (!destroy_tablespace_directories(tablespaceoid, false))
     493                 :             :         {
     494                 :             :                 /*
     495                 :             :                  * Not all files deleted?  However, there can be lingering empty files
     496                 :             :                  * in the directories, left behind by for example DROP TABLE, that
     497                 :             :                  * have been scheduled for deletion at next checkpoint (see comments
     498                 :             :                  * in mdunlink() for details).  We could just delete them immediately,
     499                 :             :                  * but we can't tell them apart from important data files that we
     500                 :             :                  * mustn't delete.  So instead, we force a checkpoint which will clean
     501                 :             :                  * out any lingering files, and try again.
     502                 :             :                  */
     503                 :           2 :                 RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
     504                 :             : 
     505                 :             :                 /*
     506                 :             :                  * On Windows, an unlinked file persists in the directory listing
     507                 :             :                  * until no process retains an open handle for the file.  The DDL
     508                 :             :                  * commands that schedule files for unlink send invalidation messages
     509                 :             :                  * directing other PostgreSQL processes to close the files, but
     510                 :             :                  * nothing guarantees they'll be processed in time.  So, we'll also
     511                 :             :                  * use a global barrier to ask all backends to close all files, and
     512                 :             :                  * wait until they're finished.
     513                 :             :                  */
     514                 :           2 :                 LWLockRelease(TablespaceCreateLock);
     515                 :           2 :                 WaitForProcSignalBarrier(EmitProcSignalBarrier(PROCSIGNAL_BARRIER_SMGRRELEASE));
     516                 :           2 :                 LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
     517                 :             : 
     518                 :             :                 /* And now try again. */
     519         [ +  + ]:           2 :                 if (!destroy_tablespace_directories(tablespaceoid, false))
     520                 :             :                 {
     521                 :             :                         /* Still not empty, the files must be important then */
     522   [ +  -  +  - ]:           1 :                         ereport(ERROR,
     523                 :             :                                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     524                 :             :                                          errmsg("tablespace \"%s\" is not empty",
     525                 :             :                                                         tablespacename)));
     526                 :           0 :                 }
     527                 :           1 :         }
     528                 :             : 
     529                 :             :         /* Record the filesystem change in XLOG */
     530                 :             :         {
     531                 :           2 :                 xl_tblspc_drop_rec xlrec;
     532                 :             : 
     533                 :           2 :                 xlrec.ts_id = tablespaceoid;
     534                 :             : 
     535                 :           2 :                 XLogBeginInsert();
     536                 :           2 :                 XLogRegisterData(&xlrec, sizeof(xl_tblspc_drop_rec));
     537                 :             : 
     538                 :           2 :                 (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_DROP);
     539                 :           2 :         }
     540                 :             : 
     541                 :             :         /*
     542                 :             :          * Note: because we checked that the tablespace was empty, there should be
     543                 :             :          * no need to worry about flushing shared buffers or free space map
     544                 :             :          * entries for relations in the tablespace.
     545                 :             :          */
     546                 :             : 
     547                 :             :         /*
     548                 :             :          * Force synchronous commit, to minimize the window between removing the
     549                 :             :          * files on-disk and marking the transaction committed.  It's not great
     550                 :             :          * that there is any window at all, but definitely we don't want to make
     551                 :             :          * it larger than necessary.
     552                 :             :          */
     553                 :           2 :         ForceSyncCommit();
     554                 :             : 
     555                 :             :         /*
     556                 :             :          * Allow TablespaceCreateDbspace again.
     557                 :             :          */
     558                 :           2 :         LWLockRelease(TablespaceCreateLock);
     559                 :             : 
     560                 :             :         /* We keep the lock on pg_tablespace until commit */
     561                 :           2 :         table_close(rel, NoLock);
     562         [ -  + ]:           2 : }
     563                 :             : 
     564                 :             : 
     565                 :             : /*
     566                 :             :  * create_tablespace_directories
     567                 :             :  *
     568                 :             :  *      Attempt to create filesystem infrastructure linking $PGDATA/pg_tblspc/
     569                 :             :  *      to the specified directory
     570                 :             :  */
     571                 :             : static void
     572                 :           3 : create_tablespace_directories(const char *location, const Oid tablespaceoid)
     573                 :             : {
     574                 :           3 :         char       *linkloc;
     575                 :           3 :         char       *location_with_version_dir;
     576                 :           3 :         struct stat st;
     577                 :           3 :         bool            in_place;
     578                 :             : 
     579                 :           3 :         linkloc = psprintf("%s/%u", PG_TBLSPC_DIR, tablespaceoid);
     580                 :             : 
     581                 :             :         /*
     582                 :             :          * If we're asked to make an 'in place' tablespace, create the directory
     583                 :             :          * directly where the symlink would normally go.  This is a developer-only
     584                 :             :          * option for now, to facilitate regression testing.
     585                 :             :          */
     586                 :           3 :         in_place = strlen(location) == 0;
     587                 :             : 
     588         [ +  + ]:           3 :         if (in_place)
     589                 :             :         {
     590   [ -  +  #  # ]:           2 :                 if (MakePGDirectory(linkloc) < 0 && errno != EEXIST)
     591   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     592                 :             :                                         (errcode_for_file_access(),
     593                 :             :                                          errmsg("could not create directory \"%s\": %m",
     594                 :             :                                                         linkloc)));
     595                 :           2 :         }
     596                 :             : 
     597         [ +  + ]:           3 :         location_with_version_dir = psprintf("%s/%s", in_place ? linkloc : location,
     598                 :             :                                                                                  TABLESPACE_VERSION_DIRECTORY);
     599                 :             : 
     600                 :             :         /*
     601                 :             :          * Attempt to coerce target directory to safe permissions.  If this fails,
     602                 :             :          * it doesn't exist or has the wrong owner.  Not needed for in-place mode,
     603                 :             :          * because in that case we created the directory with the desired
     604                 :             :          * permissions.
     605                 :             :          */
     606   [ +  +  -  + ]:           3 :         if (!in_place && chmod(location, pg_dir_create_mode) != 0)
     607                 :             :         {
     608         [ +  - ]:           1 :                 if (errno == ENOENT)
     609   [ +  -  +  -  :           1 :                         ereport(ERROR,
                   -  + ]
     610                 :             :                                         (errcode(ERRCODE_UNDEFINED_FILE),
     611                 :             :                                          errmsg("directory \"%s\" does not exist", location),
     612                 :             :                                          InRecovery ? errhint("Create this directory for the tablespace before "
     613                 :             :                                                                                   "restarting the server.") : 0));
     614                 :             :                 else
     615   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     616                 :             :                                         (errcode_for_file_access(),
     617                 :             :                                          errmsg("could not set permissions on directory \"%s\": %m",
     618                 :             :                                                         location)));
     619                 :           0 :         }
     620                 :             : 
     621                 :             :         /*
     622                 :             :          * The creation of the version directory prevents more than one tablespace
     623                 :             :          * in a single location.  This imitates TablespaceCreateDbspace(), but it
     624                 :             :          * ignores concurrency and missing parent directories.  The chmod() would
     625                 :             :          * have failed in the absence of a parent.  pg_tablespace_spcname_index
     626                 :             :          * prevents concurrency.
     627                 :             :          */
     628         [ -  + ]:           2 :         if (stat(location_with_version_dir, &st) < 0)
     629                 :             :         {
     630         [ +  - ]:           2 :                 if (errno != ENOENT)
     631   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     632                 :             :                                         (errcode_for_file_access(),
     633                 :             :                                          errmsg("could not stat directory \"%s\": %m",
     634                 :             :                                                         location_with_version_dir)));
     635         [ +  - ]:           2 :                 else if (MakePGDirectory(location_with_version_dir) < 0)
     636   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     637                 :             :                                         (errcode_for_file_access(),
     638                 :             :                                          errmsg("could not create directory \"%s\": %m",
     639                 :             :                                                         location_with_version_dir)));
     640                 :           2 :         }
     641         [ #  # ]:           0 :         else if (!S_ISDIR(st.st_mode))
     642   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     643                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     644                 :             :                                  errmsg("\"%s\" exists but is not a directory",
     645                 :             :                                                 location_with_version_dir)));
     646         [ #  # ]:           0 :         else if (!InRecovery)
     647   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     648                 :             :                                 (errcode(ERRCODE_OBJECT_IN_USE),
     649                 :             :                                  errmsg("directory \"%s\" already in use as a tablespace",
     650                 :             :                                                 location_with_version_dir)));
     651                 :             : 
     652                 :             :         /*
     653                 :             :          * In recovery, remove old symlink, in case it points to the wrong place.
     654                 :             :          */
     655   [ -  +  #  # ]:           2 :         if (!in_place && InRecovery)
     656                 :           0 :                 remove_tablespace_symlink(linkloc);
     657                 :             : 
     658                 :             :         /*
     659                 :             :          * Create the symlink under PGDATA
     660                 :             :          */
     661   [ -  +  #  # ]:           2 :         if (!in_place && symlink(location, linkloc) < 0)
     662   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     663                 :             :                                 (errcode_for_file_access(),
     664                 :             :                                  errmsg("could not create symbolic link \"%s\": %m",
     665                 :             :                                                 linkloc)));
     666                 :             : 
     667                 :           2 :         pfree(linkloc);
     668                 :           2 :         pfree(location_with_version_dir);
     669                 :           2 : }
     670                 :             : 
     671                 :             : 
     672                 :             : /*
     673                 :             :  * destroy_tablespace_directories
     674                 :             :  *
     675                 :             :  * Attempt to remove filesystem infrastructure for the tablespace.
     676                 :             :  *
     677                 :             :  * 'redo' indicates we are redoing a drop from XLOG; in that case we should
     678                 :             :  * not throw an ERROR for problems, just LOG them.  The worst consequence of
     679                 :             :  * not removing files here would be failure to release some disk space, which
     680                 :             :  * does not justify throwing an error that would require manual intervention
     681                 :             :  * to get the database running again.
     682                 :             :  *
     683                 :             :  * Returns true if successful, false if some subdirectory is not empty
     684                 :             :  */
     685                 :             : static bool
     686                 :           5 : destroy_tablespace_directories(Oid tablespaceoid, bool redo)
     687                 :             : {
     688                 :           5 :         char       *linkloc;
     689                 :           5 :         char       *linkloc_with_version_dir;
     690                 :           5 :         DIR                *dirdesc;
     691                 :           5 :         struct dirent *de;
     692                 :           5 :         char       *subfile;
     693                 :           5 :         struct stat st;
     694                 :             : 
     695                 :           5 :         linkloc_with_version_dir = psprintf("%s/%u/%s", PG_TBLSPC_DIR, tablespaceoid,
     696                 :             :                                                                                 TABLESPACE_VERSION_DIRECTORY);
     697                 :             : 
     698                 :             :         /*
     699                 :             :          * Check if the tablespace still contains any files.  We try to rmdir each
     700                 :             :          * per-database directory we find in it.  rmdir failure implies there are
     701                 :             :          * still files in that subdirectory, so give up.  (We do not have to worry
     702                 :             :          * about undoing any already completed rmdirs, since the next attempt to
     703                 :             :          * use the tablespace from that database will simply recreate the
     704                 :             :          * subdirectory via TablespaceCreateDbspace.)
     705                 :             :          *
     706                 :             :          * Since we hold TablespaceCreateLock, no one else should be creating any
     707                 :             :          * fresh subdirectories in parallel. It is possible that new files are
     708                 :             :          * being created within subdirectories, though, so the rmdir call could
     709                 :             :          * fail.  Worst consequence is a less friendly error message.
     710                 :             :          *
     711                 :             :          * If redo is true then ENOENT is a likely outcome here, and we allow it
     712                 :             :          * to pass without comment.  In normal operation we still allow it, but
     713                 :             :          * with a warning.  This is because even though ProcessUtility disallows
     714                 :             :          * DROP TABLESPACE in a transaction block, it's possible that a previous
     715                 :             :          * DROP failed and rolled back after removing the tablespace directories
     716                 :             :          * and/or symlink.  We want to allow a new DROP attempt to succeed at
     717                 :             :          * removing the catalog entries (and symlink if still present), so we
     718                 :             :          * should not give a hard error here.
     719                 :             :          */
     720                 :           5 :         dirdesc = AllocateDir(linkloc_with_version_dir);
     721         [ +  - ]:           5 :         if (dirdesc == NULL)
     722                 :             :         {
     723         [ #  # ]:           0 :                 if (errno == ENOENT)
     724                 :             :                 {
     725         [ #  # ]:           0 :                         if (!redo)
     726   [ #  #  #  # ]:           0 :                                 ereport(WARNING,
     727                 :             :                                                 (errcode_for_file_access(),
     728                 :             :                                                  errmsg("could not open directory \"%s\": %m",
     729                 :             :                                                                 linkloc_with_version_dir)));
     730                 :             :                         /* The symlink might still exist, so go try to remove it */
     731                 :           0 :                         goto remove_symlink;
     732                 :             :                 }
     733         [ #  # ]:           0 :                 else if (redo)
     734                 :             :                 {
     735                 :             :                         /* in redo, just log other types of error */
     736   [ #  #  #  # ]:           0 :                         ereport(LOG,
     737                 :             :                                         (errcode_for_file_access(),
     738                 :             :                                          errmsg("could not open directory \"%s\": %m",
     739                 :             :                                                         linkloc_with_version_dir)));
     740                 :           0 :                         pfree(linkloc_with_version_dir);
     741                 :           0 :                         return false;
     742                 :             :                 }
     743                 :             :                 /* else let ReadDir report the error */
     744                 :           0 :         }
     745                 :             : 
     746         [ +  + ]:          16 :         while ((de = ReadDir(dirdesc, linkloc_with_version_dir)) != NULL)
     747                 :             :         {
     748   [ +  +  +  + ]:          14 :                 if (strcmp(de->d_name, ".") == 0 ||
     749                 :           9 :                         strcmp(de->d_name, "..") == 0)
     750                 :          10 :                         continue;
     751                 :             : 
     752                 :           4 :                 subfile = psprintf("%s/%s", linkloc_with_version_dir, de->d_name);
     753                 :             : 
     754                 :             :                 /* This check is just to deliver a friendlier error message */
     755   [ +  -  +  + ]:           4 :                 if (!redo && !directory_is_empty(subfile))
     756                 :             :                 {
     757                 :           3 :                         FreeDir(dirdesc);
     758                 :           3 :                         pfree(subfile);
     759                 :           3 :                         pfree(linkloc_with_version_dir);
     760                 :           3 :                         return false;
     761                 :             :                 }
     762                 :             : 
     763                 :             :                 /* remove empty directory */
     764         [ +  - ]:           1 :                 if (rmdir(subfile) < 0)
     765   [ #  #  #  #  :           0 :                         ereport(redo ? LOG : ERROR,
          #  #  #  #  #  
                      # ]
     766                 :             :                                         (errcode_for_file_access(),
     767                 :             :                                          errmsg("could not remove directory \"%s\": %m",
     768                 :             :                                                         subfile)));
     769                 :             : 
     770                 :           1 :                 pfree(subfile);
     771                 :             :         }
     772                 :             : 
     773                 :           2 :         FreeDir(dirdesc);
     774                 :             : 
     775                 :             :         /* remove version directory */
     776         [ +  - ]:           2 :         if (rmdir(linkloc_with_version_dir) < 0)
     777                 :             :         {
     778   [ #  #  #  #  :           0 :                 ereport(redo ? LOG : ERROR,
          #  #  #  #  #  
                      # ]
     779                 :             :                                 (errcode_for_file_access(),
     780                 :             :                                  errmsg("could not remove directory \"%s\": %m",
     781                 :             :                                                 linkloc_with_version_dir)));
     782                 :           0 :                 pfree(linkloc_with_version_dir);
     783                 :           0 :                 return false;
     784                 :             :         }
     785                 :             : 
     786                 :             :         /*
     787                 :             :          * Try to remove the symlink.  We must however deal with the possibility
     788                 :             :          * that it's a directory instead of a symlink --- this could happen during
     789                 :             :          * WAL replay (see TablespaceCreateDbspace).
     790                 :             :          *
     791                 :             :          * Note: in the redo case, we'll return true if this final step fails;
     792                 :             :          * there's no point in retrying it.  Also, ENOENT should provoke no more
     793                 :             :          * than a warning.
     794                 :             :          */
     795                 :             : remove_symlink:
     796                 :           2 :         linkloc = pstrdup(linkloc_with_version_dir);
     797                 :           2 :         get_parent_directory(linkloc);
     798         [ +  - ]:           2 :         if (lstat(linkloc, &st) < 0)
     799                 :             :         {
     800                 :           0 :                 int                     saved_errno = errno;
     801                 :             : 
     802   [ #  #  #  #  :           0 :                 ereport(redo ? LOG : (saved_errno == ENOENT ? WARNING : ERROR),
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
     803                 :             :                                 (errcode_for_file_access(),
     804                 :             :                                  errmsg("could not stat file \"%s\": %m",
     805                 :             :                                                 linkloc)));
     806                 :           0 :         }
     807         [ +  - ]:           2 :         else if (S_ISDIR(st.st_mode))
     808                 :             :         {
     809         [ +  - ]:           2 :                 if (rmdir(linkloc) < 0)
     810                 :             :                 {
     811                 :           0 :                         int                     saved_errno = errno;
     812                 :             : 
     813   [ #  #  #  #  :           0 :                         ereport(redo ? LOG : (saved_errno == ENOENT ? WARNING : ERROR),
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
     814                 :             :                                         (errcode_for_file_access(),
     815                 :             :                                          errmsg("could not remove directory \"%s\": %m",
     816                 :             :                                                         linkloc)));
     817                 :           0 :                 }
     818                 :           2 :         }
     819         [ #  # ]:           0 :         else if (S_ISLNK(st.st_mode))
     820                 :             :         {
     821         [ #  # ]:           0 :                 if (unlink(linkloc) < 0)
     822                 :             :                 {
     823                 :           0 :                         int                     saved_errno = errno;
     824                 :             : 
     825   [ #  #  #  #  :           0 :                         ereport(redo ? LOG : (saved_errno == ENOENT ? WARNING : ERROR),
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                #  #  # ]
     826                 :             :                                         (errcode_for_file_access(),
     827                 :             :                                          errmsg("could not remove symbolic link \"%s\": %m",
     828                 :             :                                                         linkloc)));
     829                 :           0 :                 }
     830                 :           0 :         }
     831                 :             :         else
     832                 :             :         {
     833                 :             :                 /* Refuse to remove anything that's not a directory or symlink */
     834   [ #  #  #  #  :           0 :                 ereport(redo ? LOG : ERROR,
          #  #  #  #  #  
                      # ]
     835                 :             :                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     836                 :             :                                  errmsg("\"%s\" is not a directory or symbolic link",
     837                 :             :                                                 linkloc)));
     838                 :             :         }
     839                 :             : 
     840                 :           2 :         pfree(linkloc_with_version_dir);
     841                 :           2 :         pfree(linkloc);
     842                 :             : 
     843                 :           2 :         return true;
     844                 :           5 : }
     845                 :             : 
     846                 :             : 
     847                 :             : /*
     848                 :             :  * Check if a directory is empty.
     849                 :             :  *
     850                 :             :  * This probably belongs somewhere else, but not sure where...
     851                 :             :  */
     852                 :             : bool
     853                 :          10 : directory_is_empty(const char *path)
     854                 :             : {
     855                 :          10 :         DIR                *dirdesc;
     856                 :          10 :         struct dirent *de;
     857                 :             : 
     858                 :          10 :         dirdesc = AllocateDir(path);
     859                 :             : 
     860         [ +  + ]:          30 :         while ((de = ReadDir(dirdesc, path)) != NULL)
     861                 :             :         {
     862   [ +  +  +  + ]:          29 :                 if (strcmp(de->d_name, ".") == 0 ||
     863                 :          19 :                         strcmp(de->d_name, "..") == 0)
     864                 :          20 :                         continue;
     865                 :           9 :                 FreeDir(dirdesc);
     866                 :           9 :                 return false;
     867                 :             :         }
     868                 :             : 
     869                 :           1 :         FreeDir(dirdesc);
     870                 :           1 :         return true;
     871                 :          10 : }
     872                 :             : 
     873                 :             : /*
     874                 :             :  *      remove_tablespace_symlink
     875                 :             :  *
     876                 :             :  * This function removes symlinks in pg_tblspc.  On Windows, junction points
     877                 :             :  * act like directories so we must be able to apply rmdir.  This function
     878                 :             :  * works like the symlink removal code in destroy_tablespace_directories,
     879                 :             :  * except that failure to remove is always an ERROR.  But if the file doesn't
     880                 :             :  * exist at all, that's OK.
     881                 :             :  */
     882                 :             : void
     883                 :           0 : remove_tablespace_symlink(const char *linkloc)
     884                 :             : {
     885                 :           0 :         struct stat st;
     886                 :             : 
     887         [ #  # ]:           0 :         if (lstat(linkloc, &st) < 0)
     888                 :             :         {
     889         [ #  # ]:           0 :                 if (errno == ENOENT)
     890                 :           0 :                         return;
     891   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     892                 :             :                                 (errcode_for_file_access(),
     893                 :             :                                  errmsg("could not stat file \"%s\": %m", linkloc)));
     894                 :           0 :         }
     895                 :             : 
     896         [ #  # ]:           0 :         if (S_ISDIR(st.st_mode))
     897                 :             :         {
     898                 :             :                 /*
     899                 :             :                  * This will fail if the directory isn't empty, but not if it's a
     900                 :             :                  * junction point.
     901                 :             :                  */
     902   [ #  #  #  # ]:           0 :                 if (rmdir(linkloc) < 0 && errno != ENOENT)
     903   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     904                 :             :                                         (errcode_for_file_access(),
     905                 :             :                                          errmsg("could not remove directory \"%s\": %m",
     906                 :             :                                                         linkloc)));
     907                 :           0 :         }
     908         [ #  # ]:           0 :         else if (S_ISLNK(st.st_mode))
     909                 :             :         {
     910   [ #  #  #  # ]:           0 :                 if (unlink(linkloc) < 0 && errno != ENOENT)
     911   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     912                 :             :                                         (errcode_for_file_access(),
     913                 :             :                                          errmsg("could not remove symbolic link \"%s\": %m",
     914                 :             :                                                         linkloc)));
     915                 :           0 :         }
     916                 :             :         else
     917                 :             :         {
     918                 :             :                 /* Refuse to remove anything that's not a directory or symlink */
     919   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     920                 :             :                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     921                 :             :                                  errmsg("\"%s\" is not a directory or symbolic link",
     922                 :             :                                                 linkloc)));
     923                 :             :         }
     924         [ #  # ]:           0 : }
     925                 :             : 
     926                 :             : /*
     927                 :             :  * Rename a tablespace
     928                 :             :  */
     929                 :             : ObjectAddress
     930                 :           1 : RenameTableSpace(const char *oldname, const char *newname)
     931                 :             : {
     932                 :           1 :         Oid                     tspId;
     933                 :           1 :         Relation        rel;
     934                 :           1 :         ScanKeyData entry[1];
     935                 :           1 :         TableScanDesc scan;
     936                 :           1 :         HeapTuple       tup;
     937                 :           1 :         HeapTuple       newtuple;
     938                 :           1 :         Form_pg_tablespace newform;
     939                 :             :         ObjectAddress address;
     940                 :             : 
     941                 :             :         /* Search pg_tablespace */
     942                 :           1 :         rel = table_open(TableSpaceRelationId, RowExclusiveLock);
     943                 :             : 
     944                 :           2 :         ScanKeyInit(&entry[0],
     945                 :             :                                 Anum_pg_tablespace_spcname,
     946                 :             :                                 BTEqualStrategyNumber, F_NAMEEQ,
     947                 :           1 :                                 CStringGetDatum(oldname));
     948                 :           1 :         scan = table_beginscan_catalog(rel, 1, entry);
     949                 :           1 :         tup = heap_getnext(scan, ForwardScanDirection);
     950         [ +  - ]:           1 :         if (!HeapTupleIsValid(tup))
     951   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     952                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     953                 :             :                                  errmsg("tablespace \"%s\" does not exist",
     954                 :             :                                                 oldname)));
     955                 :             : 
     956                 :           1 :         newtuple = heap_copytuple(tup);
     957                 :           1 :         newform = (Form_pg_tablespace) GETSTRUCT(newtuple);
     958                 :           1 :         tspId = newform->oid;
     959                 :             : 
     960                 :           1 :         table_endscan(scan);
     961                 :             : 
     962                 :             :         /* Must be owner */
     963         [ +  - ]:           1 :         if (!object_ownercheck(TableSpaceRelationId, tspId, GetUserId()))
     964                 :           0 :                 aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_TABLESPACE, oldname);
     965                 :             : 
     966                 :             :         /* Validate new name */
     967   [ +  -  +  - ]:           1 :         if (!allowSystemTableMods && IsReservedName(newname))
     968   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     969                 :             :                                 (errcode(ERRCODE_RESERVED_NAME),
     970                 :             :                                  errmsg("unacceptable tablespace name \"%s\"", newname),
     971                 :             :                                  errdetail("The prefix \"pg_\" is reserved for system tablespaces.")));
     972                 :             : 
     973                 :             :         /*
     974                 :             :          * If built with appropriate switch, whine when regression-testing
     975                 :             :          * conventions for tablespace names are violated.
     976                 :             :          */
     977                 :             : #ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
     978                 :             :         if (strncmp(newname, "regress_", 8) != 0)
     979                 :             :                 elog(WARNING, "tablespaces created by regression test cases should have names starting with \"regress_\"");
     980                 :             : #endif
     981                 :             : 
     982                 :             :         /* Make sure the new name doesn't exist */
     983                 :           2 :         ScanKeyInit(&entry[0],
     984                 :             :                                 Anum_pg_tablespace_spcname,
     985                 :             :                                 BTEqualStrategyNumber, F_NAMEEQ,
     986                 :           1 :                                 CStringGetDatum(newname));
     987                 :           1 :         scan = table_beginscan_catalog(rel, 1, entry);
     988                 :           1 :         tup = heap_getnext(scan, ForwardScanDirection);
     989         [ +  - ]:           1 :         if (HeapTupleIsValid(tup))
     990   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     991                 :             :                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     992                 :             :                                  errmsg("tablespace \"%s\" already exists",
     993                 :             :                                                 newname)));
     994                 :             : 
     995                 :           1 :         table_endscan(scan);
     996                 :             : 
     997                 :             :         /* OK, update the entry */
     998                 :           1 :         namestrcpy(&(newform->spcname), newname);
     999                 :             : 
    1000                 :           1 :         CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
    1001                 :             : 
    1002         [ +  - ]:           1 :         InvokeObjectPostAlterHook(TableSpaceRelationId, tspId, 0);
    1003                 :             : 
    1004                 :           1 :         ObjectAddressSet(address, TableSpaceRelationId, tspId);
    1005                 :             : 
    1006                 :           1 :         table_close(rel, NoLock);
    1007                 :             : 
    1008                 :             :         return address;
    1009                 :           1 : }
    1010                 :             : 
    1011                 :             : /*
    1012                 :             :  * Alter table space options
    1013                 :             :  */
    1014                 :             : Oid
    1015                 :           4 : AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
    1016                 :             : {
    1017                 :           4 :         Relation        rel;
    1018                 :           4 :         ScanKeyData entry[1];
    1019                 :           4 :         TableScanDesc scandesc;
    1020                 :           4 :         HeapTuple       tup;
    1021                 :           4 :         Oid                     tablespaceoid;
    1022                 :           4 :         Datum           datum;
    1023                 :           4 :         Datum           newOptions;
    1024                 :           4 :         Datum           repl_val[Natts_pg_tablespace];
    1025                 :           4 :         bool            isnull;
    1026                 :           4 :         bool            repl_null[Natts_pg_tablespace];
    1027                 :           4 :         bool            repl_repl[Natts_pg_tablespace];
    1028                 :           4 :         HeapTuple       newtuple;
    1029                 :             : 
    1030                 :             :         /* Search pg_tablespace */
    1031                 :           4 :         rel = table_open(TableSpaceRelationId, RowExclusiveLock);
    1032                 :             : 
    1033                 :           8 :         ScanKeyInit(&entry[0],
    1034                 :             :                                 Anum_pg_tablespace_spcname,
    1035                 :             :                                 BTEqualStrategyNumber, F_NAMEEQ,
    1036                 :           4 :                                 CStringGetDatum(stmt->tablespacename));
    1037                 :           4 :         scandesc = table_beginscan_catalog(rel, 1, entry);
    1038                 :           4 :         tup = heap_getnext(scandesc, ForwardScanDirection);
    1039         [ +  - ]:           4 :         if (!HeapTupleIsValid(tup))
    1040   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    1041                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    1042                 :             :                                  errmsg("tablespace \"%s\" does not exist",
    1043                 :             :                                                 stmt->tablespacename)));
    1044                 :             : 
    1045                 :           4 :         tablespaceoid = ((Form_pg_tablespace) GETSTRUCT(tup))->oid;
    1046                 :             : 
    1047                 :             :         /* Must be owner of the existing object */
    1048         [ +  - ]:           4 :         if (!object_ownercheck(TableSpaceRelationId, tablespaceoid, GetUserId()))
    1049                 :           0 :                 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TABLESPACE,
    1050                 :           0 :                                            stmt->tablespacename);
    1051                 :             : 
    1052                 :             :         /* Generate new proposed spcoptions (text array) */
    1053                 :           8 :         datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions,
    1054                 :           4 :                                                  RelationGetDescr(rel), &isnull);
    1055         [ +  + ]:           4 :         newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
    1056                 :           4 :                                                                          stmt->options, NULL, NULL, false,
    1057                 :           4 :                                                                          stmt->isReset);
    1058                 :           4 :         (void) tablespace_reloptions(newOptions, true);
    1059                 :             : 
    1060                 :             :         /* Build new tuple. */
    1061                 :           4 :         memset(repl_null, false, sizeof(repl_null));
    1062                 :           4 :         memset(repl_repl, false, sizeof(repl_repl));
    1063         [ +  - ]:           4 :         if (newOptions != (Datum) 0)
    1064                 :           4 :                 repl_val[Anum_pg_tablespace_spcoptions - 1] = newOptions;
    1065                 :             :         else
    1066                 :           0 :                 repl_null[Anum_pg_tablespace_spcoptions - 1] = true;
    1067                 :           4 :         repl_repl[Anum_pg_tablespace_spcoptions - 1] = true;
    1068                 :           8 :         newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val,
    1069                 :           4 :                                                                  repl_null, repl_repl);
    1070                 :             : 
    1071                 :             :         /* Update system catalog. */
    1072                 :           4 :         CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
    1073                 :             : 
    1074         [ +  - ]:           4 :         InvokeObjectPostAlterHook(TableSpaceRelationId, tablespaceoid, 0);
    1075                 :             : 
    1076                 :           4 :         heap_freetuple(newtuple);
    1077                 :             : 
    1078                 :             :         /* Conclude heap scan. */
    1079                 :           4 :         table_endscan(scandesc);
    1080                 :           4 :         table_close(rel, NoLock);
    1081                 :             : 
    1082                 :           8 :         return tablespaceoid;
    1083                 :           4 : }
    1084                 :             : 
    1085                 :             : /*
    1086                 :             :  * Routines for handling the GUC variable 'default_tablespace'.
    1087                 :             :  */
    1088                 :             : 
    1089                 :             : /* check_hook: validate new default_tablespace */
    1090                 :             : bool
    1091                 :          94 : check_default_tablespace(char **newval, void **extra, GucSource source)
    1092                 :             : {
    1093                 :             :         /*
    1094                 :             :          * If we aren't inside a transaction, or connected to a database, we
    1095                 :             :          * cannot do the catalog accesses necessary to verify the name.  Must
    1096                 :             :          * accept the value on faith.
    1097                 :             :          */
    1098   [ +  +  -  + ]:          94 :         if (IsTransactionState() && MyDatabaseId != InvalidOid)
    1099                 :             :         {
    1100   [ +  +  +  - ]:          88 :                 if (**newval != '\0' &&
    1101                 :           8 :                         !OidIsValid(get_tablespace_oid(*newval, true)))
    1102                 :             :                 {
    1103                 :             :                         /*
    1104                 :             :                          * When source == PGC_S_TEST, don't throw a hard error for a
    1105                 :             :                          * nonexistent tablespace, only a NOTICE.  See comments in guc.h.
    1106                 :             :                          */
    1107         [ #  # ]:           0 :                         if (source == PGC_S_TEST)
    1108                 :             :                         {
    1109   [ #  #  #  # ]:           0 :                                 ereport(NOTICE,
    1110                 :             :                                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    1111                 :             :                                                  errmsg("tablespace \"%s\" does not exist",
    1112                 :             :                                                                 *newval)));
    1113                 :           0 :                         }
    1114                 :             :                         else
    1115                 :             :                         {
    1116                 :           0 :                                 GUC_check_errdetail("Tablespace \"%s\" does not exist.",
    1117                 :           0 :                                                                         *newval);
    1118                 :           0 :                                 return false;
    1119                 :             :                         }
    1120                 :           0 :                 }
    1121                 :          88 :         }
    1122                 :             : 
    1123                 :          94 :         return true;
    1124                 :          94 : }
    1125                 :             : 
    1126                 :             : /*
    1127                 :             :  * GetDefaultTablespace -- get the OID of the current default tablespace
    1128                 :             :  *
    1129                 :             :  * Temporary objects have different default tablespaces, hence the
    1130                 :             :  * relpersistence parameter must be specified.  Also, for partitioned tables,
    1131                 :             :  * we disallow specifying the database default, so that needs to be specified
    1132                 :             :  * too.
    1133                 :             :  *
    1134                 :             :  * May return InvalidOid to indicate "use the database's default tablespace".
    1135                 :             :  *
    1136                 :             :  * Note that caller is expected to check appropriate permissions for any
    1137                 :             :  * result other than InvalidOid.
    1138                 :             :  *
    1139                 :             :  * This exists to hide (and possibly optimize the use of) the
    1140                 :             :  * default_tablespace GUC variable.
    1141                 :             :  */
    1142                 :             : Oid
    1143                 :        7684 : GetDefaultTablespace(char relpersistence, bool partitioned)
    1144                 :             : {
    1145                 :        7684 :         Oid                     result;
    1146                 :             : 
    1147                 :             :         /* The temp-table case is handled elsewhere */
    1148         [ +  + ]:        7684 :         if (relpersistence == RELPERSISTENCE_TEMP)
    1149                 :             :         {
    1150                 :         699 :                 PrepareTempTablespaces();
    1151                 :         699 :                 return GetNextTempTableSpace();
    1152                 :             :         }
    1153                 :             : 
    1154                 :             :         /* Fast path for default_tablespace == "" */
    1155   [ +  -  +  + ]:        6985 :         if (default_tablespace == NULL || default_tablespace[0] == '\0')
    1156                 :        6974 :                 return InvalidOid;
    1157                 :             : 
    1158                 :             :         /*
    1159                 :             :          * It is tempting to cache this lookup for more speed, but then we would
    1160                 :             :          * fail to detect the case where the tablespace was dropped since the GUC
    1161                 :             :          * variable was set.  Note also that we don't complain if the value fails
    1162                 :             :          * to refer to an existing tablespace; we just silently return InvalidOid,
    1163                 :             :          * causing the new object to be created in the database's tablespace.
    1164                 :             :          */
    1165                 :          11 :         result = get_tablespace_oid(default_tablespace, true);
    1166                 :             : 
    1167                 :             :         /*
    1168                 :             :          * Allow explicit specification of database's default tablespace in
    1169                 :             :          * default_tablespace without triggering permissions checks.  Don't allow
    1170                 :             :          * specifying that when creating a partitioned table, however, since the
    1171                 :             :          * result is confusing.
    1172                 :             :          */
    1173         [ +  + ]:          11 :         if (result == MyDatabaseTableSpace)
    1174                 :             :         {
    1175         [ -  + ]:           2 :                 if (partitioned)
    1176   [ +  -  +  - ]:           2 :                         ereport(ERROR,
    1177                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1178                 :             :                                          errmsg("cannot specify default tablespace for partitioned relations")));
    1179                 :           0 :                 result = InvalidOid;
    1180                 :           0 :         }
    1181                 :           9 :         return result;
    1182                 :        7682 : }
    1183                 :             : 
    1184                 :             : 
    1185                 :             : /*
    1186                 :             :  * Routines for handling the GUC variable 'temp_tablespaces'.
    1187                 :             :  */
    1188                 :             : 
    1189                 :             : typedef struct
    1190                 :             : {
    1191                 :             :         /* Array of OIDs to be passed to SetTempTablespaces() */
    1192                 :             :         int                     numSpcs;
    1193                 :             :         Oid                     tblSpcs[FLEXIBLE_ARRAY_MEMBER];
    1194                 :             : } temp_tablespaces_extra;
    1195                 :             : 
    1196                 :             : /* check_hook: validate new temp_tablespaces */
    1197                 :             : bool
    1198                 :           8 : check_temp_tablespaces(char **newval, void **extra, GucSource source)
    1199                 :             : {
    1200                 :           8 :         char       *rawname;
    1201                 :           8 :         List       *namelist;
    1202                 :             : 
    1203                 :             :         /* Need a modifiable copy of string */
    1204                 :           8 :         rawname = pstrdup(*newval);
    1205                 :             : 
    1206                 :             :         /* Parse string into list of identifiers */
    1207         [ +  - ]:           8 :         if (!SplitIdentifierString(rawname, ',', &namelist))
    1208                 :             :         {
    1209                 :             :                 /* syntax error in name list */
    1210                 :           0 :                 GUC_check_errdetail("List syntax is invalid.");
    1211                 :           0 :                 pfree(rawname);
    1212                 :           0 :                 list_free(namelist);
    1213                 :           0 :                 return false;
    1214                 :             :         }
    1215                 :             : 
    1216                 :             :         /*
    1217                 :             :          * If we aren't inside a transaction, or connected to a database, we
    1218                 :             :          * cannot do the catalog accesses necessary to verify the name.  Must
    1219                 :             :          * accept the value on faith. Fortunately, there's then also no need to
    1220                 :             :          * pass the data to fd.c.
    1221                 :             :          */
    1222   [ +  +  -  + ]:           8 :         if (IsTransactionState() && MyDatabaseId != InvalidOid)
    1223                 :             :         {
    1224                 :           2 :                 temp_tablespaces_extra *myextra;
    1225                 :           2 :                 Oid                *tblSpcs;
    1226                 :           2 :                 int                     numSpcs;
    1227                 :           2 :                 ListCell   *l;
    1228                 :             : 
    1229                 :             :                 /* temporary workspace until we are done verifying the list */
    1230                 :           2 :                 tblSpcs = (Oid *) palloc(list_length(namelist) * sizeof(Oid));
    1231                 :           2 :                 numSpcs = 0;
    1232   [ -  +  #  #  :           2 :                 foreach(l, namelist)
                   -  + ]
    1233                 :             :                 {
    1234                 :           0 :                         char       *curname = (char *) lfirst(l);
    1235                 :           0 :                         Oid                     curoid;
    1236                 :           0 :                         AclResult       aclresult;
    1237                 :             : 
    1238                 :             :                         /* Allow an empty string (signifying database default) */
    1239         [ #  # ]:           0 :                         if (curname[0] == '\0')
    1240                 :             :                         {
    1241                 :             :                                 /* InvalidOid signifies database's default tablespace */
    1242                 :           0 :                                 tblSpcs[numSpcs++] = InvalidOid;
    1243                 :           0 :                                 continue;
    1244                 :             :                         }
    1245                 :             : 
    1246                 :             :                         /*
    1247                 :             :                          * In an interactive SET command, we ereport for bad info.  When
    1248                 :             :                          * source == PGC_S_TEST, don't throw a hard error for a
    1249                 :             :                          * nonexistent tablespace, only a NOTICE.  See comments in guc.h.
    1250                 :             :                          */
    1251                 :           0 :                         curoid = get_tablespace_oid(curname, source <= PGC_S_TEST);
    1252         [ #  # ]:           0 :                         if (curoid == InvalidOid)
    1253                 :             :                         {
    1254         [ #  # ]:           0 :                                 if (source == PGC_S_TEST)
    1255   [ #  #  #  # ]:           0 :                                         ereport(NOTICE,
    1256                 :             :                                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
    1257                 :             :                                                          errmsg("tablespace \"%s\" does not exist",
    1258                 :             :                                                                         curname)));
    1259                 :           0 :                                 continue;
    1260                 :             :                         }
    1261                 :             : 
    1262                 :             :                         /*
    1263                 :             :                          * Allow explicit specification of database's default tablespace
    1264                 :             :                          * in temp_tablespaces without triggering permissions checks.
    1265                 :             :                          */
    1266         [ #  # ]:           0 :                         if (curoid == MyDatabaseTableSpace)
    1267                 :             :                         {
    1268                 :             :                                 /* InvalidOid signifies database's default tablespace */
    1269                 :           0 :                                 tblSpcs[numSpcs++] = InvalidOid;
    1270                 :           0 :                                 continue;
    1271                 :             :                         }
    1272                 :             : 
    1273                 :             :                         /* Check permissions, similarly complaining only if interactive */
    1274                 :           0 :                         aclresult = object_aclcheck(TableSpaceRelationId, curoid, GetUserId(),
    1275                 :             :                                                                                 ACL_CREATE);
    1276         [ #  # ]:           0 :                         if (aclresult != ACLCHECK_OK)
    1277                 :             :                         {
    1278         [ #  # ]:           0 :                                 if (source >= PGC_S_INTERACTIVE)
    1279                 :           0 :                                         aclcheck_error(aclresult, OBJECT_TABLESPACE, curname);
    1280                 :           0 :                                 continue;
    1281                 :             :                         }
    1282                 :             : 
    1283                 :           0 :                         tblSpcs[numSpcs++] = curoid;
    1284      [ #  #  # ]:           0 :                 }
    1285                 :             : 
    1286                 :             :                 /* Now prepare an "extra" struct for assign_temp_tablespaces */
    1287                 :           2 :                 myextra = guc_malloc(LOG, offsetof(temp_tablespaces_extra, tblSpcs) +
    1288                 :           2 :                                                          numSpcs * sizeof(Oid));
    1289         [ +  - ]:           2 :                 if (!myextra)
    1290                 :           0 :                         return false;
    1291                 :           2 :                 myextra->numSpcs = numSpcs;
    1292                 :           2 :                 memcpy(myextra->tblSpcs, tblSpcs, numSpcs * sizeof(Oid));
    1293                 :           2 :                 *extra = myextra;
    1294                 :             : 
    1295                 :           2 :                 pfree(tblSpcs);
    1296         [ -  + ]:           2 :         }
    1297                 :             : 
    1298                 :           8 :         pfree(rawname);
    1299                 :           8 :         list_free(namelist);
    1300                 :             : 
    1301                 :           8 :         return true;
    1302                 :           8 : }
    1303                 :             : 
    1304                 :             : /* assign_hook: do extra actions as needed */
    1305                 :             : void
    1306                 :           8 : assign_temp_tablespaces(const char *newval, void *extra)
    1307                 :             : {
    1308                 :           8 :         temp_tablespaces_extra *myextra = (temp_tablespaces_extra *) extra;
    1309                 :             : 
    1310                 :             :         /*
    1311                 :             :          * If check_temp_tablespaces was executed inside a transaction, then pass
    1312                 :             :          * the list it made to fd.c.  Otherwise, clear fd.c's list; we must be
    1313                 :             :          * still outside a transaction, or else restoring during transaction exit,
    1314                 :             :          * and in either case we can just let the next PrepareTempTablespaces call
    1315                 :             :          * make things sane.
    1316                 :             :          */
    1317         [ +  + ]:           8 :         if (myextra)
    1318                 :           1 :                 SetTempTablespaces(myextra->tblSpcs, myextra->numSpcs);
    1319                 :             :         else
    1320                 :           7 :                 SetTempTablespaces(NULL, 0);
    1321                 :           8 : }
    1322                 :             : 
    1323                 :             : /*
    1324                 :             :  * PrepareTempTablespaces -- prepare to use temp tablespaces
    1325                 :             :  *
    1326                 :             :  * If we have not already done so in the current transaction, parse the
    1327                 :             :  * temp_tablespaces GUC variable and tell fd.c which tablespace(s) to use
    1328                 :             :  * for temp files.
    1329                 :             :  */
    1330                 :             : void
    1331                 :        1454 : PrepareTempTablespaces(void)
    1332                 :             : {
    1333                 :        1454 :         char       *rawname;
    1334                 :        1454 :         List       *namelist;
    1335                 :        1454 :         Oid                *tblSpcs;
    1336                 :        1454 :         int                     numSpcs;
    1337                 :        1454 :         ListCell   *l;
    1338                 :             : 
    1339                 :             :         /* No work if already done in current transaction */
    1340         [ +  + ]:        1454 :         if (TempTablespacesAreSet())
    1341                 :         788 :                 return;
    1342                 :             : 
    1343                 :             :         /*
    1344                 :             :          * Can't do catalog access unless within a transaction.  This is just a
    1345                 :             :          * safety check in case this function is called by low-level code that
    1346                 :             :          * could conceivably execute outside a transaction.  Note that in such a
    1347                 :             :          * scenario, fd.c will fall back to using the current database's default
    1348                 :             :          * tablespace, which should always be OK.
    1349                 :             :          */
    1350         [ +  - ]:         666 :         if (!IsTransactionState())
    1351                 :           0 :                 return;
    1352                 :             : 
    1353                 :             :         /* Need a modifiable copy of string */
    1354                 :         666 :         rawname = pstrdup(temp_tablespaces);
    1355                 :             : 
    1356                 :             :         /* Parse string into list of identifiers */
    1357         [ -  + ]:         666 :         if (!SplitIdentifierString(rawname, ',', &namelist))
    1358                 :             :         {
    1359                 :             :                 /* syntax error in name list */
    1360                 :           0 :                 SetTempTablespaces(NULL, 0);
    1361                 :           0 :                 pfree(rawname);
    1362                 :           0 :                 list_free(namelist);
    1363                 :           0 :                 return;
    1364                 :             :         }
    1365                 :             : 
    1366                 :             :         /* Store tablespace OIDs in an array in TopTransactionContext */
    1367                 :        1332 :         tblSpcs = (Oid *) MemoryContextAlloc(TopTransactionContext,
    1368                 :         666 :                                                                                  list_length(namelist) * sizeof(Oid));
    1369                 :         666 :         numSpcs = 0;
    1370   [ -  +  #  #  :         666 :         foreach(l, namelist)
                   -  + ]
    1371                 :             :         {
    1372                 :           0 :                 char       *curname = (char *) lfirst(l);
    1373                 :           0 :                 Oid                     curoid;
    1374                 :           0 :                 AclResult       aclresult;
    1375                 :             : 
    1376                 :             :                 /* Allow an empty string (signifying database default) */
    1377         [ #  # ]:           0 :                 if (curname[0] == '\0')
    1378                 :             :                 {
    1379                 :             :                         /* InvalidOid signifies database's default tablespace */
    1380                 :           0 :                         tblSpcs[numSpcs++] = InvalidOid;
    1381                 :           0 :                         continue;
    1382                 :             :                 }
    1383                 :             : 
    1384                 :             :                 /* Else verify that name is a valid tablespace name */
    1385                 :           0 :                 curoid = get_tablespace_oid(curname, true);
    1386         [ #  # ]:           0 :                 if (curoid == InvalidOid)
    1387                 :             :                 {
    1388                 :             :                         /* Skip any bad list elements */
    1389                 :           0 :                         continue;
    1390                 :             :                 }
    1391                 :             : 
    1392                 :             :                 /*
    1393                 :             :                  * Allow explicit specification of database's default tablespace in
    1394                 :             :                  * temp_tablespaces without triggering permissions checks.
    1395                 :             :                  */
    1396         [ #  # ]:           0 :                 if (curoid == MyDatabaseTableSpace)
    1397                 :             :                 {
    1398                 :             :                         /* InvalidOid signifies database's default tablespace */
    1399                 :           0 :                         tblSpcs[numSpcs++] = InvalidOid;
    1400                 :           0 :                         continue;
    1401                 :             :                 }
    1402                 :             : 
    1403                 :             :                 /* Check permissions similarly */
    1404                 :           0 :                 aclresult = object_aclcheck(TableSpaceRelationId, curoid, GetUserId(),
    1405                 :             :                                                                         ACL_CREATE);
    1406         [ #  # ]:           0 :                 if (aclresult != ACLCHECK_OK)
    1407                 :           0 :                         continue;
    1408                 :             : 
    1409                 :           0 :                 tblSpcs[numSpcs++] = curoid;
    1410         [ #  # ]:           0 :         }
    1411                 :             : 
    1412                 :         666 :         SetTempTablespaces(tblSpcs, numSpcs);
    1413                 :             : 
    1414                 :         666 :         pfree(rawname);
    1415                 :         666 :         list_free(namelist);
    1416                 :        1454 : }
    1417                 :             : 
    1418                 :             : 
    1419                 :             : /*
    1420                 :             :  * get_tablespace_oid - given a tablespace name, look up the OID
    1421                 :             :  *
    1422                 :             :  * If missing_ok is false, throw an error if tablespace name not found.  If
    1423                 :             :  * true, just return InvalidOid.
    1424                 :             :  */
    1425                 :             : Oid
    1426                 :         135 : get_tablespace_oid(const char *tablespacename, bool missing_ok)
    1427                 :             : {
    1428                 :         135 :         Oid                     result;
    1429                 :         135 :         Relation        rel;
    1430                 :         135 :         TableScanDesc scandesc;
    1431                 :         135 :         HeapTuple       tuple;
    1432                 :         135 :         ScanKeyData entry[1];
    1433                 :             : 
    1434                 :             :         /*
    1435                 :             :          * Search pg_tablespace.  We use a heapscan here even though there is an
    1436                 :             :          * index on name, on the theory that pg_tablespace will usually have just
    1437                 :             :          * a few entries and so an indexed lookup is a waste of effort.
    1438                 :             :          */
    1439                 :         135 :         rel = table_open(TableSpaceRelationId, AccessShareLock);
    1440                 :             : 
    1441                 :         270 :         ScanKeyInit(&entry[0],
    1442                 :             :                                 Anum_pg_tablespace_spcname,
    1443                 :             :                                 BTEqualStrategyNumber, F_NAMEEQ,
    1444                 :         135 :                                 CStringGetDatum(tablespacename));
    1445                 :         135 :         scandesc = table_beginscan_catalog(rel, 1, entry);
    1446                 :         135 :         tuple = heap_getnext(scandesc, ForwardScanDirection);
    1447                 :             : 
    1448                 :             :         /* We assume that there can be at most one matching tuple */
    1449         [ +  + ]:         135 :         if (HeapTupleIsValid(tuple))
    1450                 :         129 :                 result = ((Form_pg_tablespace) GETSTRUCT(tuple))->oid;
    1451                 :             :         else
    1452                 :           6 :                 result = InvalidOid;
    1453                 :             : 
    1454                 :         135 :         table_endscan(scandesc);
    1455                 :         135 :         table_close(rel, AccessShareLock);
    1456                 :             : 
    1457   [ +  +  +  + ]:         135 :         if (!OidIsValid(result) && !missing_ok)
    1458   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    1459                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    1460                 :             :                                  errmsg("tablespace \"%s\" does not exist",
    1461                 :             :                                                 tablespacename)));
    1462                 :             : 
    1463                 :         266 :         return result;
    1464                 :         133 : }
    1465                 :             : 
    1466                 :             : /*
    1467                 :             :  * get_tablespace_name - given a tablespace OID, look up the name
    1468                 :             :  *
    1469                 :             :  * Returns a palloc'd string, or NULL if no such tablespace.
    1470                 :             :  */
    1471                 :             : char *
    1472                 :          59 : get_tablespace_name(Oid spc_oid)
    1473                 :             : {
    1474                 :          59 :         char       *result;
    1475                 :          59 :         Relation        rel;
    1476                 :          59 :         TableScanDesc scandesc;
    1477                 :          59 :         HeapTuple       tuple;
    1478                 :          59 :         ScanKeyData entry[1];
    1479                 :             : 
    1480                 :             :         /*
    1481                 :             :          * Search pg_tablespace.  We use a heapscan here even though there is an
    1482                 :             :          * index on oid, on the theory that pg_tablespace will usually have just a
    1483                 :             :          * few entries and so an indexed lookup is a waste of effort.
    1484                 :             :          */
    1485                 :          59 :         rel = table_open(TableSpaceRelationId, AccessShareLock);
    1486                 :             : 
    1487                 :         118 :         ScanKeyInit(&entry[0],
    1488                 :             :                                 Anum_pg_tablespace_oid,
    1489                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
    1490                 :          59 :                                 ObjectIdGetDatum(spc_oid));
    1491                 :          59 :         scandesc = table_beginscan_catalog(rel, 1, entry);
    1492                 :          59 :         tuple = heap_getnext(scandesc, ForwardScanDirection);
    1493                 :             : 
    1494                 :             :         /* We assume that there can be at most one matching tuple */
    1495         [ +  + ]:          59 :         if (HeapTupleIsValid(tuple))
    1496                 :          56 :                 result = pstrdup(NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname));
    1497                 :             :         else
    1498                 :           3 :                 result = NULL;
    1499                 :             : 
    1500                 :          59 :         table_endscan(scandesc);
    1501                 :          59 :         table_close(rel, AccessShareLock);
    1502                 :             : 
    1503                 :         118 :         return result;
    1504                 :          59 : }
    1505                 :             : 
    1506                 :             : 
    1507                 :             : /*
    1508                 :             :  * TABLESPACE resource manager's routines
    1509                 :             :  */
    1510                 :             : void
    1511                 :           0 : tblspc_redo(XLogReaderState *record)
    1512                 :             : {
    1513                 :           0 :         uint8           info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
    1514                 :             : 
    1515                 :             :         /* Backup blocks are not used in tblspc records */
    1516         [ #  # ]:           0 :         Assert(!XLogRecHasAnyBlockRefs(record));
    1517                 :             : 
    1518         [ #  # ]:           0 :         if (info == XLOG_TBLSPC_CREATE)
    1519                 :             :         {
    1520                 :           0 :                 xl_tblspc_create_rec *xlrec = (xl_tblspc_create_rec *) XLogRecGetData(record);
    1521                 :           0 :                 char       *location = xlrec->ts_path;
    1522                 :             : 
    1523                 :           0 :                 create_tablespace_directories(location, xlrec->ts_id);
    1524                 :           0 :         }
    1525         [ #  # ]:           0 :         else if (info == XLOG_TBLSPC_DROP)
    1526                 :             :         {
    1527                 :           0 :                 xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *) XLogRecGetData(record);
    1528                 :             : 
    1529                 :             :                 /* Close all smgr fds in all backends. */
    1530                 :           0 :                 WaitForProcSignalBarrier(EmitProcSignalBarrier(PROCSIGNAL_BARRIER_SMGRRELEASE));
    1531                 :             : 
    1532                 :             :                 /*
    1533                 :             :                  * If we issued a WAL record for a drop tablespace it implies that
    1534                 :             :                  * there were no files in it at all when the DROP was done. That means
    1535                 :             :                  * that no permanent objects can exist in it at this point.
    1536                 :             :                  *
    1537                 :             :                  * It is possible for standby users to be using this tablespace as a
    1538                 :             :                  * location for their temporary files, so if we fail to remove all
    1539                 :             :                  * files then do conflict processing and try again, if currently
    1540                 :             :                  * enabled.
    1541                 :             :                  *
    1542                 :             :                  * Other possible reasons for failure include bollixed file
    1543                 :             :                  * permissions on a standby server when they were okay on the primary,
    1544                 :             :                  * etc etc. There's not much we can do about that, so just remove what
    1545                 :             :                  * we can and press on.
    1546                 :             :                  */
    1547         [ #  # ]:           0 :                 if (!destroy_tablespace_directories(xlrec->ts_id, true))
    1548                 :             :                 {
    1549                 :           0 :                         ResolveRecoveryConflictWithTablespace(xlrec->ts_id);
    1550                 :             : 
    1551                 :             :                         /*
    1552                 :             :                          * If we did recovery processing then hopefully the backends who
    1553                 :             :                          * wrote temp files should have cleaned up and exited by now.  So
    1554                 :             :                          * retry before complaining.  If we fail again, this is just a LOG
    1555                 :             :                          * condition, because it's not worth throwing an ERROR for (as
    1556                 :             :                          * that would crash the database and require manual intervention
    1557                 :             :                          * before we could get past this WAL record on restart).
    1558                 :             :                          */
    1559         [ #  # ]:           0 :                         if (!destroy_tablespace_directories(xlrec->ts_id, true))
    1560   [ #  #  #  # ]:           0 :                                 ereport(LOG,
    1561                 :             :                                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1562                 :             :                                                  errmsg("directories for tablespace %u could not be removed",
    1563                 :             :                                                                 xlrec->ts_id),
    1564                 :             :                                                  errhint("You can remove the directories manually if necessary.")));
    1565                 :           0 :                 }
    1566                 :           0 :         }
    1567                 :             :         else
    1568   [ #  #  #  # ]:           0 :                 elog(PANIC, "tblspc_redo: unknown op code %u", info);
    1569                 :           0 : }
        

Generated by: LCOV version 2.3.2-1