From 7dc40a2be053a11544c708f576f2bb2858f14aa9 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Sat, 10 Aug 2002 20:43:46 +0000 Subject: [PATCH] Major improvement in CLUSTER which preserves table characteristics using relfilenode. I sent the CLUSTER patch a few days ago and I think it was missed. I append it again, this time including the regression test files. For the committer, please note that you have to cvs add the files as they don't exist. Maybe add to the parallel and serial schedules also, but I don't know such stuff. Alvaro Herrera () --- doc/src/sgml/ref/cluster.sgml | 36 ++-- src/backend/commands/cluster.c | 306 ++++++++++++++++++++++------ src/test/regress/output/cluster.out | 232 +++++++++++++++++++++ src/test/regress/sql/cluster.sql | 72 +++++++ 4 files changed, 561 insertions(+), 85 deletions(-) create mode 100644 src/test/regress/output/cluster.out create mode 100644 src/test/regress/sql/cluster.sql 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 @@ @@ -75,19 +75,6 @@ CLUSTER -ERROR: relation <tablerelation_number> inherits "table" - - - - - This is not documented anywhere. It seems not to be possible to - cluster a table that is inherited. - - - - - - ERROR: Relation table does not exist! @@ -138,13 +125,6 @@ ERROR: Relation table does not exis Notes - - 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. - - 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 columnlist INTO TABLE + + + 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. + + + + 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. + + 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;