LCOV - code coverage report
Current view: top level - src/backend/commands - tablespace.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 78.3 % 410 321
Test Date: 2026-04-06 03:16:28 Functions: 100.0 % 17 17
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1