@@ -69,8 +69,8 @@ typedef struct
6969
7070
7171static void cluster_multiple_rels (List * rtcs , ClusterParams * params );
72- static void rebuild_relation (Relation OldHeap , Oid indexOid , bool verbose );
73- static void copy_table_data (Oid OIDNewHeap , Oid OIDOldHeap , Oid OIDOldIndex ,
72+ static void rebuild_relation (Relation OldHeap , Relation index , bool verbose );
73+ static void copy_table_data (Relation NewHeap , Relation OldHeap , Relation OldIndex ,
7474 bool verbose , bool * pSwapToastByContent ,
7575 TransactionId * pFreezeXid , MultiXactId * pCutoffMulti );
7676static List * get_tables_to_cluster (MemoryContext cluster_context );
@@ -191,13 +191,11 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
191191 stmt -> indexname , stmt -> relation -> relname )));
192192 }
193193
194+ /* For non-partitioned tables, do what we came here to do. */
194195 if (rel -> rd_rel -> relkind != RELKIND_PARTITIONED_TABLE )
195196 {
196- /* close relation, keep lock till commit */
197- table_close (rel , NoLock );
198-
199- /* Do the job. */
200- cluster_rel (tableOid , indexOid , & params );
197+ cluster_rel (rel , indexOid , & params );
198+ /* cluster_rel closes the relation, but keeps lock */
201199
202200 return ;
203201 }
@@ -274,15 +272,19 @@ cluster_multiple_rels(List *rtcs, ClusterParams *params)
274272 foreach (lc , rtcs )
275273 {
276274 RelToCluster * rtc = (RelToCluster * ) lfirst (lc );
275+ Relation rel ;
277276
278277 /* Start a new transaction for each relation. */
279278 StartTransactionCommand ();
280279
281280 /* functions in indexes may want a snapshot set */
282281 PushActiveSnapshot (GetTransactionSnapshot ());
283282
284- /* Do the job. */
285- cluster_rel (rtc -> tableOid , rtc -> indexOid , params );
283+ rel = table_open (rtc -> tableOid , AccessExclusiveLock );
284+
285+ /* Process this table */
286+ cluster_rel (rel , rtc -> indexOid , params );
287+ /* cluster_rel closes the relation, but keeps lock */
286288
287289 PopActiveSnapshot ();
288290 CommitTransactionCommand ();
@@ -295,8 +297,7 @@ cluster_multiple_rels(List *rtcs, ClusterParams *params)
295297 * This clusters the table by creating a new, clustered table and
296298 * swapping the relfilenumbers of the new table and the old table, so
297299 * the OID of the original table is preserved. Thus we do not lose
298- * GRANT, inheritance nor references to this table (this was a bug
299- * in releases through 7.3).
300+ * GRANT, inheritance nor references to this table.
300301 *
301302 * Indexes are rebuilt too, via REINDEX. Since we are effectively bulk-loading
302303 * the new table, it's better to create the indexes afterwards than to fill
@@ -307,14 +308,17 @@ cluster_multiple_rels(List *rtcs, ClusterParams *params)
307308 * and error messages should refer to the operation as VACUUM not CLUSTER.
308309 */
309310void
310- cluster_rel (Oid tableOid , Oid indexOid , ClusterParams * params )
311+ cluster_rel (Relation OldHeap , Oid indexOid , ClusterParams * params )
311312{
312- Relation OldHeap ;
313+ Oid tableOid = RelationGetRelid ( OldHeap ) ;
313314 Oid save_userid ;
314315 int save_sec_context ;
315316 int save_nestlevel ;
316317 bool verbose = ((params -> options & CLUOPT_VERBOSE ) != 0 );
317318 bool recheck = ((params -> options & CLUOPT_RECHECK ) != 0 );
319+ Relation index ;
320+
321+ Assert (CheckRelationLockedByMe (OldHeap , AccessExclusiveLock , false));
318322
319323 /* Check for user-requested abort. */
320324 CHECK_FOR_INTERRUPTS ();
@@ -327,21 +331,6 @@ cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
327331 pgstat_progress_update_param (PROGRESS_CLUSTER_COMMAND ,
328332 PROGRESS_CLUSTER_COMMAND_VACUUM_FULL );
329333
330- /*
331- * We grab exclusive access to the target rel and index for the duration
332- * of the transaction. (This is redundant for the single-transaction
333- * case, since cluster() already did it.) The index lock is taken inside
334- * check_index_is_clusterable.
335- */
336- OldHeap = try_relation_open (tableOid , AccessExclusiveLock );
337-
338- /* If the table has gone away, we can skip processing it */
339- if (!OldHeap )
340- {
341- pgstat_progress_end_command ();
342- return ;
343- }
344-
345334 /*
346335 * Switch to the table owner's userid, so that any index functions are run
347336 * as that user. Also lock down security-restricted operations and
@@ -444,7 +433,14 @@ cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
444433
445434 /* Check heap and index are valid to cluster on */
446435 if (OidIsValid (indexOid ))
436+ {
437+ /* verify the index is good and lock it */
447438 check_index_is_clusterable (OldHeap , indexOid , AccessExclusiveLock );
439+ /* also open it */
440+ index = index_open (indexOid , NoLock );
441+ }
442+ else
443+ index = NULL ;
448444
449445 /*
450446 * Quietly ignore the request if this is a materialized view which has not
@@ -473,9 +469,8 @@ cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
473469 TransferPredicateLocksToHeapRelation (OldHeap );
474470
475471 /* rebuild_relation does all the dirty work */
476- rebuild_relation (OldHeap , indexOid , verbose );
477-
478- /* NB: rebuild_relation does table_close() on OldHeap */
472+ rebuild_relation (OldHeap , index , verbose );
473+ /* rebuild_relation closes OldHeap, and index if valid */
479474
480475out :
481476 /* Roll back any GUC changes executed by index functions */
@@ -623,45 +618,68 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
623618/*
624619 * rebuild_relation: rebuild an existing relation in index or physical order
625620 *
626- * OldHeap: table to rebuild --- must be opened and exclusive-locked!
627- * indexOid : index to cluster by, or InvalidOid to rewrite in physical order.
621+ * OldHeap: table to rebuild.
622+ * index : index to cluster by, or NULL to rewrite in physical order.
628623 *
629- * NB: this routine closes OldHeap at the right time; caller should not.
624+ * On entry, heap and index (if one is given) must be open, and
625+ * AccessExclusiveLock held on them.
626+ * On exit, they are closed, but locks on them are not released.
630627 */
631628static void
632- rebuild_relation (Relation OldHeap , Oid indexOid , bool verbose )
629+ rebuild_relation (Relation OldHeap , Relation index , bool verbose )
633630{
634631 Oid tableOid = RelationGetRelid (OldHeap );
635632 Oid accessMethod = OldHeap -> rd_rel -> relam ;
636633 Oid tableSpace = OldHeap -> rd_rel -> reltablespace ;
637634 Oid OIDNewHeap ;
635+ Relation NewHeap ;
638636 char relpersistence ;
639637 bool is_system_catalog ;
640638 bool swap_toast_by_content ;
641639 TransactionId frozenXid ;
642640 MultiXactId cutoffMulti ;
643641
644- if (OidIsValid (indexOid ))
642+ Assert (CheckRelationLockedByMe (OldHeap , AccessExclusiveLock , false) &&
643+ (index == NULL || CheckRelationLockedByMe (index , AccessExclusiveLock , false)));
644+
645+ if (index )
645646 /* Mark the correct index as clustered */
646- mark_index_clustered (OldHeap , indexOid , true);
647+ mark_index_clustered (OldHeap , RelationGetRelid ( index ) , true);
647648
648649 /* Remember info about rel before closing OldHeap */
649650 relpersistence = OldHeap -> rd_rel -> relpersistence ;
650651 is_system_catalog = IsSystemRelation (OldHeap );
651652
652- /* Close relcache entry, but keep lock until transaction commit */
653- table_close (OldHeap , NoLock );
654-
655- /* Create the transient table that will receive the re-ordered data */
653+ /*
654+ * Create the transient table that will receive the re-ordered data.
655+ *
656+ * OldHeap is already locked, so no need to lock it again. make_new_heap
657+ * obtains AccessExclusiveLock on the new heap and its toast table.
658+ */
656659 OIDNewHeap = make_new_heap (tableOid , tableSpace ,
657660 accessMethod ,
658661 relpersistence ,
659- AccessExclusiveLock );
662+ NoLock );
663+ Assert (CheckRelationOidLockedByMe (OIDNewHeap , AccessExclusiveLock , false));
664+ NewHeap = table_open (OIDNewHeap , NoLock );
660665
661666 /* Copy the heap data into the new table in the desired order */
662- copy_table_data (OIDNewHeap , tableOid , indexOid , verbose ,
667+ copy_table_data (NewHeap , OldHeap , index , verbose ,
663668 & swap_toast_by_content , & frozenXid , & cutoffMulti );
664669
670+
671+ /* Close relcache entries, but keep lock until transaction commit */
672+ table_close (OldHeap , NoLock );
673+ if (index )
674+ index_close (index , NoLock );
675+
676+ /*
677+ * Close the new relation so it can be dropped as soon as the storage is
678+ * swapped. The relation is not visible to others, so no need to unlock it
679+ * explicitly.
680+ */
681+ table_close (NewHeap , NoLock );
682+
665683 /*
666684 * Swap the physical files of the target and transient tables, then
667685 * rebuild the target's indexes and throw away the transient table.
@@ -810,13 +828,10 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod,
810828 * *pCutoffMulti receives the MultiXactId used as a cutoff point.
811829 */
812830static void
813- copy_table_data (Oid OIDNewHeap , Oid OIDOldHeap , Oid OIDOldIndex , bool verbose ,
831+ copy_table_data (Relation NewHeap , Relation OldHeap , Relation OldIndex , bool verbose ,
814832 bool * pSwapToastByContent , TransactionId * pFreezeXid ,
815833 MultiXactId * pCutoffMulti )
816834{
817- Relation NewHeap ,
818- OldHeap ,
819- OldIndex ;
820835 Relation relRelation ;
821836 HeapTuple reltup ;
822837 Form_pg_class relform ;
@@ -835,16 +850,6 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
835850
836851 pg_rusage_init (& ru0 );
837852
838- /*
839- * Open the relations we need.
840- */
841- NewHeap = table_open (OIDNewHeap , AccessExclusiveLock );
842- OldHeap = table_open (OIDOldHeap , AccessExclusiveLock );
843- if (OidIsValid (OIDOldIndex ))
844- OldIndex = index_open (OIDOldIndex , AccessExclusiveLock );
845- else
846- OldIndex = NULL ;
847-
848853 /* Store a copy of the namespace name for logging purposes */
849854 nspname = get_namespace_name (RelationGetNamespace (OldHeap ));
850855
@@ -945,7 +950,8 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
945950 * provided, else plain seqscan.
946951 */
947952 if (OldIndex != NULL && OldIndex -> rd_rel -> relam == BTREE_AM_OID )
948- use_sort = plan_cluster_use_sort (OIDOldHeap , OIDOldIndex );
953+ use_sort = plan_cluster_use_sort (RelationGetRelid (OldHeap ),
954+ RelationGetRelid (OldIndex ));
949955 else
950956 use_sort = false;
951957
@@ -1000,24 +1006,21 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
10001006 tups_recently_dead ,
10011007 pg_rusage_show (& ru0 ))));
10021008
1003- if (OldIndex != NULL )
1004- index_close (OldIndex , NoLock );
1005- table_close (OldHeap , NoLock );
1006- table_close (NewHeap , NoLock );
1007-
10081009 /* Update pg_class to reflect the correct values of pages and tuples. */
10091010 relRelation = table_open (RelationRelationId , RowExclusiveLock );
10101011
1011- reltup = SearchSysCacheCopy1 (RELOID , ObjectIdGetDatum (OIDNewHeap ));
1012+ reltup = SearchSysCacheCopy1 (RELOID ,
1013+ ObjectIdGetDatum (RelationGetRelid (NewHeap )));
10121014 if (!HeapTupleIsValid (reltup ))
1013- elog (ERROR , "cache lookup failed for relation %u" , OIDNewHeap );
1015+ elog (ERROR , "cache lookup failed for relation %u" ,
1016+ RelationGetRelid (NewHeap ));
10141017 relform = (Form_pg_class ) GETSTRUCT (reltup );
10151018
10161019 relform -> relpages = num_pages ;
10171020 relform -> reltuples = num_tuples ;
10181021
10191022 /* Don't update the stats for pg_class. See swap_relation_files. */
1020- if (OIDOldHeap != RelationRelationId )
1023+ if (RelationGetRelid ( OldHeap ) != RelationRelationId )
10211024 CatalogTupleUpdate (relRelation , & reltup -> t_self , reltup );
10221025 else
10231026 CacheInvalidateRelcacheByTuple (reltup );
0 commit comments