LCOV - code coverage report
Current view: top level - src/backend/commands - tablespace.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 80.0 % 415 332
Test Date: 2026-09-26 04:15:43 Functions: 100.0 % 17 17
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 51.1 % 380 194

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

Generated by: LCOV version 2.0-1