static void cluster_multiple_rels(List *rtcs, ClusterParams *params);
-static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose);
-static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
+static void rebuild_relation(Relation OldHeap, Relation index, bool verbose);
+static void copy_table_data(Relation NewHeap, Relation OldHeap, Relation OldIndex,
bool verbose, bool *pSwapToastByContent,
TransactionId *pFreezeXid, MultiXactId *pCutoffMulti);
static List *get_tables_to_cluster(MemoryContext cluster_context);
stmt->indexname, stmt->relation->relname)));
}
+ /* For non-partitioned tables, do what we came here to do. */
if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
{
- /* close relation, keep lock till commit */
- table_close(rel, NoLock);
-
- /* Do the job. */
- cluster_rel(tableOid, indexOid, ¶ms);
+ cluster_rel(rel, indexOid, ¶ms);
+ /* cluster_rel closes the relation, but keeps lock */
return;
}
foreach(lc, rtcs)
{
RelToCluster *rtc = (RelToCluster *) lfirst(lc);
+ Relation rel;
/* Start a new transaction for each relation. */
StartTransactionCommand();
/* functions in indexes may want a snapshot set */
PushActiveSnapshot(GetTransactionSnapshot());
- /* Do the job. */
- cluster_rel(rtc->tableOid, rtc->indexOid, params);
+ rel = table_open(rtc->tableOid, AccessExclusiveLock);
+
+ /* Process this table */
+ cluster_rel(rel, rtc->indexOid, params);
+ /* cluster_rel closes the relation, but keeps lock */
PopActiveSnapshot();
CommitTransactionCommand();
* This clusters the table by creating a new, clustered table and
* swapping the relfilenumbers of the new table and the old table, so
* the OID of the original table is preserved. Thus we do not lose
- * GRANT, inheritance nor references to this table (this was a bug
- * in releases through 7.3).
+ * GRANT, inheritance nor references to this table.
*
* Indexes are rebuilt too, via REINDEX. Since we are effectively bulk-loading
* the new table, it's better to create the indexes afterwards than to fill
* and error messages should refer to the operation as VACUUM not CLUSTER.
*/
void
-cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
+cluster_rel(Relation OldHeap, Oid indexOid, ClusterParams *params)
{
- Relation OldHeap;
+ Oid tableOid = RelationGetRelid(OldHeap);
Oid save_userid;
int save_sec_context;
int save_nestlevel;
bool verbose = ((params->options & CLUOPT_VERBOSE) != 0);
bool recheck = ((params->options & CLUOPT_RECHECK) != 0);
+ Relation index;
+
+ Assert(CheckRelationLockedByMe(OldHeap, AccessExclusiveLock, false));
/* Check for user-requested abort. */
CHECK_FOR_INTERRUPTS();
pgstat_progress_update_param(PROGRESS_CLUSTER_COMMAND,
PROGRESS_CLUSTER_COMMAND_VACUUM_FULL);
- /*
- * We grab exclusive access to the target rel and index for the duration
- * of the transaction. (This is redundant for the single-transaction
- * case, since cluster() already did it.) The index lock is taken inside
- * check_index_is_clusterable.
- */
- OldHeap = try_relation_open(tableOid, AccessExclusiveLock);
-
- /* If the table has gone away, we can skip processing it */
- if (!OldHeap)
- {
- pgstat_progress_end_command();
- return;
- }
-
/*
* Switch to the table owner's userid, so that any index functions are run
* as that user. Also lock down security-restricted operations and
/* Check heap and index are valid to cluster on */
if (OidIsValid(indexOid))
+ {
+ /* verify the index is good and lock it */
check_index_is_clusterable(OldHeap, indexOid, AccessExclusiveLock);
+ /* also open it */
+ index = index_open(indexOid, NoLock);
+ }
+ else
+ index = NULL;
/*
* Quietly ignore the request if this is a materialized view which has not
TransferPredicateLocksToHeapRelation(OldHeap);
/* rebuild_relation does all the dirty work */
- rebuild_relation(OldHeap, indexOid, verbose);
-
- /* NB: rebuild_relation does table_close() on OldHeap */
+ rebuild_relation(OldHeap, index, verbose);
+ /* rebuild_relation closes OldHeap, and index if valid */
out:
/* Roll back any GUC changes executed by index functions */
/*
* rebuild_relation: rebuild an existing relation in index or physical order
*
- * OldHeap: table to rebuild --- must be opened and exclusive-locked!
- * indexOid: index to cluster by, or InvalidOid to rewrite in physical order.
+ * OldHeap: table to rebuild.
+ * index: index to cluster by, or NULL to rewrite in physical order.
*
- * NB: this routine closes OldHeap at the right time; caller should not.
+ * On entry, heap and index (if one is given) must be open, and
+ * AccessExclusiveLock held on them.
+ * On exit, they are closed, but locks on them are not released.
*/
static void
-rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
+rebuild_relation(Relation OldHeap, Relation index, bool verbose)
{
Oid tableOid = RelationGetRelid(OldHeap);
Oid accessMethod = OldHeap->rd_rel->relam;
Oid tableSpace = OldHeap->rd_rel->reltablespace;
Oid OIDNewHeap;
+ Relation NewHeap;
char relpersistence;
bool is_system_catalog;
bool swap_toast_by_content;
TransactionId frozenXid;
MultiXactId cutoffMulti;
- if (OidIsValid(indexOid))
+ Assert(CheckRelationLockedByMe(OldHeap, AccessExclusiveLock, false) &&
+ (index == NULL || CheckRelationLockedByMe(index, AccessExclusiveLock, false)));
+
+ if (index)
/* Mark the correct index as clustered */
- mark_index_clustered(OldHeap, indexOid, true);
+ mark_index_clustered(OldHeap, RelationGetRelid(index), true);
/* Remember info about rel before closing OldHeap */
relpersistence = OldHeap->rd_rel->relpersistence;
is_system_catalog = IsSystemRelation(OldHeap);
- /* Close relcache entry, but keep lock until transaction commit */
- table_close(OldHeap, NoLock);
-
- /* Create the transient table that will receive the re-ordered data */
+ /*
+ * Create the transient table that will receive the re-ordered data.
+ *
+ * OldHeap is already locked, so no need to lock it again. make_new_heap
+ * obtains AccessExclusiveLock on the new heap and its toast table.
+ */
OIDNewHeap = make_new_heap(tableOid, tableSpace,
accessMethod,
relpersistence,
- AccessExclusiveLock);
+ NoLock);
+ Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock, false));
+ NewHeap = table_open(OIDNewHeap, NoLock);
/* Copy the heap data into the new table in the desired order */
- copy_table_data(OIDNewHeap, tableOid, indexOid, verbose,
+ copy_table_data(NewHeap, OldHeap, index, verbose,
&swap_toast_by_content, &frozenXid, &cutoffMulti);
+
+ /* Close relcache entries, but keep lock until transaction commit */
+ table_close(OldHeap, NoLock);
+ if (index)
+ index_close(index, NoLock);
+
+ /*
+ * Close the new relation so it can be dropped as soon as the storage is
+ * swapped. The relation is not visible to others, so no need to unlock it
+ * explicitly.
+ */
+ table_close(NewHeap, NoLock);
+
/*
* Swap the physical files of the target and transient tables, then
* rebuild the target's indexes and throw away the transient table.
* *pCutoffMulti receives the MultiXactId used as a cutoff point.
*/
static void
-copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
+copy_table_data(Relation NewHeap, Relation OldHeap, Relation OldIndex, bool verbose,
bool *pSwapToastByContent, TransactionId *pFreezeXid,
MultiXactId *pCutoffMulti)
{
- Relation NewHeap,
- OldHeap,
- OldIndex;
Relation relRelation;
HeapTuple reltup;
Form_pg_class relform;
pg_rusage_init(&ru0);
- /*
- * Open the relations we need.
- */
- NewHeap = table_open(OIDNewHeap, AccessExclusiveLock);
- OldHeap = table_open(OIDOldHeap, AccessExclusiveLock);
- if (OidIsValid(OIDOldIndex))
- OldIndex = index_open(OIDOldIndex, AccessExclusiveLock);
- else
- OldIndex = NULL;
-
/* Store a copy of the namespace name for logging purposes */
nspname = get_namespace_name(RelationGetNamespace(OldHeap));
* provided, else plain seqscan.
*/
if (OldIndex != NULL && OldIndex->rd_rel->relam == BTREE_AM_OID)
- use_sort = plan_cluster_use_sort(OIDOldHeap, OIDOldIndex);
+ use_sort = plan_cluster_use_sort(RelationGetRelid(OldHeap),
+ RelationGetRelid(OldIndex));
else
use_sort = false;
tups_recently_dead,
pg_rusage_show(&ru0))));
- if (OldIndex != NULL)
- index_close(OldIndex, NoLock);
- table_close(OldHeap, NoLock);
- table_close(NewHeap, NoLock);
-
/* Update pg_class to reflect the correct values of pages and tuples. */
relRelation = table_open(RelationRelationId, RowExclusiveLock);
- reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDNewHeap));
+ reltup = SearchSysCacheCopy1(RELOID,
+ ObjectIdGetDatum(RelationGetRelid(NewHeap)));
if (!HeapTupleIsValid(reltup))
- elog(ERROR, "cache lookup failed for relation %u", OIDNewHeap);
+ elog(ERROR, "cache lookup failed for relation %u",
+ RelationGetRelid(NewHeap));
relform = (Form_pg_class) GETSTRUCT(reltup);
relform->relpages = num_pages;
relform->reltuples = num_tuples;
/* Don't update the stats for pg_class. See swap_relation_files. */
- if (OIDOldHeap != RelationRelationId)
+ if (RelationGetRelid(OldHeap) != RelationRelationId)
CatalogTupleUpdate(relRelation, &reltup->t_self, reltup);
else
CacheInvalidateRelcacheByTuple(reltup);