diff --git a/doc/src/sgml/ref/cluster.sgml b/doc/src/sgml/ref/cluster.sgml index c8499ca058..4600e9e7b9 100644 --- a/doc/src/sgml/ref/cluster.sgml +++ b/doc/src/sgml/ref/cluster.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/cluster.sgml,v 1.16 2002/04/23 02:07:15 tgl Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/cluster.sgml,v 1.17 2002/08/10 20:43:46 momjian Exp $ PostgreSQL documentation --> @@ -75,19 +75,6 @@ CLUSTER </varlistentry> <varlistentry> <term><computeroutput> -ERROR: relation <<replaceable class="PARAMETER">tablerelation_number</replaceable>> inherits "<replaceable class="PARAMETER">table</replaceable>" - </computeroutput></term> - <listitem> - <para> - <comment> - This is not documented anywhere. It seems not to be possible to - cluster a table that is inherited. - </comment> - </para> - </listitem> - </varlistentry> - <varlistentry> - <term><computeroutput> ERROR: Relation <replaceable class="PARAMETER">table</replaceable> does not exist! </computeroutput></term> <listitem> @@ -138,13 +125,6 @@ ERROR: Relation <replaceable class="PARAMETER">table</replaceable> does not exis Notes </title> - <para> - The table is actually copied to a temporary table in index - order, then renamed back to the original name. For this - reason, all grant permissions and other indexes are lost - when clustering is performed. - </para> - <para> In cases where you are accessing single rows randomly within a table, the actual order of the data in the heap @@ -194,6 +174,20 @@ SELECT <replaceable class="parameter">columnlist</replaceable> INTO TABLE <repla fast because most of the heap data has already been ordered, and the existing index is used. </para> + + <para> + During the cluster operation, a temporal table is created that contains + the table in the index order. Due to this, you need to have free space + on disk at least the size of the table itself, or the biggest index if + you have one that is larger than the table. + </para> + + <para> + As opposed to previous releases, CLUSTER does not lose GRANT, + inheritance or foreign key information, and preserves indexes + other than the one being used for the CLUSTER. + </para> + </refsect2> </refsect1> diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 837390744f..ff3ac9f8e5 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.83 2002/07/12 18:43:15 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.84 2002/08/10 20:43:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -27,45 +27,74 @@ #include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/index.h" +#include "catalog/indexing.h" +#include "catalog/catname.h" #include "catalog/pg_index.h" #include "catalog/pg_proc.h" #include "commands/cluster.h" #include "commands/tablecmds.h" #include "miscadmin.h" #include "utils/builtins.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/syscache.h" +#include "utils/relcache.h" +/* + * We need one of these structs for each index in the relation to be + * clustered. It's basically the data needed by index_create() so + * we can recreate the indexes after destroying the old heap. + */ +typedef struct +{ + char *indexName; + IndexInfo *indexInfo; + Oid accessMethodOID; + Oid *classOID; + Oid indexOID; + bool isPrimary; +} IndexAttrs; static Oid copy_heap(Oid OIDOldHeap, const char *NewName); -static Oid copy_index(Oid OIDOldIndex, Oid OIDNewHeap, - const char *NewIndexName); static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex); +static List *get_indexattr_list (Oid OIDOldHeap); +static void recreate_indexattr(Oid OIDOldHeap, List *indexes); +static void swap_relfilenodes(Oid r1, Oid r2); + +Relation RelationIdGetRelation(Oid relationId); /* * cluster * - * STILL TO DO: - * Create a list of all the other indexes on this relation. Because - * the cluster will wreck all the tids, I'll need to destroy bogus - * indexes. The user will have to re-create them. Not nice, but - * I'm not a nice guy. The alternative is to try some kind of post - * destroy re-build. This may be possible. I'll check out what the - * index create functiond want in the way of paramaters. On the other - * hand, re-creating n indexes may blow out the space. + * This clusters the table by creating a new, clustered table and + * swapping the relfilenodes of the new table and the old table, so + * the OID of the original table is preserved. Thus we do not lose + * GRANT, inheritance nor references to this table (this was a bug + * in releases thru 7.3) + * + * Also create new indexes and swap the filenodes with the old indexes + * the same way we do for the relation. + * + * TODO: + * maybe we can get away with AccessShareLock for the table. + * Concurrency would be much improved. Only acquire + * AccessExclusiveLock right before swapping the filenodes. + * This would allow users to CLUSTER on a regular basis, + * practically eliminating the need for auto-clustered indexes. + * + * Preserve constraint bit for the indexes. */ void cluster(RangeVar *oldrelation, char *oldindexname) { Oid OIDOldHeap, OIDOldIndex, - OIDNewHeap, - OIDNewIndex; + OIDNewHeap; Relation OldHeap, OldIndex; char NewHeapName[NAMEDATALEN]; - char NewIndexName[NAMEDATALEN]; ObjectAddress object; + List *indexes; /* * We grab exclusive access to the target rel and index for the @@ -96,6 +125,9 @@ cluster(RangeVar *oldrelation, char *oldindexname) heap_close(OldHeap, NoLock); index_close(OldIndex); + /* Save the information of all indexes on the relation. */ + indexes = get_indexattr_list(OIDOldHeap); + /* * Create the new heap with a temporary name. */ @@ -113,29 +145,27 @@ cluster(RangeVar *oldrelation, char *oldindexname) /* To make the new heap's data visible. */ CommandCounterIncrement(); - /* Create new index over the tuples of the new heap. */ - snprintf(NewIndexName, NAMEDATALEN, "temp_%u", OIDOldIndex); - - OIDNewIndex = copy_index(OIDOldIndex, OIDNewHeap, NewIndexName); + /* Swap the relfilenodes of the old and new heaps. */ + swap_relfilenodes(OIDNewHeap, OIDOldHeap); CommandCounterIncrement(); - /* Destroy old heap (along with its index) and rename new. */ + /* Destroy new heap with old filenode */ object.classId = RelOid_pg_class; - object.objectId = OIDOldHeap; + object.objectId = OIDNewHeap; object.objectSubId = 0; - /* XXX better to use DROP_CASCADE here? */ + /* The relation is local to our transaction and we know nothin + * depends on it, so DROP_RESTRICT should be OK. + */ performDeletion(&object, DROP_RESTRICT); /* performDeletion does CommandCounterIncrement at end */ - renamerel(OIDNewHeap, oldrelation->relname); - - /* This one might be unnecessary, but let's be safe. */ - CommandCounterIncrement(); - - renamerel(OIDNewIndex, oldindexname); + /* Recreate the indexes on the relation. We do not need + * CommandCounterIncrement() because recreate_indexattr does it. + */ + recreate_indexattr(OIDOldHeap, indexes); } static Oid @@ -181,43 +211,6 @@ copy_heap(Oid OIDOldHeap, const char *NewName) return OIDNewHeap; } -static Oid -copy_index(Oid OIDOldIndex, Oid OIDNewHeap, const char *NewIndexName) -{ - Oid OIDNewIndex; - Relation OldIndex, - NewHeap; - IndexInfo *indexInfo; - - NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock); - OldIndex = index_open(OIDOldIndex); - - /* - * Create a new index like the old one. To do this I get the info - * from pg_index, and add a new index with a temporary name (that will - * be changed later). - */ - indexInfo = BuildIndexInfo(OldIndex->rd_index); - - OIDNewIndex = index_create(OIDNewHeap, - NewIndexName, - indexInfo, - OldIndex->rd_rel->relam, - OldIndex->rd_index->indclass, - OldIndex->rd_index->indisprimary, - false, /* XXX losing constraint status */ - allowSystemTableMods); - - setRelhasindex(OIDNewHeap, true, - OldIndex->rd_index->indisprimary, InvalidOid); - - index_close(OldIndex); - heap_close(NewHeap, NoLock); - - return OIDNewIndex; -} - - static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) { @@ -261,3 +254,188 @@ rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) heap_close(LocalOldHeap, NoLock); heap_close(LocalNewHeap, NoLock); } + +/* Get the necessary info about the indexes in the relation and + * return a List of IndexAttrs. + */ +List * +get_indexattr_list (Oid OIDOldHeap) +{ + ScanKeyData entry; + HeapScanDesc scan; + Relation indexRelation; + HeapTuple indexTuple; + List *indexes = NIL; + IndexAttrs *attrs; + HeapTuple tuple; + Form_pg_index index; + + /* Grab the index tuples by looking into RelationRelationName + * by the OID of the old heap. + */ + indexRelation = heap_openr(IndexRelationName, AccessShareLock); + ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid, + F_OIDEQ, ObjectIdGetDatum(OIDOldHeap)); + scan = heap_beginscan(indexRelation, SnapshotNow, 1, &entry); + while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + index = (Form_pg_index) GETSTRUCT(indexTuple); + + attrs = (IndexAttrs *) palloc(sizeof(IndexAttrs)); + attrs->indexInfo = BuildIndexInfo(index); + attrs->isPrimary = index->indisprimary; + attrs->indexOID = index->indexrelid; + + /* The opclasses are copied verbatim from the original indexes. + */ + attrs->classOID = (Oid *)palloc(sizeof(Oid) * + attrs->indexInfo->ii_NumIndexAttrs); + memcpy(attrs->classOID, index->indclass, + sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs); + + /* Name and access method of each index come from + * RelationRelationName. + */ + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(attrs->indexOID), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "CLUSTER: cannot find index %u", attrs->indexOID); + attrs->indexName = pstrdup(NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname)); + attrs->accessMethodOID = ((Form_pg_class) GETSTRUCT(tuple))->relam; + ReleaseSysCache(tuple); + + /* Cons the gathered data into the list. We do not care about + * ordering, and this is more efficient than append. + */ + indexes=lcons((void *)attrs, indexes); + } + heap_endscan(scan); + heap_close(indexRelation, AccessShareLock); + return indexes; +} + +/* Create new indexes and swap the filenodes with old indexes. Then drop + * the new index (carrying the old heap along). + */ +void +recreate_indexattr(Oid OIDOldHeap, List *indexes) +{ + IndexAttrs *attrs; + List *elem; + Oid newIndexOID; + char newIndexName[NAMEDATALEN]; + ObjectAddress object; + + foreach (elem, indexes) + { + attrs=(IndexAttrs *) lfirst(elem); + + /* Create the new index under a temporary name */ + snprintf(newIndexName, NAMEDATALEN, "temp_%u", attrs->indexOID); + + /* The new index will have constraint status set to false, + * but since we will only use its filenode it doesn't matter: + * after the filenode swap the index will keep the constraint + * status of the old index. + */ + newIndexOID = index_create(OIDOldHeap, newIndexName, + attrs->indexInfo, attrs->accessMethodOID, + attrs->classOID, attrs->isPrimary, + false, allowSystemTableMods); + CommandCounterIncrement(); + + /* Swap the filenodes. */ + swap_relfilenodes(newIndexOID, attrs->indexOID); + setRelhasindex(OIDOldHeap, true, attrs->isPrimary, InvalidOid); + + /* I'm not sure this one is needed, but let's be safe. */ + CommandCounterIncrement(); + + /* Destroy new index with old filenode */ + object.classId = RelOid_pg_class; + object.objectId = newIndexOID; + object.objectSubId = 0; + + /* The 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 its end */ + + pfree(attrs->classOID); + pfree(attrs); + } + freeList(indexes); +} + +/* Swap the relfilenodes for two given relations. + */ +void +swap_relfilenodes(Oid r1, Oid r2) +{ + /* I can probably keep RelationRelationName open in the main + * function and pass the Relation around so I don't have to open + * it every time. + */ + Relation relRelation, + irels[Num_pg_class_indices], + rel; + HeapTuple reltup[2]; + Oid tempRFNode; + int i; + + /* We need both RelationRelationName tuples. */ + relRelation = heap_openr(RelationRelationName, RowExclusiveLock); + + reltup[0] = SearchSysCacheCopy(RELOID, + ObjectIdGetDatum(r1), + 0, 0, 0); + if (!HeapTupleIsValid(reltup[0])) + elog(ERROR, "CLUSTER: Cannot find tuple for relation %u", r1); + reltup[1] = SearchSysCacheCopy(RELOID, + ObjectIdGetDatum(r2), + 0, 0, 0); + if (!HeapTupleIsValid(reltup[1])) + elog(ERROR, "CLUSTER: Cannot find tuple for relation %u", r2); + + /* The buffer manager gets confused if we swap relfilenodes for + * relations that are not both local or non-local to this transaction. + * Flush the buffers on both relations so the buffer manager can + * forget about'em. + */ + + rel = RelationIdGetRelation(r1); + i = FlushRelationBuffers(rel, 0); + if (i < 0) + elog(ERROR, "CLUSTER: FlushRelationBuffers returned %d", i); + RelationClose(rel); + rel = RelationIdGetRelation(r1); + i = FlushRelationBuffers(rel, 0); + if (i < 0) + elog(ERROR, "CLUSTER: FlushRelationBuffers returned %d", i); + RelationClose(rel); + + /* Actually swap the filenodes */ + + tempRFNode = ((Form_pg_class) GETSTRUCT(reltup[0]))->relfilenode; + ((Form_pg_class) GETSTRUCT(reltup[0]))->relfilenode = + ((Form_pg_class) GETSTRUCT(reltup[1]))->relfilenode; + ((Form_pg_class) GETSTRUCT(reltup[1]))->relfilenode = tempRFNode; + + /* Update the RelationRelationName tuples */ + simple_heap_update(relRelation, &reltup[1]->t_self, reltup[1]); + simple_heap_update(relRelation, &reltup[0]->t_self, reltup[0]); + + /* To keep system catalogs current. */ + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, irels); + CatalogIndexInsert(irels, Num_pg_class_indices, relRelation, reltup[1]); + CatalogIndexInsert(irels, Num_pg_class_indices, relRelation, reltup[0]); + CatalogCloseIndices(Num_pg_class_indices, irels); + CommandCounterIncrement(); + + heap_close(relRelation, NoLock); + heap_freetuple(reltup[0]); + heap_freetuple(reltup[1]); +} diff --git a/src/test/regress/output/cluster.out b/src/test/regress/output/cluster.out new file mode 100644 index 0000000000..67f1f8a8b7 --- /dev/null +++ b/src/test/regress/output/cluster.out @@ -0,0 +1,232 @@ +-- +-- CLUSTER +-- +CREATE TABLE clstr_tst_s (rf_a SERIAL PRIMARY KEY, + b INT); +NOTICE: CREATE TABLE will create implicit sequence 'clstr_tst_s_rf_a_seq' for SERIAL column 'clstr_tst_s.rf_a' +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'clstr_tst_s_pkey' for table 'clstr_tst_s' +CREATE TABLE clstr_tst (a SERIAL PRIMARY KEY, + b INT, + c TEXT, + CONSTRAINT clstr_tst_con FOREIGN KEY (b) REFERENCES clstr_tst_s); +NOTICE: CREATE TABLE will create implicit sequence 'clstr_tst_a_seq' for SERIAL column 'clstr_tst.a' +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'clstr_tst_pkey' for table 'clstr_tst' +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +CREATE INDEX clstr_tst_b ON clstr_tst (b); +CREATE INDEX clstr_tst_c ON clstr_tst (c); +CREATE INDEX clstr_tst_c_b ON clstr_tst (c,b); +CREATE INDEX clstr_tst_b_c ON clstr_tst (b,c); +INSERT INTO clstr_tst_s (b) VALUES (0); +INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; +INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; +INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; +INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; +INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; +CREATE TABLE clstr_tst_inh () INHERITS (clstr_tst); +INSERT INTO clstr_tst (b, c) VALUES (11, 'once'); +INSERT INTO clstr_tst (b, c) VALUES (10, 'diez'); +INSERT INTO clstr_tst (b, c) VALUES (31, 'treinta y uno'); +INSERT INTO clstr_tst (b, c) VALUES (22, 'veintidos'); +INSERT INTO clstr_tst (b, c) VALUES (3, 'tres'); +INSERT INTO clstr_tst (b, c) VALUES (20, 'veinte'); +INSERT INTO clstr_tst (b, c) VALUES (23, 'veintitres'); +INSERT INTO clstr_tst (b, c) VALUES (21, 'veintiuno'); +INSERT INTO clstr_tst (b, c) VALUES (4, 'cuatro'); +INSERT INTO clstr_tst (b, c) VALUES (14, 'catorce'); +INSERT INTO clstr_tst (b, c) VALUES (2, 'dos'); +INSERT INTO clstr_tst (b, c) VALUES (18, 'dieciocho'); +INSERT INTO clstr_tst (b, c) VALUES (27, 'veintisiete'); +INSERT INTO clstr_tst (b, c) VALUES (25, 'veinticinco'); +INSERT INTO clstr_tst (b, c) VALUES (13, 'trece'); +INSERT INTO clstr_tst (b, c) VALUES (28, 'veintiocho'); +INSERT INTO clstr_tst (b, c) VALUES (32, 'treinta y dos'); +INSERT INTO clstr_tst (b, c) VALUES (5, 'cinco'); +INSERT INTO clstr_tst (b, c) VALUES (29, 'veintinueve'); +INSERT INTO clstr_tst (b, c) VALUES (1, 'uno'); +INSERT INTO clstr_tst (b, c) VALUES (24, 'veinticuatro'); +INSERT INTO clstr_tst (b, c) VALUES (30, 'treinta'); +INSERT INTO clstr_tst (b, c) VALUES (12, 'doce'); +INSERT INTO clstr_tst (b, c) VALUES (17, 'diecisiete'); +INSERT INTO clstr_tst (b, c) VALUES (9, 'nueve'); +INSERT INTO clstr_tst (b, c) VALUES (19, 'diecinueve'); +INSERT INTO clstr_tst (b, c) VALUES (26, 'veintiseis'); +INSERT INTO clstr_tst (b, c) VALUES (15, 'quince'); +INSERT INTO clstr_tst (b, c) VALUES (7, 'siete'); +INSERT INTO clstr_tst (b, c) VALUES (16, 'dieciseis'); +INSERT INTO clstr_tst (b, c) VALUES (8, 'ocho'); +INSERT INTO clstr_tst (b, c) VALUES (6, 'seis'); +CLUSTER clstr_tst_c ON clstr_tst; +SELECT * from clstr_tst; + a | b | c +----+----+--------------- + 10 | 14 | catorce + 18 | 5 | cinco + 9 | 4 | cuatro + 26 | 19 | diecinueve + 12 | 18 | dieciocho + 30 | 16 | dieciseis + 24 | 17 | diecisiete + 2 | 10 | diez + 23 | 12 | doce + 11 | 2 | dos + 25 | 9 | nueve + 31 | 8 | ocho + 1 | 11 | once + 28 | 15 | quince + 32 | 6 | seis + 29 | 7 | siete + 15 | 13 | trece + 22 | 30 | treinta + 17 | 32 | treinta y dos + 3 | 31 | treinta y uno + 5 | 3 | tres + 20 | 1 | uno + 6 | 20 | veinte + 14 | 25 | veinticinco + 21 | 24 | veinticuatro + 4 | 22 | veintidos + 19 | 29 | veintinueve + 16 | 28 | veintiocho + 27 | 26 | veintiseis + 13 | 27 | veintisiete + 7 | 23 | veintitres + 8 | 21 | veintiuno +(32 rows) + +SELECT * from clstr_tst ORDER BY a; + a | b | c +----+----+--------------- + 1 | 11 | once + 2 | 10 | diez + 3 | 31 | treinta y uno + 4 | 22 | veintidos + 5 | 3 | tres + 6 | 20 | veinte + 7 | 23 | veintitres + 8 | 21 | veintiuno + 9 | 4 | cuatro + 10 | 14 | catorce + 11 | 2 | dos + 12 | 18 | dieciocho + 13 | 27 | veintisiete + 14 | 25 | veinticinco + 15 | 13 | trece + 16 | 28 | veintiocho + 17 | 32 | treinta y dos + 18 | 5 | cinco + 19 | 29 | veintinueve + 20 | 1 | uno + 21 | 24 | veinticuatro + 22 | 30 | treinta + 23 | 12 | doce + 24 | 17 | diecisiete + 25 | 9 | nueve + 26 | 19 | diecinueve + 27 | 26 | veintiseis + 28 | 15 | quince + 29 | 7 | siete + 30 | 16 | dieciseis + 31 | 8 | ocho + 32 | 6 | seis +(32 rows) + +SELECT * from clstr_tst ORDER BY b; + a | b | c +----+----+--------------- + 20 | 1 | uno + 11 | 2 | dos + 5 | 3 | tres + 9 | 4 | cuatro + 18 | 5 | cinco + 32 | 6 | seis + 29 | 7 | siete + 31 | 8 | ocho + 25 | 9 | nueve + 2 | 10 | diez + 1 | 11 | once + 23 | 12 | doce + 15 | 13 | trece + 10 | 14 | catorce + 28 | 15 | quince + 30 | 16 | dieciseis + 24 | 17 | diecisiete + 12 | 18 | dieciocho + 26 | 19 | diecinueve + 6 | 20 | veinte + 8 | 21 | veintiuno + 4 | 22 | veintidos + 7 | 23 | veintitres + 21 | 24 | veinticuatro + 14 | 25 | veinticinco + 27 | 26 | veintiseis + 13 | 27 | veintisiete + 16 | 28 | veintiocho + 19 | 29 | veintinueve + 22 | 30 | treinta + 3 | 31 | treinta y uno + 17 | 32 | treinta y dos +(32 rows) + +SELECT * from clstr_tst ORDER BY c; + a | b | c +----+----+--------------- + 10 | 14 | catorce + 18 | 5 | cinco + 9 | 4 | cuatro + 26 | 19 | diecinueve + 12 | 18 | dieciocho + 30 | 16 | dieciseis + 24 | 17 | diecisiete + 2 | 10 | diez + 23 | 12 | doce + 11 | 2 | dos + 25 | 9 | nueve + 31 | 8 | ocho + 1 | 11 | once + 28 | 15 | quince + 32 | 6 | seis + 29 | 7 | siete + 15 | 13 | trece + 22 | 30 | treinta + 17 | 32 | treinta y dos + 3 | 31 | treinta y uno + 5 | 3 | tres + 20 | 1 | uno + 6 | 20 | veinte + 14 | 25 | veinticinco + 21 | 24 | veinticuatro + 4 | 22 | veintidos + 19 | 29 | veintinueve + 16 | 28 | veintiocho + 27 | 26 | veintiseis + 13 | 27 | veintisiete + 7 | 23 | veintitres + 8 | 21 | veintiuno +(32 rows) + +SELECT conname FROM pg_constraint WHERE conrelid=(SELECT oid FROM pg_class + WHERE relname='clstr_tst'); + conname +---------------- + clstr_tst_pkey + clstr_tst_con +(2 rows) + +SELECT relname FROM pg_class WHERE relname LIKE 'clstr_tst%' ORDER BY relname; + relname +---------------------- + clstr_tst + clstr_tst_a_seq + clstr_tst_b + clstr_tst_b_c + clstr_tst_c + clstr_tst_c_b + clstr_tst_inh + clstr_tst_pkey + clstr_tst_s + clstr_tst_s_pkey + clstr_tst_s_rf_a_seq +(11 rows) + +DROP TABLE clstr_tst_inh; +DROP TABLE clstr_tst; diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql new file mode 100644 index 0000000000..6879c618f2 --- /dev/null +++ b/src/test/regress/sql/cluster.sql @@ -0,0 +1,72 @@ +-- +-- CLUSTER +-- + +CREATE TABLE clstr_tst_s (rf_a SERIAL PRIMARY KEY, + b INT); + +CREATE TABLE clstr_tst (a SERIAL PRIMARY KEY, + b INT, + c TEXT, + CONSTRAINT clstr_tst_con FOREIGN KEY (b) REFERENCES clstr_tst_s); + +CREATE INDEX clstr_tst_b ON clstr_tst (b); +CREATE INDEX clstr_tst_c ON clstr_tst (c); +CREATE INDEX clstr_tst_c_b ON clstr_tst (c,b); +CREATE INDEX clstr_tst_b_c ON clstr_tst (b,c); + +INSERT INTO clstr_tst_s (b) VALUES (0); +INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; +INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; +INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; +INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; +INSERT INTO clstr_tst_s (b) SELECT b FROM clstr_tst_s; + +CREATE TABLE clstr_tst_inh () INHERITS (clstr_tst); + +INSERT INTO clstr_tst (b, c) VALUES (11, 'once'); +INSERT INTO clstr_tst (b, c) VALUES (10, 'diez'); +INSERT INTO clstr_tst (b, c) VALUES (31, 'treinta y uno'); +INSERT INTO clstr_tst (b, c) VALUES (22, 'veintidos'); +INSERT INTO clstr_tst (b, c) VALUES (3, 'tres'); +INSERT INTO clstr_tst (b, c) VALUES (20, 'veinte'); +INSERT INTO clstr_tst (b, c) VALUES (23, 'veintitres'); +INSERT INTO clstr_tst (b, c) VALUES (21, 'veintiuno'); +INSERT INTO clstr_tst (b, c) VALUES (4, 'cuatro'); +INSERT INTO clstr_tst (b, c) VALUES (14, 'catorce'); +INSERT INTO clstr_tst (b, c) VALUES (2, 'dos'); +INSERT INTO clstr_tst (b, c) VALUES (18, 'dieciocho'); +INSERT INTO clstr_tst (b, c) VALUES (27, 'veintisiete'); +INSERT INTO clstr_tst (b, c) VALUES (25, 'veinticinco'); +INSERT INTO clstr_tst (b, c) VALUES (13, 'trece'); +INSERT INTO clstr_tst (b, c) VALUES (28, 'veintiocho'); +INSERT INTO clstr_tst (b, c) VALUES (32, 'treinta y dos'); +INSERT INTO clstr_tst (b, c) VALUES (5, 'cinco'); +INSERT INTO clstr_tst (b, c) VALUES (29, 'veintinueve'); +INSERT INTO clstr_tst (b, c) VALUES (1, 'uno'); +INSERT INTO clstr_tst (b, c) VALUES (24, 'veinticuatro'); +INSERT INTO clstr_tst (b, c) VALUES (30, 'treinta'); +INSERT INTO clstr_tst (b, c) VALUES (12, 'doce'); +INSERT INTO clstr_tst (b, c) VALUES (17, 'diecisiete'); +INSERT INTO clstr_tst (b, c) VALUES (9, 'nueve'); +INSERT INTO clstr_tst (b, c) VALUES (19, 'diecinueve'); +INSERT INTO clstr_tst (b, c) VALUES (26, 'veintiseis'); +INSERT INTO clstr_tst (b, c) VALUES (15, 'quince'); +INSERT INTO clstr_tst (b, c) VALUES (7, 'siete'); +INSERT INTO clstr_tst (b, c) VALUES (16, 'dieciseis'); +INSERT INTO clstr_tst (b, c) VALUES (8, 'ocho'); +INSERT INTO clstr_tst (b, c) VALUES (6, 'seis'); + +CLUSTER clstr_tst_c ON clstr_tst; + +SELECT * from clstr_tst; +SELECT * from clstr_tst ORDER BY a; +SELECT * from clstr_tst ORDER BY b; +SELECT * from clstr_tst ORDER BY c; + +SELECT conname FROM pg_constraint WHERE conrelid=(SELECT oid FROM pg_class + WHERE relname='clstr_tst'); +SELECT relname FROM pg_class WHERE relname LIKE 'clstr_tst%' ORDER BY relname; + +DROP TABLE clstr_tst_inh; +DROP TABLE clstr_tst;