Greetings, It's a day late and I'm a dollar short, but attached is a (very) minor patch to allow users to more easily move their various objects from one tablespace to another. Included are docs and a regression test; I'm happy to improve on both should folks send me suggestions.
As we use tablespaces quite a bit, this can be extremely handy for us and I expect others will find it useful too. Thoughts? Thanks, Stephen
diff --git a/doc/src/sgml/ref/alter_tablespace.sgml b/doc/src/sgml/ref/alter_tablespace.sgml new file mode 100644 index 7d3ee2c..fcf4155 100644 *** a/doc/src/sgml/ref/alter_tablespace.sgml --- b/doc/src/sgml/ref/alter_tablespace.sgml *************** PostgreSQL documentation *** 12,18 **** <refnamediv> <refname>ALTER TABLESPACE</refname> ! <refpurpose>change the definition of a tablespace</refpurpose> </refnamediv> <indexterm zone="sql-altertablespace"> --- 12,18 ---- <refnamediv> <refname>ALTER TABLESPACE</refname> ! <refpurpose>change the definition of a tablespace or affect objects of a tablespace</refpurpose> </refnamediv> <indexterm zone="sql-altertablespace"> *************** ALTER TABLESPACE <replaceable>name</repl *** 25,30 **** --- 25,31 ---- ALTER TABLESPACE <replaceable>name</replaceable> OWNER TO <replaceable>new_owner</replaceable> ALTER TABLESPACE <replaceable>name</replaceable> SET ( <replaceable class="PARAMETER">tablespace_option</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] ) ALTER TABLESPACE <replaceable>name</replaceable> RESET ( <replaceable class="PARAMETER">tablespace_option</replaceable> [, ... ] ) + ALTER TABLESPACE <replaceable>name</replaceable> MOVE ALL TO <replaceable>new_tablespace</replaceable> [ NOWAIT ] </synopsis> </refsynopsisdiv> *************** ALTER TABLESPACE <replaceable>name</repl *** 32,46 **** <title>Description</title> <para> ! <command>ALTER TABLESPACE</command> changes the definition of ! a tablespace. </para> <para> ! You must own the tablespace to use <command>ALTER TABLESPACE</>. To alter the owner, you must also be a direct or indirect member of the new owning role. (Note that superusers have these privileges automatically.) </para> </refsect1> --- 33,60 ---- <title>Description</title> <para> ! <command>ALTER TABLESPACE</command> can be used to change the definition of ! a tablespace or to migrate all of the objects in the current database which ! are owned by the user out of a given tablespace. </para> <para> ! You must own the tablespace to change the definition of a tablespace. To alter the owner, you must also be a direct or indirect member of the new owning role. (Note that superusers have these privileges automatically.) + + Users may use ALTER TABLESPACE ... MOVE ALL, but they must have CREATE + rights on the new tablespace and only objects, directly or indirectly, owned + by the user will be moved. Note that the superuser is considered an owner + of all objects and therefore an ALTER TABLESPACE ... MOVE ALL issued by the + superuser will move all objects in the current database which are in the + tablespace. + + System catalogs will not be moved by this command- individuals wishing to + move a whole database should use ALTER DATABASE, or call ALTER TABLE on the + individual system catalogs. Note that relations in <literal>information_schema</literal> + will be moved, just as any other normal database objects. </para> </refsect1> *************** ALTER TABLESPACE <replaceable>name</repl *** 94,99 **** --- 108,137 ---- </para> </listitem> </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">new_tablespace</replaceable></term> + <listitem> + <para> + The name of the tablespace to move objects into. The user must have + CREATE rights on the new tablespace to move objects into that + tablespace, unless the tablespace being moved into is the default + tablespace for the database connected to. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">NOWAIT</replaceable></term> + <listitem> + <para> + The NOWAIT option causes the ALTER TABLESPACE command to fail immediately + if it is unable to acquire the necessary lock on all of the objects being + move. + </para> + </listitem> + </varlistentry> + </variablelist> </refsect1> *************** ALTER TABLESPACE index_space RENAME TO f *** 112,117 **** --- 150,162 ---- <programlisting> ALTER TABLESPACE index_space OWNER TO mary; </programlisting></para> + + <para> + Move all of the objects which I own from the default tablespace to + the <literal>fast_raid</literal> tablespace: + <programlisting> + ALTER TABLESPACE pg_default MOVE ALL TO fast_raid; + </programlisting></para> </refsect1> <refsect1> diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c new file mode 100644 index 07f5221..c47d13c 100644 *** a/src/backend/commands/tablespace.c --- b/src/backend/commands/tablespace.c *************** *** 59,78 **** --- 59,83 ---- #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" *************** AlterTableSpaceOptions(AlterTableSpaceOp *** 956,961 **** --- 961,1101 ---- } /* + * Alter table space move all + */ + Oid + AlterTableSpaceMove(AlterTableSpaceMoveStmt *stmt) + { + List *relations = NIL; + ListCell *l; + ScanKeyData key[1]; + Relation rel; + HeapScanDesc scan; + HeapTuple tuple; + Oid orig_tablespaceoid; + Oid new_tablespaceoid; + + /* 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 if not going to + * the database default tablespace + */ + 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 our database in the tablespace and move them */ + 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; + + 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 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'. */ diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c new file mode 100644 index fb4ce2c..19e5f04 100644 *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** _copyAlterTableSpaceOptionsStmt(const Al *** 3397,3402 **** --- 3397,3414 ---- return newnode; } + static AlterTableSpaceMoveStmt * + _copyAlterTableSpaceMoveStmt(const AlterTableSpaceMoveStmt *from) + { + AlterTableSpaceMoveStmt *newnode = makeNode(AlterTableSpaceMoveStmt); + + COPY_STRING_FIELD(orig_tablespacename); + COPY_STRING_FIELD(new_tablespacename); + COPY_SCALAR_FIELD(nowait); + + return newnode; + } + static CreateExtensionStmt * _copyCreateExtensionStmt(const CreateExtensionStmt *from) { *************** copyObject(const void *from) *** 4408,4413 **** --- 4420,4428 ---- case T_AlterTableSpaceOptionsStmt: retval = _copyAlterTableSpaceOptionsStmt(from); break; + case T_AlterTableSpaceMoveStmt: + retval = _copyAlterTableSpaceMoveStmt(from); + break; case T_CreateExtensionStmt: retval = _copyCreateExtensionStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c new file mode 100644 index ccf7267..55c548d 100644 *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** _equalAlterTableSpaceOptionsStmt(const A *** 1635,1640 **** --- 1635,1651 ---- } static bool + _equalAlterTableSpaceMoveStmt(const AlterTableSpaceMoveStmt *a, + const AlterTableSpaceMoveStmt *b) + { + COMPARE_STRING_FIELD(orig_tablespacename); + COMPARE_STRING_FIELD(new_tablespacename); + COMPARE_SCALAR_FIELD(nowait); + + return true; + } + + static bool _equalCreateExtensionStmt(const CreateExtensionStmt *a, const CreateExtensionStmt *b) { COMPARE_STRING_FIELD(extname); *************** equal(const void *a, const void *b) *** 2877,2882 **** --- 2888,2896 ---- case T_AlterTableSpaceOptionsStmt: retval = _equalAlterTableSpaceOptionsStmt(a, b); break; + case T_AlterTableSpaceMoveStmt: + retval = _equalAlterTableSpaceMoveStmt(a, b); + break; case T_CreateExtensionStmt: retval = _equalCreateExtensionStmt(a, b); break; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y new file mode 100644 index f0b9507..9f5dcc4 100644 *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** RenameStmt: ALTER AGGREGATE func_name ag *** 7319,7324 **** --- 7319,7333 ---- n->missing_ok = false; $$ = (Node *)n; } + | ALTER TABLESPACE name MOVE ALL TO name opt_nowait + { + AlterTableSpaceMoveStmt *n = + makeNode(AlterTableSpaceMoveStmt); + n->orig_tablespacename = $3; + n->new_tablespacename = $7; + n->nowait = $8; + $$ = (Node *)n; + } | ALTER TABLESPACE name SET reloptions { AlterTableSpaceOptionsStmt *n = diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c new file mode 100644 index 36cf72d..f4d25bd 100644 *** a/src/backend/tcop/utility.c --- b/src/backend/tcop/utility.c *************** check_xact_readonly(Node *parsetree) *** 243,248 **** --- 243,249 ---- case T_AlterUserMappingStmt: case T_DropUserMappingStmt: case T_AlterTableSpaceOptionsStmt: + case T_AlterTableSpaceMoveStmt: case T_CreateForeignTableStmt: case T_SecLabelStmt: PreventCommandIfReadOnly(CreateCommandTag(parsetree)); *************** standard_ProcessUtility(Node *parsetree, *** 548,553 **** --- 549,559 ---- AlterTableSpaceOptions((AlterTableSpaceOptionsStmt *) parsetree); break; + case T_AlterTableSpaceMoveStmt: + /* no event triggers for global objects */ + AlterTableSpaceMove((AlterTableSpaceMoveStmt *) parsetree); + break; + case T_TruncateStmt: ExecuteTruncate((TruncateStmt *) parsetree); break; *************** CreateCommandTag(Node *parsetree) *** 1822,1827 **** --- 1828,1837 ---- tag = "ALTER TABLESPACE"; break; + case T_AlterTableSpaceMoveStmt: + tag = "ALTER TABLESPACE"; + break; + case T_CreateExtensionStmt: tag = "CREATE EXTENSION"; break; *************** GetCommandLogLevel(Node *parsetree) *** 2514,2519 **** --- 2524,2533 ---- lev = LOGSTMT_DDL; break; + case T_AlterTableSpaceMoveStmt: + lev = LOGSTMT_DDL; + break; + case T_CreateExtensionStmt: case T_AlterExtensionStmt: case T_AlterExtensionContentsStmt: diff --git a/src/include/commands/tablespace.h b/src/include/commands/tablespace.h new file mode 100644 index c7af559..1603f67 100644 *** a/src/include/commands/tablespace.h --- b/src/include/commands/tablespace.h *************** extern Oid CreateTableSpace(CreateTableS *** 43,48 **** --- 43,49 ---- extern void DropTableSpace(DropTableSpaceStmt *stmt); extern Oid RenameTableSpace(const char *oldname, const char *newname); extern Oid AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt); + extern Oid AlterTableSpaceMove(AlterTableSpaceMoveStmt *stmt); extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h new file mode 100644 index ae12c0d..dfcc013 100644 *** a/src/include/nodes/nodes.h --- b/src/include/nodes/nodes.h *************** typedef enum NodeTag *** 354,359 **** --- 354,360 ---- T_AlterUserMappingStmt, T_DropUserMappingStmt, T_AlterTableSpaceOptionsStmt, + T_AlterTableSpaceMoveStmt, T_SecLabelStmt, T_CreateForeignTableStmt, T_CreateExtensionStmt, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h new file mode 100644 index 9a3a5d7..2079cd9 100644 *** a/src/include/nodes/parsenodes.h --- b/src/include/nodes/parsenodes.h *************** typedef struct AlterTableSpaceOptionsStm *** 1686,1691 **** --- 1686,1699 ---- bool isReset; } AlterTableSpaceOptionsStmt; + typedef struct AlterTableSpaceMoveStmt + { + NodeTag type; + char *orig_tablespacename; + char *new_tablespacename; + bool nowait; + } AlterTableSpaceMoveStmt; + /* ---------------------- * Create/Alter Extension Statements * ---------------------- diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source new file mode 100644 index 4f17b09..8ee7efa 100644 *** a/src/test/regress/input/tablespace.source --- b/src/test/regress/input/tablespace.source *************** CREATE TABLE tablespace_table (i int) TA *** 66,75 **** ALTER TABLESPACE testspace RENAME TO testspace_renamed; ! DROP SCHEMA testschema CASCADE; -- Should succeed DROP TABLESPACE testspace_renamed; DROP ROLE tablespace_testuser1; DROP ROLE tablespace_testuser2; --- 66,80 ---- ALTER TABLESPACE testspace RENAME TO testspace_renamed; ! ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default; ! ! -- Should show notice that nothing was done ! ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default; -- Should succeed DROP TABLESPACE testspace_renamed; + DROP SCHEMA testschema CASCADE; + DROP ROLE tablespace_testuser1; DROP ROLE tablespace_testuser2; diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source new file mode 100644 index 2868169..5035ab0 100644 *** a/src/test/regress/output/tablespace.source --- b/src/test/regress/output/tablespace.source *************** CREATE TABLE tablespace_table (i int) TA *** 80,92 **** ERROR: permission denied for tablespace testspace \c - ALTER TABLESPACE testspace RENAME TO testspace_renamed; DROP SCHEMA testschema CASCADE; NOTICE: drop cascades to 4 other objects DETAIL: drop cascades to table testschema.foo drop cascades to table testschema.asselect drop cascades to table testschema.asexecute drop cascades to table testschema.atable - -- Should succeed - DROP TABLESPACE testspace_renamed; DROP ROLE tablespace_testuser1; DROP ROLE tablespace_testuser2; --- 80,96 ---- ERROR: permission denied for tablespace testspace \c - ALTER TABLESPACE testspace RENAME TO testspace_renamed; + ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default; + -- Should show notice that nothing was done + ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default; + NOTICE: no relations in tablespace "testspace_renamed" found + -- Should succeed + DROP TABLESPACE testspace_renamed; DROP SCHEMA testschema CASCADE; NOTICE: drop cascades to 4 other objects DETAIL: drop cascades to table testschema.foo drop cascades to table testschema.asselect drop cascades to table testschema.asexecute drop cascades to table testschema.atable DROP ROLE tablespace_testuser1; DROP ROLE tablespace_testuser2; diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list new file mode 100644 index e3058be..1f735b7 100644 *** a/src/tools/pgindent/typedefs.list --- b/src/tools/pgindent/typedefs.list *************** AlterTSConfigurationStmt *** 76,81 **** --- 76,82 ---- AlterTSDictionaryStmt AlterTableCmd AlterTableSpaceOptionsStmt + AlterTableSpaceMoveStmt AlterTableStmt AlterTableType AlterUserMappingStmt
signature.asc
Description: Digital signature