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