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

Generated by: LCOV version 1.14