Skip to content

Commit cc811f9

Browse files
committed
Adjust signature of cluster_rel() and its subroutines
cluster_rel() receives the OID of the relation to process, which it opens and locks; but then its subroutine copy_table_data() also receives the relation OID and opens it by itself. This is a bit wasteful. It's better to have cluster_rel() receive the relation already open, and pass it down to its subroutines as necessary; then cluster_rel closes the rel before returning. This simplifies things. But a better motivation to make this change is that a future command to do logical-decoding-based "concurrent VACUUM FULL" will need to release all locks on the relation (and possibly on the clustering index) at some point. Since it makes little sense to keep the relation reference without the lock, the cluster_rel() function will also close it (and the index). With this arrangement, neither the function nor its subroutines need open extra references, which, again, makes things simpler. Author: Antonin Houska <ah@cybertec.at> Discussion: https://postgr.es/m/82651.1720540558@antos
1 parent 2310064 commit cc811f9

File tree

4 files changed

+75
-73
lines changed

4 files changed

+75
-73
lines changed

src/backend/commands/cluster.c

Lines changed: 69 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ typedef struct
6969

7070

7171
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,
7474
bool verbose, bool *pSwapToastByContent,
7575
TransactionId *pFreezeXid, MultiXactId *pCutoffMulti);
7676
static 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
*/
309310
void
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

480475
out:
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
*/
631628
static 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
*/
812830
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,
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);

src/backend/commands/matview.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ RefreshMatViewByOid(Oid matviewOid, bool is_create, bool skipData,
319319
OIDNewHeap = make_new_heap(matviewOid, tableSpace,
320320
matviewRel->rd_rel->relam,
321321
relpersistence, ExclusiveLock);
322-
LockRelationOid(OIDNewHeap, AccessExclusiveLock);
322+
Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock, false));
323323

324324
/* Generate the data, if wanted. */
325325
if (!skipData)

src/backend/commands/vacuum.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2218,15 +2218,14 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params,
22182218
{
22192219
ClusterParams cluster_params = {0};
22202220

2221-
/* close relation before vacuuming, but hold lock until commit */
2222-
relation_close(rel, NoLock);
2223-
rel = NULL;
2224-
22252221
if ((params->options & VACOPT_VERBOSE) != 0)
22262222
cluster_params.options |= CLUOPT_VERBOSE;
22272223

22282224
/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
2229-
cluster_rel(relid, InvalidOid, &cluster_params);
2225+
cluster_rel(rel, InvalidOid, &cluster_params);
2226+
/* cluster_rel closes the relation, but keeps lock */
2227+
2228+
rel = NULL;
22302229
}
22312230
else
22322231
table_relation_vacuum(rel, params, bstrategy);

src/include/commands/cluster.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ typedef struct ClusterParams
3232
} ClusterParams;
3333

3434
extern void cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel);
35-
extern void cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params);
35+
extern void cluster_rel(Relation OldHeap, Oid indexOid, ClusterParams *params);
3636
extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
3737
LOCKMODE lockmode);
3838
extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal);

0 commit comments

Comments
 (0)