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