Header And Logo

PostgreSQL
| The world's most advanced open source database.

tablespace.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * tablespace.c
00004  *    Commands to manipulate table spaces
00005  *
00006  * Tablespaces in PostgreSQL are designed to allow users to determine
00007  * where the data file(s) for a given database object reside on the file
00008  * system.
00009  *
00010  * A tablespace represents a directory on the file system. At tablespace
00011  * creation time, the directory must be empty. To simplify things and
00012  * remove the possibility of having file name conflicts, we isolate
00013  * files within a tablespace into database-specific subdirectories.
00014  *
00015  * To support file access via the information given in RelFileNode, we
00016  * maintain a symbolic-link map in $PGDATA/pg_tblspc. The symlinks are
00017  * named by tablespace OIDs and point to the actual tablespace directories.
00018  * There is also a per-cluster version directory in each tablespace.
00019  * Thus the full path to an arbitrary file is
00020  *          $PGDATA/pg_tblspc/spcoid/PG_MAJORVER_CATVER/dboid/relfilenode
00021  * e.g.
00022  *          $PGDATA/pg_tblspc/20981/PG_9.0_201002161/719849/83292814
00023  *
00024  * There are two tablespaces created at initdb time: pg_global (for shared
00025  * tables) and pg_default (for everything else).  For backwards compatibility
00026  * and to remain functional on platforms without symlinks, these tablespaces
00027  * are accessed specially: they are respectively
00028  *          $PGDATA/global/relfilenode
00029  *          $PGDATA/base/dboid/relfilenode
00030  *
00031  * To allow CREATE DATABASE to give a new database a default tablespace
00032  * that's different from the template database's default, we make the
00033  * provision that a zero in pg_class.reltablespace means the database's
00034  * default tablespace.  Without this, CREATE DATABASE would have to go in
00035  * and munge the system catalogs of the new database.
00036  *
00037  *
00038  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00039  * Portions Copyright (c) 1994, Regents of the University of California
00040  *
00041  *
00042  * IDENTIFICATION
00043  *    src/backend/commands/tablespace.c
00044  *
00045  *-------------------------------------------------------------------------
00046  */
00047 #include "postgres.h"
00048 
00049 #include <unistd.h>
00050 #include <dirent.h>
00051 #include <sys/types.h>
00052 #include <sys/stat.h>
00053 
00054 #include "access/heapam.h"
00055 #include "access/reloptions.h"
00056 #include "access/htup_details.h"
00057 #include "access/sysattr.h"
00058 #include "access/xact.h"
00059 #include "catalog/catalog.h"
00060 #include "catalog/dependency.h"
00061 #include "catalog/indexing.h"
00062 #include "catalog/objectaccess.h"
00063 #include "catalog/pg_tablespace.h"
00064 #include "commands/comment.h"
00065 #include "commands/seclabel.h"
00066 #include "commands/tablespace.h"
00067 #include "common/relpath.h"
00068 #include "miscadmin.h"
00069 #include "postmaster/bgwriter.h"
00070 #include "storage/fd.h"
00071 #include "storage/standby.h"
00072 #include "utils/acl.h"
00073 #include "utils/builtins.h"
00074 #include "utils/fmgroids.h"
00075 #include "utils/guc.h"
00076 #include "utils/memutils.h"
00077 #include "utils/rel.h"
00078 #include "utils/tqual.h"
00079 
00080 
00081 /* GUC variables */
00082 char       *default_tablespace = NULL;
00083 char       *temp_tablespaces = NULL;
00084 
00085 
00086 static void create_tablespace_directories(const char *location,
00087                               const Oid tablespaceoid);
00088 static bool destroy_tablespace_directories(Oid tablespaceoid, bool redo);
00089 
00090 
00091 /*
00092  * Each database using a table space is isolated into its own name space
00093  * by a subdirectory named for the database OID.  On first creation of an
00094  * object in the tablespace, create the subdirectory.  If the subdirectory
00095  * already exists, fall through quietly.
00096  *
00097  * isRedo indicates that we are creating an object during WAL replay.
00098  * In this case we will cope with the possibility of the tablespace
00099  * directory not being there either --- this could happen if we are
00100  * replaying an operation on a table in a subsequently-dropped tablespace.
00101  * We handle this by making a directory in the place where the tablespace
00102  * symlink would normally be.  This isn't an exact replay of course, but
00103  * it's the best we can do given the available information.
00104  *
00105  * If tablespaces are not supported, we still need it in case we have to
00106  * re-create a database subdirectory (of $PGDATA/base) during WAL replay.
00107  */
00108 void
00109 TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo)
00110 {
00111     struct stat st;
00112     char       *dir;
00113 
00114     /*
00115      * The global tablespace doesn't have per-database subdirectories, so
00116      * nothing to do for it.
00117      */
00118     if (spcNode == GLOBALTABLESPACE_OID)
00119         return;
00120 
00121     Assert(OidIsValid(spcNode));
00122     Assert(OidIsValid(dbNode));
00123 
00124     dir = GetDatabasePath(dbNode, spcNode);
00125 
00126     if (stat(dir, &st) < 0)
00127     {
00128         /* Directory does not exist? */
00129         if (errno == ENOENT)
00130         {
00131             /*
00132              * Acquire TablespaceCreateLock to ensure that no DROP TABLESPACE
00133              * or TablespaceCreateDbspace is running concurrently.
00134              */
00135             LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
00136 
00137             /*
00138              * Recheck to see if someone created the directory while we were
00139              * waiting for lock.
00140              */
00141             if (stat(dir, &st) == 0 && S_ISDIR(st.st_mode))
00142             {
00143                 /* Directory was created */
00144             }
00145             else
00146             {
00147                 /* Directory creation failed? */
00148                 if (mkdir(dir, S_IRWXU) < 0)
00149                 {
00150                     char       *parentdir;
00151 
00152                     /* Failure other than not exists or not in WAL replay? */
00153                     if (errno != ENOENT || !isRedo)
00154                         ereport(ERROR,
00155                                 (errcode_for_file_access(),
00156                               errmsg("could not create directory \"%s\": %m",
00157                                      dir)));
00158 
00159                     /*
00160                      * Parent directories are missing during WAL replay, so
00161                      * continue by creating simple parent directories rather
00162                      * than a symlink.
00163                      */
00164 
00165                     /* create two parents up if not exist */
00166                     parentdir = pstrdup(dir);
00167                     get_parent_directory(parentdir);
00168                     get_parent_directory(parentdir);
00169                     /* Can't create parent and it doesn't already exist? */
00170                     if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
00171                         ereport(ERROR,
00172                                 (errcode_for_file_access(),
00173                               errmsg("could not create directory \"%s\": %m",
00174                                      parentdir)));
00175                     pfree(parentdir);
00176 
00177                     /* create one parent up if not exist */
00178                     parentdir = pstrdup(dir);
00179                     get_parent_directory(parentdir);
00180                     /* Can't create parent and it doesn't already exist? */
00181                     if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
00182                         ereport(ERROR,
00183                                 (errcode_for_file_access(),
00184                               errmsg("could not create directory \"%s\": %m",
00185                                      parentdir)));
00186                     pfree(parentdir);
00187 
00188                     /* Create database directory */
00189                     if (mkdir(dir, S_IRWXU) < 0)
00190                         ereport(ERROR,
00191                                 (errcode_for_file_access(),
00192                               errmsg("could not create directory \"%s\": %m",
00193                                      dir)));
00194                 }
00195             }
00196 
00197             LWLockRelease(TablespaceCreateLock);
00198         }
00199         else
00200         {
00201             ereport(ERROR,
00202                     (errcode_for_file_access(),
00203                      errmsg("could not stat directory \"%s\": %m", dir)));
00204         }
00205     }
00206     else
00207     {
00208         /* Is it not a directory? */
00209         if (!S_ISDIR(st.st_mode))
00210             ereport(ERROR,
00211                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00212                      errmsg("\"%s\" exists but is not a directory",
00213                             dir)));
00214     }
00215 
00216     pfree(dir);
00217 }
00218 
00219 /*
00220  * Create a table space
00221  *
00222  * Only superusers can create a tablespace. This seems a reasonable restriction
00223  * since we're determining the system layout and, anyway, we probably have
00224  * root if we're doing this kind of activity
00225  */
00226 Oid
00227 CreateTableSpace(CreateTableSpaceStmt *stmt)
00228 {
00229 #ifdef HAVE_SYMLINK
00230     Relation    rel;
00231     Datum       values[Natts_pg_tablespace];
00232     bool        nulls[Natts_pg_tablespace];
00233     HeapTuple   tuple;
00234     Oid         tablespaceoid;
00235     char       *location;
00236     Oid         ownerId;
00237 
00238     /* Must be super user */
00239     if (!superuser())
00240         ereport(ERROR,
00241                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00242                  errmsg("permission denied to create tablespace \"%s\"",
00243                         stmt->tablespacename),
00244                  errhint("Must be superuser to create a tablespace.")));
00245 
00246     /* However, the eventual owner of the tablespace need not be */
00247     if (stmt->owner)
00248         ownerId = get_role_oid(stmt->owner, false);
00249     else
00250         ownerId = GetUserId();
00251 
00252     /* Unix-ify the offered path, and strip any trailing slashes */
00253     location = pstrdup(stmt->location);
00254     canonicalize_path(location);
00255 
00256     /* disallow quotes, else CREATE DATABASE would be at risk */
00257     if (strchr(location, '\''))
00258         ereport(ERROR,
00259                 (errcode(ERRCODE_INVALID_NAME),
00260                  errmsg("tablespace location cannot contain single quotes")));
00261 
00262     /*
00263      * Allowing relative paths seems risky
00264      *
00265      * this also helps us ensure that location is not empty or whitespace
00266      */
00267     if (!is_absolute_path(location))
00268         ereport(ERROR,
00269                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00270                  errmsg("tablespace location must be an absolute path")));
00271 
00272     /*
00273      * Check that location isn't too long. Remember that we're going to append
00274      * 'PG_XXX/<dboid>/<relid>.<nnn>'.  FYI, we never actually reference the
00275      * whole path, but mkdir() uses the first two parts.
00276      */
00277     if (strlen(location) + 1 + strlen(TABLESPACE_VERSION_DIRECTORY) + 1 +
00278         OIDCHARS + 1 + OIDCHARS + 1 + OIDCHARS > MAXPGPATH)
00279         ereport(ERROR,
00280                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00281                  errmsg("tablespace location \"%s\" is too long",
00282                         location)));
00283 
00284     /*
00285      * Disallow creation of tablespaces named "pg_xxx"; we reserve this
00286      * namespace for system purposes.
00287      */
00288     if (!allowSystemTableMods && IsReservedName(stmt->tablespacename))
00289         ereport(ERROR,
00290                 (errcode(ERRCODE_RESERVED_NAME),
00291                  errmsg("unacceptable tablespace name \"%s\"",
00292                         stmt->tablespacename),
00293         errdetail("The prefix \"pg_\" is reserved for system tablespaces.")));
00294 
00295     /*
00296      * Check that there is no other tablespace by this name.  (The unique
00297      * index would catch this anyway, but might as well give a friendlier
00298      * message.)
00299      */
00300     if (OidIsValid(get_tablespace_oid(stmt->tablespacename, true)))
00301         ereport(ERROR,
00302                 (errcode(ERRCODE_DUPLICATE_OBJECT),
00303                  errmsg("tablespace \"%s\" already exists",
00304                         stmt->tablespacename)));
00305 
00306     /*
00307      * Insert tuple into pg_tablespace.  The purpose of doing this first is to
00308      * lock the proposed tablename against other would-be creators. The
00309      * insertion will roll back if we find problems below.
00310      */
00311     rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
00312 
00313     MemSet(nulls, false, sizeof(nulls));
00314 
00315     values[Anum_pg_tablespace_spcname - 1] =
00316         DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename));
00317     values[Anum_pg_tablespace_spcowner - 1] =
00318         ObjectIdGetDatum(ownerId);
00319     nulls[Anum_pg_tablespace_spcacl - 1] = true;
00320     nulls[Anum_pg_tablespace_spcoptions - 1] = true;
00321 
00322     tuple = heap_form_tuple(rel->rd_att, values, nulls);
00323 
00324     tablespaceoid = simple_heap_insert(rel, tuple);
00325 
00326     CatalogUpdateIndexes(rel, tuple);
00327 
00328     heap_freetuple(tuple);
00329 
00330     /* Record dependency on owner */
00331     recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId);
00332 
00333     /* Post creation hook for new tablespace */
00334     InvokeObjectPostCreateHook(TableSpaceRelationId, tablespaceoid, 0);
00335 
00336     create_tablespace_directories(location, tablespaceoid);
00337 
00338     /* Record the filesystem change in XLOG */
00339     {
00340         xl_tblspc_create_rec xlrec;
00341         XLogRecData rdata[2];
00342 
00343         xlrec.ts_id = tablespaceoid;
00344         rdata[0].data = (char *) &xlrec;
00345         rdata[0].len = offsetof(xl_tblspc_create_rec, ts_path);
00346         rdata[0].buffer = InvalidBuffer;
00347         rdata[0].next = &(rdata[1]);
00348 
00349         rdata[1].data = (char *) location;
00350         rdata[1].len = strlen(location) + 1;
00351         rdata[1].buffer = InvalidBuffer;
00352         rdata[1].next = NULL;
00353 
00354         (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_CREATE, rdata);
00355     }
00356 
00357     /*
00358      * Force synchronous commit, to minimize the window between creating the
00359      * symlink on-disk and marking the transaction committed.  It's not great
00360      * that there is any window at all, but definitely we don't want to make
00361      * it larger than necessary.
00362      */
00363     ForceSyncCommit();
00364 
00365     pfree(location);
00366 
00367     /* We keep the lock on pg_tablespace until commit */
00368     heap_close(rel, NoLock);
00369 #else                           /* !HAVE_SYMLINK */
00370     ereport(ERROR,
00371             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00372              errmsg("tablespaces are not supported on this platform")));
00373 #endif   /* HAVE_SYMLINK */
00374 
00375     return tablespaceoid;
00376 }
00377 
00378 /*
00379  * Drop a table space
00380  *
00381  * Be careful to check that the tablespace is empty.
00382  */
00383 void
00384 DropTableSpace(DropTableSpaceStmt *stmt)
00385 {
00386 #ifdef HAVE_SYMLINK
00387     char       *tablespacename = stmt->tablespacename;
00388     HeapScanDesc scandesc;
00389     Relation    rel;
00390     HeapTuple   tuple;
00391     ScanKeyData entry[1];
00392     Oid         tablespaceoid;
00393 
00394     /*
00395      * Find the target tuple
00396      */
00397     rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
00398 
00399     ScanKeyInit(&entry[0],
00400                 Anum_pg_tablespace_spcname,
00401                 BTEqualStrategyNumber, F_NAMEEQ,
00402                 CStringGetDatum(tablespacename));
00403     scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
00404     tuple = heap_getnext(scandesc, ForwardScanDirection);
00405 
00406     if (!HeapTupleIsValid(tuple))
00407     {
00408         if (!stmt->missing_ok)
00409         {
00410             ereport(ERROR,
00411                     (errcode(ERRCODE_UNDEFINED_OBJECT),
00412                      errmsg("tablespace \"%s\" does not exist",
00413                             tablespacename)));
00414         }
00415         else
00416         {
00417             ereport(NOTICE,
00418                     (errmsg("tablespace \"%s\" does not exist, skipping",
00419                             tablespacename)));
00420             /* XXX I assume I need one or both of these next two calls */
00421             heap_endscan(scandesc);
00422             heap_close(rel, NoLock);
00423         }
00424         return;
00425     }
00426 
00427     tablespaceoid = HeapTupleGetOid(tuple);
00428 
00429     /* Must be tablespace owner */
00430     if (!pg_tablespace_ownercheck(tablespaceoid, GetUserId()))
00431         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE,
00432                        tablespacename);
00433 
00434     /* Disallow drop of the standard tablespaces, even by superuser */
00435     if (tablespaceoid == GLOBALTABLESPACE_OID ||
00436         tablespaceoid == DEFAULTTABLESPACE_OID)
00437         aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE,
00438                        tablespacename);
00439 
00440     /* DROP hook for the tablespace being removed */
00441     InvokeObjectDropHook(TableSpaceRelationId, tablespaceoid, 0);
00442 
00443     /*
00444      * Remove the pg_tablespace tuple (this will roll back if we fail below)
00445      */
00446     simple_heap_delete(rel, &tuple->t_self);
00447 
00448     heap_endscan(scandesc);
00449 
00450     /*
00451      * Remove any comments or security labels on this tablespace.
00452      */
00453     DeleteSharedComments(tablespaceoid, TableSpaceRelationId);
00454     DeleteSharedSecurityLabel(tablespaceoid, TableSpaceRelationId);
00455 
00456     /*
00457      * Remove dependency on owner.
00458      */
00459     deleteSharedDependencyRecordsFor(TableSpaceRelationId, tablespaceoid, 0);
00460 
00461     /*
00462      * Acquire TablespaceCreateLock to ensure that no TablespaceCreateDbspace
00463      * is running concurrently.
00464      */
00465     LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
00466 
00467     /*
00468      * Try to remove the physical infrastructure.
00469      */
00470     if (!destroy_tablespace_directories(tablespaceoid, false))
00471     {
00472         /*
00473          * Not all files deleted?  However, there can be lingering empty files
00474          * in the directories, left behind by for example DROP TABLE, that
00475          * have been scheduled for deletion at next checkpoint (see comments
00476          * in mdunlink() for details).  We could just delete them immediately,
00477          * but we can't tell them apart from important data files that we
00478          * mustn't delete.  So instead, we force a checkpoint which will clean
00479          * out any lingering files, and try again.
00480          */
00481         RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
00482         if (!destroy_tablespace_directories(tablespaceoid, false))
00483         {
00484             /* Still not empty, the files must be important then */
00485             ereport(ERROR,
00486                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00487                      errmsg("tablespace \"%s\" is not empty",
00488                             tablespacename)));
00489         }
00490     }
00491 
00492     /* Record the filesystem change in XLOG */
00493     {
00494         xl_tblspc_drop_rec xlrec;
00495         XLogRecData rdata[1];
00496 
00497         xlrec.ts_id = tablespaceoid;
00498         rdata[0].data = (char *) &xlrec;
00499         rdata[0].len = sizeof(xl_tblspc_drop_rec);
00500         rdata[0].buffer = InvalidBuffer;
00501         rdata[0].next = NULL;
00502 
00503         (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_DROP, rdata);
00504     }
00505 
00506     /*
00507      * Note: because we checked that the tablespace was empty, there should be
00508      * no need to worry about flushing shared buffers or free space map
00509      * entries for relations in the tablespace.
00510      */
00511 
00512     /*
00513      * Force synchronous commit, to minimize the window between removing the
00514      * files on-disk and marking the transaction committed.  It's not great
00515      * that there is any window at all, but definitely we don't want to make
00516      * it larger than necessary.
00517      */
00518     ForceSyncCommit();
00519 
00520     /*
00521      * Allow TablespaceCreateDbspace again.
00522      */
00523     LWLockRelease(TablespaceCreateLock);
00524 
00525     /* We keep the lock on pg_tablespace until commit */
00526     heap_close(rel, NoLock);
00527 #else                           /* !HAVE_SYMLINK */
00528     ereport(ERROR,
00529             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00530              errmsg("tablespaces are not supported on this platform")));
00531 #endif   /* HAVE_SYMLINK */
00532 }
00533 
00534 
00535 /*
00536  * create_tablespace_directories
00537  *
00538  *  Attempt to create filesystem infrastructure linking $PGDATA/pg_tblspc/
00539  *  to the specified directory
00540  */
00541 static void
00542 create_tablespace_directories(const char *location, const Oid tablespaceoid)
00543 {
00544     char       *linkloc = palloc(OIDCHARS + OIDCHARS + 1);
00545     char       *location_with_version_dir = palloc(strlen(location) + 1 +
00546                                    strlen(TABLESPACE_VERSION_DIRECTORY) + 1);
00547 
00548     sprintf(linkloc, "pg_tblspc/%u", tablespaceoid);
00549     sprintf(location_with_version_dir, "%s/%s", location,
00550             TABLESPACE_VERSION_DIRECTORY);
00551 
00552     /*
00553      * Attempt to coerce target directory to safe permissions.  If this fails,
00554      * it doesn't exist or has the wrong owner.
00555      */
00556     if (chmod(location, S_IRWXU) != 0)
00557     {
00558         if (errno == ENOENT)
00559             ereport(ERROR,
00560                     (errcode(ERRCODE_UNDEFINED_FILE),
00561                      errmsg("directory \"%s\" does not exist", location),
00562                      InRecovery ? errhint("Create this directory for the tablespace before "
00563                                           "restarting the server.") : 0));
00564         else
00565             ereport(ERROR,
00566                     (errcode_for_file_access(),
00567                   errmsg("could not set permissions on directory \"%s\": %m",
00568                          location)));
00569     }
00570 
00571     if (InRecovery)
00572     {
00573         struct stat st;
00574 
00575         /*
00576          * Our theory for replaying a CREATE is to forcibly drop the target
00577          * subdirectory if present, and then recreate it. This may be more
00578          * work than needed, but it is simple to implement.
00579          */
00580         if (stat(location_with_version_dir, &st) == 0 && S_ISDIR(st.st_mode))
00581         {
00582             if (!rmtree(location_with_version_dir, true))
00583                 /* If this failed, mkdir() below is going to error. */
00584                 ereport(WARNING,
00585                         (errmsg("some useless files may be left behind in old database directory \"%s\"",
00586                                 location_with_version_dir)));
00587         }
00588     }
00589 
00590     /*
00591      * The creation of the version directory prevents more than one tablespace
00592      * in a single location.
00593      */
00594     if (mkdir(location_with_version_dir, S_IRWXU) < 0)
00595     {
00596         if (errno == EEXIST)
00597             ereport(ERROR,
00598                     (errcode(ERRCODE_OBJECT_IN_USE),
00599                      errmsg("directory \"%s\" already in use as a tablespace",
00600                             location_with_version_dir)));
00601         else
00602             ereport(ERROR,
00603                     (errcode_for_file_access(),
00604                      errmsg("could not create directory \"%s\": %m",
00605                             location_with_version_dir)));
00606     }
00607 
00608     /* Remove old symlink in recovery, in case it points to the wrong place */
00609     if (InRecovery)
00610     {
00611         if (unlink(linkloc) < 0 && errno != ENOENT)
00612             ereport(ERROR,
00613                     (errcode_for_file_access(),
00614                      errmsg("could not remove symbolic link \"%s\": %m",
00615                             linkloc)));
00616     }
00617 
00618     /*
00619      * Create the symlink under PGDATA
00620      */
00621     if (symlink(location, linkloc) < 0)
00622         ereport(ERROR,
00623                 (errcode_for_file_access(),
00624                  errmsg("could not create symbolic link \"%s\": %m",
00625                         linkloc)));
00626 
00627     pfree(linkloc);
00628     pfree(location_with_version_dir);
00629 }
00630 
00631 
00632 /*
00633  * destroy_tablespace_directories
00634  *
00635  * Attempt to remove filesystem infrastructure for the tablespace.
00636  *
00637  * 'redo' indicates we are redoing a drop from XLOG; in that case we should
00638  * not throw an ERROR for problems, just LOG them.  The worst consequence of
00639  * not removing files here would be failure to release some disk space, which
00640  * does not justify throwing an error that would require manual intervention
00641  * to get the database running again.
00642  *
00643  * Returns TRUE if successful, FALSE if some subdirectory is not empty
00644  */
00645 static bool
00646 destroy_tablespace_directories(Oid tablespaceoid, bool redo)
00647 {
00648     char       *linkloc;
00649     char       *linkloc_with_version_dir;
00650     DIR        *dirdesc;
00651     struct dirent *de;
00652     char       *subfile;
00653     struct stat st;
00654 
00655     linkloc_with_version_dir = palloc(9 + 1 + OIDCHARS + 1 +
00656                                       strlen(TABLESPACE_VERSION_DIRECTORY));
00657     sprintf(linkloc_with_version_dir, "pg_tblspc/%u/%s", tablespaceoid,
00658             TABLESPACE_VERSION_DIRECTORY);
00659 
00660     /*
00661      * Check if the tablespace still contains any files.  We try to rmdir each
00662      * per-database directory we find in it.  rmdir failure implies there are
00663      * still files in that subdirectory, so give up.  (We do not have to worry
00664      * about undoing any already completed rmdirs, since the next attempt to
00665      * use the tablespace from that database will simply recreate the
00666      * subdirectory via TablespaceCreateDbspace.)
00667      *
00668      * Since we hold TablespaceCreateLock, no one else should be creating any
00669      * fresh subdirectories in parallel. It is possible that new files are
00670      * being created within subdirectories, though, so the rmdir call could
00671      * fail.  Worst consequence is a less friendly error message.
00672      *
00673      * If redo is true then ENOENT is a likely outcome here, and we allow it
00674      * to pass without comment.  In normal operation we still allow it, but
00675      * with a warning.  This is because even though ProcessUtility disallows
00676      * DROP TABLESPACE in a transaction block, it's possible that a previous
00677      * DROP failed and rolled back after removing the tablespace directories
00678      * and/or symlink.  We want to allow a new DROP attempt to succeed at
00679      * removing the catalog entries (and symlink if still present), so we
00680      * should not give a hard error here.
00681      */
00682     dirdesc = AllocateDir(linkloc_with_version_dir);
00683     if (dirdesc == NULL)
00684     {
00685         if (errno == ENOENT)
00686         {
00687             if (!redo)
00688                 ereport(WARNING,
00689                         (errcode_for_file_access(),
00690                          errmsg("could not open directory \"%s\": %m",
00691                                 linkloc_with_version_dir)));
00692             /* The symlink might still exist, so go try to remove it */
00693             goto remove_symlink;
00694         }
00695         else if (redo)
00696         {
00697             /* in redo, just log other types of error */
00698             ereport(LOG,
00699                     (errcode_for_file_access(),
00700                      errmsg("could not open directory \"%s\": %m",
00701                             linkloc_with_version_dir)));
00702             pfree(linkloc_with_version_dir);
00703             return false;
00704         }
00705         /* else let ReadDir report the error */
00706     }
00707 
00708     while ((de = ReadDir(dirdesc, linkloc_with_version_dir)) != NULL)
00709     {
00710         if (strcmp(de->d_name, ".") == 0 ||
00711             strcmp(de->d_name, "..") == 0)
00712             continue;
00713 
00714         subfile = palloc(strlen(linkloc_with_version_dir) + 1 + strlen(de->d_name) + 1);
00715         sprintf(subfile, "%s/%s", linkloc_with_version_dir, de->d_name);
00716 
00717         /* This check is just to deliver a friendlier error message */
00718         if (!redo && !directory_is_empty(subfile))
00719         {
00720             FreeDir(dirdesc);
00721             pfree(subfile);
00722             pfree(linkloc_with_version_dir);
00723             return false;
00724         }
00725 
00726         /* remove empty directory */
00727         if (rmdir(subfile) < 0)
00728             ereport(redo ? LOG : ERROR,
00729                     (errcode_for_file_access(),
00730                      errmsg("could not remove directory \"%s\": %m",
00731                             subfile)));
00732 
00733         pfree(subfile);
00734     }
00735 
00736     FreeDir(dirdesc);
00737 
00738     /* remove version directory */
00739     if (rmdir(linkloc_with_version_dir) < 0)
00740     {
00741         ereport(redo ? LOG : ERROR,
00742                 (errcode_for_file_access(),
00743                  errmsg("could not remove directory \"%s\": %m",
00744                         linkloc_with_version_dir)));
00745         pfree(linkloc_with_version_dir);
00746         return false;
00747     }
00748 
00749     /*
00750      * Try to remove the symlink.  We must however deal with the possibility
00751      * that it's a directory instead of a symlink --- this could happen during
00752      * WAL replay (see TablespaceCreateDbspace), and it is also the case on
00753      * Windows where junction points lstat() as directories.
00754      *
00755      * Note: in the redo case, we'll return true if this final step fails;
00756      * there's no point in retrying it.  Also, ENOENT should provoke no more
00757      * than a warning.
00758      */
00759 remove_symlink:
00760     linkloc = pstrdup(linkloc_with_version_dir);
00761     get_parent_directory(linkloc);
00762     if (lstat(linkloc, &st) == 0 && S_ISDIR(st.st_mode))
00763     {
00764         if (rmdir(linkloc) < 0)
00765             ereport(redo ? LOG : ERROR,
00766                     (errcode_for_file_access(),
00767                      errmsg("could not remove directory \"%s\": %m",
00768                             linkloc)));
00769     }
00770     else
00771     {
00772         if (unlink(linkloc) < 0)
00773             ereport(redo ? LOG : (errno == ENOENT ? WARNING : ERROR),
00774                     (errcode_for_file_access(),
00775                      errmsg("could not remove symbolic link \"%s\": %m",
00776                             linkloc)));
00777     }
00778 
00779     pfree(linkloc_with_version_dir);
00780     pfree(linkloc);
00781 
00782     return true;
00783 }
00784 
00785 
00786 /*
00787  * Check if a directory is empty.
00788  *
00789  * This probably belongs somewhere else, but not sure where...
00790  */
00791 bool
00792 directory_is_empty(const char *path)
00793 {
00794     DIR        *dirdesc;
00795     struct dirent *de;
00796 
00797     dirdesc = AllocateDir(path);
00798 
00799     while ((de = ReadDir(dirdesc, path)) != NULL)
00800     {
00801         if (strcmp(de->d_name, ".") == 0 ||
00802             strcmp(de->d_name, "..") == 0)
00803             continue;
00804         FreeDir(dirdesc);
00805         return false;
00806     }
00807 
00808     FreeDir(dirdesc);
00809     return true;
00810 }
00811 
00812 
00813 /*
00814  * Rename a tablespace
00815  */
00816 Oid
00817 RenameTableSpace(const char *oldname, const char *newname)
00818 {
00819     Oid         tspId;
00820     Relation    rel;
00821     ScanKeyData entry[1];
00822     HeapScanDesc scan;
00823     HeapTuple   tup;
00824     HeapTuple   newtuple;
00825     Form_pg_tablespace newform;
00826 
00827     /* Search pg_tablespace */
00828     rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
00829 
00830     ScanKeyInit(&entry[0],
00831                 Anum_pg_tablespace_spcname,
00832                 BTEqualStrategyNumber, F_NAMEEQ,
00833                 CStringGetDatum(oldname));
00834     scan = heap_beginscan(rel, SnapshotNow, 1, entry);
00835     tup = heap_getnext(scan, ForwardScanDirection);
00836     if (!HeapTupleIsValid(tup))
00837         ereport(ERROR,
00838                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00839                  errmsg("tablespace \"%s\" does not exist",
00840                         oldname)));
00841 
00842     tspId = HeapTupleGetOid(tup);
00843     newtuple = heap_copytuple(tup);
00844     newform = (Form_pg_tablespace) GETSTRUCT(newtuple);
00845 
00846     heap_endscan(scan);
00847 
00848     /* Must be owner */
00849     if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId()))
00850         aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, oldname);
00851 
00852     /* Validate new name */
00853     if (!allowSystemTableMods && IsReservedName(newname))
00854         ereport(ERROR,
00855                 (errcode(ERRCODE_RESERVED_NAME),
00856                  errmsg("unacceptable tablespace name \"%s\"", newname),
00857         errdetail("The prefix \"pg_\" is reserved for system tablespaces.")));
00858 
00859     /* Make sure the new name doesn't exist */
00860     ScanKeyInit(&entry[0],
00861                 Anum_pg_tablespace_spcname,
00862                 BTEqualStrategyNumber, F_NAMEEQ,
00863                 CStringGetDatum(newname));
00864     scan = heap_beginscan(rel, SnapshotNow, 1, entry);
00865     tup = heap_getnext(scan, ForwardScanDirection);
00866     if (HeapTupleIsValid(tup))
00867         ereport(ERROR,
00868                 (errcode(ERRCODE_DUPLICATE_OBJECT),
00869                  errmsg("tablespace \"%s\" already exists",
00870                         newname)));
00871 
00872     heap_endscan(scan);
00873 
00874     /* OK, update the entry */
00875     namestrcpy(&(newform->spcname), newname);
00876 
00877     simple_heap_update(rel, &newtuple->t_self, newtuple);
00878     CatalogUpdateIndexes(rel, newtuple);
00879 
00880     InvokeObjectPostAlterHook(TableSpaceRelationId, tspId, 0);
00881 
00882     heap_close(rel, NoLock);
00883 
00884     return tspId;
00885 }
00886 
00887 /*
00888  * Alter table space options
00889  */
00890 Oid
00891 AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
00892 {
00893     Relation    rel;
00894     ScanKeyData entry[1];
00895     HeapScanDesc scandesc;
00896     HeapTuple   tup;
00897     Oid         tablespaceoid;
00898     Datum       datum;
00899     Datum       newOptions;
00900     Datum       repl_val[Natts_pg_tablespace];
00901     bool        isnull;
00902     bool        repl_null[Natts_pg_tablespace];
00903     bool        repl_repl[Natts_pg_tablespace];
00904     HeapTuple   newtuple;
00905 
00906     /* Search pg_tablespace */
00907     rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
00908 
00909     ScanKeyInit(&entry[0],
00910                 Anum_pg_tablespace_spcname,
00911                 BTEqualStrategyNumber, F_NAMEEQ,
00912                 CStringGetDatum(stmt->tablespacename));
00913     scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
00914     tup = heap_getnext(scandesc, ForwardScanDirection);
00915     if (!HeapTupleIsValid(tup))
00916         ereport(ERROR,
00917                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00918                  errmsg("tablespace \"%s\" does not exist",
00919                         stmt->tablespacename)));
00920 
00921     tablespaceoid = HeapTupleGetOid(tup);
00922 
00923     /* Must be owner of the existing object */
00924     if (!pg_tablespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
00925         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE,
00926                        stmt->tablespacename);
00927 
00928     /* Generate new proposed spcoptions (text array) */
00929     datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions,
00930                          RelationGetDescr(rel), &isnull);
00931     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
00932                                      stmt->options, NULL, NULL, false,
00933                                      stmt->isReset);
00934     (void) tablespace_reloptions(newOptions, true);
00935 
00936     /* Build new tuple. */
00937     memset(repl_null, false, sizeof(repl_null));
00938     memset(repl_repl, false, sizeof(repl_repl));
00939     if (newOptions != (Datum) 0)
00940         repl_val[Anum_pg_tablespace_spcoptions - 1] = newOptions;
00941     else
00942         repl_null[Anum_pg_tablespace_spcoptions - 1] = true;
00943     repl_repl[Anum_pg_tablespace_spcoptions - 1] = true;
00944     newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val,
00945                                  repl_null, repl_repl);
00946 
00947     /* Update system catalog. */
00948     simple_heap_update(rel, &newtuple->t_self, newtuple);
00949     CatalogUpdateIndexes(rel, newtuple);
00950 
00951     InvokeObjectPostAlterHook(TableSpaceRelationId, HeapTupleGetOid(tup), 0);
00952 
00953     heap_freetuple(newtuple);
00954 
00955     /* Conclude heap scan. */
00956     heap_endscan(scandesc);
00957     heap_close(rel, NoLock);
00958 
00959     return tablespaceoid;
00960 }
00961 
00962 /*
00963  * Routines for handling the GUC variable 'default_tablespace'.
00964  */
00965 
00966 /* check_hook: validate new default_tablespace */
00967 bool
00968 check_default_tablespace(char **newval, void **extra, GucSource source)
00969 {
00970     /*
00971      * If we aren't inside a transaction, we cannot do database access so
00972      * cannot verify the name.  Must accept the value on faith.
00973      */
00974     if (IsTransactionState())
00975     {
00976         if (**newval != '\0' &&
00977             !OidIsValid(get_tablespace_oid(*newval, true)))
00978         {
00979             /*
00980              * When source == PGC_S_TEST, we are checking the argument of an
00981              * ALTER DATABASE SET or ALTER USER SET command.  pg_dumpall dumps
00982              * all roles before tablespaces, so if we're restoring a
00983              * pg_dumpall script the tablespace might not yet exist, but will
00984              * be created later.  Because of that, issue a NOTICE if source ==
00985              * PGC_S_TEST, but accept the value anyway.
00986              */
00987             if (source == PGC_S_TEST)
00988             {
00989                 ereport(NOTICE,
00990                         (errcode(ERRCODE_UNDEFINED_OBJECT),
00991                          errmsg("tablespace \"%s\" does not exist",
00992                                 *newval)));
00993             }
00994             else
00995             {
00996                 GUC_check_errdetail("Tablespace \"%s\" does not exist.",
00997                                     *newval);
00998                 return false;
00999             }
01000         }
01001     }
01002 
01003     return true;
01004 }
01005 
01006 /*
01007  * GetDefaultTablespace -- get the OID of the current default tablespace
01008  *
01009  * Temporary objects have different default tablespaces, hence the
01010  * relpersistence parameter must be specified.
01011  *
01012  * May return InvalidOid to indicate "use the database's default tablespace".
01013  *
01014  * Note that caller is expected to check appropriate permissions for any
01015  * result other than InvalidOid.
01016  *
01017  * This exists to hide (and possibly optimize the use of) the
01018  * default_tablespace GUC variable.
01019  */
01020 Oid
01021 GetDefaultTablespace(char relpersistence)
01022 {
01023     Oid         result;
01024 
01025     /* The temp-table case is handled elsewhere */
01026     if (relpersistence == RELPERSISTENCE_TEMP)
01027     {
01028         PrepareTempTablespaces();
01029         return GetNextTempTableSpace();
01030     }
01031 
01032     /* Fast path for default_tablespace == "" */
01033     if (default_tablespace == NULL || default_tablespace[0] == '\0')
01034         return InvalidOid;
01035 
01036     /*
01037      * It is tempting to cache this lookup for more speed, but then we would
01038      * fail to detect the case where the tablespace was dropped since the GUC
01039      * variable was set.  Note also that we don't complain if the value fails
01040      * to refer to an existing tablespace; we just silently return InvalidOid,
01041      * causing the new object to be created in the database's tablespace.
01042      */
01043     result = get_tablespace_oid(default_tablespace, true);
01044 
01045     /*
01046      * Allow explicit specification of database's default tablespace in
01047      * default_tablespace without triggering permissions checks.
01048      */
01049     if (result == MyDatabaseTableSpace)
01050         result = InvalidOid;
01051     return result;
01052 }
01053 
01054 
01055 /*
01056  * Routines for handling the GUC variable 'temp_tablespaces'.
01057  */
01058 
01059 typedef struct
01060 {
01061     int         numSpcs;
01062     Oid         tblSpcs[1];     /* VARIABLE LENGTH ARRAY */
01063 } temp_tablespaces_extra;
01064 
01065 /* check_hook: validate new temp_tablespaces */
01066 bool
01067 check_temp_tablespaces(char **newval, void **extra, GucSource source)
01068 {
01069     char       *rawname;
01070     List       *namelist;
01071 
01072     /* Need a modifiable copy of string */
01073     rawname = pstrdup(*newval);
01074 
01075     /* Parse string into list of identifiers */
01076     if (!SplitIdentifierString(rawname, ',', &namelist))
01077     {
01078         /* syntax error in name list */
01079         GUC_check_errdetail("List syntax is invalid.");
01080         pfree(rawname);
01081         list_free(namelist);
01082         return false;
01083     }
01084 
01085     /*
01086      * If we aren't inside a transaction, we cannot do database access so
01087      * cannot verify the individual names.  Must accept the list on faith.
01088      * Fortunately, there's then also no need to pass the data to fd.c.
01089      */
01090     if (IsTransactionState())
01091     {
01092         temp_tablespaces_extra *myextra;
01093         Oid        *tblSpcs;
01094         int         numSpcs;
01095         ListCell   *l;
01096 
01097         /* temporary workspace until we are done verifying the list */
01098         tblSpcs = (Oid *) palloc(list_length(namelist) * sizeof(Oid));
01099         numSpcs = 0;
01100         foreach(l, namelist)
01101         {
01102             char       *curname = (char *) lfirst(l);
01103             Oid         curoid;
01104             AclResult   aclresult;
01105 
01106             /* Allow an empty string (signifying database default) */
01107             if (curname[0] == '\0')
01108             {
01109                 tblSpcs[numSpcs++] = InvalidOid;
01110                 continue;
01111             }
01112 
01113             /*
01114              * In an interactive SET command, we ereport for bad info.  When
01115              * source == PGC_S_TEST, we are checking the argument of an ALTER
01116              * DATABASE SET or ALTER USER SET command.  pg_dumpall dumps all
01117              * roles before tablespaces, so if we're restoring a pg_dumpall
01118              * script the tablespace might not yet exist, but will be created
01119              * later.  Because of that, issue a NOTICE if source ==
01120              * PGC_S_TEST, but accept the value anyway.  Otherwise, silently
01121              * ignore any bad list elements.
01122              */
01123             curoid = get_tablespace_oid(curname, source <= PGC_S_TEST);
01124             if (curoid == InvalidOid)
01125             {
01126                 if (source == PGC_S_TEST)
01127                     ereport(NOTICE,
01128                             (errcode(ERRCODE_UNDEFINED_OBJECT),
01129                              errmsg("tablespace \"%s\" does not exist",
01130                                     curname)));
01131                 continue;
01132             }
01133 
01134             /*
01135              * Allow explicit specification of database's default tablespace
01136              * in temp_tablespaces without triggering permissions checks.
01137              */
01138             if (curoid == MyDatabaseTableSpace)
01139             {
01140                 tblSpcs[numSpcs++] = InvalidOid;
01141                 continue;
01142             }
01143 
01144             /* Check permissions, similarly complaining only if interactive */
01145             aclresult = pg_tablespace_aclcheck(curoid, GetUserId(),
01146                                                ACL_CREATE);
01147             if (aclresult != ACLCHECK_OK)
01148             {
01149                 if (source >= PGC_S_INTERACTIVE)
01150                     aclcheck_error(aclresult, ACL_KIND_TABLESPACE, curname);
01151                 continue;
01152             }
01153 
01154             tblSpcs[numSpcs++] = curoid;
01155         }
01156 
01157         /* Now prepare an "extra" struct for assign_temp_tablespaces */
01158         myextra = malloc(offsetof(temp_tablespaces_extra, tblSpcs) +
01159                          numSpcs * sizeof(Oid));
01160         if (!myextra)
01161             return false;
01162         myextra->numSpcs = numSpcs;
01163         memcpy(myextra->tblSpcs, tblSpcs, numSpcs * sizeof(Oid));
01164         *extra = (void *) myextra;
01165 
01166         pfree(tblSpcs);
01167     }
01168 
01169     pfree(rawname);
01170     list_free(namelist);
01171 
01172     return true;
01173 }
01174 
01175 /* assign_hook: do extra actions as needed */
01176 void
01177 assign_temp_tablespaces(const char *newval, void *extra)
01178 {
01179     temp_tablespaces_extra *myextra = (temp_tablespaces_extra *) extra;
01180 
01181     /*
01182      * If check_temp_tablespaces was executed inside a transaction, then pass
01183      * the list it made to fd.c.  Otherwise, clear fd.c's list; we must be
01184      * still outside a transaction, or else restoring during transaction exit,
01185      * and in either case we can just let the next PrepareTempTablespaces call
01186      * make things sane.
01187      */
01188     if (myextra)
01189         SetTempTablespaces(myextra->tblSpcs, myextra->numSpcs);
01190     else
01191         SetTempTablespaces(NULL, 0);
01192 }
01193 
01194 /*
01195  * PrepareTempTablespaces -- prepare to use temp tablespaces
01196  *
01197  * If we have not already done so in the current transaction, parse the
01198  * temp_tablespaces GUC variable and tell fd.c which tablespace(s) to use
01199  * for temp files.
01200  */
01201 void
01202 PrepareTempTablespaces(void)
01203 {
01204     char       *rawname;
01205     List       *namelist;
01206     Oid        *tblSpcs;
01207     int         numSpcs;
01208     ListCell   *l;
01209 
01210     /* No work if already done in current transaction */
01211     if (TempTablespacesAreSet())
01212         return;
01213 
01214     /*
01215      * Can't do catalog access unless within a transaction.  This is just a
01216      * safety check in case this function is called by low-level code that
01217      * could conceivably execute outside a transaction.  Note that in such a
01218      * scenario, fd.c will fall back to using the current database's default
01219      * tablespace, which should always be OK.
01220      */
01221     if (!IsTransactionState())
01222         return;
01223 
01224     /* Need a modifiable copy of string */
01225     rawname = pstrdup(temp_tablespaces);
01226 
01227     /* Parse string into list of identifiers */
01228     if (!SplitIdentifierString(rawname, ',', &namelist))
01229     {
01230         /* syntax error in name list */
01231         SetTempTablespaces(NULL, 0);
01232         pfree(rawname);
01233         list_free(namelist);
01234         return;
01235     }
01236 
01237     /* Store tablespace OIDs in an array in TopTransactionContext */
01238     tblSpcs = (Oid *) MemoryContextAlloc(TopTransactionContext,
01239                                          list_length(namelist) * sizeof(Oid));
01240     numSpcs = 0;
01241     foreach(l, namelist)
01242     {
01243         char       *curname = (char *) lfirst(l);
01244         Oid         curoid;
01245         AclResult   aclresult;
01246 
01247         /* Allow an empty string (signifying database default) */
01248         if (curname[0] == '\0')
01249         {
01250             tblSpcs[numSpcs++] = InvalidOid;
01251             continue;
01252         }
01253 
01254         /* Else verify that name is a valid tablespace name */
01255         curoid = get_tablespace_oid(curname, true);
01256         if (curoid == InvalidOid)
01257         {
01258             /* Skip any bad list elements */
01259             continue;
01260         }
01261 
01262         /*
01263          * Allow explicit specification of database's default tablespace in
01264          * temp_tablespaces without triggering permissions checks.
01265          */
01266         if (curoid == MyDatabaseTableSpace)
01267         {
01268             tblSpcs[numSpcs++] = InvalidOid;
01269             continue;
01270         }
01271 
01272         /* Check permissions similarly */
01273         aclresult = pg_tablespace_aclcheck(curoid, GetUserId(),
01274                                            ACL_CREATE);
01275         if (aclresult != ACLCHECK_OK)
01276             continue;
01277 
01278         tblSpcs[numSpcs++] = curoid;
01279     }
01280 
01281     SetTempTablespaces(tblSpcs, numSpcs);
01282 
01283     pfree(rawname);
01284     list_free(namelist);
01285 }
01286 
01287 
01288 /*
01289  * get_tablespace_oid - given a tablespace name, look up the OID
01290  *
01291  * If missing_ok is false, throw an error if tablespace name not found.  If
01292  * true, just return InvalidOid.
01293  */
01294 Oid
01295 get_tablespace_oid(const char *tablespacename, bool missing_ok)
01296 {
01297     Oid         result;
01298     Relation    rel;
01299     HeapScanDesc scandesc;
01300     HeapTuple   tuple;
01301     ScanKeyData entry[1];
01302 
01303     /*
01304      * Search pg_tablespace.  We use a heapscan here even though there is an
01305      * index on name, on the theory that pg_tablespace will usually have just
01306      * a few entries and so an indexed lookup is a waste of effort.
01307      */
01308     rel = heap_open(TableSpaceRelationId, AccessShareLock);
01309 
01310     ScanKeyInit(&entry[0],
01311                 Anum_pg_tablespace_spcname,
01312                 BTEqualStrategyNumber, F_NAMEEQ,
01313                 CStringGetDatum(tablespacename));
01314     scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
01315     tuple = heap_getnext(scandesc, ForwardScanDirection);
01316 
01317     /* We assume that there can be at most one matching tuple */
01318     if (HeapTupleIsValid(tuple))
01319         result = HeapTupleGetOid(tuple);
01320     else
01321         result = InvalidOid;
01322 
01323     heap_endscan(scandesc);
01324     heap_close(rel, AccessShareLock);
01325 
01326     if (!OidIsValid(result) && !missing_ok)
01327         ereport(ERROR,
01328                 (errcode(ERRCODE_UNDEFINED_OBJECT),
01329                  errmsg("tablespace \"%s\" does not exist",
01330                         tablespacename)));
01331 
01332     return result;
01333 }
01334 
01335 /*
01336  * get_tablespace_name - given a tablespace OID, look up the name
01337  *
01338  * Returns a palloc'd string, or NULL if no such tablespace.
01339  */
01340 char *
01341 get_tablespace_name(Oid spc_oid)
01342 {
01343     char       *result;
01344     Relation    rel;
01345     HeapScanDesc scandesc;
01346     HeapTuple   tuple;
01347     ScanKeyData entry[1];
01348 
01349     /*
01350      * Search pg_tablespace.  We use a heapscan here even though there is an
01351      * index on oid, on the theory that pg_tablespace will usually have just a
01352      * few entries and so an indexed lookup is a waste of effort.
01353      */
01354     rel = heap_open(TableSpaceRelationId, AccessShareLock);
01355 
01356     ScanKeyInit(&entry[0],
01357                 ObjectIdAttributeNumber,
01358                 BTEqualStrategyNumber, F_OIDEQ,
01359                 ObjectIdGetDatum(spc_oid));
01360     scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
01361     tuple = heap_getnext(scandesc, ForwardScanDirection);
01362 
01363     /* We assume that there can be at most one matching tuple */
01364     if (HeapTupleIsValid(tuple))
01365         result = pstrdup(NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname));
01366     else
01367         result = NULL;
01368 
01369     heap_endscan(scandesc);
01370     heap_close(rel, AccessShareLock);
01371 
01372     return result;
01373 }
01374 
01375 
01376 /*
01377  * TABLESPACE resource manager's routines
01378  */
01379 void
01380 tblspc_redo(XLogRecPtr lsn, XLogRecord *record)
01381 {
01382     uint8       info = record->xl_info & ~XLR_INFO_MASK;
01383 
01384     /* Backup blocks are not used in tblspc records */
01385     Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
01386 
01387     if (info == XLOG_TBLSPC_CREATE)
01388     {
01389         xl_tblspc_create_rec *xlrec = (xl_tblspc_create_rec *) XLogRecGetData(record);
01390         char       *location = xlrec->ts_path;
01391 
01392         create_tablespace_directories(location, xlrec->ts_id);
01393     }
01394     else if (info == XLOG_TBLSPC_DROP)
01395     {
01396         xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *) XLogRecGetData(record);
01397 
01398         /*
01399          * If we issued a WAL record for a drop tablespace it implies that
01400          * there were no files in it at all when the DROP was done. That means
01401          * that no permanent objects can exist in it at this point.
01402          *
01403          * It is possible for standby users to be using this tablespace as a
01404          * location for their temporary files, so if we fail to remove all
01405          * files then do conflict processing and try again, if currently
01406          * enabled.
01407          *
01408          * Other possible reasons for failure include bollixed file
01409          * permissions on a standby server when they were okay on the primary,
01410          * etc etc. There's not much we can do about that, so just remove what
01411          * we can and press on.
01412          */
01413         if (!destroy_tablespace_directories(xlrec->ts_id, true))
01414         {
01415             ResolveRecoveryConflictWithTablespace(xlrec->ts_id);
01416 
01417             /*
01418              * If we did recovery processing then hopefully the backends who
01419              * wrote temp files should have cleaned up and exited by now.  So
01420              * retry before complaining.  If we fail again, this is just a LOG
01421              * condition, because it's not worth throwing an ERROR for (as
01422              * that would crash the database and require manual intervention
01423              * before we could get past this WAL record on restart).
01424              */
01425             if (!destroy_tablespace_directories(xlrec->ts_id, true))
01426                 ereport(LOG,
01427                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
01428                  errmsg("directories for tablespace %u could not be removed",
01429                         xlrec->ts_id),
01430                          errhint("You can remove the directories manually if necessary.")));
01431         }
01432     }
01433     else
01434         elog(PANIC, "tblspc_redo: unknown op code %u", info);
01435 }