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