1111 *
1212 *
1313 * IDENTIFICATION
14- * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.122 2004/05/06 16:10:57 tgl Exp $
14+ * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.123 2004/05/08 00:34:49 tgl Exp $
1515 *
1616 *-------------------------------------------------------------------------
1717 */
3737#include "utils/relcache.h"
3838
3939
40- /*
41- * We need one of these structs for each index in the relation to be
42- * clustered. It's basically the data needed by index_create() so
43- * we can rebuild the indexes on the new heap.
44- */
45- typedef struct
46- {
47- Oid indexOID ;
48- char * indexName ;
49- IndexInfo * indexInfo ;
50- Oid accessMethodOID ;
51- Oid * classOID ;
52- } IndexAttrs ;
53-
5440/*
5541 * This struct is used to pass around the information on tables to be
5642 * clustered. We need this so we can make a list of them when invoked without
@@ -64,6 +50,7 @@ typedef struct
6450
6551
6652static void cluster_rel (RelToCluster * rv , bool recheck );
53+ static void rebuild_relation (Relation OldHeap , Oid indexOid );
6754static void copy_heap_data (Oid OIDNewHeap , Oid OIDOldHeap , Oid OIDOldIndex );
6855static List * get_tables_to_cluster (MemoryContext cluster_context );
6956
@@ -411,30 +398,99 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid)
411398}
412399
413400/*
414- * rebuild_relation: rebuild an existing relation
401+ * mark_index_clustered: mark the specified index as the one clustered on
415402 *
416- * This is shared code between CLUSTER and TRUNCATE. In the TRUNCATE
417- * case, the new relation is built and left empty. In the CLUSTER case,
418- * it is filled with data read from the old relation in the order specified
419- * by the index.
403+ * With indexOid == InvalidOid, will mark all indexes of rel not-clustered.
404+ */
405+ void
406+ mark_index_clustered (Relation rel , Oid indexOid )
407+ {
408+ HeapTuple indexTuple ;
409+ Form_pg_index indexForm ;
410+ Relation pg_index ;
411+ List * index ;
412+
413+ /*
414+ * If the index is already marked clustered, no need to do anything.
415+ */
416+ if (OidIsValid (indexOid ))
417+ {
418+ indexTuple = SearchSysCache (INDEXRELID ,
419+ ObjectIdGetDatum (indexOid ),
420+ 0 , 0 , 0 );
421+ if (!HeapTupleIsValid (indexTuple ))
422+ elog (ERROR , "cache lookup failed for index %u" , indexOid );
423+ indexForm = (Form_pg_index ) GETSTRUCT (indexTuple );
424+
425+ if (indexForm -> indisclustered )
426+ {
427+ ReleaseSysCache (indexTuple );
428+ return ;
429+ }
430+
431+ ReleaseSysCache (indexTuple );
432+ }
433+
434+ /*
435+ * Check each index of the relation and set/clear the bit as needed.
436+ */
437+ pg_index = heap_openr (IndexRelationName , RowExclusiveLock );
438+
439+ foreach (index , RelationGetIndexList (rel ))
440+ {
441+ Oid thisIndexOid = lfirsto (index );
442+
443+ indexTuple = SearchSysCacheCopy (INDEXRELID ,
444+ ObjectIdGetDatum (thisIndexOid ),
445+ 0 , 0 , 0 );
446+ if (!HeapTupleIsValid (indexTuple ))
447+ elog (ERROR , "cache lookup failed for index %u" , thisIndexOid );
448+ indexForm = (Form_pg_index ) GETSTRUCT (indexTuple );
449+
450+ /*
451+ * Unset the bit if set. We know it's wrong because we checked
452+ * this earlier.
453+ */
454+ if (indexForm -> indisclustered )
455+ {
456+ indexForm -> indisclustered = false;
457+ simple_heap_update (pg_index , & indexTuple -> t_self , indexTuple );
458+ CatalogUpdateIndexes (pg_index , indexTuple );
459+ /* Ensure we see the update in the index's relcache entry */
460+ CacheInvalidateRelcacheByRelid (thisIndexOid );
461+ }
462+ else if (thisIndexOid == indexOid )
463+ {
464+ indexForm -> indisclustered = true;
465+ simple_heap_update (pg_index , & indexTuple -> t_self , indexTuple );
466+ CatalogUpdateIndexes (pg_index , indexTuple );
467+ /* Ensure we see the update in the index's relcache entry */
468+ CacheInvalidateRelcacheByRelid (thisIndexOid );
469+ }
470+ heap_freetuple (indexTuple );
471+ }
472+
473+ heap_close (pg_index , RowExclusiveLock );
474+ }
475+
476+ /*
477+ * rebuild_relation: rebuild an existing relation in index order
420478 *
421479 * OldHeap: table to rebuild --- must be opened and exclusive-locked!
422- * indexOid: index to cluster by, or InvalidOid in TRUNCATE case
480+ * indexOid: index to cluster by
423481 *
424482 * NB: this routine closes OldHeap at the right time; caller should not.
425483 */
426- void
484+ static void
427485rebuild_relation (Relation OldHeap , Oid indexOid )
428486{
429487 Oid tableOid = RelationGetRelid (OldHeap );
430- Oid oldClusterIndex ;
431- List * indexes ;
432488 Oid OIDNewHeap ;
433489 char NewHeapName [NAMEDATALEN ];
434490 ObjectAddress object ;
435491
436- /* Save the information about all indexes on the relation. */
437- indexes = get_indexattr_list (OldHeap , & oldClusterIndex );
492+ /* Mark the correct index as clustered */
493+ mark_index_clustered (OldHeap , indexOid );
438494
439495 /* Close relcache entry, but keep lock until transaction commit */
440496 heap_close (OldHeap , NoLock );
@@ -459,8 +515,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
459515 /*
460516 * Copy the heap data into the new table in the desired order.
461517 */
462- if (OidIsValid (indexOid ))
463- copy_heap_data (OIDNewHeap , tableOid , indexOid );
518+ copy_heap_data (OIDNewHeap , tableOid , indexOid );
464519
465520 /* To make the new heap's data visible (probably not needed?). */
466521 CommandCounterIncrement ();
@@ -484,11 +539,11 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
484539 /* performDeletion does CommandCounterIncrement at end */
485540
486541 /*
487- * Recreate each index on the relation. We do not need
488- * CommandCounterIncrement() because rebuild_indexes does it.
542+ * Rebuild each index on the relation (but not the toast table,
543+ * which is all-new at this point). We do not need
544+ * CommandCounterIncrement() because reindex_relation does it.
489545 */
490- rebuild_indexes (tableOid , indexes ,
491- (OidIsValid (indexOid ) ? indexOid : oldClusterIndex ));
546+ reindex_relation (tableOid , false);
492547}
493548
494549/*
@@ -589,138 +644,6 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
589644 heap_close (NewHeap , NoLock );
590645}
591646
592- /*
593- * Get the necessary info about the indexes of the relation and
594- * return a list of IndexAttrs structures. Also, *OldClusterIndex
595- * is set to the OID of the existing clustered index, or InvalidOid
596- * if there is none.
597- */
598- List *
599- get_indexattr_list (Relation OldHeap , Oid * OldClusterIndex )
600- {
601- List * indexes = NIL ;
602- List * indlist ;
603-
604- * OldClusterIndex = InvalidOid ;
605-
606- /* Ask the relcache to produce a list of the indexes of the old rel */
607- foreach (indlist , RelationGetIndexList (OldHeap ))
608- {
609- Oid indexOID = lfirsto (indlist );
610- Relation oldIndex ;
611- IndexAttrs * attrs ;
612-
613- oldIndex = index_open (indexOID );
614-
615- attrs = (IndexAttrs * ) palloc (sizeof (IndexAttrs ));
616- attrs -> indexOID = indexOID ;
617- attrs -> indexName = pstrdup (NameStr (oldIndex -> rd_rel -> relname ));
618- attrs -> accessMethodOID = oldIndex -> rd_rel -> relam ;
619- attrs -> indexInfo = BuildIndexInfo (oldIndex );
620- attrs -> classOID = (Oid * )
621- palloc (sizeof (Oid ) * attrs -> indexInfo -> ii_NumIndexAttrs );
622- memcpy (attrs -> classOID , oldIndex -> rd_index -> indclass ,
623- sizeof (Oid ) * attrs -> indexInfo -> ii_NumIndexAttrs );
624- if (oldIndex -> rd_index -> indisclustered )
625- * OldClusterIndex = indexOID ;
626-
627- index_close (oldIndex );
628-
629- indexes = lappend (indexes , attrs );
630- }
631-
632- return indexes ;
633- }
634-
635- /*
636- * Create new indexes and swap the filenodes with old indexes. Then drop
637- * the new index (carrying the old index filenode along).
638- *
639- * OIDClusterIndex is the OID of the index to be marked as clustered, or
640- * InvalidOid if none should be marked clustered.
641- */
642- void
643- rebuild_indexes (Oid OIDOldHeap , List * indexes , Oid OIDClusterIndex )
644- {
645- List * elem ;
646-
647- foreach (elem , indexes )
648- {
649- IndexAttrs * attrs = (IndexAttrs * ) lfirst (elem );
650- Oid oldIndexOID = attrs -> indexOID ;
651- Oid newIndexOID ;
652- char newIndexName [NAMEDATALEN ];
653- bool isclustered ;
654- ObjectAddress object ;
655- Form_pg_index index ;
656- HeapTuple tuple ;
657- Relation pg_index ;
658-
659- /* Create the new index under a temporary name */
660- snprintf (newIndexName , sizeof (newIndexName ),
661- "pg_temp_%u" , oldIndexOID );
662-
663- /*
664- * The new index will have primary and constraint status set to
665- * false, but since we will only use its filenode it doesn't
666- * matter: after the filenode swap the index will keep the
667- * constraint status of the old index.
668- */
669- newIndexOID = index_create (OIDOldHeap ,
670- newIndexName ,
671- attrs -> indexInfo ,
672- attrs -> accessMethodOID ,
673- attrs -> classOID ,
674- false,
675- false,
676- allowSystemTableMods ,
677- false);
678- CommandCounterIncrement ();
679-
680- /* Swap the filenodes. */
681- swap_relfilenodes (oldIndexOID , newIndexOID );
682-
683- CommandCounterIncrement ();
684-
685- /*
686- * Make sure that indisclustered is correct: it should be set only
687- * for the index specified by the caller.
688- */
689- isclustered = (oldIndexOID == OIDClusterIndex );
690-
691- pg_index = heap_openr (IndexRelationName , RowExclusiveLock );
692- tuple = SearchSysCacheCopy (INDEXRELID ,
693- ObjectIdGetDatum (oldIndexOID ),
694- 0 , 0 , 0 );
695- if (!HeapTupleIsValid (tuple ))
696- elog (ERROR , "cache lookup failed for index %u" , oldIndexOID );
697- index = (Form_pg_index ) GETSTRUCT (tuple );
698- if (index -> indisclustered != isclustered )
699- {
700- index -> indisclustered = isclustered ;
701- simple_heap_update (pg_index , & tuple -> t_self , tuple );
702- CatalogUpdateIndexes (pg_index , tuple );
703- /* Ensure we see the update in the index's relcache entry */
704- CacheInvalidateRelcacheByRelid (oldIndexOID );
705- }
706- heap_freetuple (tuple );
707- heap_close (pg_index , RowExclusiveLock );
708-
709- /* Destroy new index with old filenode */
710- object .classId = RelOid_pg_class ;
711- object .objectId = newIndexOID ;
712- object .objectSubId = 0 ;
713-
714- /*
715- * The relation is local to our transaction and we know nothing
716- * depends on it, so DROP_RESTRICT should be OK.
717- */
718- performDeletion (& object , DROP_RESTRICT );
719-
720- /* performDeletion does CommandCounterIncrement() at its end */
721- }
722- }
723-
724647/*
725648 * Swap the relfilenodes for two given relations.
726649 *
0 commit comments