00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047 #include "postgres.h"
00048
00049 #include <unistd.h>
00050 #include <dirent.h>
00051 #include <sys/types.h>
00052 #include <sys/stat.h>
00053
00054 #include "access/heapam.h"
00055 #include "access/reloptions.h"
00056 #include "access/htup_details.h"
00057 #include "access/sysattr.h"
00058 #include "access/xact.h"
00059 #include "catalog/catalog.h"
00060 #include "catalog/dependency.h"
00061 #include "catalog/indexing.h"
00062 #include "catalog/objectaccess.h"
00063 #include "catalog/pg_tablespace.h"
00064 #include "commands/comment.h"
00065 #include "commands/seclabel.h"
00066 #include "commands/tablespace.h"
00067 #include "common/relpath.h"
00068 #include "miscadmin.h"
00069 #include "postmaster/bgwriter.h"
00070 #include "storage/fd.h"
00071 #include "storage/standby.h"
00072 #include "utils/acl.h"
00073 #include "utils/builtins.h"
00074 #include "utils/fmgroids.h"
00075 #include "utils/guc.h"
00076 #include "utils/memutils.h"
00077 #include "utils/rel.h"
00078 #include "utils/tqual.h"
00079
00080
00081
00082 char *default_tablespace = NULL;
00083 char *temp_tablespaces = NULL;
00084
00085
00086 static void create_tablespace_directories(const char *location,
00087 const Oid tablespaceoid);
00088 static bool destroy_tablespace_directories(Oid tablespaceoid, bool redo);
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108 void
00109 TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo)
00110 {
00111 struct stat st;
00112 char *dir;
00113
00114
00115
00116
00117
00118 if (spcNode == GLOBALTABLESPACE_OID)
00119 return;
00120
00121 Assert(OidIsValid(spcNode));
00122 Assert(OidIsValid(dbNode));
00123
00124 dir = GetDatabasePath(dbNode, spcNode);
00125
00126 if (stat(dir, &st) < 0)
00127 {
00128
00129 if (errno == ENOENT)
00130 {
00131
00132
00133
00134
00135 LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
00136
00137
00138
00139
00140
00141 if (stat(dir, &st) == 0 && S_ISDIR(st.st_mode))
00142 {
00143
00144 }
00145 else
00146 {
00147
00148 if (mkdir(dir, S_IRWXU) < 0)
00149 {
00150 char *parentdir;
00151
00152
00153 if (errno != ENOENT || !isRedo)
00154 ereport(ERROR,
00155 (errcode_for_file_access(),
00156 errmsg("could not create directory \"%s\": %m",
00157 dir)));
00158
00159
00160
00161
00162
00163
00164
00165
00166 parentdir = pstrdup(dir);
00167 get_parent_directory(parentdir);
00168 get_parent_directory(parentdir);
00169
00170 if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
00171 ereport(ERROR,
00172 (errcode_for_file_access(),
00173 errmsg("could not create directory \"%s\": %m",
00174 parentdir)));
00175 pfree(parentdir);
00176
00177
00178 parentdir = pstrdup(dir);
00179 get_parent_directory(parentdir);
00180
00181 if (mkdir(parentdir, S_IRWXU) < 0 && errno != EEXIST)
00182 ereport(ERROR,
00183 (errcode_for_file_access(),
00184 errmsg("could not create directory \"%s\": %m",
00185 parentdir)));
00186 pfree(parentdir);
00187
00188
00189 if (mkdir(dir, S_IRWXU) < 0)
00190 ereport(ERROR,
00191 (errcode_for_file_access(),
00192 errmsg("could not create directory \"%s\": %m",
00193 dir)));
00194 }
00195 }
00196
00197 LWLockRelease(TablespaceCreateLock);
00198 }
00199 else
00200 {
00201 ereport(ERROR,
00202 (errcode_for_file_access(),
00203 errmsg("could not stat directory \"%s\": %m", dir)));
00204 }
00205 }
00206 else
00207 {
00208
00209 if (!S_ISDIR(st.st_mode))
00210 ereport(ERROR,
00211 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00212 errmsg("\"%s\" exists but is not a directory",
00213 dir)));
00214 }
00215
00216 pfree(dir);
00217 }
00218
00219
00220
00221
00222
00223
00224
00225
00226 Oid
00227 CreateTableSpace(CreateTableSpaceStmt *stmt)
00228 {
00229 #ifdef HAVE_SYMLINK
00230 Relation rel;
00231 Datum values[Natts_pg_tablespace];
00232 bool nulls[Natts_pg_tablespace];
00233 HeapTuple tuple;
00234 Oid tablespaceoid;
00235 char *location;
00236 Oid ownerId;
00237
00238
00239 if (!superuser())
00240 ereport(ERROR,
00241 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00242 errmsg("permission denied to create tablespace \"%s\"",
00243 stmt->tablespacename),
00244 errhint("Must be superuser to create a tablespace.")));
00245
00246
00247 if (stmt->owner)
00248 ownerId = get_role_oid(stmt->owner, false);
00249 else
00250 ownerId = GetUserId();
00251
00252
00253 location = pstrdup(stmt->location);
00254 canonicalize_path(location);
00255
00256
00257 if (strchr(location, '\''))
00258 ereport(ERROR,
00259 (errcode(ERRCODE_INVALID_NAME),
00260 errmsg("tablespace location cannot contain single quotes")));
00261
00262
00263
00264
00265
00266
00267 if (!is_absolute_path(location))
00268 ereport(ERROR,
00269 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00270 errmsg("tablespace location must be an absolute path")));
00271
00272
00273
00274
00275
00276
00277 if (strlen(location) + 1 + strlen(TABLESPACE_VERSION_DIRECTORY) + 1 +
00278 OIDCHARS + 1 + OIDCHARS + 1 + OIDCHARS > MAXPGPATH)
00279 ereport(ERROR,
00280 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00281 errmsg("tablespace location \"%s\" is too long",
00282 location)));
00283
00284
00285
00286
00287
00288 if (!allowSystemTableMods && IsReservedName(stmt->tablespacename))
00289 ereport(ERROR,
00290 (errcode(ERRCODE_RESERVED_NAME),
00291 errmsg("unacceptable tablespace name \"%s\"",
00292 stmt->tablespacename),
00293 errdetail("The prefix \"pg_\" is reserved for system tablespaces.")));
00294
00295
00296
00297
00298
00299
00300 if (OidIsValid(get_tablespace_oid(stmt->tablespacename, true)))
00301 ereport(ERROR,
00302 (errcode(ERRCODE_DUPLICATE_OBJECT),
00303 errmsg("tablespace \"%s\" already exists",
00304 stmt->tablespacename)));
00305
00306
00307
00308
00309
00310
00311 rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
00312
00313 MemSet(nulls, false, sizeof(nulls));
00314
00315 values[Anum_pg_tablespace_spcname - 1] =
00316 DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename));
00317 values[Anum_pg_tablespace_spcowner - 1] =
00318 ObjectIdGetDatum(ownerId);
00319 nulls[Anum_pg_tablespace_spcacl - 1] = true;
00320 nulls[Anum_pg_tablespace_spcoptions - 1] = true;
00321
00322 tuple = heap_form_tuple(rel->rd_att, values, nulls);
00323
00324 tablespaceoid = simple_heap_insert(rel, tuple);
00325
00326 CatalogUpdateIndexes(rel, tuple);
00327
00328 heap_freetuple(tuple);
00329
00330
00331 recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId);
00332
00333
00334 InvokeObjectPostCreateHook(TableSpaceRelationId, tablespaceoid, 0);
00335
00336 create_tablespace_directories(location, tablespaceoid);
00337
00338
00339 {
00340 xl_tblspc_create_rec xlrec;
00341 XLogRecData rdata[2];
00342
00343 xlrec.ts_id = tablespaceoid;
00344 rdata[0].data = (char *) &xlrec;
00345 rdata[0].len = offsetof(xl_tblspc_create_rec, ts_path);
00346 rdata[0].buffer = InvalidBuffer;
00347 rdata[0].next = &(rdata[1]);
00348
00349 rdata[1].data = (char *) location;
00350 rdata[1].len = strlen(location) + 1;
00351 rdata[1].buffer = InvalidBuffer;
00352 rdata[1].next = NULL;
00353
00354 (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_CREATE, rdata);
00355 }
00356
00357
00358
00359
00360
00361
00362
00363 ForceSyncCommit();
00364
00365 pfree(location);
00366
00367
00368 heap_close(rel, NoLock);
00369 #else
00370 ereport(ERROR,
00371 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00372 errmsg("tablespaces are not supported on this platform")));
00373 #endif
00374
00375 return tablespaceoid;
00376 }
00377
00378
00379
00380
00381
00382
00383 void
00384 DropTableSpace(DropTableSpaceStmt *stmt)
00385 {
00386 #ifdef HAVE_SYMLINK
00387 char *tablespacename = stmt->tablespacename;
00388 HeapScanDesc scandesc;
00389 Relation rel;
00390 HeapTuple tuple;
00391 ScanKeyData entry[1];
00392 Oid tablespaceoid;
00393
00394
00395
00396
00397 rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
00398
00399 ScanKeyInit(&entry[0],
00400 Anum_pg_tablespace_spcname,
00401 BTEqualStrategyNumber, F_NAMEEQ,
00402 CStringGetDatum(tablespacename));
00403 scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
00404 tuple = heap_getnext(scandesc, ForwardScanDirection);
00405
00406 if (!HeapTupleIsValid(tuple))
00407 {
00408 if (!stmt->missing_ok)
00409 {
00410 ereport(ERROR,
00411 (errcode(ERRCODE_UNDEFINED_OBJECT),
00412 errmsg("tablespace \"%s\" does not exist",
00413 tablespacename)));
00414 }
00415 else
00416 {
00417 ereport(NOTICE,
00418 (errmsg("tablespace \"%s\" does not exist, skipping",
00419 tablespacename)));
00420
00421 heap_endscan(scandesc);
00422 heap_close(rel, NoLock);
00423 }
00424 return;
00425 }
00426
00427 tablespaceoid = HeapTupleGetOid(tuple);
00428
00429
00430 if (!pg_tablespace_ownercheck(tablespaceoid, GetUserId()))
00431 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE,
00432 tablespacename);
00433
00434
00435 if (tablespaceoid == GLOBALTABLESPACE_OID ||
00436 tablespaceoid == DEFAULTTABLESPACE_OID)
00437 aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE,
00438 tablespacename);
00439
00440
00441 InvokeObjectDropHook(TableSpaceRelationId, tablespaceoid, 0);
00442
00443
00444
00445
00446 simple_heap_delete(rel, &tuple->t_self);
00447
00448 heap_endscan(scandesc);
00449
00450
00451
00452
00453 DeleteSharedComments(tablespaceoid, TableSpaceRelationId);
00454 DeleteSharedSecurityLabel(tablespaceoid, TableSpaceRelationId);
00455
00456
00457
00458
00459 deleteSharedDependencyRecordsFor(TableSpaceRelationId, tablespaceoid, 0);
00460
00461
00462
00463
00464
00465 LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE);
00466
00467
00468
00469
00470 if (!destroy_tablespace_directories(tablespaceoid, false))
00471 {
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481 RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
00482 if (!destroy_tablespace_directories(tablespaceoid, false))
00483 {
00484
00485 ereport(ERROR,
00486 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00487 errmsg("tablespace \"%s\" is not empty",
00488 tablespacename)));
00489 }
00490 }
00491
00492
00493 {
00494 xl_tblspc_drop_rec xlrec;
00495 XLogRecData rdata[1];
00496
00497 xlrec.ts_id = tablespaceoid;
00498 rdata[0].data = (char *) &xlrec;
00499 rdata[0].len = sizeof(xl_tblspc_drop_rec);
00500 rdata[0].buffer = InvalidBuffer;
00501 rdata[0].next = NULL;
00502
00503 (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_DROP, rdata);
00504 }
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518 ForceSyncCommit();
00519
00520
00521
00522
00523 LWLockRelease(TablespaceCreateLock);
00524
00525
00526 heap_close(rel, NoLock);
00527 #else
00528 ereport(ERROR,
00529 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00530 errmsg("tablespaces are not supported on this platform")));
00531 #endif
00532 }
00533
00534
00535
00536
00537
00538
00539
00540
00541 static void
00542 create_tablespace_directories(const char *location, const Oid tablespaceoid)
00543 {
00544 char *linkloc = palloc(OIDCHARS + OIDCHARS + 1);
00545 char *location_with_version_dir = palloc(strlen(location) + 1 +
00546 strlen(TABLESPACE_VERSION_DIRECTORY) + 1);
00547
00548 sprintf(linkloc, "pg_tblspc/%u", tablespaceoid);
00549 sprintf(location_with_version_dir, "%s/%s", location,
00550 TABLESPACE_VERSION_DIRECTORY);
00551
00552
00553
00554
00555
00556 if (chmod(location, S_IRWXU) != 0)
00557 {
00558 if (errno == ENOENT)
00559 ereport(ERROR,
00560 (errcode(ERRCODE_UNDEFINED_FILE),
00561 errmsg("directory \"%s\" does not exist", location),
00562 InRecovery ? errhint("Create this directory for the tablespace before "
00563 "restarting the server.") : 0));
00564 else
00565 ereport(ERROR,
00566 (errcode_for_file_access(),
00567 errmsg("could not set permissions on directory \"%s\": %m",
00568 location)));
00569 }
00570
00571 if (InRecovery)
00572 {
00573 struct stat st;
00574
00575
00576
00577
00578
00579
00580 if (stat(location_with_version_dir, &st) == 0 && S_ISDIR(st.st_mode))
00581 {
00582 if (!rmtree(location_with_version_dir, true))
00583
00584 ereport(WARNING,
00585 (errmsg("some useless files may be left behind in old database directory \"%s\"",
00586 location_with_version_dir)));
00587 }
00588 }
00589
00590
00591
00592
00593
00594 if (mkdir(location_with_version_dir, S_IRWXU) < 0)
00595 {
00596 if (errno == EEXIST)
00597 ereport(ERROR,
00598 (errcode(ERRCODE_OBJECT_IN_USE),
00599 errmsg("directory \"%s\" already in use as a tablespace",
00600 location_with_version_dir)));
00601 else
00602 ereport(ERROR,
00603 (errcode_for_file_access(),
00604 errmsg("could not create directory \"%s\": %m",
00605 location_with_version_dir)));
00606 }
00607
00608
00609 if (InRecovery)
00610 {
00611 if (unlink(linkloc) < 0 && errno != ENOENT)
00612 ereport(ERROR,
00613 (errcode_for_file_access(),
00614 errmsg("could not remove symbolic link \"%s\": %m",
00615 linkloc)));
00616 }
00617
00618
00619
00620
00621 if (symlink(location, linkloc) < 0)
00622 ereport(ERROR,
00623 (errcode_for_file_access(),
00624 errmsg("could not create symbolic link \"%s\": %m",
00625 linkloc)));
00626
00627 pfree(linkloc);
00628 pfree(location_with_version_dir);
00629 }
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645 static bool
00646 destroy_tablespace_directories(Oid tablespaceoid, bool redo)
00647 {
00648 char *linkloc;
00649 char *linkloc_with_version_dir;
00650 DIR *dirdesc;
00651 struct dirent *de;
00652 char *subfile;
00653 struct stat st;
00654
00655 linkloc_with_version_dir = palloc(9 + 1 + OIDCHARS + 1 +
00656 strlen(TABLESPACE_VERSION_DIRECTORY));
00657 sprintf(linkloc_with_version_dir, "pg_tblspc/%u/%s", tablespaceoid,
00658 TABLESPACE_VERSION_DIRECTORY);
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682 dirdesc = AllocateDir(linkloc_with_version_dir);
00683 if (dirdesc == NULL)
00684 {
00685 if (errno == ENOENT)
00686 {
00687 if (!redo)
00688 ereport(WARNING,
00689 (errcode_for_file_access(),
00690 errmsg("could not open directory \"%s\": %m",
00691 linkloc_with_version_dir)));
00692
00693 goto remove_symlink;
00694 }
00695 else if (redo)
00696 {
00697
00698 ereport(LOG,
00699 (errcode_for_file_access(),
00700 errmsg("could not open directory \"%s\": %m",
00701 linkloc_with_version_dir)));
00702 pfree(linkloc_with_version_dir);
00703 return false;
00704 }
00705
00706 }
00707
00708 while ((de = ReadDir(dirdesc, linkloc_with_version_dir)) != NULL)
00709 {
00710 if (strcmp(de->d_name, ".") == 0 ||
00711 strcmp(de->d_name, "..") == 0)
00712 continue;
00713
00714 subfile = palloc(strlen(linkloc_with_version_dir) + 1 + strlen(de->d_name) + 1);
00715 sprintf(subfile, "%s/%s", linkloc_with_version_dir, de->d_name);
00716
00717
00718 if (!redo && !directory_is_empty(subfile))
00719 {
00720 FreeDir(dirdesc);
00721 pfree(subfile);
00722 pfree(linkloc_with_version_dir);
00723 return false;
00724 }
00725
00726
00727 if (rmdir(subfile) < 0)
00728 ereport(redo ? LOG : ERROR,
00729 (errcode_for_file_access(),
00730 errmsg("could not remove directory \"%s\": %m",
00731 subfile)));
00732
00733 pfree(subfile);
00734 }
00735
00736 FreeDir(dirdesc);
00737
00738
00739 if (rmdir(linkloc_with_version_dir) < 0)
00740 {
00741 ereport(redo ? LOG : ERROR,
00742 (errcode_for_file_access(),
00743 errmsg("could not remove directory \"%s\": %m",
00744 linkloc_with_version_dir)));
00745 pfree(linkloc_with_version_dir);
00746 return false;
00747 }
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759 remove_symlink:
00760 linkloc = pstrdup(linkloc_with_version_dir);
00761 get_parent_directory(linkloc);
00762 if (lstat(linkloc, &st) == 0 && S_ISDIR(st.st_mode))
00763 {
00764 if (rmdir(linkloc) < 0)
00765 ereport(redo ? LOG : ERROR,
00766 (errcode_for_file_access(),
00767 errmsg("could not remove directory \"%s\": %m",
00768 linkloc)));
00769 }
00770 else
00771 {
00772 if (unlink(linkloc) < 0)
00773 ereport(redo ? LOG : (errno == ENOENT ? WARNING : ERROR),
00774 (errcode_for_file_access(),
00775 errmsg("could not remove symbolic link \"%s\": %m",
00776 linkloc)));
00777 }
00778
00779 pfree(linkloc_with_version_dir);
00780 pfree(linkloc);
00781
00782 return true;
00783 }
00784
00785
00786
00787
00788
00789
00790
00791 bool
00792 directory_is_empty(const char *path)
00793 {
00794 DIR *dirdesc;
00795 struct dirent *de;
00796
00797 dirdesc = AllocateDir(path);
00798
00799 while ((de = ReadDir(dirdesc, path)) != NULL)
00800 {
00801 if (strcmp(de->d_name, ".") == 0 ||
00802 strcmp(de->d_name, "..") == 0)
00803 continue;
00804 FreeDir(dirdesc);
00805 return false;
00806 }
00807
00808 FreeDir(dirdesc);
00809 return true;
00810 }
00811
00812
00813
00814
00815
00816 Oid
00817 RenameTableSpace(const char *oldname, const char *newname)
00818 {
00819 Oid tspId;
00820 Relation rel;
00821 ScanKeyData entry[1];
00822 HeapScanDesc scan;
00823 HeapTuple tup;
00824 HeapTuple newtuple;
00825 Form_pg_tablespace newform;
00826
00827
00828 rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
00829
00830 ScanKeyInit(&entry[0],
00831 Anum_pg_tablespace_spcname,
00832 BTEqualStrategyNumber, F_NAMEEQ,
00833 CStringGetDatum(oldname));
00834 scan = heap_beginscan(rel, SnapshotNow, 1, entry);
00835 tup = heap_getnext(scan, ForwardScanDirection);
00836 if (!HeapTupleIsValid(tup))
00837 ereport(ERROR,
00838 (errcode(ERRCODE_UNDEFINED_OBJECT),
00839 errmsg("tablespace \"%s\" does not exist",
00840 oldname)));
00841
00842 tspId = HeapTupleGetOid(tup);
00843 newtuple = heap_copytuple(tup);
00844 newform = (Form_pg_tablespace) GETSTRUCT(newtuple);
00845
00846 heap_endscan(scan);
00847
00848
00849 if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId()))
00850 aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, oldname);
00851
00852
00853 if (!allowSystemTableMods && IsReservedName(newname))
00854 ereport(ERROR,
00855 (errcode(ERRCODE_RESERVED_NAME),
00856 errmsg("unacceptable tablespace name \"%s\"", newname),
00857 errdetail("The prefix \"pg_\" is reserved for system tablespaces.")));
00858
00859
00860 ScanKeyInit(&entry[0],
00861 Anum_pg_tablespace_spcname,
00862 BTEqualStrategyNumber, F_NAMEEQ,
00863 CStringGetDatum(newname));
00864 scan = heap_beginscan(rel, SnapshotNow, 1, entry);
00865 tup = heap_getnext(scan, ForwardScanDirection);
00866 if (HeapTupleIsValid(tup))
00867 ereport(ERROR,
00868 (errcode(ERRCODE_DUPLICATE_OBJECT),
00869 errmsg("tablespace \"%s\" already exists",
00870 newname)));
00871
00872 heap_endscan(scan);
00873
00874
00875 namestrcpy(&(newform->spcname), newname);
00876
00877 simple_heap_update(rel, &newtuple->t_self, newtuple);
00878 CatalogUpdateIndexes(rel, newtuple);
00879
00880 InvokeObjectPostAlterHook(TableSpaceRelationId, tspId, 0);
00881
00882 heap_close(rel, NoLock);
00883
00884 return tspId;
00885 }
00886
00887
00888
00889
00890 Oid
00891 AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
00892 {
00893 Relation rel;
00894 ScanKeyData entry[1];
00895 HeapScanDesc scandesc;
00896 HeapTuple tup;
00897 Oid tablespaceoid;
00898 Datum datum;
00899 Datum newOptions;
00900 Datum repl_val[Natts_pg_tablespace];
00901 bool isnull;
00902 bool repl_null[Natts_pg_tablespace];
00903 bool repl_repl[Natts_pg_tablespace];
00904 HeapTuple newtuple;
00905
00906
00907 rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
00908
00909 ScanKeyInit(&entry[0],
00910 Anum_pg_tablespace_spcname,
00911 BTEqualStrategyNumber, F_NAMEEQ,
00912 CStringGetDatum(stmt->tablespacename));
00913 scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
00914 tup = heap_getnext(scandesc, ForwardScanDirection);
00915 if (!HeapTupleIsValid(tup))
00916 ereport(ERROR,
00917 (errcode(ERRCODE_UNDEFINED_OBJECT),
00918 errmsg("tablespace \"%s\" does not exist",
00919 stmt->tablespacename)));
00920
00921 tablespaceoid = HeapTupleGetOid(tup);
00922
00923
00924 if (!pg_tablespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
00925 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE,
00926 stmt->tablespacename);
00927
00928
00929 datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions,
00930 RelationGetDescr(rel), &isnull);
00931 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
00932 stmt->options, NULL, NULL, false,
00933 stmt->isReset);
00934 (void) tablespace_reloptions(newOptions, true);
00935
00936
00937 memset(repl_null, false, sizeof(repl_null));
00938 memset(repl_repl, false, sizeof(repl_repl));
00939 if (newOptions != (Datum) 0)
00940 repl_val[Anum_pg_tablespace_spcoptions - 1] = newOptions;
00941 else
00942 repl_null[Anum_pg_tablespace_spcoptions - 1] = true;
00943 repl_repl[Anum_pg_tablespace_spcoptions - 1] = true;
00944 newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val,
00945 repl_null, repl_repl);
00946
00947
00948 simple_heap_update(rel, &newtuple->t_self, newtuple);
00949 CatalogUpdateIndexes(rel, newtuple);
00950
00951 InvokeObjectPostAlterHook(TableSpaceRelationId, HeapTupleGetOid(tup), 0);
00952
00953 heap_freetuple(newtuple);
00954
00955
00956 heap_endscan(scandesc);
00957 heap_close(rel, NoLock);
00958
00959 return tablespaceoid;
00960 }
00961
00962
00963
00964
00965
00966
00967 bool
00968 check_default_tablespace(char **newval, void **extra, GucSource source)
00969 {
00970
00971
00972
00973
00974 if (IsTransactionState())
00975 {
00976 if (**newval != '\0' &&
00977 !OidIsValid(get_tablespace_oid(*newval, true)))
00978 {
00979
00980
00981
00982
00983
00984
00985
00986
00987 if (source == PGC_S_TEST)
00988 {
00989 ereport(NOTICE,
00990 (errcode(ERRCODE_UNDEFINED_OBJECT),
00991 errmsg("tablespace \"%s\" does not exist",
00992 *newval)));
00993 }
00994 else
00995 {
00996 GUC_check_errdetail("Tablespace \"%s\" does not exist.",
00997 *newval);
00998 return false;
00999 }
01000 }
01001 }
01002
01003 return true;
01004 }
01005
01006
01007
01008
01009
01010
01011
01012
01013
01014
01015
01016
01017
01018
01019
01020 Oid
01021 GetDefaultTablespace(char relpersistence)
01022 {
01023 Oid result;
01024
01025
01026 if (relpersistence == RELPERSISTENCE_TEMP)
01027 {
01028 PrepareTempTablespaces();
01029 return GetNextTempTableSpace();
01030 }
01031
01032
01033 if (default_tablespace == NULL || default_tablespace[0] == '\0')
01034 return InvalidOid;
01035
01036
01037
01038
01039
01040
01041
01042
01043 result = get_tablespace_oid(default_tablespace, true);
01044
01045
01046
01047
01048
01049 if (result == MyDatabaseTableSpace)
01050 result = InvalidOid;
01051 return result;
01052 }
01053
01054
01055
01056
01057
01058
01059 typedef struct
01060 {
01061 int numSpcs;
01062 Oid tblSpcs[1];
01063 } temp_tablespaces_extra;
01064
01065
01066 bool
01067 check_temp_tablespaces(char **newval, void **extra, GucSource source)
01068 {
01069 char *rawname;
01070 List *namelist;
01071
01072
01073 rawname = pstrdup(*newval);
01074
01075
01076 if (!SplitIdentifierString(rawname, ',', &namelist))
01077 {
01078
01079 GUC_check_errdetail("List syntax is invalid.");
01080 pfree(rawname);
01081 list_free(namelist);
01082 return false;
01083 }
01084
01085
01086
01087
01088
01089
01090 if (IsTransactionState())
01091 {
01092 temp_tablespaces_extra *myextra;
01093 Oid *tblSpcs;
01094 int numSpcs;
01095 ListCell *l;
01096
01097
01098 tblSpcs = (Oid *) palloc(list_length(namelist) * sizeof(Oid));
01099 numSpcs = 0;
01100 foreach(l, namelist)
01101 {
01102 char *curname = (char *) lfirst(l);
01103 Oid curoid;
01104 AclResult aclresult;
01105
01106
01107 if (curname[0] == '\0')
01108 {
01109 tblSpcs[numSpcs++] = InvalidOid;
01110 continue;
01111 }
01112
01113
01114
01115
01116
01117
01118
01119
01120
01121
01122
01123 curoid = get_tablespace_oid(curname, source <= PGC_S_TEST);
01124 if (curoid == InvalidOid)
01125 {
01126 if (source == PGC_S_TEST)
01127 ereport(NOTICE,
01128 (errcode(ERRCODE_UNDEFINED_OBJECT),
01129 errmsg("tablespace \"%s\" does not exist",
01130 curname)));
01131 continue;
01132 }
01133
01134
01135
01136
01137
01138 if (curoid == MyDatabaseTableSpace)
01139 {
01140 tblSpcs[numSpcs++] = InvalidOid;
01141 continue;
01142 }
01143
01144
01145 aclresult = pg_tablespace_aclcheck(curoid, GetUserId(),
01146 ACL_CREATE);
01147 if (aclresult != ACLCHECK_OK)
01148 {
01149 if (source >= PGC_S_INTERACTIVE)
01150 aclcheck_error(aclresult, ACL_KIND_TABLESPACE, curname);
01151 continue;
01152 }
01153
01154 tblSpcs[numSpcs++] = curoid;
01155 }
01156
01157
01158 myextra = malloc(offsetof(temp_tablespaces_extra, tblSpcs) +
01159 numSpcs * sizeof(Oid));
01160 if (!myextra)
01161 return false;
01162 myextra->numSpcs = numSpcs;
01163 memcpy(myextra->tblSpcs, tblSpcs, numSpcs * sizeof(Oid));
01164 *extra = (void *) myextra;
01165
01166 pfree(tblSpcs);
01167 }
01168
01169 pfree(rawname);
01170 list_free(namelist);
01171
01172 return true;
01173 }
01174
01175
01176 void
01177 assign_temp_tablespaces(const char *newval, void *extra)
01178 {
01179 temp_tablespaces_extra *myextra = (temp_tablespaces_extra *) extra;
01180
01181
01182
01183
01184
01185
01186
01187
01188 if (myextra)
01189 SetTempTablespaces(myextra->tblSpcs, myextra->numSpcs);
01190 else
01191 SetTempTablespaces(NULL, 0);
01192 }
01193
01194
01195
01196
01197
01198
01199
01200
01201 void
01202 PrepareTempTablespaces(void)
01203 {
01204 char *rawname;
01205 List *namelist;
01206 Oid *tblSpcs;
01207 int numSpcs;
01208 ListCell *l;
01209
01210
01211 if (TempTablespacesAreSet())
01212 return;
01213
01214
01215
01216
01217
01218
01219
01220
01221 if (!IsTransactionState())
01222 return;
01223
01224
01225 rawname = pstrdup(temp_tablespaces);
01226
01227
01228 if (!SplitIdentifierString(rawname, ',', &namelist))
01229 {
01230
01231 SetTempTablespaces(NULL, 0);
01232 pfree(rawname);
01233 list_free(namelist);
01234 return;
01235 }
01236
01237
01238 tblSpcs = (Oid *) MemoryContextAlloc(TopTransactionContext,
01239 list_length(namelist) * sizeof(Oid));
01240 numSpcs = 0;
01241 foreach(l, namelist)
01242 {
01243 char *curname = (char *) lfirst(l);
01244 Oid curoid;
01245 AclResult aclresult;
01246
01247
01248 if (curname[0] == '\0')
01249 {
01250 tblSpcs[numSpcs++] = InvalidOid;
01251 continue;
01252 }
01253
01254
01255 curoid = get_tablespace_oid(curname, true);
01256 if (curoid == InvalidOid)
01257 {
01258
01259 continue;
01260 }
01261
01262
01263
01264
01265
01266 if (curoid == MyDatabaseTableSpace)
01267 {
01268 tblSpcs[numSpcs++] = InvalidOid;
01269 continue;
01270 }
01271
01272
01273 aclresult = pg_tablespace_aclcheck(curoid, GetUserId(),
01274 ACL_CREATE);
01275 if (aclresult != ACLCHECK_OK)
01276 continue;
01277
01278 tblSpcs[numSpcs++] = curoid;
01279 }
01280
01281 SetTempTablespaces(tblSpcs, numSpcs);
01282
01283 pfree(rawname);
01284 list_free(namelist);
01285 }
01286
01287
01288
01289
01290
01291
01292
01293
01294 Oid
01295 get_tablespace_oid(const char *tablespacename, bool missing_ok)
01296 {
01297 Oid result;
01298 Relation rel;
01299 HeapScanDesc scandesc;
01300 HeapTuple tuple;
01301 ScanKeyData entry[1];
01302
01303
01304
01305
01306
01307
01308 rel = heap_open(TableSpaceRelationId, AccessShareLock);
01309
01310 ScanKeyInit(&entry[0],
01311 Anum_pg_tablespace_spcname,
01312 BTEqualStrategyNumber, F_NAMEEQ,
01313 CStringGetDatum(tablespacename));
01314 scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
01315 tuple = heap_getnext(scandesc, ForwardScanDirection);
01316
01317
01318 if (HeapTupleIsValid(tuple))
01319 result = HeapTupleGetOid(tuple);
01320 else
01321 result = InvalidOid;
01322
01323 heap_endscan(scandesc);
01324 heap_close(rel, AccessShareLock);
01325
01326 if (!OidIsValid(result) && !missing_ok)
01327 ereport(ERROR,
01328 (errcode(ERRCODE_UNDEFINED_OBJECT),
01329 errmsg("tablespace \"%s\" does not exist",
01330 tablespacename)));
01331
01332 return result;
01333 }
01334
01335
01336
01337
01338
01339
01340 char *
01341 get_tablespace_name(Oid spc_oid)
01342 {
01343 char *result;
01344 Relation rel;
01345 HeapScanDesc scandesc;
01346 HeapTuple tuple;
01347 ScanKeyData entry[1];
01348
01349
01350
01351
01352
01353
01354 rel = heap_open(TableSpaceRelationId, AccessShareLock);
01355
01356 ScanKeyInit(&entry[0],
01357 ObjectIdAttributeNumber,
01358 BTEqualStrategyNumber, F_OIDEQ,
01359 ObjectIdGetDatum(spc_oid));
01360 scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
01361 tuple = heap_getnext(scandesc, ForwardScanDirection);
01362
01363
01364 if (HeapTupleIsValid(tuple))
01365 result = pstrdup(NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname));
01366 else
01367 result = NULL;
01368
01369 heap_endscan(scandesc);
01370 heap_close(rel, AccessShareLock);
01371
01372 return result;
01373 }
01374
01375
01376
01377
01378
01379 void
01380 tblspc_redo(XLogRecPtr lsn, XLogRecord *record)
01381 {
01382 uint8 info = record->xl_info & ~XLR_INFO_MASK;
01383
01384
01385 Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
01386
01387 if (info == XLOG_TBLSPC_CREATE)
01388 {
01389 xl_tblspc_create_rec *xlrec = (xl_tblspc_create_rec *) XLogRecGetData(record);
01390 char *location = xlrec->ts_path;
01391
01392 create_tablespace_directories(location, xlrec->ts_id);
01393 }
01394 else if (info == XLOG_TBLSPC_DROP)
01395 {
01396 xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *) XLogRecGetData(record);
01397
01398
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413 if (!destroy_tablespace_directories(xlrec->ts_id, true))
01414 {
01415 ResolveRecoveryConflictWithTablespace(xlrec->ts_id);
01416
01417
01418
01419
01420
01421
01422
01423
01424
01425 if (!destroy_tablespace_directories(xlrec->ts_id, true))
01426 ereport(LOG,
01427 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
01428 errmsg("directories for tablespace %u could not be removed",
01429 xlrec->ts_id),
01430 errhint("You can remove the directories manually if necessary.")));
01431 }
01432 }
01433 else
01434 elog(PANIC, "tblspc_redo: unknown op code %u", info);
01435 }