Index: doc/src/sgml/ref/alter_table.sgml =================================================================== RCS file: /usr/local/cvsroot/pgsql-server/doc/src/sgml/ref/alter_table.sgml,v retrieving revision 1.72 diff -2 -c -r1.72 alter_table.sgml *** doc/src/sgml/ref/alter_table.sgml 2 Jun 2004 21:04:40 -0000 1.72 --- doc/src/sgml/ref/alter_table.sgml 20 Jun 2004 02:54:30 -0000 *************** *** 44,47 **** --- 44,48 ---- CLUSTER ON index_name SET WITHOUT CLUSTER + SET TABLESPACE tablespace_name *************** *** 233,237 **** ! RENAME --- 234,250 ---- ! ! ! SET TABLESPACE ! ! ! This form changes the table's tablespace to the specified tablespace and ! moves the data file(s) associated with the table to the new tablespace. ! See also ! . ! ! ! ! RENAME *************** *** 358,361 **** --- 371,382 ---- + tablespace_name + + + The tablespace name to which the table will be moved. + + + + CASCADE *************** *** 552,555 **** --- 573,584 ---- + + + To move a table to a different tablespace: + + ALTER TABLE distributors SET TABLESPACE fasttablespace; + + + Index: src/backend/commands/cluster.c =================================================================== RCS file: /usr/local/cvsroot/pgsql-server/src/backend/commands/cluster.c,v retrieving revision 1.126 diff -2 -c -r1.126 cluster.c *** src/backend/commands/cluster.c 18 Jun 2004 06:13:22 -0000 1.126 --- src/backend/commands/cluster.c 30 Jun 2004 14:48:06 -0000 *************** *** 506,510 **** snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", tableOid); ! OIDNewHeap = make_new_heap(tableOid, NewHeapName); /* --- 506,512 ---- snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", tableOid); ! OIDNewHeap = make_new_heap(tableOid, NewHeapName, ! OldHeap->rd_rel->reltablespace, allowSystemTableMods, ! true); /* *************** *** 522,526 **** /* Swap the relfilenodes of the old and new heaps. */ ! swap_relfilenodes(tableOid, OIDNewHeap); CommandCounterIncrement(); --- 524,528 ---- /* Swap the relfilenodes of the old and new heaps. */ ! swap_relfilenodes(tableOid, OIDNewHeap, true); CommandCounterIncrement(); *************** *** 551,555 **** */ Oid ! make_new_heap(Oid OIDOldHeap, const char *NewName) { TupleDesc OldHeapDesc, --- 553,558 ---- */ Oid ! make_new_heap(Oid OIDOldHeap, const char *NewName, Oid newtablespaceId, ! bool allow_system_table_mods, bool create_toast) { TupleDesc OldHeapDesc, *************** *** 558,562 **** Relation OldHeap; ! OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock); OldHeapDesc = RelationGetDescr(OldHeap); --- 561,565 ---- Relation OldHeap; ! OldHeap = relation_open(OIDOldHeap, AccessExclusiveLock); OldHeapDesc = RelationGetDescr(OldHeap); *************** *** 568,580 **** OIDNewHeap = heap_create_with_catalog(NewName, ! RelationGetNamespace(OldHeap), ! OldHeap->rd_rel->reltablespace, ! tupdesc, ! OldHeap->rd_rel->relkind, ! OldHeap->rd_rel->relisshared, ! true, ! 0, ! ONCOMMIT_NOOP, ! allowSystemTableMods); /* --- 571,584 ---- OIDNewHeap = heap_create_with_catalog(NewName, ! RelationGetNamespace(OldHeap), ! newtablespaceId, ! tupdesc, ! OldHeap->rd_rel->relkind == RELKIND_INDEX ? ! RELKIND_RELATION : OldHeap->rd_rel->relkind, ! OldHeap->rd_rel->relisshared, ! true, ! 0, ! ONCOMMIT_NOOP, ! allow_system_table_mods); /* *************** *** 589,595 **** * that the TOAST table will be visible for insertion. */ ! AlterTableCreateToastTable(OIDNewHeap, true); ! heap_close(OldHeap, NoLock); return OIDNewHeap; --- 593,600 ---- * that the TOAST table will be visible for insertion. */ ! if(create_toast) ! AlterTableCreateToastTable(OIDNewHeap, true); ! relation_close(OldHeap, NoLock); return OIDNewHeap; *************** *** 649,657 **** * Swap the relfilenodes for two given relations. * ! * Also swap any TOAST links, so that the toast data moves along with ! * the main-table data. */ void ! swap_relfilenodes(Oid r1, Oid r2) { Relation relRelation, --- 654,661 ---- * Swap the relfilenodes for two given relations. * ! * Swap tablespace oids, since we use this for ALTER TALE SET TABLESPACE */ void ! swap_relfilenodes(Oid r1, Oid r2, bool swap_toast) { Relation relRelation, *************** *** 696,700 **** /* ! * Actually swap the filenode and TOAST fields in the two tuples */ swaptemp = relform1->relfilenode; --- 700,704 ---- /* ! * Actually swap the filenode and tablespace in the two tuples */ swaptemp = relform1->relfilenode; *************** *** 702,708 **** relform2->relfilenode = swaptemp; ! swaptemp = relform1->reltoastrelid; ! relform1->reltoastrelid = relform2->reltoastrelid; ! relform2->reltoastrelid = swaptemp; /* we should not swap reltoastidxid */ --- 706,719 ---- relform2->relfilenode = swaptemp; ! if(swap_toast) ! { ! swaptemp = relform1->reltoastrelid; ! relform1->reltoastrelid = relform2->reltoastrelid; ! relform2->reltoastrelid = swaptemp; ! } ! ! swaptemp = relform1->reltablespace; ! relform1->reltablespace = relform2->reltablespace; ! relform2->reltablespace = swaptemp; /* we should not swap reltoastidxid */ Index: src/backend/commands/tablecmds.c =================================================================== RCS file: /usr/local/cvsroot/pgsql-server/src/backend/commands/tablecmds.c,v retrieving revision 1.117 diff -2 -c -r1.117 tablecmds.c *** src/backend/commands/tablecmds.c 25 Jun 2004 21:55:53 -0000 1.117 --- src/backend/commands/tablecmds.c 30 Jun 2004 14:48:50 -0000 *************** *** 53,56 **** --- 53,57 ---- #include "parser/parse_type.h" #include "rewrite/rewriteHandler.h" + #include "storage/smgr.h" #include "utils/acl.h" #include "utils/builtins.h" *************** *** 231,234 **** --- 232,236 ---- bool recurse, bool recursing, AlterTableCmd *cmd); + static void ATPrepSetTablespace(List **wqueue, Relation rel, char *name); static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, const char *colName, TypeName *typename); *************** *** 238,241 **** --- 240,244 ---- static void ATExecClusterOn(Relation rel, const char *indexName); static void ATExecDropCluster(Relation rel); + static void ATExecSetTablespace(Relation rel, char *name); static int ri_trigger_type(Oid tgfoid); static void update_ri_trigger_args(Oid relid, *************** *** 244,247 **** --- 247,253 ---- bool fk_scan, bool update_relname); + static void copy_heap(Relation old, Relation new); + static Oid make_new_table(Oid OIDOldHeap, const char *NewName, Oid newtablespaceId); + static void create_new_rel(Relation old, Oid tablespaceId); *************** *** 1947,1950 **** --- 1953,1961 ---- pass = AT_PASS_DROP; break; + case AT_SetTablespace: /* SET TABLESPACE */ + /* Don't do permissions check if we're recursing */ + ATPrepSetTablespace(wqueue, rel, cmd->name); + pass = AT_PASS_MISC; + break; default: /* oops */ elog(ERROR, "unrecognized alter table type: %d", *************** *** 2098,2101 **** --- 2109,2115 ---- */ break; + case AT_SetTablespace: /* SET TABLESPACE */ + ATExecSetTablespace(rel, cmd->name); + break; default: /* oops */ elog(ERROR, "unrecognized alter table type: %d", *************** *** 2171,2175 **** "pg_temp_%u", tab->relid); ! OIDNewHeap = make_new_heap(tab->relid, NewHeapName); /* --- 2185,2190 ---- "pg_temp_%u", tab->relid); ! OIDNewHeap = make_new_heap(tab->relid, NewHeapName, ! OldHeap->rd_rel->reltablespace, allowSystemTableMods, true); /* *************** *** 2181,2185 **** /* Swap the relfilenodes of the old and new heaps. */ ! swap_relfilenodes(tab->relid, OIDNewHeap); CommandCounterIncrement(); --- 2196,2200 ---- /* Swap the relfilenodes of the old and new heaps. */ ! swap_relfilenodes(tab->relid, OIDNewHeap, true); CommandCounterIncrement(); *************** *** 4614,4617 **** --- 4629,4668 ---- } + + /* ALTER TABLE SET TABLESPACE */ + + static void + ATPrepSetTablespace(List **wqueue, Relation rel, char *name) + { + AclResult aclresult; + Oid tablespaceId; + + /* Permissions checks */ + if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + + if (!allowSystemTableMods && IsSystemRelation(rel)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied: \"%s\" is a system catalog", + RelationGetRelationName(rel)))); + + /* Check that the tablespace exists */ + + tablespaceId = get_tablespace_oid(name); + if (!OidIsValid(tablespaceId)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("tablespace \"%s\" does not exist", name))); + + /* check permissions */ + aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_TABLESPACE, + name); + } + static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, *************** *** 5186,5189 **** --- 5237,5379 ---- } + /* + * ALTER TABLE SET TABLESPACE + * + * Update the table with new tablespace and move table (heap) data + * to new tablespace + */ + static void + ATExecSetTablespace(Relation rel, char *name) + { + Oid tableOid = RelationGetRelid(rel); + Oid tablespaceId; + Oid toastoid = InvalidOid; + + tablespaceId = get_tablespace_oid(name); + if(!OidIsValid(tablespaceId)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("tablespace \"%s\" does not exist", + name))); + + /* + * See if its already in the tablespace + * XXX: is there an SQL state for this? + */ + + if(tablespaceId == rel->rd_rel->reltablespace) + elog(ERROR, "table \"%s\" is already in tablespace \"%s\"", + RelationGetRelationName(rel), name); + + + if(rel->rd_rel->relkind != RELKIND_RELATION && + rel->rd_rel->relkind != RELKIND_INDEX) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("object \"%s\" must be a table or index", + RelationGetRelationName(rel)))); + /* Save toast OID now as we can't rely on it after create_new_rel() */ + + toastoid = rel->rd_rel->reltoastrelid; + create_new_rel(rel, tablespaceId); + + /* may not have toast or may be an index */ + if(OidIsValid(toastoid)) + { + Relation toastrel; + Oid toastidxoid; + + toastrel = relation_open(toastoid, AccessExclusiveLock); + toastidxoid = toastrel->rd_rel->reltoastidxid; + create_new_rel(toastrel, tablespaceId); + + if(OidIsValid(toastidxoid)) + { + Relation toastidx; + toastidx = relation_open(toastidxoid, AccessExclusiveLock); + create_new_rel(toastidx, tablespaceId); + } + } + } + + /* + * Copy data in one relnode to another without losing OIDs and other defaults + */ + + static void + create_new_rel(Relation old, Oid tablespaceId) + { + Relation new; + Oid newoid, + oldoid; + ObjectAddress object; + + char newname[NAMEDATALEN]; + + oldoid = RelationGetRelid(old); + + snprintf(newname, sizeof(newname), "pg_temp_%u", + oldoid); + + /* + * We tell make_new_heap() NOT to create a TOAST table as we will + * continue to use the existing one + */ + newoid = make_new_heap(oldoid, newname, tablespaceId, true, false); + + new = relation_open(newoid, AccessExclusiveLock); + + copy_heap(old, new); + + /* We have to clcose becore we call swap_relfilenodes() */ + + relation_close(old, NoLock); + relation_close(new, NoLock); + + /* Swap the relfilenodes of the old and new heaps. */ + swap_relfilenodes(oldoid, newoid, false); + + CommandCounterIncrement(); + + /* Destroy new heap with old filenode */ + + object.classId = RelOid_pg_class; + object.objectId = newoid; + object.objectSubId = 0; + + /* + * The new relation is local to our transaction and we know nothing + * depends on it, so DROP_RESTRICT should be OK. + */ + performDeletion(&object, DROP_RESTRICT); + + /* performDeletion does CommandCounterIncrement at end */ + } + + /* + * Copy old, block by block, to new + */ + + static void + copy_heap(Relation old, Relation new) + { + BlockNumber b; + + if(!old->rd_smgr) + old->rd_smgr = smgropen(old->rd_node); + + if(!new->rd_smgr) + new->rd_smgr = smgropen(new->rd_node); + + for(b = 0; b < RelationGetNumberOfBlocks(old); b++) + { + char buf[BLCKSZ]; + + smgrread(old->rd_smgr, b, buf); + smgrwrite(new->rd_smgr, b, buf, new->rd_istemp); + + /* Add WAL */ + } + } /* Index: src/backend/parser/gram.y =================================================================== RCS file: /usr/local/cvsroot/pgsql-server/src/backend/parser/gram.y,v retrieving revision 2.465 diff -2 -c -r2.465 gram.y *** src/backend/parser/gram.y 28 Jun 2004 01:19:11 -0000 2.465 --- src/backend/parser/gram.y 28 Jun 2004 05:56:11 -0000 *************** *** 1287,1290 **** --- 1287,1297 ---- $$ = (Node *)n; } + | SET TABLESPACE name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetTablespace; + n->name = $3; + $$ = (Node *)n; + } ; Index: src/include/commands/cluster.h =================================================================== RCS file: /usr/local/cvsroot/pgsql-server/src/include/commands/cluster.h,v retrieving revision 1.23 diff -2 -c -r1.23 cluster.h *** src/include/commands/cluster.h 8 May 2004 00:34:49 -0000 1.23 --- src/include/commands/cluster.h 30 Jun 2004 14:47:46 -0000 *************** *** 22,27 **** extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid); extern void mark_index_clustered(Relation rel, Oid indexOid); ! extern Oid make_new_heap(Oid OIDOldHeap, const char *NewName); ! extern void swap_relfilenodes(Oid r1, Oid r2); #endif /* CLUSTER_H */ --- 22,28 ---- extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid); extern void mark_index_clustered(Relation rel, Oid indexOid); ! extern Oid make_new_heap(Oid OIDOldHeap, const char *NewName, ! Oid tablespaceId, bool allow_system_table_mods, bool create_toast); ! extern void swap_relfilenodes(Oid r1, Oid r2, bool swap_toast); #endif /* CLUSTER_H */ Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /usr/local/cvsroot/pgsql-server/src/include/nodes/parsenodes.h,v retrieving revision 1.260 diff -2 -c -r1.260 parsenodes.h *** src/include/nodes/parsenodes.h 25 Jun 2004 21:55:59 -0000 1.260 --- src/include/nodes/parsenodes.h 28 Jun 2004 05:56:18 -0000 *************** *** 807,811 **** AT_ClusterOn, /* CLUSTER ON */ AT_DropCluster, /* SET WITHOUT CLUSTER */ ! AT_DropOids /* SET WITHOUT OIDS */ } AlterTableType; --- 807,812 ---- AT_ClusterOn, /* CLUSTER ON */ AT_DropCluster, /* SET WITHOUT CLUSTER */ ! AT_DropOids, /* SET WITHOUT OIDS */ ! AT_SetTablespace /* SET TABLESPACE */ } AlterTableType;