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