mirror of
https://github.com/postgres/postgres.git
synced 2025-05-06 19:59:18 +03:00
Rewrite hashbulkdelete() to make it amenable to new bucket locking
scheme. A pleasant side effect is that it is *much* faster when deleting a large fraction of the indexed tuples, because of elimination of redundant hash_step activity induced by hash_adjscans. Various other continuing code cleanup.
This commit is contained in:
parent
5f65345a57
commit
39673ca47b
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.65 2003/08/04 02:39:57 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.66 2003/09/02 02:18:38 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* This file contains only the public interface routines.
|
* This file contains only the public interface routines.
|
||||||
@ -449,40 +449,178 @@ hashbulkdelete(PG_FUNCTION_ARGS)
|
|||||||
BlockNumber num_pages;
|
BlockNumber num_pages;
|
||||||
double tuples_removed;
|
double tuples_removed;
|
||||||
double num_index_tuples;
|
double num_index_tuples;
|
||||||
IndexScanDesc iscan;
|
uint32 deleted_tuples;
|
||||||
|
uint32 tuples_remaining;
|
||||||
tuples_removed = 0;
|
uint32 orig_ntuples;
|
||||||
num_index_tuples = 0;
|
Bucket orig_maxbucket;
|
||||||
|
Bucket cur_maxbucket;
|
||||||
|
Bucket cur_bucket;
|
||||||
|
Buffer metabuf;
|
||||||
|
HashMetaPage metap;
|
||||||
|
HashMetaPageData local_metapage;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX generic implementation --- should be improved!
|
* keep track of counts in both float form (to return) and integer form
|
||||||
|
* (to update hashm_ntuples). It'd be better to make hashm_ntuples a
|
||||||
|
* double, but that will have to wait for an initdb.
|
||||||
*/
|
*/
|
||||||
|
tuples_removed = 0;
|
||||||
|
num_index_tuples = 0;
|
||||||
|
deleted_tuples = 0;
|
||||||
|
tuples_remaining = 0;
|
||||||
|
|
||||||
/* walk through the entire index */
|
/*
|
||||||
iscan = index_beginscan(NULL, rel, SnapshotAny, 0, (ScanKey) NULL);
|
* Read the metapage to fetch original bucket and tuple counts. Also,
|
||||||
/* including killed tuples */
|
* we keep a copy of the last-seen metapage so that we can use its
|
||||||
iscan->ignore_killed_tuples = false;
|
* hashm_spares[] values to compute bucket page addresses. This is a
|
||||||
|
* bit hokey but perfectly safe, since the interesting entries in the
|
||||||
|
* spares array cannot change under us; and it beats rereading the
|
||||||
|
* metapage for each bucket.
|
||||||
|
*/
|
||||||
|
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
|
||||||
|
metap = (HashMetaPage) BufferGetPage(metabuf);
|
||||||
|
_hash_checkpage((Page) metap, LH_META_PAGE);
|
||||||
|
orig_maxbucket = metap->hashm_maxbucket;
|
||||||
|
orig_ntuples = metap->hashm_ntuples;
|
||||||
|
memcpy(&local_metapage, metap, sizeof(local_metapage));
|
||||||
|
_hash_relbuf(rel, metabuf, HASH_READ);
|
||||||
|
|
||||||
while (index_getnext_indexitem(iscan, ForwardScanDirection))
|
/* Scan the buckets that we know exist */
|
||||||
|
cur_bucket = 0;
|
||||||
|
cur_maxbucket = orig_maxbucket;
|
||||||
|
|
||||||
|
loop_top:
|
||||||
|
while (cur_bucket <= cur_maxbucket)
|
||||||
{
|
{
|
||||||
if (callback(&iscan->xs_ctup.t_self, callback_state))
|
BlockNumber bucket_blkno;
|
||||||
|
BlockNumber blkno;
|
||||||
|
bool bucket_dirty = false;
|
||||||
|
|
||||||
|
/* Get address of bucket's start page */
|
||||||
|
bucket_blkno = BUCKET_TO_BLKNO(&local_metapage, cur_bucket);
|
||||||
|
|
||||||
|
/* XXX lock bucket here */
|
||||||
|
|
||||||
|
/* Scan each page in bucket */
|
||||||
|
blkno = bucket_blkno;
|
||||||
|
while (BlockNumberIsValid(blkno))
|
||||||
{
|
{
|
||||||
ItemPointerData indextup = iscan->currentItemData;
|
Buffer buf;
|
||||||
|
Page page;
|
||||||
|
HashPageOpaque opaque;
|
||||||
|
OffsetNumber offno;
|
||||||
|
OffsetNumber maxoffno;
|
||||||
|
bool page_dirty = false;
|
||||||
|
|
||||||
/* adjust any active scans that will be affected by deletion */
|
buf = _hash_getbuf(rel, blkno, HASH_WRITE);
|
||||||
/* (namely, my own scan) */
|
page = BufferGetPage(buf);
|
||||||
_hash_adjscans(rel, &indextup);
|
_hash_checkpage(page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
|
||||||
|
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||||
|
Assert(opaque->hasho_bucket == cur_bucket);
|
||||||
|
|
||||||
/* delete the data from the page */
|
/* Scan each tuple in page */
|
||||||
_hash_pagedel(rel, &indextup);
|
offno = FirstOffsetNumber;
|
||||||
|
maxoffno = PageGetMaxOffsetNumber(page);
|
||||||
|
while (offno <= maxoffno)
|
||||||
|
{
|
||||||
|
HashItem hitem;
|
||||||
|
ItemPointer htup;
|
||||||
|
|
||||||
tuples_removed += 1;
|
hitem = (HashItem) PageGetItem(page,
|
||||||
|
PageGetItemId(page, offno));
|
||||||
|
htup = &(hitem->hash_itup.t_tid);
|
||||||
|
if (callback(htup, callback_state))
|
||||||
|
{
|
||||||
|
ItemPointerData indextup;
|
||||||
|
|
||||||
|
/* adjust any active scans that will be affected */
|
||||||
|
/* (this should be unnecessary) */
|
||||||
|
ItemPointerSet(&indextup, blkno, offno);
|
||||||
|
_hash_adjscans(rel, &indextup);
|
||||||
|
|
||||||
|
/* delete the item from the page */
|
||||||
|
PageIndexTupleDelete(page, offno);
|
||||||
|
bucket_dirty = page_dirty = true;
|
||||||
|
|
||||||
|
/* don't increment offno, instead decrement maxoffno */
|
||||||
|
maxoffno = OffsetNumberPrev(maxoffno);
|
||||||
|
|
||||||
|
tuples_removed += 1;
|
||||||
|
deleted_tuples += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offno = OffsetNumberNext(offno);
|
||||||
|
|
||||||
|
num_index_tuples += 1;
|
||||||
|
tuples_remaining += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write or free page if needed, advance to next page. We want
|
||||||
|
* to preserve the invariant that overflow pages are nonempty.
|
||||||
|
*/
|
||||||
|
blkno = opaque->hasho_nextblkno;
|
||||||
|
|
||||||
|
if (PageIsEmpty(page) && (opaque->hasho_flag & LH_OVERFLOW_PAGE))
|
||||||
|
_hash_freeovflpage(rel, buf);
|
||||||
|
else if (page_dirty)
|
||||||
|
_hash_wrtbuf(rel, buf);
|
||||||
|
else
|
||||||
|
_hash_relbuf(rel, buf, HASH_WRITE);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
num_index_tuples += 1;
|
/* If we deleted anything, try to compact free space */
|
||||||
|
if (bucket_dirty)
|
||||||
|
_hash_squeezebucket(rel, cur_bucket, bucket_blkno);
|
||||||
|
|
||||||
|
/* XXX unlock bucket here */
|
||||||
|
|
||||||
|
/* Advance to next bucket */
|
||||||
|
cur_bucket++;
|
||||||
}
|
}
|
||||||
|
|
||||||
index_endscan(iscan);
|
/* Write-lock metapage and check for split since we started */
|
||||||
|
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_WRITE);
|
||||||
|
metap = (HashMetaPage) BufferGetPage(metabuf);
|
||||||
|
_hash_checkpage((Page) metap, LH_META_PAGE);
|
||||||
|
|
||||||
|
if (cur_maxbucket != metap->hashm_maxbucket)
|
||||||
|
{
|
||||||
|
/* There's been a split, so process the additional bucket(s) */
|
||||||
|
cur_maxbucket = metap->hashm_maxbucket;
|
||||||
|
memcpy(&local_metapage, metap, sizeof(local_metapage));
|
||||||
|
_hash_relbuf(rel, metabuf, HASH_WRITE);
|
||||||
|
goto loop_top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Okay, we're really done. Update tuple count in metapage. */
|
||||||
|
|
||||||
|
if (orig_maxbucket == metap->hashm_maxbucket &&
|
||||||
|
orig_ntuples == metap->hashm_ntuples)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* No one has split or inserted anything since start of scan,
|
||||||
|
* so believe our count as gospel.
|
||||||
|
*/
|
||||||
|
metap->hashm_ntuples = tuples_remaining;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Otherwise, our count is untrustworthy since we may have
|
||||||
|
* double-scanned tuples in split buckets. Proceed by
|
||||||
|
* dead-reckoning.
|
||||||
|
*/
|
||||||
|
if (metap->hashm_ntuples > deleted_tuples)
|
||||||
|
metap->hashm_ntuples -= deleted_tuples;
|
||||||
|
else
|
||||||
|
metap->hashm_ntuples = 0;
|
||||||
|
num_index_tuples = metap->hashm_ntuples;
|
||||||
|
}
|
||||||
|
|
||||||
|
_hash_wrtbuf(rel, metabuf);
|
||||||
|
|
||||||
/* return statistics */
|
/* return statistics */
|
||||||
num_pages = RelationGetNumberOfBlocks(rel);
|
num_pages = RelationGetNumberOfBlocks(rel);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashovfl.c,v 1.38 2003/09/01 20:26:34 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashovfl.c,v 1.39 2003/09/02 02:18:38 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Overflow pages look like ordinary relation pages.
|
* Overflow pages look like ordinary relation pages.
|
||||||
@ -444,11 +444,13 @@ _hash_initbitmap(Relation rel, HashMetaPage metap, BlockNumber blkno)
|
|||||||
* first page in the bucket chain. The read page works backward and
|
* first page in the bucket chain. The read page works backward and
|
||||||
* the write page works forward; the procedure terminates when the
|
* the write page works forward; the procedure terminates when the
|
||||||
* read page and write page are the same page.
|
* read page and write page are the same page.
|
||||||
|
*
|
||||||
|
* Caller must hold exclusive lock on the target bucket.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
_hash_squeezebucket(Relation rel,
|
_hash_squeezebucket(Relation rel,
|
||||||
HashMetaPage metap,
|
Bucket bucket,
|
||||||
Bucket bucket)
|
BlockNumber bucket_blkno)
|
||||||
{
|
{
|
||||||
Buffer wbuf;
|
Buffer wbuf;
|
||||||
Buffer rbuf = 0;
|
Buffer rbuf = 0;
|
||||||
@ -466,7 +468,7 @@ _hash_squeezebucket(Relation rel,
|
|||||||
/*
|
/*
|
||||||
* start squeezing into the base bucket page.
|
* start squeezing into the base bucket page.
|
||||||
*/
|
*/
|
||||||
wblkno = BUCKET_TO_BLKNO(bucket);
|
wblkno = bucket_blkno;
|
||||||
wbuf = _hash_getbuf(rel, wblkno, HASH_WRITE);
|
wbuf = _hash_getbuf(rel, wblkno, HASH_WRITE);
|
||||||
wpage = BufferGetPage(wbuf);
|
wpage = BufferGetPage(wbuf);
|
||||||
_hash_checkpage(wpage, LH_BUCKET_PAGE);
|
_hash_checkpage(wpage, LH_BUCKET_PAGE);
|
||||||
@ -484,11 +486,6 @@ _hash_squeezebucket(Relation rel,
|
|||||||
/*
|
/*
|
||||||
* find the last page in the bucket chain by starting at the base
|
* find the last page in the bucket chain by starting at the base
|
||||||
* bucket page and working forward.
|
* bucket page and working forward.
|
||||||
*
|
|
||||||
* XXX if chains tend to be long, we should probably move forward using
|
|
||||||
* HASH_READ and then _hash_chgbufaccess to HASH_WRITE when we reach
|
|
||||||
* the end. if they are short we probably don't care very much. if
|
|
||||||
* the hash function is working at all, they had better be short..
|
|
||||||
*/
|
*/
|
||||||
ropaque = wopaque;
|
ropaque = wopaque;
|
||||||
do
|
do
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashpage.c,v 1.39 2003/09/01 20:26:34 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashpage.c,v 1.40 2003/09/02 02:18:38 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Postgres hash pages look like ordinary relation pages. The opaque
|
* Postgres hash pages look like ordinary relation pages. The opaque
|
||||||
@ -143,7 +143,7 @@ _hash_metapinit(Relation rel)
|
|||||||
*/
|
*/
|
||||||
for (i = 0; i <= 1; i++)
|
for (i = 0; i <= 1; i++)
|
||||||
{
|
{
|
||||||
buf = _hash_getbuf(rel, BUCKET_TO_BLKNO(i), HASH_WRITE);
|
buf = _hash_getbuf(rel, BUCKET_TO_BLKNO(metap, i), HASH_WRITE);
|
||||||
pg = BufferGetPage(buf);
|
pg = BufferGetPage(buf);
|
||||||
_hash_pageinit(pg, BufferGetPageSize(buf));
|
_hash_pageinit(pg, BufferGetPageSize(buf));
|
||||||
pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg);
|
pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg);
|
||||||
@ -456,6 +456,8 @@ _hash_splitbucket(Relation rel,
|
|||||||
Buffer ovflbuf;
|
Buffer ovflbuf;
|
||||||
BlockNumber oblkno;
|
BlockNumber oblkno;
|
||||||
BlockNumber nblkno;
|
BlockNumber nblkno;
|
||||||
|
BlockNumber start_oblkno;
|
||||||
|
BlockNumber start_nblkno;
|
||||||
bool null;
|
bool null;
|
||||||
Datum datum;
|
Datum datum;
|
||||||
HashItem hitem;
|
HashItem hitem;
|
||||||
@ -475,8 +477,10 @@ _hash_splitbucket(Relation rel,
|
|||||||
_hash_checkpage((Page) metap, LH_META_PAGE);
|
_hash_checkpage((Page) metap, LH_META_PAGE);
|
||||||
|
|
||||||
/* get the buffers & pages */
|
/* get the buffers & pages */
|
||||||
oblkno = BUCKET_TO_BLKNO(obucket);
|
start_oblkno = BUCKET_TO_BLKNO(metap, obucket);
|
||||||
nblkno = BUCKET_TO_BLKNO(nbucket);
|
start_nblkno = BUCKET_TO_BLKNO(metap, nbucket);
|
||||||
|
oblkno = start_oblkno;
|
||||||
|
nblkno = start_nblkno;
|
||||||
obuf = _hash_getbuf(rel, oblkno, HASH_WRITE);
|
obuf = _hash_getbuf(rel, oblkno, HASH_WRITE);
|
||||||
nbuf = _hash_getbuf(rel, nblkno, HASH_WRITE);
|
nbuf = _hash_getbuf(rel, nblkno, HASH_WRITE);
|
||||||
opage = BufferGetPage(obuf);
|
opage = BufferGetPage(obuf);
|
||||||
@ -571,7 +575,7 @@ _hash_splitbucket(Relation rel,
|
|||||||
*/
|
*/
|
||||||
_hash_wrtbuf(rel, obuf);
|
_hash_wrtbuf(rel, obuf);
|
||||||
_hash_wrtbuf(rel, nbuf);
|
_hash_wrtbuf(rel, nbuf);
|
||||||
_hash_squeezebucket(rel, metap, obucket);
|
_hash_squeezebucket(rel, obucket, start_oblkno);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -639,7 +643,7 @@ _hash_splitbucket(Relation rel,
|
|||||||
if (!BlockNumberIsValid(oblkno))
|
if (!BlockNumberIsValid(oblkno))
|
||||||
{
|
{
|
||||||
_hash_wrtbuf(rel, nbuf);
|
_hash_wrtbuf(rel, nbuf);
|
||||||
_hash_squeezebucket(rel, metap, obucket);
|
_hash_squeezebucket(rel, obucket, start_oblkno);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashsearch.c,v 1.31 2003/08/04 02:39:57 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashsearch.c,v 1.32 2003/09/02 02:18:38 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -19,8 +19,10 @@
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* _hash_search() -- Finds the page/bucket that the contains the
|
* _hash_search() -- Find the bucket that contains the scankey
|
||||||
* scankey and loads it into *bufP. the buffer has a read lock.
|
* and fetch its primary bucket page into *bufP.
|
||||||
|
*
|
||||||
|
* the buffer has a read lock.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
_hash_search(Relation rel,
|
_hash_search(Relation rel,
|
||||||
@ -30,22 +32,23 @@ _hash_search(Relation rel,
|
|||||||
HashMetaPage metap)
|
HashMetaPage metap)
|
||||||
{
|
{
|
||||||
BlockNumber blkno;
|
BlockNumber blkno;
|
||||||
Datum keyDatum;
|
|
||||||
Bucket bucket;
|
Bucket bucket;
|
||||||
|
|
||||||
if (scankey == (ScanKey) NULL ||
|
if (scankey == NULL)
|
||||||
(keyDatum = scankey[0].sk_argument) == (Datum) NULL)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If the scankey argument is NULL, all tuples will satisfy the
|
* If the scankey is empty, all tuples will satisfy the
|
||||||
* scan so we start the scan at the first bucket (bucket 0).
|
* scan so we start the scan at the first bucket (bucket 0).
|
||||||
*/
|
*/
|
||||||
bucket = 0;
|
bucket = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
bucket = _hash_call(rel, metap, keyDatum);
|
{
|
||||||
|
Assert(!(scankey[0].sk_flags & SK_ISNULL));
|
||||||
|
bucket = _hash_call(rel, metap, scankey[0].sk_argument);
|
||||||
|
}
|
||||||
|
|
||||||
blkno = BUCKET_TO_BLKNO(bucket);
|
blkno = BUCKET_TO_BLKNO(metap, bucket);
|
||||||
|
|
||||||
*bufP = _hash_getbuf(rel, blkno, HASH_READ);
|
*bufP = _hash_getbuf(rel, blkno, HASH_READ);
|
||||||
}
|
}
|
||||||
@ -330,7 +333,7 @@ _hash_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir, Buffer metabuf)
|
|||||||
if (allbuckets && bucket < metap->hashm_maxbucket)
|
if (allbuckets && bucket < metap->hashm_maxbucket)
|
||||||
{
|
{
|
||||||
++bucket;
|
++bucket;
|
||||||
blkno = BUCKET_TO_BLKNO(bucket);
|
blkno = BUCKET_TO_BLKNO(metap, bucket);
|
||||||
buf = _hash_getbuf(rel, blkno, HASH_READ);
|
buf = _hash_getbuf(rel, blkno, HASH_READ);
|
||||||
page = BufferGetPage(buf);
|
page = BufferGetPage(buf);
|
||||||
_hash_checkpage(page, LH_BUCKET_PAGE);
|
_hash_checkpage(page, LH_BUCKET_PAGE);
|
||||||
@ -380,7 +383,7 @@ _hash_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir, Buffer metabuf)
|
|||||||
if (allbuckets && bucket > 0)
|
if (allbuckets && bucket > 0)
|
||||||
{
|
{
|
||||||
--bucket;
|
--bucket;
|
||||||
blkno = BUCKET_TO_BLKNO(bucket);
|
blkno = BUCKET_TO_BLKNO(metap, bucket);
|
||||||
buf = _hash_getbuf(rel, blkno, HASH_READ);
|
buf = _hash_getbuf(rel, blkno, HASH_READ);
|
||||||
page = BufferGetPage(buf);
|
page = BufferGetPage(buf);
|
||||||
_hash_checkpage(page, LH_BUCKET_PAGE);
|
_hash_checkpage(page, LH_BUCKET_PAGE);
|
||||||
|
@ -8,11 +8,10 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashutil.c,v 1.33 2003/08/04 02:39:57 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashutil.c,v 1.34 2003/09/02 02:18:38 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "access/genam.h"
|
#include "access/genam.h"
|
||||||
@ -20,20 +19,23 @@
|
|||||||
#include "access/iqual.h"
|
#include "access/iqual.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _hash_mkscankey -- build a scan key matching the given indextuple
|
||||||
|
*
|
||||||
|
* Note: this is prepared for multiple index columns, but very little
|
||||||
|
* else in access/hash is ...
|
||||||
|
*/
|
||||||
ScanKey
|
ScanKey
|
||||||
_hash_mkscankey(Relation rel, IndexTuple itup)
|
_hash_mkscankey(Relation rel, IndexTuple itup)
|
||||||
{
|
{
|
||||||
ScanKey skey;
|
ScanKey skey;
|
||||||
TupleDesc itupdesc;
|
TupleDesc itupdesc = RelationGetDescr(rel);
|
||||||
int natts;
|
int natts = rel->rd_rel->relnatts;
|
||||||
AttrNumber i;
|
AttrNumber i;
|
||||||
Datum arg;
|
Datum arg;
|
||||||
FmgrInfo *procinfo;
|
FmgrInfo *procinfo;
|
||||||
bool isnull;
|
bool isnull;
|
||||||
|
|
||||||
natts = rel->rd_rel->relnatts;
|
|
||||||
itupdesc = RelationGetDescr(rel);
|
|
||||||
|
|
||||||
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
|
skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
|
||||||
|
|
||||||
for (i = 0; i < natts; i++)
|
for (i = 0; i < natts; i++)
|
||||||
@ -41,7 +43,7 @@ _hash_mkscankey(Relation rel, IndexTuple itup)
|
|||||||
arg = index_getattr(itup, i + 1, itupdesc, &isnull);
|
arg = index_getattr(itup, i + 1, itupdesc, &isnull);
|
||||||
procinfo = index_getprocinfo(rel, i + 1, HASHPROC);
|
procinfo = index_getprocinfo(rel, i + 1, HASHPROC);
|
||||||
ScanKeyEntryInitializeWithInfo(&skey[i],
|
ScanKeyEntryInitializeWithInfo(&skey[i],
|
||||||
0x0,
|
isnull ? SK_ISNULL : 0x0,
|
||||||
(AttrNumber) (i + 1),
|
(AttrNumber) (i + 1),
|
||||||
procinfo,
|
procinfo,
|
||||||
CurrentMemoryContext,
|
CurrentMemoryContext,
|
||||||
@ -57,18 +59,19 @@ _hash_freeskey(ScanKey skey)
|
|||||||
pfree(skey);
|
pfree(skey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _hash_checkqual -- does the index tuple satisfy the scan conditions?
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
_hash_checkqual(IndexScanDesc scan, IndexTuple itup)
|
_hash_checkqual(IndexScanDesc scan, IndexTuple itup)
|
||||||
{
|
{
|
||||||
if (scan->numberOfKeys > 0)
|
return index_keytest(itup, RelationGetDescr(scan->indexRelation),
|
||||||
return (index_keytest(itup,
|
scan->numberOfKeys, scan->keyData);
|
||||||
RelationGetDescr(scan->indexRelation),
|
|
||||||
scan->numberOfKeys, scan->keyData));
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _hash_formitem -- construct a hash index entry
|
||||||
|
*/
|
||||||
HashItem
|
HashItem
|
||||||
_hash_formitem(IndexTuple itup)
|
_hash_formitem(IndexTuple itup)
|
||||||
{
|
{
|
||||||
@ -82,17 +85,27 @@ _hash_formitem(IndexTuple itup)
|
|||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("hash indexes cannot include null keys")));
|
errmsg("hash indexes cannot include null keys")));
|
||||||
|
|
||||||
/* make a copy of the index tuple with room for the sequence number */
|
/*
|
||||||
|
* make a copy of the index tuple (XXX do we still need to copy?)
|
||||||
|
*
|
||||||
|
* HashItemData used to have more fields than IndexTupleData, but no
|
||||||
|
* longer...
|
||||||
|
*/
|
||||||
tuplen = IndexTupleSize(itup);
|
tuplen = IndexTupleSize(itup);
|
||||||
nbytes_hitem = tuplen +
|
nbytes_hitem = tuplen +
|
||||||
(sizeof(HashItemData) - sizeof(IndexTupleData));
|
(sizeof(HashItemData) - sizeof(IndexTupleData));
|
||||||
|
|
||||||
hitem = (HashItem) palloc(nbytes_hitem);
|
hitem = (HashItem) palloc(nbytes_hitem);
|
||||||
memmove((char *) &(hitem->hash_itup), (char *) itup, tuplen);
|
memcpy((char *) &(hitem->hash_itup), (char *) itup, tuplen);
|
||||||
|
|
||||||
return hitem;
|
return hitem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _hash_call -- given a Datum, call the index's hash procedure
|
||||||
|
*
|
||||||
|
* Returns the bucket number that the hash key maps to.
|
||||||
|
*/
|
||||||
Bucket
|
Bucket
|
||||||
_hash_call(Relation rel, HashMetaPage metap, Datum key)
|
_hash_call(Relation rel, HashMetaPage metap, Datum key)
|
||||||
{
|
{
|
||||||
@ -103,9 +116,11 @@ _hash_call(Relation rel, HashMetaPage metap, Datum key)
|
|||||||
/* XXX assumes index has only one attribute */
|
/* XXX assumes index has only one attribute */
|
||||||
procinfo = index_getprocinfo(rel, 1, HASHPROC);
|
procinfo = index_getprocinfo(rel, 1, HASHPROC);
|
||||||
n = DatumGetUInt32(FunctionCall1(procinfo, key));
|
n = DatumGetUInt32(FunctionCall1(procinfo, key));
|
||||||
|
|
||||||
bucket = n & metap->hashm_highmask;
|
bucket = n & metap->hashm_highmask;
|
||||||
if (bucket > metap->hashm_maxbucket)
|
if (bucket > metap->hashm_maxbucket)
|
||||||
bucket = bucket & metap->hashm_lowmask;
|
bucket = bucket & metap->hashm_lowmask;
|
||||||
|
|
||||||
return bucket;
|
return bucket;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +134,7 @@ _hash_log2(uint32 num)
|
|||||||
limit;
|
limit;
|
||||||
|
|
||||||
limit = 1;
|
limit = 1;
|
||||||
for (i = 0; limit < num; limit = limit << 1, i++)
|
for (i = 0; limit < num; limit <<= 1, i++)
|
||||||
;
|
;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -130,20 +145,19 @@ _hash_log2(uint32 num)
|
|||||||
void
|
void
|
||||||
_hash_checkpage(Page page, int flags)
|
_hash_checkpage(Page page, int flags)
|
||||||
{
|
{
|
||||||
HashPageOpaque opaque;
|
#ifdef USE_ASSERT_CHECKING
|
||||||
|
|
||||||
Assert(page);
|
Assert(page);
|
||||||
Assert(((PageHeader) (page))->pd_lower >= SizeOfPageHeaderData);
|
Assert(((PageHeader) (page))->pd_lower >= SizeOfPageHeaderData);
|
||||||
#if 1
|
|
||||||
Assert(((PageHeader) (page))->pd_upper <=
|
Assert(((PageHeader) (page))->pd_upper <=
|
||||||
(BLCKSZ - MAXALIGN(sizeof(HashPageOpaqueData))));
|
(BLCKSZ - MAXALIGN(sizeof(HashPageOpaqueData))));
|
||||||
Assert(((PageHeader) (page))->pd_special ==
|
Assert(((PageHeader) (page))->pd_special ==
|
||||||
(BLCKSZ - MAXALIGN(sizeof(HashPageOpaqueData))));
|
(BLCKSZ - MAXALIGN(sizeof(HashPageOpaqueData))));
|
||||||
Assert(PageGetPageSize(page) == BLCKSZ);
|
Assert(PageGetPageSize(page) == BLCKSZ);
|
||||||
#endif
|
|
||||||
if (flags)
|
if (flags)
|
||||||
{
|
{
|
||||||
opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
HashPageOpaque opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
||||||
|
|
||||||
Assert(opaque->hasho_flag & flags);
|
Assert(opaque->hasho_flag & flags);
|
||||||
}
|
}
|
||||||
|
#endif /* USE_ASSERT_CHECKING */
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: hash.h,v 1.50 2003/09/01 20:26:34 tgl Exp $
|
* $Id: hash.h,v 1.51 2003/09/02 02:18:38 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* modeled after Margo Seltzer's hash implementation for unix.
|
* modeled after Margo Seltzer's hash implementation for unix.
|
||||||
@ -25,13 +25,12 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Mapping from hash bucket number to physical block number of bucket's
|
* Mapping from hash bucket number to physical block number of bucket's
|
||||||
* starting page. Beware of multiple evaluations of argument! Also notice
|
* starting page. Beware of multiple evaluations of argument!
|
||||||
* macro's implicit dependency on "metap".
|
|
||||||
*/
|
*/
|
||||||
typedef uint32 Bucket;
|
typedef uint32 Bucket;
|
||||||
|
|
||||||
#define BUCKET_TO_BLKNO(B) \
|
#define BUCKET_TO_BLKNO(metap,B) \
|
||||||
((BlockNumber) ((B) + ((B) ? metap->hashm_spares[_hash_log2((B)+1)-1] : 0)) + 1)
|
((BlockNumber) ((B) + ((B) ? (metap)->hashm_spares[_hash_log2((B)+1)-1] : 0)) + 1)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Special space for hash index pages.
|
* Special space for hash index pages.
|
||||||
@ -243,8 +242,8 @@ extern Buffer _hash_addovflpage(Relation rel, Buffer metabuf, Buffer buf);
|
|||||||
extern BlockNumber _hash_freeovflpage(Relation rel, Buffer ovflbuf);
|
extern BlockNumber _hash_freeovflpage(Relation rel, Buffer ovflbuf);
|
||||||
extern void _hash_initbitmap(Relation rel, HashMetaPage metap,
|
extern void _hash_initbitmap(Relation rel, HashMetaPage metap,
|
||||||
BlockNumber blkno);
|
BlockNumber blkno);
|
||||||
extern void _hash_squeezebucket(Relation rel, HashMetaPage metap,
|
extern void _hash_squeezebucket(Relation rel,
|
||||||
Bucket bucket);
|
Bucket bucket, BlockNumber bucket_blkno);
|
||||||
|
|
||||||
/* hashpage.c */
|
/* hashpage.c */
|
||||||
extern void _hash_metapinit(Relation rel);
|
extern void _hash_metapinit(Relation rel);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user