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