@@ -69,8 +69,8 @@ typedef struct
69
69
70
70
71
71
static 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 ,
74
74
bool verbose , bool * pSwapToastByContent ,
75
75
TransactionId * pFreezeXid , MultiXactId * pCutoffMulti );
76
76
static List * get_tables_to_cluster (MemoryContext cluster_context );
@@ -191,13 +191,11 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
191
191
stmt -> indexname , stmt -> relation -> relname )));
192
192
}
193
193
194
+ /* For non-partitioned tables, do what we came here to do. */
194
195
if (rel -> rd_rel -> relkind != RELKIND_PARTITIONED_TABLE )
195
196
{
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 */
201
199
202
200
return ;
203
201
}
@@ -274,15 +272,19 @@ cluster_multiple_rels(List *rtcs, ClusterParams *params)
274
272
foreach (lc , rtcs )
275
273
{
276
274
RelToCluster * rtc = (RelToCluster * ) lfirst (lc );
275
+ Relation rel ;
277
276
278
277
/* Start a new transaction for each relation. */
279
278
StartTransactionCommand ();
280
279
281
280
/* functions in indexes may want a snapshot set */
282
281
PushActiveSnapshot (GetTransactionSnapshot ());
283
282
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 */
286
288
287
289
PopActiveSnapshot ();
288
290
CommitTransactionCommand ();
@@ -295,8 +297,7 @@ cluster_multiple_rels(List *rtcs, ClusterParams *params)
295
297
* This clusters the table by creating a new, clustered table and
296
298
* swapping the relfilenumbers of the new table and the old table, so
297
299
* 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.
300
301
*
301
302
* Indexes are rebuilt too, via REINDEX. Since we are effectively bulk-loading
302
303
* 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)
307
308
* and error messages should refer to the operation as VACUUM not CLUSTER.
308
309
*/
309
310
void
310
- cluster_rel (Oid tableOid , Oid indexOid , ClusterParams * params )
311
+ cluster_rel (Relation OldHeap , Oid indexOid , ClusterParams * params )
311
312
{
312
- Relation OldHeap ;
313
+ Oid tableOid = RelationGetRelid ( OldHeap ) ;
313
314
Oid save_userid ;
314
315
int save_sec_context ;
315
316
int save_nestlevel ;
316
317
bool verbose = ((params -> options & CLUOPT_VERBOSE ) != 0 );
317
318
bool recheck = ((params -> options & CLUOPT_RECHECK ) != 0 );
319
+ Relation index ;
320
+
321
+ Assert (CheckRelationLockedByMe (OldHeap , AccessExclusiveLock , false));
318
322
319
323
/* Check for user-requested abort. */
320
324
CHECK_FOR_INTERRUPTS ();
@@ -327,21 +331,6 @@ cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
327
331
pgstat_progress_update_param (PROGRESS_CLUSTER_COMMAND ,
328
332
PROGRESS_CLUSTER_COMMAND_VACUUM_FULL );
329
333
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
-
345
334
/*
346
335
* Switch to the table owner's userid, so that any index functions are run
347
336
* as that user. Also lock down security-restricted operations and
@@ -444,7 +433,14 @@ cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
444
433
445
434
/* Check heap and index are valid to cluster on */
446
435
if (OidIsValid (indexOid ))
436
+ {
437
+ /* verify the index is good and lock it */
447
438
check_index_is_clusterable (OldHeap , indexOid , AccessExclusiveLock );
439
+ /* also open it */
440
+ index = index_open (indexOid , NoLock );
441
+ }
442
+ else
443
+ index = NULL ;
448
444
449
445
/*
450
446
* 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)
473
469
TransferPredicateLocksToHeapRelation (OldHeap );
474
470
475
471
/* 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 */
479
474
480
475
out :
481
476
/* Roll back any GUC changes executed by index functions */
@@ -623,45 +618,68 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
623
618
/*
624
619
* rebuild_relation: rebuild an existing relation in index or physical order
625
620
*
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.
628
623
*
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.
630
627
*/
631
628
static void
632
- rebuild_relation (Relation OldHeap , Oid indexOid , bool verbose )
629
+ rebuild_relation (Relation OldHeap , Relation index , bool verbose )
633
630
{
634
631
Oid tableOid = RelationGetRelid (OldHeap );
635
632
Oid accessMethod = OldHeap -> rd_rel -> relam ;
636
633
Oid tableSpace = OldHeap -> rd_rel -> reltablespace ;
637
634
Oid OIDNewHeap ;
635
+ Relation NewHeap ;
638
636
char relpersistence ;
639
637
bool is_system_catalog ;
640
638
bool swap_toast_by_content ;
641
639
TransactionId frozenXid ;
642
640
MultiXactId cutoffMulti ;
643
641
644
- if (OidIsValid (indexOid ))
642
+ Assert (CheckRelationLockedByMe (OldHeap , AccessExclusiveLock , false) &&
643
+ (index == NULL || CheckRelationLockedByMe (index , AccessExclusiveLock , false)));
644
+
645
+ if (index )
645
646
/* Mark the correct index as clustered */
646
- mark_index_clustered (OldHeap , indexOid , true);
647
+ mark_index_clustered (OldHeap , RelationGetRelid ( index ) , true);
647
648
648
649
/* Remember info about rel before closing OldHeap */
649
650
relpersistence = OldHeap -> rd_rel -> relpersistence ;
650
651
is_system_catalog = IsSystemRelation (OldHeap );
651
652
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
+ */
656
659
OIDNewHeap = make_new_heap (tableOid , tableSpace ,
657
660
accessMethod ,
658
661
relpersistence ,
659
- AccessExclusiveLock );
662
+ NoLock );
663
+ Assert (CheckRelationOidLockedByMe (OIDNewHeap , AccessExclusiveLock , false));
664
+ NewHeap = table_open (OIDNewHeap , NoLock );
660
665
661
666
/* 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 ,
663
668
& swap_toast_by_content , & frozenXid , & cutoffMulti );
664
669
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
+
665
683
/*
666
684
* Swap the physical files of the target and transient tables, then
667
685
* rebuild the target's indexes and throw away the transient table.
@@ -810,13 +828,10 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod,
810
828
* *pCutoffMulti receives the MultiXactId used as a cutoff point.
811
829
*/
812
830
static 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 ,
814
832
bool * pSwapToastByContent , TransactionId * pFreezeXid ,
815
833
MultiXactId * pCutoffMulti )
816
834
{
817
- Relation NewHeap ,
818
- OldHeap ,
819
- OldIndex ;
820
835
Relation relRelation ;
821
836
HeapTuple reltup ;
822
837
Form_pg_class relform ;
@@ -835,16 +850,6 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
835
850
836
851
pg_rusage_init (& ru0 );
837
852
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
-
848
853
/* Store a copy of the namespace name for logging purposes */
849
854
nspname = get_namespace_name (RelationGetNamespace (OldHeap ));
850
855
@@ -945,7 +950,8 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
945
950
* provided, else plain seqscan.
946
951
*/
947
952
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 ));
949
955
else
950
956
use_sort = false;
951
957
@@ -1000,24 +1006,21 @@ copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
1000
1006
tups_recently_dead ,
1001
1007
pg_rusage_show (& ru0 ))));
1002
1008
1003
- if (OldIndex != NULL )
1004
- index_close (OldIndex , NoLock );
1005
- table_close (OldHeap , NoLock );
1006
- table_close (NewHeap , NoLock );
1007
-
1008
1009
/* Update pg_class to reflect the correct values of pages and tuples. */
1009
1010
relRelation = table_open (RelationRelationId , RowExclusiveLock );
1010
1011
1011
- reltup = SearchSysCacheCopy1 (RELOID , ObjectIdGetDatum (OIDNewHeap ));
1012
+ reltup = SearchSysCacheCopy1 (RELOID ,
1013
+ ObjectIdGetDatum (RelationGetRelid (NewHeap )));
1012
1014
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 ));
1014
1017
relform = (Form_pg_class ) GETSTRUCT (reltup );
1015
1018
1016
1019
relform -> relpages = num_pages ;
1017
1020
relform -> reltuples = num_tuples ;
1018
1021
1019
1022
/* Don't update the stats for pg_class. See swap_relation_files. */
1020
- if (OIDOldHeap != RelationRelationId )
1023
+ if (RelationGetRelid ( OldHeap ) != RelationRelationId )
1021
1024
CatalogTupleUpdate (relRelation , & reltup -> t_self , reltup );
1022
1025
else
1023
1026
CacheInvalidateRelcacheByTuple (reltup );
0 commit comments