mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Mark index entries "killed" when they are no longer visible to any
transaction, so as to avoid returning them out of the index AM. Saves repeated heap_fetch operations on frequently-updated rows. Also detect queries on unique keys (equality to all columns of a unique index), and don't bother continuing scan once we have found first match. Killing is implemented in the btree and hash AMs, but not yet in rtree or gist, because there isn't an equally convenient place to do it in those AMs (the outer amgetnext routine can't do it without re-pinning the index page). Did some small cleanup on APIs of HeapTupleSatisfies, heap_fetch, and index_insert to make this a little easier.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.57 2002/05/20 23:51:41 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.58 2002/05/24 18:57:55 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* This file contains only the public interface routines.
|
||||
@@ -166,8 +166,8 @@ hashinsert(PG_FUNCTION_ARGS)
|
||||
ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
|
||||
#ifdef NOT_USED
|
||||
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
|
||||
bool checkUnique = PG_GETARG_BOOL(5);
|
||||
#endif
|
||||
|
||||
InsertIndexResult res;
|
||||
HashItem hitem;
|
||||
IndexTuple itup;
|
||||
@@ -210,6 +210,9 @@ hashgettuple(PG_FUNCTION_ARGS)
|
||||
{
|
||||
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
|
||||
ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
|
||||
HashScanOpaque so = (HashScanOpaque) scan->opaque;
|
||||
Page page;
|
||||
OffsetNumber offnum;
|
||||
bool res;
|
||||
|
||||
/*
|
||||
@@ -217,12 +220,49 @@ hashgettuple(PG_FUNCTION_ARGS)
|
||||
* the appropriate direction. If we haven't done so yet, we call a
|
||||
* routine to get the first item in the scan.
|
||||
*/
|
||||
|
||||
if (ItemPointerIsValid(&(scan->currentItemData)))
|
||||
{
|
||||
/*
|
||||
* Check to see if we should kill the previously-fetched tuple.
|
||||
*/
|
||||
if (scan->kill_prior_tuple)
|
||||
{
|
||||
/*
|
||||
* Yes, so mark it by setting the LP_DELETE bit in the item flags.
|
||||
*/
|
||||
offnum = ItemPointerGetOffsetNumber(&(scan->currentItemData));
|
||||
page = BufferGetPage(so->hashso_curbuf);
|
||||
PageGetItemId(page, offnum)->lp_flags |= LP_DELETE;
|
||||
/*
|
||||
* Since this can be redone later if needed, it's treated the
|
||||
* same as a commit-hint-bit status update for heap tuples:
|
||||
* we mark the buffer dirty but don't make a WAL log entry.
|
||||
*/
|
||||
SetBufferCommitInfoNeedsSave(so->hashso_curbuf);
|
||||
}
|
||||
/*
|
||||
* Now continue the scan.
|
||||
*/
|
||||
res = _hash_next(scan, dir);
|
||||
}
|
||||
else
|
||||
res = _hash_first(scan, dir);
|
||||
|
||||
/*
|
||||
* Skip killed tuples if asked to.
|
||||
*/
|
||||
if (scan->ignore_killed_tuples)
|
||||
{
|
||||
while (res)
|
||||
{
|
||||
offnum = ItemPointerGetOffsetNumber(&(scan->currentItemData));
|
||||
page = BufferGetPage(so->hashso_curbuf);
|
||||
if (!ItemIdDeleted(PageGetItemId(page, offnum)))
|
||||
break;
|
||||
res = _hash_next(scan, dir);
|
||||
}
|
||||
}
|
||||
|
||||
PG_RETURN_BOOL(res);
|
||||
}
|
||||
|
||||
@@ -418,6 +458,8 @@ hashbulkdelete(PG_FUNCTION_ARGS)
|
||||
|
||||
/* walk through the entire index */
|
||||
iscan = index_beginscan(NULL, rel, SnapshotAny, 0, (ScanKey) NULL);
|
||||
/* including killed tuples */
|
||||
iscan->ignore_killed_tuples = false;
|
||||
|
||||
while (index_getnext_indexitem(iscan, ForwardScanDirection))
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashscan.c,v 1.26 2002/05/20 23:51:41 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashscan.c,v 1.27 2002/05/24 18:57:55 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Because we can be doing an index scan on a relation while we
|
||||
@@ -32,8 +32,6 @@
|
||||
|
||||
#include "access/hash.h"
|
||||
|
||||
static void _hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
|
||||
static bool _hash_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
|
||||
|
||||
typedef struct HashScanListData
|
||||
{
|
||||
@@ -46,6 +44,10 @@ typedef HashScanListData *HashScanList;
|
||||
static HashScanList HashScans = (HashScanList) NULL;
|
||||
|
||||
|
||||
static void _hash_scandel(IndexScanDesc scan,
|
||||
BlockNumber blkno, OffsetNumber offno);
|
||||
|
||||
|
||||
/*
|
||||
* AtEOXact_hash() --- clean up hash subsystem at xact abort or commit.
|
||||
*
|
||||
@@ -129,63 +131,51 @@ static void
|
||||
_hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
|
||||
{
|
||||
ItemPointer current;
|
||||
ItemPointer mark;
|
||||
Buffer buf;
|
||||
Buffer metabuf;
|
||||
HashScanOpaque so;
|
||||
|
||||
if (!_hash_scantouched(scan, blkno, offno))
|
||||
return;
|
||||
|
||||
metabuf = _hash_getbuf(scan->indexRelation, HASH_METAPAGE, HASH_READ);
|
||||
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
buf = so->hashso_curbuf;
|
||||
|
||||
current = &(scan->currentItemData);
|
||||
mark = &(scan->currentMarkData);
|
||||
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
{
|
||||
metabuf = _hash_getbuf(scan->indexRelation, HASH_METAPAGE, HASH_READ);
|
||||
buf = so->hashso_curbuf;
|
||||
_hash_step(scan, &buf, BackwardScanDirection, metabuf);
|
||||
so->hashso_curbuf = buf;
|
||||
}
|
||||
|
||||
current = &(scan->currentMarkData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
if (ItemPointerIsValid(mark)
|
||||
&& ItemPointerGetBlockNumber(mark) == blkno
|
||||
&& ItemPointerGetOffsetNumber(mark) >= offno)
|
||||
{
|
||||
ItemPointerData tmp;
|
||||
/*
|
||||
* The idea here is to exchange the current and mark positions,
|
||||
* then step backwards (affecting current), then exchange again.
|
||||
*/
|
||||
ItemPointerData tmpitem;
|
||||
Buffer tmpbuf;
|
||||
|
||||
tmp = *current;
|
||||
*current = scan->currentItemData;
|
||||
scan->currentItemData = tmp;
|
||||
tmpitem = *mark;
|
||||
*mark = *current;
|
||||
*current = tmpitem;
|
||||
tmpbuf = so->hashso_mrkbuf;
|
||||
so->hashso_mrkbuf = so->hashso_curbuf;
|
||||
so->hashso_curbuf = tmpbuf;
|
||||
|
||||
metabuf = _hash_getbuf(scan->indexRelation, HASH_METAPAGE, HASH_READ);
|
||||
buf = so->hashso_curbuf;
|
||||
_hash_step(scan, &buf, BackwardScanDirection, metabuf);
|
||||
so->hashso_mrkbuf = buf;
|
||||
tmp = *current;
|
||||
*current = scan->currentItemData;
|
||||
scan->currentItemData = tmp;
|
||||
|
||||
tmpitem = *mark;
|
||||
*mark = *current;
|
||||
*current = tmpitem;
|
||||
tmpbuf = so->hashso_mrkbuf;
|
||||
so->hashso_mrkbuf = so->hashso_curbuf;
|
||||
so->hashso_curbuf = tmpbuf;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
_hash_scantouched(IndexScanDesc scan,
|
||||
BlockNumber blkno,
|
||||
OffsetNumber offno)
|
||||
{
|
||||
ItemPointer current;
|
||||
|
||||
current = &(scan->currentItemData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
return true;
|
||||
|
||||
current = &(scan->currentMarkData);
|
||||
if (ItemPointerIsValid(current)
|
||||
&& ItemPointerGetBlockNumber(current) == blkno
|
||||
&& ItemPointerGetOffsetNumber(current) >= offno)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashsearch.c,v 1.28 2002/05/20 23:51:41 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashsearch.c,v 1.29 2002/05/24 18:57:55 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -54,10 +54,10 @@ _hash_search(Relation rel,
|
||||
* _hash_next() -- Get the next item in a scan.
|
||||
*
|
||||
* On entry, we have a valid currentItemData in the scan, and a
|
||||
* read lock on the page that contains that item. We do not have
|
||||
* the page pinned. We return the next item in the scan. On
|
||||
* exit, we have the page containing the next item locked but not
|
||||
* pinned.
|
||||
* pin and read lock on the page that contains that item.
|
||||
* We find the next item in the scan, if any.
|
||||
* On success exit, we have the page containing the next item
|
||||
* pinned and locked.
|
||||
*/
|
||||
bool
|
||||
_hash_next(IndexScanDesc scan, ScanDirection dir)
|
||||
@@ -74,25 +74,12 @@ _hash_next(IndexScanDesc scan, ScanDirection dir)
|
||||
|
||||
rel = scan->indexRelation;
|
||||
so = (HashScanOpaque) scan->opaque;
|
||||
current = &(scan->currentItemData);
|
||||
|
||||
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
|
||||
|
||||
/*
|
||||
* XXX 10 may 91: somewhere there's a bug in our management of the
|
||||
* cached buffer for this scan. wei discovered it. the following is
|
||||
* a workaround so he can work until i figure out what's going on.
|
||||
*/
|
||||
|
||||
if (!BufferIsValid(so->hashso_curbuf))
|
||||
{
|
||||
so->hashso_curbuf = _hash_getbuf(rel,
|
||||
ItemPointerGetBlockNumber(current),
|
||||
HASH_READ);
|
||||
}
|
||||
|
||||
/* we still have the buffer pinned and locked */
|
||||
buf = so->hashso_curbuf;
|
||||
Assert(BufferIsValid(buf));
|
||||
|
||||
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
|
||||
|
||||
/*
|
||||
* step to next valid tuple. note that _hash_step releases our lock
|
||||
|
||||
Reference in New Issue
Block a user