1111 *
1212 *
1313 * IDENTIFICATION
14- * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.121 2004/05/05 04:48:45 tgl Exp $
14+ * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.122 2004/05/06 16:10:57 tgl Exp $
1515 *
1616 *-------------------------------------------------------------------------
1717 */
3131#include "miscadmin.h"
3232#include "utils/acl.h"
3333#include "utils/fmgroids.h"
34+ #include "utils/inval.h"
3435#include "utils/lsyscache.h"
3536#include "utils/syscache.h"
3637#include "utils/relcache.h"
@@ -48,7 +49,6 @@ typedef struct
4849IndexInfo * indexInfo ;
4950Oid accessMethodOID ;
5051Oid * classOID ;
51- bool isclustered ;
5252}IndexAttrs ;
5353
5454/*
@@ -246,8 +246,7 @@ cluster(ClusterStmt *stmt)
246246static void
247247cluster_rel (RelToCluster * rvtc ,bool recheck )
248248{
249- Relation OldHeap ,
250- OldIndex ;
249+ Relation OldHeap ;
251250
252251/* Check for user-requested abort. */
253252CHECK_FOR_INTERRUPTS ();
@@ -300,18 +299,41 @@ cluster_rel(RelToCluster *rvtc, bool recheck)
300299/*
301300 * We grab exclusive access to the target rel and index for the
302301 * duration of the transaction. (This is redundant for the single-
303- * transaction case, since cluster() already did it.)
302+ * transaction case, since cluster() already did it.) The index
303+ * lock is taken inside check_index_is_clusterable.
304304 */
305305OldHeap = heap_open (rvtc -> tableOid ,AccessExclusiveLock );
306306
307- OldIndex = index_open (rvtc -> indexOid );
307+ /* Check index is valid to cluster on */
308+ check_index_is_clusterable (OldHeap ,rvtc -> indexOid );
309+
310+ /* rebuild_relation does all the dirty work */
311+ rebuild_relation (OldHeap ,rvtc -> indexOid );
312+
313+ /* NB: rebuild_relation does heap_close() on OldHeap */
314+ }
315+
316+ /*
317+ * Verify that the specified index is a legitimate index to cluster on
318+ *
319+ * Side effect: obtains exclusive lock on the index. The caller should
320+ * already have exclusive lock on the table, so the index lock is likely
321+ * redundant, but it seems best to grab it anyway to ensure the index
322+ * definition can't change under us.
323+ */
324+ void
325+ check_index_is_clusterable (Relation OldHeap ,Oid indexOid )
326+ {
327+ Relation OldIndex ;
328+
329+ OldIndex = index_open (indexOid );
308330LockRelation (OldIndex ,AccessExclusiveLock );
309331
310332/*
311333 * Check that index is in fact an index on the given relation
312334 */
313335if (OldIndex -> rd_index == NULL ||
314- OldIndex -> rd_index -> indrelid != rvtc -> tableOid )
336+ OldIndex -> rd_index -> indrelid != RelationGetRelid ( OldHeap ) )
315337ereport (ERROR ,
316338(errcode (ERRCODE_WRONG_OBJECT_TYPE ),
317339errmsg ("\"%s\" is not an index for table \"%s\"" ,
@@ -386,11 +408,6 @@ cluster_rel(RelToCluster *rvtc, bool recheck)
386408
387409/* Drop relcache refcnt on OldIndex, but keep lock */
388410index_close (OldIndex );
389-
390- /* rebuild_relation does all the dirty work */
391- rebuild_relation (OldHeap ,rvtc -> indexOid );
392-
393- /* NB: rebuild_relation does heap_close() on OldHeap */
394411}
395412
396413/*
@@ -410,13 +427,14 @@ void
410427rebuild_relation (Relation OldHeap ,Oid indexOid )
411428{
412429Oid tableOid = RelationGetRelid (OldHeap );
430+ Oid oldClusterIndex ;
413431List * indexes ;
414432Oid OIDNewHeap ;
415433char NewHeapName [NAMEDATALEN ];
416434ObjectAddress object ;
417435
418436/* Save the information about all indexes on the relation. */
419- indexes = get_indexattr_list (OldHeap ,indexOid );
437+ indexes = get_indexattr_list (OldHeap ,& oldClusterIndex );
420438
421439/* Close relcache entry, but keep lock until transaction commit */
422440heap_close (OldHeap ,NoLock );
@@ -469,7 +487,8 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
469487 * Recreate each index on the relation. We do not need
470488 * CommandCounterIncrement() because rebuild_indexes does it.
471489 */
472- rebuild_indexes (tableOid ,indexes );
490+ rebuild_indexes (tableOid ,indexes ,
491+ (OidIsValid (indexOid ) ?indexOid :oldClusterIndex ));
473492}
474493
475494/*
@@ -572,14 +591,18 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
572591
573592/*
574593 * Get the necessary info about the indexes of the relation and
575- * return a list of IndexAttrs structures.
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.
576597 */
577598List *
578- get_indexattr_list (Relation OldHeap ,Oid OldIndex )
599+ get_indexattr_list (Relation OldHeap ,Oid * OldClusterIndex )
579600{
580601List * indexes = NIL ;
581602List * indlist ;
582603
604+ * OldClusterIndex = InvalidOid ;
605+
583606/* Ask the relcache to produce a list of the indexes of the old rel */
584607foreach (indlist ,RelationGetIndexList (OldHeap ))
585608{
@@ -598,16 +621,12 @@ get_indexattr_list(Relation OldHeap, Oid OldIndex)
598621palloc (sizeof (Oid )* attrs -> indexInfo -> ii_NumIndexAttrs );
599622memcpy (attrs -> classOID ,oldIndex -> rd_index -> indclass ,
600623sizeof (Oid )* attrs -> indexInfo -> ii_NumIndexAttrs );
601- /* We adjust the isclustered attribute to correct new state */
602- attrs -> isclustered = ( indexOID == OldIndex ) ;
624+ if ( oldIndex -> rd_index -> indisclustered )
625+ * OldClusterIndex = indexOID ;
603626
604627index_close (oldIndex );
605628
606- /*
607- * Cons the gathered data into the list. We do not care about
608- * ordering, and this is more efficient than append.
609- */
610- indexes = lcons (attrs ,indexes );
629+ indexes = lappend (indexes ,attrs );
611630}
612631
613632return indexes ;
@@ -616,25 +635,30 @@ get_indexattr_list(Relation OldHeap, Oid OldIndex)
616635/*
617636 * Create new indexes and swap the filenodes with old indexes.Then drop
618637 * 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.
619641 */
620642void
621- rebuild_indexes (Oid OIDOldHeap ,List * indexes )
643+ rebuild_indexes (Oid OIDOldHeap ,List * indexes , Oid OIDClusterIndex )
622644{
623645List * elem ;
624646
625647foreach (elem ,indexes )
626648{
627649IndexAttrs * attrs = (IndexAttrs * )lfirst (elem );
650+ Oid oldIndexOID = attrs -> indexOID ;
628651Oid newIndexOID ;
629652char newIndexName [NAMEDATALEN ];
653+ bool isclustered ;
630654ObjectAddress object ;
631655Form_pg_index index ;
632656HeapTuple tuple ;
633657Relation pg_index ;
634658
635659/* Create the new index under a temporary name */
636660snprintf (newIndexName ,sizeof (newIndexName ),
637- "pg_temp_%u" ,attrs -> indexOID );
661+ "pg_temp_%u" ,oldIndexOID );
638662
639663/*
640664 * The new index will have primary and constraint status set to
@@ -654,26 +678,30 @@ rebuild_indexes(Oid OIDOldHeap, List *indexes)
654678CommandCounterIncrement ();
655679
656680/* Swap the filenodes. */
657- swap_relfilenodes (attrs -> indexOID ,newIndexOID );
681+ swap_relfilenodes (oldIndexOID ,newIndexOID );
658682
659683CommandCounterIncrement ();
660684
661685/*
662686 * Make sure that indisclustered is correct: it should be set only
663- * for the indexwe just clustered on .
687+ * for the indexspecified by the caller .
664688 */
689+ isclustered = (oldIndexOID == OIDClusterIndex );
690+
665691pg_index = heap_openr (IndexRelationName ,RowExclusiveLock );
666692tuple = SearchSysCacheCopy (INDEXRELID ,
667- ObjectIdGetDatum (attrs -> indexOID ),
693+ ObjectIdGetDatum (oldIndexOID ),
6686940 ,0 ,0 );
669695if (!HeapTupleIsValid (tuple ))
670- elog (ERROR ,"cache lookup failed for index %u" ,attrs -> indexOID );
696+ elog (ERROR ,"cache lookup failed for index %u" ,oldIndexOID );
671697index = (Form_pg_index )GETSTRUCT (tuple );
672- if (index -> indisclustered != attrs -> isclustered )
698+ if (index -> indisclustered != isclustered )
673699{
674- index -> indisclustered = attrs -> isclustered ;
700+ index -> indisclustered = isclustered ;
675701simple_heap_update (pg_index ,& tuple -> t_self ,tuple );
676702CatalogUpdateIndexes (pg_index ,tuple );
703+ /* Ensure we see the update in the index's relcache entry */
704+ CacheInvalidateRelcacheByRelid (oldIndexOID );
677705}
678706heap_freetuple (tuple );
679707heap_close (pg_index ,RowExclusiveLock );