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

Generated by: LCOV version 1.11