LCOV - code coverage report
Current view: top level - src/backend/commands - tablespace.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 78.1 % 406 317
Test Date: 2026-02-17 17:20:33 Functions: 100.0 % 17 17
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1