*** pgsql/src/backend/commands/cluster.c 2010/01/06 03:04:00 1.190 --- pgsql/src/backend/commands/cluster.c 2010/01/06 05:31:13 1.191 *************** *** 11,17 **** * * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.189 2010/01/02 16:57:37 momjian Exp $ * *------------------------------------------------------------------------- */ --- 11,17 ---- * * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.190 2010/01/06 03:04:00 momjian Exp $ * *------------------------------------------------------------------------- */ *************** typedef struct *** 61,69 **** } 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 List *get_tables_to_cluster(MemoryContext cluster_context); --- 61,70 ---- } RelToCluster; ! 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); *************** cluster(ClusterStmt *stmt, bool isTopLev *** 101,107 **** Oid tableOid, indexOid = InvalidOid; Relation rel; - RelToCluster rvtc; /* Find and lock the table */ rel = heap_openrv(stmt->relation, AccessExclusiveLock); --- 102,107 ---- *************** cluster(ClusterStmt *stmt, bool isTopLev *** 169,183 **** 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); } else { --- 169,179 ---- stmt->indexname, stmt->relation->relname))); } /* close relation, keep lock till commit */ heap_close(rel, NoLock); /* Do the job */ ! cluster_rel(tableOid, indexOid, false, stmt->verbose, -1, -1); } else { *************** cluster(ClusterStmt *stmt, bool isTopLev *** 226,232 **** StartTransactionCommand(); /* functions in indexes may want a snapshot set */ PushActiveSnapshot(GetTransactionSnapshot()); ! cluster_rel(rvtc, true, stmt->verbose); PopActiveSnapshot(); CommitTransactionCommand(); } --- 222,228 ---- StartTransactionCommand(); /* functions in indexes may want a snapshot set */ PushActiveSnapshot(GetTransactionSnapshot()); ! cluster_rel(rvtc->tableOid, rvtc->indexOid, true, stmt->verbose, -1, -1); PopActiveSnapshot(); CommitTransactionCommand(); } *************** cluster(ClusterStmt *stmt, bool isTopLev *** 252,260 **** * 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. */ ! static void ! cluster_rel(RelToCluster *rvtc, bool recheck, bool verbose) { Relation OldHeap; --- 248,260 ---- * 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. */ ! void ! cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose, ! int freeze_min_age, int freeze_table_age) { Relation OldHeap; *************** cluster_rel(RelToCluster *rvtc, bool rec *** 267,273 **** * case, since cluster() already did it.) The index lock is taken inside * check_index_is_clusterable. */ ! OldHeap = try_relation_open(rvtc->tableOid, AccessExclusiveLock); /* If the table has gone away, we can skip processing it */ if (!OldHeap) --- 267,273 ---- * 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) *************** cluster_rel(RelToCluster *rvtc, bool rec *** 287,293 **** Form_pg_index indexForm; /* Check that the user still owns the relation */ ! if (!pg_class_ownercheck(rvtc->tableOid, GetUserId())) { relation_close(OldHeap, AccessExclusiveLock); return; --- 287,293 ---- Form_pg_index indexForm; /* Check that the user still owns the relation */ ! if (!pg_class_ownercheck(tableOid, GetUserId())) { relation_close(OldHeap, AccessExclusiveLock); return; *************** cluster_rel(RelToCluster *rvtc, bool rec *** 308,360 **** return; } ! /* ! * Check that the index still exists ! */ ! if (!SearchSysCacheExists(RELOID, ! ObjectIdGetDatum(rvtc->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) ! { ReleaseSysCache(tuple); - relation_close(OldHeap, AccessExclusiveLock); - return; } - ReleaseSysCache(tuple); } ! /* Check index is valid to cluster on */ ! check_index_is_clusterable(OldHeap, rvtc->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); /* NB: rebuild_relation does heap_close() on OldHeap */ } /* ! * Verify that the specified index is a legitimate index 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 --- 308,369 ---- return; } ! if (OidIsValid(indexOid)) { ! /* ! * 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(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); } } ! /* Check heap and index are valid to cluster on */ ! check_index_is_clusterable(OldHeap, indexOid, recheck); /* rebuild_relation does all the dirty work */ ! 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 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 *************** check_index_is_clusterable(Relation OldH *** 366,371 **** --- 375,412 ---- { 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); /* *************** check_index_is_clusterable(Relation OldH *** 448,481 **** 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); } --- 489,494 ---- *************** mark_index_clustered(Relation rel, Oid i *** 557,571 **** } /* ! * rebuild_relation: rebuild an existing relation in index order * * OldHeap: table to rebuild --- must be opened and exclusive-locked! ! * indexOid: index to cluster by * * NB: this routine closes OldHeap at the right time; caller should not. */ static void ! rebuild_relation(Relation OldHeap, Oid indexOid) { Oid tableOid = RelationGetRelid(OldHeap); Oid tableSpace = OldHeap->rd_rel->reltablespace; --- 570,585 ---- } /* ! * 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. * * NB: this routine closes OldHeap at the right time; caller should not. */ static void ! rebuild_relation(Relation OldHeap, Oid indexOid, ! int freeze_min_age, int freeze_table_age) { Oid tableOid = RelationGetRelid(OldHeap); Oid tableSpace = OldHeap->rd_rel->reltablespace; *************** rebuild_relation(Relation OldHeap, Oid i *** 576,582 **** Relation newrel; /* Mark the correct index as clustered */ ! mark_index_clustered(OldHeap, indexOid); /* Close relcache entry, but keep lock until transaction commit */ heap_close(OldHeap, NoLock); --- 590,597 ---- Relation newrel; /* Mark the correct index as clustered */ ! if (OidIsValid(indexOid)) ! mark_index_clustered(OldHeap, indexOid); /* Close relcache entry, but keep lock until transaction commit */ heap_close(OldHeap, NoLock); *************** rebuild_relation(Relation OldHeap, Oid i *** 599,605 **** /* * Copy the heap data into the new table in the desired order. */ ! frozenXid = copy_heap_data(OIDNewHeap, tableOid, indexOid); /* To make the new heap's data visible (probably not needed?). */ CommandCounterIncrement(); --- 614,621 ---- /* * Copy the heap data into the new table in the desired order. */ ! 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(); *************** make_new_heap(Oid OIDOldHeap, const char *** 758,764 **** * freeze cutoff point for the tuples. */ static TransactionId ! copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) { Relation NewHeap, OldHeap, --- 774,781 ---- * freeze cutoff point for the tuples. */ static TransactionId ! copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, ! int freeze_min_age, int freeze_table_age) { Relation NewHeap, OldHeap, *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 768,775 **** int natts; Datum *values; bool *isnull; ! IndexScanDesc scan; ! HeapTuple tuple; bool use_wal; TransactionId OldestXmin; TransactionId FreezeXid; --- 785,792 ---- int natts; Datum *values; bool *isnull; ! IndexScanDesc indexScan; ! HeapScanDesc heapScan; bool use_wal; TransactionId OldestXmin; TransactionId FreezeXid; *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 780,786 **** */ NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock); OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock); ! OldIndex = index_open(OIDOldIndex, AccessExclusiveLock); /* * Their tuple descriptors should be exactly alike, but here we only need --- 797,806 ---- */ NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock); OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock); ! if (OidIsValid(OIDOldIndex)) ! OldIndex = index_open(OIDOldIndex, AccessExclusiveLock); ! else ! OldIndex = NULL; /* * Their tuple descriptors should be exactly alike, but here we only need *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 809,816 **** * 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); /* * FreezeXid will become the table's new relfrozenxid, and that mustn't go --- 829,836 ---- * freeze_min_age to avoid having CLUSTER freeze tuples earlier than a * plain VACUUM would. */ ! 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 *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 828,852 **** * copied, we scan with SnapshotAny and use HeapTupleSatisfiesVacuum for * the visibility test. */ ! scan = index_beginscan(OldHeap, OldIndex, SnapshotAny, 0, (ScanKey) NULL); ! while ((tuple = index_getnext(scan, ForwardScanDirection)) != NULL) { HeapTuple copiedTuple; 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"); ! LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE); ! switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin, ! scan->xs_cbuf)) { case HEAPTUPLE_DEAD: /* Definitely dead */ --- 848,893 ---- * copied, we scan with SnapshotAny and use HeapTupleSatisfiesVacuum for * the visibility test. */ ! if (OldIndex != NULL) ! indexScan = index_beginscan(OldHeap, OldIndex, SnapshotAny, 0, (ScanKey) NULL); + else + heapScan = heap_beginscan(OldHeap, SnapshotAny, 0, (ScanKey) NULL); ! for (;;) { + HeapTuple tuple; HeapTuple copiedTuple; + Buffer buf; bool isdead; int i; CHECK_FOR_INTERRUPTS(); ! if (OldIndex != NULL) ! { ! tuple = index_getnext(indexScan, ForwardScanDirection); ! if (tuple == NULL) ! break; ! /* Since we used no scan keys, should never need to recheck */ ! if (indexScan->xs_recheck) ! elog(ERROR, "CLUSTER does not support lossy index conditions"); ! 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 */ *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 888,894 **** break; } ! LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK); if (isdead) { --- 929,935 ---- break; } ! LockBuffer(buf, BUFFER_LOCK_UNLOCK); if (isdead) { *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 932,938 **** heap_freetuple(copiedTuple); } ! index_endscan(scan); /* Write out any remaining tuples, and fsync if needed */ end_heap_rewrite(rwstate); --- 973,982 ---- heap_freetuple(copiedTuple); } ! if (OldIndex != NULL) ! index_endscan(indexScan); ! else ! heap_endscan(heapScan); /* Write out any remaining tuples, and fsync if needed */ end_heap_rewrite(rwstate); *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 940,946 **** pfree(values); pfree(isnull); ! index_close(OldIndex, NoLock); heap_close(OldHeap, NoLock); heap_close(NewHeap, NoLock); --- 984,991 ---- pfree(values); pfree(isnull); ! if (OldIndex != NULL) ! index_close(OldIndex, NoLock); heap_close(OldHeap, NoLock); heap_close(NewHeap, NoLock);