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

Generated by: LCOV version 1.14