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