aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/cluster.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/cluster.c')
-rw-r--r--src/backend/commands/cluster.c243
1 files changed, 144 insertions, 99 deletions
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 48ba57518f4..1b1053d8a87 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.190 2010/01/06 03:04:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.191 2010/01/06 05:31:13 itagaki Exp $
*
*-------------------------------------------------------------------------
*/
@@ -61,9 +61,10 @@ typedef struct
} RelToCluster;
-static void cluster_rel(RelToCluster *rv, bool recheck, bool verbose);
-static void rebuild_relation(Relation OldHeap, Oid indexOid);
-static TransactionId copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
+static void rebuild_relation(Relation OldHeap, Oid indexOid,
+ int freeze_min_age, int freeze_table_age);
+static TransactionId copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap,
+ Oid OIDOldIndex, int freeze_min_age, int freeze_table_age);
static List *get_tables_to_cluster(MemoryContext cluster_context);
@@ -101,7 +102,6 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
Oid tableOid,
indexOid = InvalidOid;
Relation rel;
- RelToCluster rvtc;
/* Find and lock the table */
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
@@ -169,15 +169,11 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
stmt->indexname, stmt->relation->relname)));
}
- /* All other checks are done in cluster_rel() */
- rvtc.tableOid = tableOid;
- rvtc.indexOid = indexOid;
-
/* close relation, keep lock till commit */
heap_close(rel, NoLock);
/* Do the job */
- cluster_rel(&rvtc, false, stmt->verbose);
+ cluster_rel(tableOid, indexOid, false, stmt->verbose, -1, -1);
}
else
{
@@ -226,7 +222,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
StartTransactionCommand();
/* functions in indexes may want a snapshot set */
PushActiveSnapshot(GetTransactionSnapshot());
- cluster_rel(rvtc, true, stmt->verbose);
+ cluster_rel(rvtc->tableOid, rvtc->indexOid, true, stmt->verbose, -1, -1);
PopActiveSnapshot();
CommitTransactionCommand();
}
@@ -252,9 +248,13 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
* same way we do for the relation. Since we are effectively bulk-loading
* the new table, it's better to create the indexes afterwards than to fill
* them incrementally while we load the table.
+ *
+ * If indexOid is InvalidOid, the table will be rewritten in physical order
+ * instead of index order.
*/
-static void
-cluster_rel(RelToCluster *rvtc, bool recheck, bool verbose)
+void
+cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose,
+ int freeze_min_age, int freeze_table_age)
{
Relation OldHeap;
@@ -267,7 +267,7 @@ cluster_rel(RelToCluster *rvtc, bool recheck, bool verbose)
* case, since cluster() already did it.) The index lock is taken inside
* check_index_is_clusterable.
*/
- OldHeap = try_relation_open(rvtc->tableOid, AccessExclusiveLock);
+ OldHeap = try_relation_open(tableOid, AccessExclusiveLock);
/* If the table has gone away, we can skip processing it */
if (!OldHeap)
@@ -287,7 +287,7 @@ cluster_rel(RelToCluster *rvtc, bool recheck, bool verbose)
Form_pg_index indexForm;
/* Check that the user still owns the relation */
- if (!pg_class_ownercheck(rvtc->tableOid, GetUserId()))
+ if (!pg_class_ownercheck(tableOid, GetUserId()))
{
relation_close(OldHeap, AccessExclusiveLock);
return;
@@ -308,53 +308,62 @@ cluster_rel(RelToCluster *rvtc, bool recheck, bool verbose)
return;
}
- /*
- * Check that the index still exists
- */
- if (!SearchSysCacheExists(RELOID,
- ObjectIdGetDatum(rvtc->indexOid),
- 0, 0, 0))
+ if (OidIsValid(indexOid))
{
- relation_close(OldHeap, AccessExclusiveLock);
- return;
- }
+ /*
+ * Check that the index still exists
+ */
+ if (!SearchSysCacheExists(RELOID,
+ ObjectIdGetDatum(indexOid),
+ 0, 0, 0))
+ {
+ relation_close(OldHeap, AccessExclusiveLock);
+ return;
+ }
- /*
- * Check that the index is still the one with indisclustered set.
- */
- tuple = SearchSysCache(INDEXRELID,
- ObjectIdGetDatum(rvtc->indexOid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tuple)) /* probably can't happen */
- {
- relation_close(OldHeap, AccessExclusiveLock);
- return;
- }
- indexForm = (Form_pg_index) GETSTRUCT(tuple);
- if (!indexForm->indisclustered)
- {
+ /*
+ * Check that the index is still the one with indisclustered set.
+ */
+ tuple = SearchSysCache(INDEXRELID,
+ ObjectIdGetDatum(indexOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple)) /* probably can't happen */
+ {
+ relation_close(OldHeap, AccessExclusiveLock);
+ return;
+ }
+ indexForm = (Form_pg_index) GETSTRUCT(tuple);
+ if (!indexForm->indisclustered)
+ {
+ ReleaseSysCache(tuple);
+ relation_close(OldHeap, AccessExclusiveLock);
+ return;
+ }
ReleaseSysCache(tuple);
- relation_close(OldHeap, AccessExclusiveLock);
- return;
}
- ReleaseSysCache(tuple);
}
- /* Check index is valid to cluster on */
- check_index_is_clusterable(OldHeap, rvtc->indexOid, recheck);
+ /* Check heap and index are valid to cluster on */
+ check_index_is_clusterable(OldHeap, indexOid, recheck);
/* rebuild_relation does all the dirty work */
- ereport(verbose ? INFO : DEBUG2,
- (errmsg("clustering \"%s.%s\"",
- get_namespace_name(RelationGetNamespace(OldHeap)),
- RelationGetRelationName(OldHeap))));
- rebuild_relation(OldHeap, rvtc->indexOid);
+ if (OidIsValid(indexOid))
+ ereport(verbose ? INFO : DEBUG2,
+ (errmsg("clustering \"%s.%s\"",
+ get_namespace_name(RelationGetNamespace(OldHeap)),
+ RelationGetRelationName(OldHeap))));
+ else
+ ereport(verbose ? INFO : DEBUG2,
+ (errmsg("vacuuming \"%s.%s\"",
+ get_namespace_name(RelationGetNamespace(OldHeap)),
+ RelationGetRelationName(OldHeap))));
+ rebuild_relation(OldHeap, indexOid, freeze_min_age, freeze_table_age);
/* NB: rebuild_relation does heap_close() on OldHeap */
}
/*
- * Verify that the specified index is a legitimate index to cluster on
+ * Verify that the specified heap and index are valid to cluster on
*
* Side effect: obtains exclusive lock on the index. The caller should
* already have exclusive lock on the table, so the index lock is likely
@@ -366,6 +375,38 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
{
Relation OldIndex;
+ /*
+ * Disallow clustering system relations. This will definitely NOT work
+ * for shared relations (we have no way to update pg_class rows in other
+ * databases), nor for nailed-in-cache relations (the relfilenode values
+ * for those are hardwired, see relcache.c). It might work for other
+ * system relations, but I ain't gonna risk it.
+ */
+ if (IsSystemRelation(OldHeap))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("\"%s\" is a system catalog",
+ RelationGetRelationName(OldHeap))));
+
+ /*
+ * Don't allow cluster on temp tables of other backends ... their local
+ * buffer manager is not going to cope.
+ */
+ if (RELATION_IS_OTHER_TEMP(OldHeap))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot cluster temporary tables of other sessions")));
+
+ /*
+ * Also check for active uses of the relation in the current transaction,
+ * including open scans and pending AFTER trigger events.
+ */
+ CheckTableNotInUse(OldHeap, "CLUSTER");
+
+ /* Skip checks for index if not specified. */
+ if (!OidIsValid(indexOid))
+ return;
+
OldIndex = index_open(indexOid, AccessExclusiveLock);
/*
@@ -448,34 +489,6 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
errmsg("cannot cluster on invalid index \"%s\"",
RelationGetRelationName(OldIndex))));
- /*
- * Disallow clustering system relations. This will definitely NOT work
- * for shared relations (we have no way to update pg_class rows in other
- * databases), nor for nailed-in-cache relations (the relfilenode values
- * for those are hardwired, see relcache.c). It might work for other
- * system relations, but I ain't gonna risk it.
- */
- if (IsSystemRelation(OldHeap))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("\"%s\" is a system catalog",
- RelationGetRelationName(OldHeap))));
-
- /*
- * Don't allow cluster on temp tables of other backends ... their local
- * buffer manager is not going to cope.
- */
- if (RELATION_IS_OTHER_TEMP(OldHeap))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot cluster temporary tables of other sessions")));
-
- /*
- * Also check for active uses of the relation in the current transaction,
- * including open scans and pending AFTER trigger events.
- */
- CheckTableNotInUse(OldHeap, "CLUSTER");
-
/* Drop relcache refcnt on OldIndex, but keep lock */
index_close(OldIndex, NoLock);
}
@@ -557,15 +570,16 @@ mark_index_clustered(Relation rel, Oid indexOid)
}
/*
- * rebuild_relation: rebuild an existing relation in index order
+ * 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
+ * indexOid: index to cluster by, or InvalidOid to rewrite in physical order.
*
* NB: this routine closes OldHeap at the right time; caller should not.
*/
static void
-rebuild_relation(Relation OldHeap, Oid indexOid)
+rebuild_relation(Relation OldHeap, Oid indexOid,
+ int freeze_min_age, int freeze_table_age)
{
Oid tableOid = RelationGetRelid(OldHeap);
Oid tableSpace = OldHeap->rd_rel->reltablespace;
@@ -576,7 +590,8 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
Relation newrel;
/* Mark the correct index as clustered */
- mark_index_clustered(OldHeap, indexOid);
+ if (OidIsValid(indexOid))
+ mark_index_clustered(OldHeap, indexOid);
/* Close relcache entry, but keep lock until transaction commit */
heap_close(OldHeap, NoLock);
@@ -599,7 +614,8 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
/*
* Copy the heap data into the new table in the desired order.
*/
- frozenXid = copy_heap_data(OIDNewHeap, tableOid, indexOid);
+ frozenXid = copy_heap_data(OIDNewHeap, tableOid, indexOid,
+ freeze_min_age, freeze_table_age);
/* To make the new heap's data visible (probably not needed?). */
CommandCounterIncrement();
@@ -758,7 +774,8 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
* freeze cutoff point for the tuples.
*/
static TransactionId
-copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
+copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
+ int freeze_min_age, int freeze_table_age)
{
Relation NewHeap,
OldHeap,
@@ -768,8 +785,8 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
int natts;
Datum *values;
bool *isnull;
- IndexScanDesc scan;
- HeapTuple tuple;
+ IndexScanDesc indexScan;
+ HeapScanDesc heapScan;
bool use_wal;
TransactionId OldestXmin;
TransactionId FreezeXid;
@@ -780,7 +797,10 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
*/
NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
- OldIndex = index_open(OIDOldIndex, AccessExclusiveLock);
+ if (OidIsValid(OIDOldIndex))
+ OldIndex = index_open(OIDOldIndex, AccessExclusiveLock);
+ else
+ OldIndex = NULL;
/*
* Their tuple descriptors should be exactly alike, but here we only need
@@ -809,8 +829,8 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
* freeze_min_age to avoid having CLUSTER freeze tuples earlier than a
* plain VACUUM would.
*/
- vacuum_set_xid_limits(-1, -1, OldHeap->rd_rel->relisshared,
- &OldestXmin, &FreezeXid, NULL);
+ vacuum_set_xid_limits(freeze_min_age, freeze_table_age,
+ OldHeap->rd_rel->relisshared, &OldestXmin, &FreezeXid, NULL);
/*
* FreezeXid will become the table's new relfrozenxid, and that mustn't go
@@ -828,25 +848,46 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
* copied, we scan with SnapshotAny and use HeapTupleSatisfiesVacuum for
* the visibility test.
*/
- scan = index_beginscan(OldHeap, OldIndex,
+ if (OldIndex != NULL)
+ indexScan = index_beginscan(OldHeap, OldIndex,
SnapshotAny, 0, (ScanKey) NULL);
+ else
+ heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL);
- while ((tuple = index_getnext(scan, ForwardScanDirection)) != NULL)
+ for (;;)
{
+ HeapTuple tuple;
HeapTuple copiedTuple;
+ Buffer buf;
bool isdead;
int i;
CHECK_FOR_INTERRUPTS();
- /* Since we used no scan keys, should never need to recheck */
- if (scan->xs_recheck)
- elog(ERROR, "CLUSTER does not support lossy index conditions");
+ if (OldIndex != NULL)
+ {
+ tuple = index_getnext(indexScan, ForwardScanDirection);
+ if (tuple == NULL)
+ break;
- LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
+ /* Since we used no scan keys, should never need to recheck */
+ if (indexScan->xs_recheck)
+ elog(ERROR, "CLUSTER does not support lossy index conditions");
- switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin,
- scan->xs_cbuf))
+ buf = indexScan->xs_cbuf;
+ }
+ else
+ {
+ tuple = heap_getnext(heapScan, ForwardScanDirection);
+ if (tuple == NULL)
+ break;
+
+ buf = heapScan->rs_cbuf;
+ }
+
+ LockBuffer(buf, BUFFER_LOCK_SHARE);
+
+ switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin, buf))
{
case HEAPTUPLE_DEAD:
/* Definitely dead */
@@ -888,7 +929,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
break;
}
- LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
+ LockBuffer(buf, BUFFER_LOCK_UNLOCK);
if (isdead)
{
@@ -932,7 +973,10 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
heap_freetuple(copiedTuple);
}
- index_endscan(scan);
+ if (OldIndex != NULL)
+ index_endscan(indexScan);
+ else
+ heap_endscan(heapScan);
/* Write out any remaining tuples, and fsync if needed */
end_heap_rewrite(rwstate);
@@ -940,7 +984,8 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
pfree(values);
pfree(isnull);
- index_close(OldIndex, NoLock);
+ if (OldIndex != NULL)
+ index_close(OldIndex, NoLock);
heap_close(OldHeap, NoLock);
heap_close(NewHeap, NoLock);