diff options
Diffstat (limited to 'src/backend/commands/tablespace.c')
-rw-r--r-- | src/backend/commands/tablespace.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index 07f5221088f..cb2499af7d2 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -59,20 +59,25 @@ #include "catalog/catalog.h" #include "catalog/dependency.h" #include "catalog/indexing.h" +#include "catalog/namespace.h" #include "catalog/objectaccess.h" +#include "catalog/pg_namespace.h" #include "catalog/pg_tablespace.h" #include "commands/comment.h" #include "commands/seclabel.h" +#include "commands/tablecmds.h" #include "commands/tablespace.h" #include "common/relpath.h" #include "miscadmin.h" #include "postmaster/bgwriter.h" #include "storage/fd.h" +#include "storage/lmgr.h" #include "storage/standby.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/guc.h" +#include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/rel.h" #include "utils/tqual.h" @@ -956,6 +961,172 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt) } /* + * Alter table space move + * + * Allows a user to move all of their objects in a given tablespace in the + * current database to another tablespace. Only objects which the user is + * considered to be an owner of are moved and the user must have CREATE rights + * on the new tablespace. These checks should mean that ALTER TABLE will never + * fail due to permissions, but note that permissions will also be checked at + * that level. Objects can be ALL, TABLES, INDEXES, or MATERIALIZED VIEWS. + * + * All to-be-moved objects are locked first. If NOWAIT is specified and the + * lock can't be acquired then we ereport(ERROR). + */ +Oid +AlterTableSpaceMove(AlterTableSpaceMoveStmt *stmt) +{ + List *relations = NIL; + ListCell *l; + ScanKeyData key[1]; + Relation rel; + HeapScanDesc scan; + HeapTuple tuple; + Oid orig_tablespaceoid; + Oid new_tablespaceoid; + + /* Ensure we were not asked to move something we can't */ + if (!stmt->move_all && stmt->objtype != OBJECT_TABLE && + stmt->objtype != OBJECT_INDEX && stmt->objtype != OBJECT_MATVIEW) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("only tables, indexes, and materialized views exist in tablespaces"))); + + /* Get the orig and new tablespace OIDs */ + orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false); + new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false); + + /* Can't move shared relations in to or out of pg_global */ + /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */ + if (orig_tablespaceoid == GLOBALTABLESPACE_OID || + new_tablespaceoid == GLOBALTABLESPACE_OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot move relations in to or out of pg_global tablespace"))); + + /* + * Must have CREATE rights on the new tablespace, unless it is the + * database default tablespace (which all users implicitly have CREATE + * rights on). + */ + if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace) + { + AclResult aclresult; + + aclresult = pg_tablespace_aclcheck(new_tablespaceoid, GetUserId(), + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TABLESPACE, + get_tablespace_name(new_tablespaceoid)); + } + + /* + * Now that the checks are done, check if we should set either to + * InvalidOid because it is our database's default tablespace. + */ + if (orig_tablespaceoid == MyDatabaseTableSpace) + orig_tablespaceoid = InvalidOid; + + if (new_tablespaceoid == MyDatabaseTableSpace) + new_tablespaceoid = InvalidOid; + + /* no-op */ + if (orig_tablespaceoid == new_tablespaceoid) + return new_tablespaceoid; + + /* + * Walk the list of objects in the tablespace and move them. This will + * only find objects in our database, of course. + */ + ScanKeyInit(&key[0], + Anum_pg_class_reltablespace, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(orig_tablespaceoid)); + + rel = heap_open(RelationRelationId, AccessShareLock); + scan = heap_beginscan_catalog(rel, 1, key); + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + Oid relOid = HeapTupleGetOid(tuple); + Form_pg_class relForm; + + relForm = (Form_pg_class) GETSTRUCT(tuple); + + /* + * Do not move objects in pg_catalog as part of this, if an admin + * really wishes to do so, they can issue the individual ALTER + * commands directly. + * + * Also, explicitly avoid any shared tables, temp tables, or TOAST + * (TOAST will be moved with the main table). + */ + if (IsSystemNamespace(relForm->relnamespace) || relForm->relisshared || + isAnyTempNamespace(relForm->relnamespace) || + relForm->relnamespace == PG_TOAST_NAMESPACE) + continue; + + /* + * Only move objects that we are considered an owner of and only + * objects which can actually have a tablespace. + */ + if (!pg_class_ownercheck(relOid, GetUserId()) || + (relForm->relkind != RELKIND_RELATION && + relForm->relkind != RELKIND_INDEX && + relForm->relkind != RELKIND_MATVIEW)) + continue; + + /* Check if we were asked to only move a certain type of object */ + if (!stmt->move_all && + ((stmt->objtype == OBJECT_TABLE && + relForm->relkind != RELKIND_RELATION) || + (stmt->objtype == OBJECT_INDEX && + relForm->relkind != RELKIND_INDEX) || + (stmt->objtype == OBJECT_MATVIEW && + relForm->relkind != RELKIND_MATVIEW))) + continue; + + if (stmt->nowait && + !ConditionalLockRelationOid(relOid, AccessExclusiveLock)) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_IN_USE), + errmsg("aborting due to \"%s\".\"%s\" --- lock not available", + get_namespace_name(relForm->relnamespace), + NameStr(relForm->relname)))); + else + LockRelationOid(relOid, AccessExclusiveLock); + + /* Add to our list of objects to move */ + relations = lappend_oid(relations, relOid); + } + + heap_endscan(scan); + heap_close(rel, AccessShareLock); + + if (relations == NIL) + ereport(NOTICE, + (errcode(ERRCODE_NO_DATA_FOUND), + errmsg("no matching relations in tablespace \"%s\" found", + orig_tablespaceoid == InvalidOid ? "(database default)" : + get_tablespace_name(orig_tablespaceoid)))); + + /* Everything is locked, loop through and move all of the relations. */ + foreach(l, relations) + { + List *cmds = NIL; + AlterTableCmd *cmd = makeNode(AlterTableCmd); + + cmd->subtype = AT_SetTableSpace; + cmd->name = stmt->new_tablespacename; + + cmds = lappend(cmds, cmd); + + AlterTableInternal(lfirst_oid(l), cmds, false); + } + + return new_tablespaceoid; +} + +/* * Routines for handling the GUC variable 'default_tablespace'. */ |