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