1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* hashscan.c--
|
|
|
|
* manage scans on hash tables
|
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1996-11-03 13:35:27 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashscan.c,v 1.6 1996/11/03 12:34:41 scrappy Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* NOTES
|
|
|
|
* Because we can be doing an index scan on a relation while we
|
|
|
|
* update it, we need to avoid missing data that moves around in
|
|
|
|
* the index. The routines and global variables in this file
|
|
|
|
* guarantee that all scans in the local address space stay
|
|
|
|
* correctly positioned. This is all we need to worry about, since
|
|
|
|
* write locking guarantees that no one else will be on the same
|
|
|
|
* page at the same time as we are.
|
|
|
|
*
|
|
|
|
* The scheme is to manage a list of active scans in the current
|
|
|
|
* backend. Whenever we add or remove records from an index, we
|
|
|
|
* check the list of active scans to see if any has been affected.
|
|
|
|
* A scan is affected only if it is on the same relation, and the
|
|
|
|
* same page, as the update.
|
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
1996-10-20 08:34:30 +02:00
|
|
|
#include "postgres.h"
|
1996-10-20 10:32:11 +02:00
|
|
|
|
|
|
|
#include "access/relscan.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "access/hash.h"
|
1996-10-21 07:45:21 +02:00
|
|
|
#include "utils/palloc.h"
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
static void _hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
|
|
|
|
static bool _hash_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
|
|
|
|
|
|
|
|
typedef struct HashScanListData {
|
|
|
|
IndexScanDesc hashsl_scan;
|
|
|
|
struct HashScanListData *hashsl_next;
|
|
|
|
} HashScanListData;
|
|
|
|
|
|
|
|
typedef HashScanListData *HashScanList;
|
|
|
|
|
|
|
|
static HashScanList HashScans = (HashScanList) NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* _Hash_regscan() -- register a new scan.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
_hash_regscan(IndexScanDesc scan)
|
|
|
|
{
|
|
|
|
HashScanList new_el;
|
|
|
|
|
|
|
|
new_el = (HashScanList) palloc(sizeof(HashScanListData));
|
|
|
|
new_el->hashsl_scan = scan;
|
|
|
|
new_el->hashsl_next = HashScans;
|
|
|
|
HashScans = new_el;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* _hash_dropscan() -- drop a scan from the scan list
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
_hash_dropscan(IndexScanDesc scan)
|
|
|
|
{
|
|
|
|
HashScanList chk, last;
|
|
|
|
|
|
|
|
last = (HashScanList) NULL;
|
|
|
|
for (chk = HashScans;
|
|
|
|
chk != (HashScanList) NULL && chk->hashsl_scan != scan;
|
|
|
|
chk = chk->hashsl_next) {
|
|
|
|
last = chk;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chk == (HashScanList) NULL)
|
|
|
|
elog(WARN, "hash scan list trashed; can't find 0x%lx", scan);
|
|
|
|
|
|
|
|
if (last == (HashScanList) NULL)
|
|
|
|
HashScans = chk->hashsl_next;
|
|
|
|
else
|
|
|
|
last->hashsl_next = chk->hashsl_next;
|
|
|
|
|
|
|
|
#ifdef PERFECT_MEM
|
|
|
|
pfree (chk);
|
|
|
|
#endif /* PERFECT_MEM */
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_hash_adjscans(Relation rel, ItemPointer tid)
|
|
|
|
{
|
|
|
|
HashScanList l;
|
|
|
|
Oid relid;
|
|
|
|
|
|
|
|
relid = rel->rd_id;
|
|
|
|
for (l = HashScans; l != (HashScanList) NULL; l = l->hashsl_next) {
|
|
|
|
if (relid == l->hashsl_scan->relation->rd_id)
|
|
|
|
_hash_scandel(l->hashsl_scan, ItemPointerGetBlockNumber(tid),
|
|
|
|
ItemPointerGetOffsetNumber(tid));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
|
|
|
|
{
|
|
|
|
ItemPointer current;
|
|
|
|
Buffer buf;
|
|
|
|
Buffer metabuf;
|
|
|
|
HashScanOpaque so;
|
|
|
|
|
|
|
|
if (!_hash_scantouched(scan, blkno, offno))
|
|
|
|
return;
|
|
|
|
|
|
|
|
metabuf = _hash_getbuf(scan->relation, HASH_METAPAGE, HASH_READ);
|
|
|
|
|
|
|
|
so = (HashScanOpaque) scan->opaque;
|
|
|
|
buf = so->hashso_curbuf;
|
|
|
|
|
|
|
|
current = &(scan->currentItemData);
|
|
|
|
if (ItemPointerIsValid(current)
|
|
|
|
&& ItemPointerGetBlockNumber(current) == blkno
|
|
|
|
&& ItemPointerGetOffsetNumber(current) >= offno) {
|
|
|
|
_hash_step(scan, &buf, BackwardScanDirection, metabuf);
|
|
|
|
so->hashso_curbuf = buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
current = &(scan->currentMarkData);
|
|
|
|
if (ItemPointerIsValid(current)
|
|
|
|
&& ItemPointerGetBlockNumber(current) == blkno
|
|
|
|
&& ItemPointerGetOffsetNumber(current) >= offno) {
|
|
|
|
ItemPointerData tmp;
|
|
|
|
tmp = *current;
|
|
|
|
*current = scan->currentItemData;
|
|
|
|
scan->currentItemData = tmp;
|
|
|
|
_hash_step(scan, &buf, BackwardScanDirection, metabuf);
|
|
|
|
so->hashso_mrkbuf = buf;
|
|
|
|
tmp = *current;
|
|
|
|
*current = scan->currentItemData;
|
|
|
|
scan->currentItemData = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|