At the moment, amgettuple only returns pointers to heap tuples. There is
no way to return data from the index tuples. That needs to be changed to
support index-only scans.
I propose that we split index_getnext into two steps: fetching the next
match from the index (index_next()), and fetching the corresponding heap
tuple (index_fetch()). Patch attached. There is still a shorthand
index_getnext() that is compatible with the old function, but it now
just calls index_next()+index_fetch().
The new index_fetch() function can only fetch one match from a HOT
chain. That greatly simplifies the code in indexam.c. The only caller
needing to fetch more than one tuple from a HOT chain (= using
SnapshotAny) is CLUSTER, so I moved the HOT-chain following logic into
cluster.c, with small changes to heap_hot_search_buffer().
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
***************
*** 1490,1497 **** heap_fetch(Relation relation,
* On entry, *tid is the TID of a tuple (either a simple tuple, or the root
* of a HOT chain), and buffer is the buffer holding this tuple. We search
* for the first chain member satisfying the given snapshot. If one is
! * found, we update *tid to reference that tuple's offset number, and
! * return TRUE. If no match, return FALSE without modifying *tid.
*
* If all_dead is not NULL, we check non-visible tuples to see if they are
* globally dead; *all_dead is set TRUE if all members of the HOT chain
--- 1490,1503 ----
* On entry, *tid is the TID of a tuple (either a simple tuple, or the root
* of a HOT chain), and buffer is the buffer holding this tuple. We search
* for the first chain member satisfying the given snapshot. If one is
! * found, we update *tid and *heapTuple to reference that tuple, and
! * return TRUE. If no match, return FALSE without modifying *tid (*heapTuple
! * is undefined in that case).
! *
! * To fetch the next matching tuple in the chain, call again with 'firstCall'
! * set to FALSE and *tid still pointing to the tuple returned by previous call.
! * (normally only one tuple in a chain can be visible at a time, but that does
! * not hold for special snapshots like SnapshotAny)
*
* If all_dead is not NULL, we check non-visible tuples to see if they are
* globally dead; *all_dead is set TRUE if all members of the HOT chain
***************
*** 1503,1529 **** heap_fetch(Relation relation,
*/
bool
heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
! bool *all_dead)
{
Page dp = (Page) BufferGetPage(buffer);
TransactionId prev_xmax = InvalidTransactionId;
OffsetNumber offnum;
bool at_chain_start;
! if (all_dead)
*all_dead = true;
Assert(TransactionIdIsValid(RecentGlobalXmin));
Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
offnum = ItemPointerGetOffsetNumber(tid);
! at_chain_start = true;
/* Scan through possible multiple members of HOT-chain */
for (;;)
{
ItemId lp;
- HeapTupleData heapTuple;
/* check for bogus TID */
if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
--- 1509,1537 ----
*/
bool
heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
! HeapTuple heapTuple, bool *all_dead, bool firstCall)
{
Page dp = (Page) BufferGetPage(buffer);
TransactionId prev_xmax = InvalidTransactionId;
OffsetNumber offnum;
bool at_chain_start;
+ bool skip;
! if (all_dead && firstCall)
*all_dead = true;
Assert(TransactionIdIsValid(RecentGlobalXmin));
Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
offnum = ItemPointerGetOffsetNumber(tid);
!
! at_chain_start = firstCall;
! skip = !firstCall;
/* Scan through possible multiple members of HOT-chain */
for (;;)
{
ItemId lp;
/* check for bogus TID */
if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
***************
*** 1546,1558 **** heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
break;
}
! heapTuple.t_data = (HeapTupleHeader) PageGetItem(dp, lp);
! heapTuple.t_len = ItemIdGetLength(lp);
/*
* Shouldn't see a HEAP_ONLY tuple at chain start.
*/
! if (at_chain_start && HeapTupleIsHeapOnly(&heapTuple))
break;
/*
--- 1554,1566 ----
break;
}
! heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
! heapTuple->t_len = ItemIdGetLength(lp);
/*
* Shouldn't see a HEAP_ONLY tuple at chain start.
*/
! if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
break;
/*
***************
*** 1561,1577 **** heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
*/
if (TransactionIdIsValid(prev_xmax) &&
!TransactionIdEquals(prev_xmax,
! HeapTupleHeaderGetXmin(heapTuple.t_data)))
break;
/* If it's visible per the snapshot, we must return it */
! if (HeapTupleSatisfiesVisibility(&heapTuple, snapshot, buffer))
{
ItemPointerSetOffsetNumber(tid, offnum);
if (all_dead)
*all_dead = false;
return true;
}
/*
* If we can't see it, maybe no one else can either. At caller
--- 1569,1586 ----
*/
if (TransactionIdIsValid(prev_xmax) &&
!TransactionIdEquals(prev_xmax,
! HeapTupleHeaderGetXmin(heapTuple->t_data)))
break;
/* If it's visible per the snapshot, we must return it */
! if (!skip && HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer))
{
ItemPointerSetOffsetNumber(tid, offnum);
if (all_dead)
*all_dead = false;
return true;
}
+ skip = false;
/*
* If we can't see it, maybe no one else can either. At caller
***************
*** 1579,1585 **** heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
* transactions.
*/
if (all_dead && *all_dead &&
! HeapTupleSatisfiesVacuum(heapTuple.t_data, RecentGlobalXmin,
buffer) != HEAPTUPLE_DEAD)
*all_dead = false;
--- 1588,1594 ----
* transactions.
*/
if (all_dead && *all_dead &&
! HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
buffer) != HEAPTUPLE_DEAD)
*all_dead = false;
***************
*** 1587,1599 **** heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
* Check to see if HOT chain continues past this tuple; if so fetch
* the next offnum and loop around.
*/
! if (HeapTupleIsHotUpdated(&heapTuple))
{
! Assert(ItemPointerGetBlockNumber(&heapTuple.t_data->t_ctid) ==
ItemPointerGetBlockNumber(tid));
! offnum = ItemPointerGetOffsetNumber(&heapTuple.t_data->t_ctid);
at_chain_start = false;
! prev_xmax = HeapTupleHeaderGetXmax(heapTuple.t_data);
}
else
break; /* end of chain */
--- 1596,1608 ----
* Check to see if HOT chain continues past this tuple; if so fetch
* the next offnum and loop around.
*/
! if (HeapTupleIsHotUpdated(heapTuple))
{
! Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
ItemPointerGetBlockNumber(tid));
! offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
at_chain_start = false;
! prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
}
else
break; /* end of chain */
***************
*** 1615,1624 **** heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
{
bool result;
Buffer buffer;
buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
LockBuffer(buffer, BUFFER_LOCK_SHARE);
! result = heap_hot_search_buffer(tid, buffer, snapshot, all_dead);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
ReleaseBuffer(buffer);
return result;
--- 1624,1635 ----
{
bool result;
Buffer buffer;
+ HeapTupleData heapTuple;
buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
LockBuffer(buffer, BUFFER_LOCK_SHARE);
! result = heap_hot_search_buffer(tid, buffer, snapshot, &heapTuple,
! all_dead, true);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
ReleaseBuffer(buffer);
return result;
*** a/src/backend/access/index/genam.c
--- b/src/backend/access/index/genam.c
***************
*** 97,105 **** RelationGetIndexScan(Relation indexRelation,
ItemPointerSetInvalid(&scan->xs_ctup.t_self);
scan->xs_ctup.t_data = NULL;
scan->xs_cbuf = InvalidBuffer;
- scan->xs_hot_dead = false;
- scan->xs_next_hot = InvalidOffsetNumber;
- scan->xs_prev_xmax = InvalidTransactionId;
/*
* Let the AM fill in the key and any opaque data it wants.
--- 97,102 ----
*** a/src/backend/access/index/indexam.c
--- b/src/backend/access/index/indexam.c
***************
*** 20,26 ****
* index_insert - insert an index tuple into a relation
* index_markpos - mark a scan position
* index_restrpos - restore a scan position
! * index_getnext - get the next tuple from a scan
* index_getbitmap - get all tuples from a scan
* index_bulk_delete - bulk deletion of index tuples
* index_vacuum_cleanup - post-deletion cleanup of an index
--- 20,27 ----
* index_insert - insert an index tuple into a relation
* index_markpos - mark a scan position
* index_restrpos - restore a scan position
! * index_next - advance index scan to next match
! * index_fetch - fetch heap tuple for current index scan position
* index_getbitmap - get all tuples from a scan
* index_bulk_delete - bulk deletion of index tuples
* index_vacuum_cleanup - post-deletion cleanup of an index
***************
*** 310,317 **** index_rescan(IndexScanDesc scan, ScanKey key)
scan->xs_cbuf = InvalidBuffer;
}
- scan->xs_next_hot = InvalidOffsetNumber;
-
scan->kill_prior_tuple = false; /* for safety */
FunctionCall2(procedure,
--- 311,316 ----
***************
*** 389,420 **** index_restrpos(IndexScanDesc scan)
SCAN_CHECKS;
GET_SCAN_PROCEDURE(amrestrpos);
- scan->xs_next_hot = InvalidOffsetNumber;
-
scan->kill_prior_tuple = false; /* for safety */
FunctionCall1(procedure, PointerGetDatum(scan));
}
/* ----------------
! * index_getnext - get the next heap tuple from a scan
*
! * The result is the next heap tuple satisfying the scan keys and the
! * snapshot, or NULL if no more matching tuples exist. On success,
! * the buffer containing the heap tuple is pinned (the pin will be dropped
! * at the next index_getnext or index_endscan).
*
* Note: caller must check scan->xs_recheck, and perform rechecking of the
* scan keys if required. We do not do that here because we don't have
* enough information to do it efficiently in the general case.
* ----------------
*/
! HeapTuple
! index_getnext(IndexScanDesc scan, ScanDirection direction)
{
- HeapTuple heapTuple = &scan->xs_ctup;
- ItemPointer tid = &heapTuple->t_self;
FmgrInfo *procedure;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(amgettuple);
--- 388,415 ----
SCAN_CHECKS;
GET_SCAN_PROCEDURE(amrestrpos);
scan->kill_prior_tuple = false; /* for safety */
FunctionCall1(procedure, PointerGetDatum(scan));
}
/* ----------------
! * index_next - get the next index tuple from a scan
*
! * Advances to the next index tuple satisfying the scan keys, returning TRUE
! * if there was one, FALSE otherwise. The heap TID pointer of the next match
! * is stored in scan->xs_ctup.self.
*
* Note: caller must check scan->xs_recheck, and perform rechecking of the
* scan keys if required. We do not do that here because we don't have
* enough information to do it efficiently in the general case.
* ----------------
*/
! bool
! index_next(IndexScanDesc scan, ScanDirection direction)
{
FmgrInfo *procedure;
+ bool found;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(amgettuple);
***************
*** 422,641 **** index_getnext(IndexScanDesc scan, ScanDirection direction)
Assert(TransactionIdIsValid(RecentGlobalXmin));
/*
! * We always reset xs_hot_dead; if we are here then either we are just
! * starting the scan, or we previously returned a visible tuple, and in
! * either case it's inappropriate to kill the prior index entry.
*/
! scan->xs_hot_dead = false;
! for (;;)
! {
! OffsetNumber offnum;
! bool at_chain_start;
! Page dp;
! if (scan->xs_next_hot != InvalidOffsetNumber)
! {
! /*
! * We are resuming scan of a HOT chain after having returned an
! * earlier member. Must still hold pin on current heap page.
! */
! Assert(BufferIsValid(scan->xs_cbuf));
! Assert(ItemPointerGetBlockNumber(tid) ==
! BufferGetBlockNumber(scan->xs_cbuf));
! Assert(TransactionIdIsValid(scan->xs_prev_xmax));
! offnum = scan->xs_next_hot;
! at_chain_start = false;
! scan->xs_next_hot = InvalidOffsetNumber;
! }
! else
{
! bool found;
! Buffer prev_buf;
!
! /*
! * If we scanned a whole HOT chain and found only dead tuples,
! * tell index AM to kill its entry for that TID.
! */
! scan->kill_prior_tuple = scan->xs_hot_dead;
!
! /*
! * The AM's gettuple proc finds the next index entry matching the
! * scan keys, and puts the TID in xs_ctup.t_self (ie, *tid). It
! * should also set scan->xs_recheck, though we pay no attention to
! * that here.
! */
! found = DatumGetBool(FunctionCall2(procedure,
! PointerGetDatum(scan),
! Int32GetDatum(direction)));
!
! /* Reset kill flag immediately for safety */
! scan->kill_prior_tuple = false;
!
! /* If we're out of index entries, break out of outer loop */
! if (!found)
! break;
!
! pgstat_count_index_tuples(scan->indexRelation, 1);
!
! /* Switch to correct buffer if we don't have it already */
! prev_buf = scan->xs_cbuf;
! scan->xs_cbuf = ReleaseAndReadBuffer(scan->xs_cbuf,
! scan->heapRelation,
! ItemPointerGetBlockNumber(tid));
!
! /*
! * Prune page, but only if we weren't already on this page
! */
! if (prev_buf != scan->xs_cbuf)
! heap_page_prune_opt(scan->heapRelation, scan->xs_cbuf,
! RecentGlobalXmin);
!
! /* Prepare to scan HOT chain starting at index-referenced offnum */
! offnum = ItemPointerGetOffsetNumber(tid);
! at_chain_start = true;
!
! /* We don't know what the first tuple's xmin should be */
! scan->xs_prev_xmax = InvalidTransactionId;
!
! /* Initialize flag to detect if all entries are dead */
! scan->xs_hot_dead = true;
}
! /* Obtain share-lock on the buffer so we can examine visibility */
! LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
! dp = (Page) BufferGetPage(scan->xs_cbuf);
- /* Scan through possible multiple members of HOT-chain */
- for (;;)
- {
- ItemId lp;
- ItemPointer ctid;
-
- /* check for bogus TID */
- if (offnum < FirstOffsetNumber ||
- offnum > PageGetMaxOffsetNumber(dp))
- break;
-
- lp = PageGetItemId(dp, offnum);
-
- /* check for unused, dead, or redirected items */
- if (!ItemIdIsNormal(lp))
- {
- /* We should only see a redirect at start of chain */
- if (ItemIdIsRedirected(lp) && at_chain_start)
- {
- /* Follow the redirect */
- offnum = ItemIdGetRedirect(lp);
- at_chain_start = false;
- continue;
- }
- /* else must be end of chain */
- break;
- }
-
- /*
- * We must initialize all of *heapTuple (ie, scan->xs_ctup) since
- * it is returned to the executor on success.
- */
- heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
- heapTuple->t_len = ItemIdGetLength(lp);
- ItemPointerSetOffsetNumber(tid, offnum);
- heapTuple->t_tableOid = RelationGetRelid(scan->heapRelation);
- ctid = &heapTuple->t_data->t_ctid;
-
- /*
- * Shouldn't see a HEAP_ONLY tuple at chain start. (This test
- * should be unnecessary, since the chain root can't be removed
- * while we have pin on the index entry, but let's make it
- * anyway.)
- */
- if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
- break;
-
- /*
- * The xmin should match the previous xmax value, else chain is
- * broken. (Note: this test is not optional because it protects
- * us against the case where the prior chain member's xmax aborted
- * since we looked at it.)
- */
- if (TransactionIdIsValid(scan->xs_prev_xmax) &&
- !TransactionIdEquals(scan->xs_prev_xmax,
- HeapTupleHeaderGetXmin(heapTuple->t_data)))
- break;
-
- /* If it's visible per the snapshot, we must return it */
- if (HeapTupleSatisfiesVisibility(heapTuple, scan->xs_snapshot,
- scan->xs_cbuf))
- {
- /*
- * If the snapshot is MVCC, we know that it could accept at
- * most one member of the HOT chain, so we can skip examining
- * any more members. Otherwise, check for continuation of the
- * HOT-chain, and set state for next time.
- */
- if (IsMVCCSnapshot(scan->xs_snapshot))
- scan->xs_next_hot = InvalidOffsetNumber;
- else if (HeapTupleIsHotUpdated(heapTuple))
- {
- Assert(ItemPointerGetBlockNumber(ctid) ==
- ItemPointerGetBlockNumber(tid));
- scan->xs_next_hot = ItemPointerGetOffsetNumber(ctid);
- scan->xs_prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
- }
- else
- scan->xs_next_hot = InvalidOffsetNumber;
-
- LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
-
- pgstat_count_heap_fetch(scan->indexRelation);
-
- return heapTuple;
- }
-
- /*
- * If we can't see it, maybe no one else can either. Check to see
- * if the tuple is dead to all transactions. If we find that all
- * the tuples in the HOT chain are dead, we'll signal the index AM
- * to not return that TID on future indexscans.
- */
- if (scan->xs_hot_dead &&
- HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
- scan->xs_cbuf) != HEAPTUPLE_DEAD)
- scan->xs_hot_dead = false;
-
- /*
- * Check to see if HOT chain continues past this tuple; if so
- * fetch the next offnum (we don't bother storing it into
- * xs_next_hot, but must store xs_prev_xmax), and loop around.
- */
- if (HeapTupleIsHotUpdated(heapTuple))
- {
- Assert(ItemPointerGetBlockNumber(ctid) ==
- ItemPointerGetBlockNumber(tid));
- offnum = ItemPointerGetOffsetNumber(ctid);
- at_chain_start = false;
- scan->xs_prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
- }
- else
- break; /* end of chain */
- } /* loop over a single HOT chain */
-
- LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
-
- /* Loop around to ask index AM for another TID */
- scan->xs_next_hot = InvalidOffsetNumber;
- }
! /* Release any held pin on a heap page */
! if (BufferIsValid(scan->xs_cbuf))
{
! ReleaseBuffer(scan->xs_cbuf);
! scan->xs_cbuf = InvalidBuffer;
}
! return NULL; /* failure exit */
}
/* ----------------
--- 417,541 ----
Assert(TransactionIdIsValid(RecentGlobalXmin));
/*
! * The AM's gettuple proc finds the next index entry matching the scan
! * keys, and puts the TID in xs_ctup.t_self. It should also set
! * scan->xs_recheck, though we pay no attention to that here.
*/
! found = DatumGetBool(FunctionCall2(procedure,
! PointerGetDatum(scan),
! Int32GetDatum(direction)));
! /* Reset kill flag immediately for safety */
! scan->kill_prior_tuple = false;
! /* If we're out of index entries, release any held pin on a heap page */
! if (!found)
! {
! if (BufferIsValid(scan->xs_cbuf))
{
! ReleaseBuffer(scan->xs_cbuf);
! scan->xs_cbuf = InvalidBuffer;
}
+ return false;
+ }
! pgstat_count_index_tuples(scan->indexRelation, 1);
! return true;
! }
! /* ----------------
! * index_fetch - fetch the heap tuple the current index pointer points to
! *
! * The result is the heap tuple the current index tuple points to if it
! * matches the snapshot, or NULL if it doesn't. On success, the buffer
! * containing the heap tuple is pinned (the pin will be dropped at the next
! * index_fetch or index_endscan).
! * ----------------
! */
! HeapTuple
! index_fetch(IndexScanDesc scan)
! {
! ItemPointer tid = &scan->xs_ctup.t_self;
! bool found;
! Buffer prev_buf;
!
! /*
! * We only fetch the first matching tuple in a chain, so we can't support
! * SnapshotAny. All other snapshots only consider one tuple in a HOT chain
! * as visible at any given moment.
! */
! Assert(scan->xs_snapshot != SnapshotAny);
!
! /* Switch to correct buffer if we don't have it already */
! prev_buf = scan->xs_cbuf;
! scan->xs_cbuf = ReleaseAndReadBuffer(scan->xs_cbuf,
! scan->heapRelation,
! ItemPointerGetBlockNumber(tid));
!
! /*
! * Prune page, but only if we weren't already on this page
! */
! if (prev_buf != scan->xs_cbuf)
! heap_page_prune_opt(scan->heapRelation, scan->xs_cbuf,
! RecentGlobalXmin);
!
! /* Obtain share-lock on the buffer so we can examine visibility */
! LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
!
! found = heap_hot_search_buffer(tid, scan->xs_cbuf,
! scan->xs_snapshot, &scan->xs_ctup,
! &scan->kill_prior_tuple, true);
! LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
!
! if (found)
{
! Assert(!scan->kill_prior_tuple);
!
! /*
! * We must initialize all of *heapTuple (ie, scan->xs_ctup) since
! * it is returned to the executor on success.
! */
! scan->xs_ctup.t_tableOid = RelationGetRelid(scan->heapRelation);
!
! pgstat_count_heap_fetch(scan->indexRelation);
!
! return &scan->xs_ctup;
}
+ else
+ return NULL;
+ }
! /* ----------------
! * index_getnext - get the next heap tuple from a scan
! *
! * This is a shorthand for index_next() + index_fetch().
! *
! * The result is the next heap tuple satisfying the scan keys and the
! * snapshot, or NULL if no more matching tuples exist. On success,
! * the buffer containing the heap tuple is pinned (the pin will be dropped
! * at the next index_getnext or index_endscan).
! *
! * Note: caller must check scan->xs_recheck, and perform rechecking of the
! * scan keys if required. We do not do that here because we don't have
! * enough information to do it efficiently in the general case.
! * ----------------
! */
! HeapTuple
! index_getnext(IndexScanDesc scan, ScanDirection direction)
! {
! for(;;)
! {
! HeapTuple tup;
!
! if (!index_next(scan, direction))
! return NULL;
!
! tup = index_fetch(scan);
! if (tup != NULL)
! return tup;
! }
}
/* ----------------
*** a/src/backend/commands/cluster.c
--- b/src/backend/commands/cluster.c
***************
*** 772,777 **** copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
--- 772,778 ----
TransactionId OldestXmin;
TransactionId FreezeXid;
RewriteState rwstate;
+ ItemPointerData tid;
/*
* Open the relations we need.
***************
*** 829,847 **** copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
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))
--- 830,885 ----
scan = index_beginscan(OldHeap, OldIndex,
SnapshotAny, 0, (ScanKey) NULL);
! tuple = NULL;
! for(;;)
{
HeapTuple copiedTuple;
bool isdead;
int i;
+ bool found;
CHECK_FOR_INTERRUPTS();
! /* Advance to next index tuple if we're done with current HOT chain */
! if (tuple == NULL)
! {
! if (!index_next(scan, ForwardScanDirection))
! break;
!
! /* Since we used no scan keys, should never need to recheck */
! if (scan->xs_recheck)
! elog(ERROR, "CLUSTER does not support lossy index conditions");
!
! tid = scan->xs_ctup.t_self;
! scan->xs_cbuf = ReleaseAndReadBuffer(scan->xs_cbuf, OldHeap,
! ItemPointerGetBlockNumber(&tid));
! /* Obtain share-lock on the buffer so we can examine visibility */
! LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
! found = heap_hot_search_buffer(&tid, scan->xs_cbuf,
! scan->xs_snapshot, &scan->xs_ctup,
! &scan->kill_prior_tuple, true);
! if (!found)
! {
! LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
! break;
! }
! }
! else
! {
! /* Fetch next tuple from current HOT chain */
! LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
! found = heap_hot_search_buffer(&tid, scan->xs_cbuf,
! scan->xs_snapshot, &scan->xs_ctup,
! &scan->kill_prior_tuple, false);
! if (!found)
! {
! LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
! tuple = NULL;
! continue;
! }
! }
! tuple = &scan->xs_ctup;
switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin,
scan->xs_cbuf))
*** a/src/backend/executor/nodeBitmapHeapscan.c
--- b/src/backend/executor/nodeBitmapHeapscan.c
***************
*** 382,390 **** bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
{
OffsetNumber offnum = tbmres->offsets[curslot];
ItemPointerData tid;
ItemPointerSet(&tid, page, offnum);
! if (heap_hot_search_buffer(&tid, buffer, snapshot, NULL))
scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
}
--- 382,392 ----
{
OffsetNumber offnum = tbmres->offsets[curslot];
ItemPointerData tid;
+ HeapTupleData heapTuple;
ItemPointerSet(&tid, page, offnum);
! if (heap_hot_search_buffer(&tid, buffer, snapshot, &heapTuple,
! NULL, true))
scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
}
*** a/src/include/access/genam.h
--- b/src/include/access/genam.h
***************
*** 117,122 **** extern void index_endscan(IndexScanDesc scan);
--- 117,125 ----
extern void index_markpos(IndexScanDesc scan);
extern void index_restrpos(IndexScanDesc scan);
extern HeapTuple index_getnext(IndexScanDesc scan, ScanDirection direction);
+ extern bool index_next(IndexScanDesc scan, ScanDirection direction);
+ extern HeapTuple index_fetch(IndexScanDesc scan);
+
extern int64 index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap);
extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info,
*** a/src/include/access/heapam.h
--- b/src/include/access/heapam.h
***************
*** 83,89 **** extern bool heap_fetch(Relation relation, Snapshot snapshot,
HeapTuple tuple, Buffer *userbuf, bool keep_buf,
Relation stats_relation);
extern bool heap_hot_search_buffer(ItemPointer tid, Buffer buffer,
! Snapshot snapshot, bool *all_dead);
extern bool heap_hot_search(ItemPointer tid, Relation relation,
Snapshot snapshot, bool *all_dead);
--- 83,90 ----
HeapTuple tuple, Buffer *userbuf, bool keep_buf,
Relation stats_relation);
extern bool heap_hot_search_buffer(ItemPointer tid, Buffer buffer,
! Snapshot snapshot, HeapTuple tuple,
! bool *all_dead, bool firstCall);
extern bool heap_hot_search(ItemPointer tid, Relation relation,
Snapshot snapshot, bool *all_dead);
*** a/src/include/access/relscan.h
--- b/src/include/access/relscan.h
***************
*** 77,87 **** typedef struct IndexScanDescData
Buffer xs_cbuf; /* current heap buffer in scan, if any */
/* NB: if xs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
bool xs_recheck; /* T means scan keys must be rechecked */
-
- /* state data for traversing HOT chains in index_getnext */
- bool xs_hot_dead; /* T if all members of HOT chain are dead */
- OffsetNumber xs_next_hot; /* next member of HOT chain, if any */
- TransactionId xs_prev_xmax; /* previous HOT chain member's XMAX, if any */
} IndexScanDescData;
/* Struct for heap-or-index scans of system tables */
--- 77,82 ----
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers