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