LCOV - code coverage report
Current view: top level - src/backend/commands - tablespace.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 78.5 % 410 322
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 17 17
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 50.3 % 380 191

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

Generated by: LCOV version 2.0-1