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 : }
|