diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 2b08445601..06c9653e22 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -1,5 +1,5 @@ @@ -46,6 +46,8 @@ ALTER TABLE [ ONLY ] table [ * ] DROP CONSTRAINT constraint_name [ RESTRICT | CASCADE ] ALTER TABLE table OWNER TO new_owner +ALTER TABLE table + CLUSTER ON index_name @@ -138,6 +140,15 @@ ALTER TABLE table + + index_name + + + The index name on which the table should be marked for clustering. + + + + CASCADE @@ -344,6 +355,16 @@ ALTER TABLE table + + CLUSTER + + + This form marks a table for future + operations. + + + + diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index f23c88f6fe..3b6a83e165 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.68 2003/03/20 03:34:55 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.69 2003/03/20 18:52:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -3795,6 +3795,90 @@ CheckTupleType(Form_pg_class tuple_class) } } +/* + * ALTER TABLE CLUSTER ON + * + * The only thing we have to do is to change the indisclustered bits. + */ +void +AlterTableClusterOn(Oid relOid, const char *indexName) +{ + Relation rel, + pg_index; + List *index; + Oid indexOid; + HeapTuple indexTuple; + Form_pg_index indexForm; + + rel = heap_open(relOid, AccessExclusiveLock); + + indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace); + + if (!OidIsValid(indexOid)) + elog(ERROR, "ALTER TABLE: cannot find index \"%s\" for table \"%s\"", + indexName, NameStr(rel->rd_rel->relname)); + + indexTuple = SearchSysCache(INDEXRELID, + ObjectIdGetDatum(indexOid), + 0, 0, 0); + + if (!HeapTupleIsValid(indexTuple)) + elog(ERROR, "Cache lookup failed for index %u", + indexOid); + indexForm = (Form_pg_index) GETSTRUCT(indexTuple); + + /* + * If this is the same index the relation was previously + * clustered on, no need to do anything. + */ + if (indexForm->indisclustered) + { + elog(NOTICE, "ALTER TABLE: table \"%s\" is already being clustered on index \"%s\"", + NameStr(rel->rd_rel->relname), indexName); + heap_close(rel, AccessExclusiveLock); + return; + } + + pg_index = heap_openr(IndexRelationName, RowExclusiveLock); + + /* + * Now check each index in the relation and set the bit where needed. + */ + foreach (index, RelationGetIndexList(rel)) + { + HeapTuple idxtuple; + Form_pg_index idxForm; + + indexOid = lfirsto(index); + idxtuple = SearchSysCacheCopy(INDEXRELID, + ObjectIdGetDatum(indexOid), + 0, 0, 0); + if (!HeapTupleIsValid(idxtuple)) + elog(ERROR, "Cache lookup failed for index %u", indexOid); + idxForm = (Form_pg_index) GETSTRUCT(idxtuple); + /* + * Unset the bit if set. We know it's wrong because we checked + * this earlier. + */ + if (idxForm->indisclustered) + { + idxForm->indisclustered = false; + simple_heap_update(pg_index, &idxtuple->t_self, idxtuple); + CatalogUpdateIndexes(pg_index, idxtuple); + } + else if (idxForm->indexrelid == indexForm->indexrelid) + { + idxForm->indisclustered = true; + simple_heap_update(pg_index, &idxtuple->t_self, idxtuple); + CatalogUpdateIndexes(pg_index, idxtuple); + } + heap_freetuple(idxtuple); + } + ReleaseSysCache(indexTuple); + heap_close(rel, AccessExclusiveLock); + heap_close(pg_index, RowExclusiveLock); +} + /* * ALTER TABLE CREATE TOAST TABLE */ diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1fa5d95798..dc8b06e750 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.407 2003/03/20 07:02:08 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.408 2003/03/20 18:52:47 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1217,6 +1217,15 @@ AlterTableStmt: n->name = $6; $$ = (Node *)n; } + /* ALTER TABLE CLUSTER ON */ + | ALTER TABLE qualified_name CLUSTER ON name + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->subtype = 'L'; + n->relation = $3; + n->name = $6; + $$ = (Node *)n; + } ; alter_column_default: diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 4d533633d6..390adeb9cb 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.196 2003/03/20 07:02:11 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.197 2003/03/20 18:52:48 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -609,6 +609,9 @@ ProcessUtility(Node *parsetree, AlterTableOwner(relid, get_usesysid(stmt->name)); break; + case 'L': /* CLUSTER ON */ + AlterTableClusterOn(relid, stmt->name); + break; case 'o': /* ADD OIDS */ AlterTableAlterOids(relid, interpretInhOption(stmt->relation->inhOpt), diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index 6b533cab2f..a87dbdf5f2 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: tablecmds.h,v 1.11 2003/02/13 05:20:03 momjian Exp $ + * $Id: tablecmds.h,v 1.12 2003/03/20 18:52:48 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -43,6 +43,8 @@ extern void AlterTableDropConstraint(Oid myrelid, bool recurse, const char *constrName, DropBehavior behavior); +extern void AlterTableClusterOn(Oid relOid, const char *indexName); + extern void AlterTableCreateToastTable(Oid relOid, bool silent); extern void AlterTableOwner(Oid relationOid, int32 newOwnerSysId); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index b215f8a98f..49887f450c 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.234 2003/03/20 07:02:11 momjian Exp $ + * $Id: parsenodes.h,v 1.235 2003/03/20 18:52:48 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -704,6 +704,7 @@ typedef struct AlterTableStmt * X = drop constraint * E = create toast table * U = change owner + * L = CLUSTER ON * o = DROP OIDS *------------ */ diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out index d6cc4c969c..3e987f81f5 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -285,6 +285,18 @@ WHERE pg_class.oid=indexrelid clstr_tst_c (1 row) +-- Try changing indisclustered +ALTER TABLE clstr_tst CLUSTER ON clstr_tst_b_c; +SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 +WHERE pg_class.oid=indexrelid + AND indrelid=pg_class_2.oid + AND pg_class_2.relname = 'clstr_tst' + AND indisclustered; + relname +--------------- + clstr_tst_b_c +(1 row) + -- Verify that clustering all tables does in fact cluster the right ones CREATE USER clstr_user; CREATE TABLE clstr_1 (a INT PRIMARY KEY); diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql index 57b80ba98b..06a1441a04 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -87,6 +87,14 @@ WHERE pg_class.oid=indexrelid AND pg_class_2.relname = 'clstr_tst' AND indisclustered; +-- Try changing indisclustered +ALTER TABLE clstr_tst CLUSTER ON clstr_tst_b_c; +SELECT pg_class.relname FROM pg_index, pg_class, pg_class AS pg_class_2 +WHERE pg_class.oid=indexrelid + AND indrelid=pg_class_2.oid + AND pg_class_2.relname = 'clstr_tst' + AND indisclustered; + -- Verify that clustering all tables does in fact cluster the right ones CREATE USER clstr_user; CREATE TABLE clstr_1 (a INT PRIMARY KEY);