params->freeze_table_age,
params->multixact_freeze_min_age,
params->multixact_freeze_table_age,
+ true, /* we must be a top-level command */
&OldestXmin, &FreezeLimit, &xidFullScanLimit,
&MultiXactCutoff, &mxactFullScanLimit);
} RelToCluster;
-static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose);
+static void rebuild_relation(Relation OldHeap, Oid indexOid,
+ bool isTopLevel, bool verbose);
static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
- bool verbose, bool *pSwapToastByContent,
- TransactionId *pFreezeXid, MultiXactId *pCutoffMulti);
+ bool isTopLevel, bool verbose,
+ bool *pSwapToastByContent,
+ TransactionId *pFreezeXid,
+ MultiXactId *pCutoffMulti);
static List *get_tables_to_cluster(MemoryContext cluster_context);
table_close(rel, NoLock);
/* Do the job. */
- cluster_rel(tableOid, indexOid, stmt->options);
+ cluster_rel(tableOid, indexOid, stmt->options, isTopLevel);
}
else
{
PushActiveSnapshot(GetTransactionSnapshot());
/* Do the job. */
cluster_rel(rvtc->tableOid, rvtc->indexOid,
- stmt->options | CLUOPT_RECHECK);
+ stmt->options | CLUOPT_RECHECK,
+ isTopLevel);
PopActiveSnapshot();
CommitTransactionCommand();
}
* and error messages should refer to the operation as VACUUM not CLUSTER.
*/
void
-cluster_rel(Oid tableOid, Oid indexOid, int options)
+cluster_rel(Oid tableOid, Oid indexOid, int options, bool isTopLevel)
{
Relation OldHeap;
bool verbose = ((options & CLUOPT_VERBOSE) != 0);
TransferPredicateLocksToHeapRelation(OldHeap);
/* rebuild_relation does all the dirty work */
- rebuild_relation(OldHeap, indexOid, verbose);
+ rebuild_relation(OldHeap, indexOid, isTopLevel, verbose);
/* NB: rebuild_relation does table_close() on OldHeap */
*
* OldHeap: table to rebuild --- must be opened and exclusive-locked!
* indexOid: index to cluster by, or InvalidOid to rewrite in physical order.
+ * isTopLevel: should be passed down from ProcessUtility.
*
* NB: this routine closes OldHeap at the right time; caller should not.
*/
static void
-rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
+rebuild_relation(Relation OldHeap, Oid indexOid, bool isTopLevel, bool verbose)
{
Oid tableOid = RelationGetRelid(OldHeap);
Oid tableSpace = OldHeap->rd_rel->reltablespace;
AccessExclusiveLock);
/* Copy the heap data into the new table in the desired order */
- copy_table_data(OIDNewHeap, tableOid, indexOid, verbose,
+ copy_table_data(OIDNewHeap, tableOid, indexOid, isTopLevel, verbose,
&swap_toast_by_content, &frozenXid, &cutoffMulti);
/*
* *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(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
+ bool isTopLevel, bool verbose,
bool *pSwapToastByContent, TransactionId *pFreezeXid,
MultiXactId *pCutoffMulti)
{
* Since we're going to rewrite the whole table anyway, there's no reason
* not to be aggressive about this.
*/
- vacuum_set_xid_limits(OldHeap, 0, 0, 0, 0,
+ vacuum_set_xid_limits(OldHeap, 0, 0, 0, 0, isTopLevel,
&OldestXmin, &FreezeXid, NULL, &MultiXactCutoff,
NULL);
/*
* vacuum_set_xid_limits() -- compute oldestXmin and freeze cutoff points
*
+ * Input parameters are the target relation, applicable freeze age settings,
+ * and isTopLevel which should be passed down from ProcessUtility.
+ *
* The output parameters are:
* - oldestXmin is the cutoff value used to distinguish whether tuples are
* DEAD or RECENTLY_DEAD (see HeapTupleSatisfiesVacuum).
int freeze_table_age,
int multixact_freeze_min_age,
int multixact_freeze_table_age,
+ bool isTopLevel,
TransactionId *oldestXmin,
TransactionId *freezeLimit,
TransactionId *xidFullScanLimit,
MultiXactId mxactLimit;
MultiXactId safeMxactLimit;
- /*
- * We can always ignore processes running lazy vacuum. This is because we
- * use these values only for deciding which tuples we must keep in the
- * tables. Since lazy vacuum doesn't write its XID anywhere (usually no
- * XID assigned), it's safe to ignore it. In theory it could be
- * problematic to ignore lazy vacuums in a full vacuum, but keep in mind
- * that only one vacuum process can be working on a particular table at
- * any time, and that each vacuum is always an independent transaction.
- */
- *oldestXmin = GetOldestNonRemovableTransactionId(rel);
-
- if (OldSnapshotThresholdActive())
+ if (RELATION_IS_LOCAL(rel) && !IsInTransactionBlock(isTopLevel))
+ {
+ /*
+ * If we are processing a temp relation (which by prior checks must be
+ * one belonging to our session), and we are not inside any
+ * transaction block, then there can be no tuples in the rel that are
+ * still in-doubt, nor can there be any that are dead but possibly
+ * still interesting to some snapshot our session holds. We don't
+ * need to care whether other sessions could see such tuples, either.
+ * So we can aggressively set the cutoff xmin to be the nextXid.
+ */
+ *oldestXmin = ReadNewTransactionId();
+ }
+ else
{
- TransactionId limit_xmin;
- TimestampTz limit_ts;
+ /*
+ * Otherwise, calculate the cutoff xmin normally.
+ *
+ * We can always ignore processes running lazy vacuum. This is
+ * because we use these values only for deciding which tuples we must
+ * keep in the tables. Since lazy vacuum doesn't write its XID
+ * anywhere (usually no XID assigned), it's safe to ignore it. In
+ * theory it could be problematic to ignore lazy vacuums in a full
+ * vacuum, but keep in mind that only one vacuum process can be
+ * working on a particular table at any time, and that each vacuum is
+ * always an independent transaction.
+ */
+ *oldestXmin = GetOldestNonRemovableTransactionId(rel);
- if (TransactionIdLimitedForOldSnapshots(*oldestXmin, rel, &limit_xmin, &limit_ts))
+ if (OldSnapshotThresholdActive())
{
- /*
- * TODO: We should only set the threshold if we are pruning on the
- * basis of the increased limits. Not as crucial here as it is for
- * opportunistic pruning (which often happens at a much higher
- * frequency), but would still be a significant improvement.
- */
- SetOldSnapshotThresholdTimestamp(limit_ts, limit_xmin);
- *oldestXmin = limit_xmin;
+ TransactionId limit_xmin;
+ TimestampTz limit_ts;
+
+ if (TransactionIdLimitedForOldSnapshots(*oldestXmin, rel,
+ &limit_xmin, &limit_ts))
+ {
+ /*
+ * TODO: We should only set the threshold if we are pruning on
+ * the basis of the increased limits. Not as crucial here as
+ * it is for opportunistic pruning (which often happens at a
+ * much higher frequency), but would still be a significant
+ * improvement.
+ */
+ SetOldSnapshotThresholdTimestamp(limit_ts, limit_xmin);
+ *oldestXmin = limit_xmin;
+ }
}
}
cluster_options |= CLUOPT_VERBOSE;
/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
- cluster_rel(relid, InvalidOid, cluster_options);
+ cluster_rel(relid, InvalidOid, cluster_options, true);
}
else
table_relation_vacuum(onerel, params, vac_strategy);
extern void cluster(ClusterStmt *stmt, bool isTopLevel);
-extern void cluster_rel(Oid tableOid, Oid indexOid, int options);
+extern void cluster_rel(Oid tableOid, Oid indexOid, int options,
+ bool isTopLevel);
extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
bool recheck, LOCKMODE lockmode);
extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal);
int freeze_min_age, int freeze_table_age,
int multixact_freeze_min_age,
int multixact_freeze_table_age,
+ bool isTopLevel,
TransactionId *oldestXmin,
TransactionId *freezeLimit,
TransactionId *xidFullScanLimit,