1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* hashinsert.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Item insertion in hash tables for Postgres.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2008-01-01 20:46:01 +01:00
|
|
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2008-01-01 20:46:01 +01:00
|
|
|
* $PostgreSQL: pgsql/src/backend/access/hash/hashinsert.c,v 1.48 2008/01/01 19:45:46 momjian Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "postgres.h"
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "access/hash.h"
|
2003-09-05 00:06:27 +02:00
|
|
|
|
|
|
|
|
|
|
|
static OffsetNumber _hash_pgaddtup(Relation rel, Buffer buf,
|
2006-01-26 00:26:11 +01:00
|
|
|
Size itemsize, IndexTuple itup);
|
1996-10-20 08:34:30 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
2006-01-26 00:26:11 +01:00
|
|
|
* _hash_doinsert() -- Handle insertion of a single index tuple.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* This routine is called by the public interface routines, hashbuild
|
2006-01-26 00:26:11 +01:00
|
|
|
* and hashinsert. By here, itup is completely filled in.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2005-03-21 02:24:04 +01:00
|
|
|
void
|
2006-01-26 00:26:11 +01:00
|
|
|
_hash_doinsert(Relation rel, IndexTuple itup)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Buffer buf;
|
|
|
|
Buffer metabuf;
|
|
|
|
HashMetaPage metap;
|
2003-09-05 00:06:27 +02:00
|
|
|
BlockNumber blkno;
|
1997-09-08 04:41:22 +02:00
|
|
|
Page page;
|
2003-09-05 00:06:27 +02:00
|
|
|
HashPageOpaque pageopaque;
|
|
|
|
Size itemsz;
|
|
|
|
bool do_expand;
|
|
|
|
uint32 hashkey;
|
|
|
|
Bucket bucket;
|
|
|
|
Datum datum;
|
|
|
|
bool isnull;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Compute the hash key for the item. We do this first so as not to need
|
|
|
|
* to hold any locks while running the hash function.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2003-09-05 00:06:27 +02:00
|
|
|
if (rel->rd_rel->relnatts != 1)
|
|
|
|
elog(ERROR, "hash indexes support only one index key");
|
|
|
|
datum = index_getattr(itup, 1, RelationGetDescr(rel), &isnull);
|
|
|
|
Assert(!isnull);
|
|
|
|
hashkey = _hash_datum2hashkey(rel, datum);
|
|
|
|
|
|
|
|
/* compute item size too */
|
2006-01-26 00:26:11 +01:00
|
|
|
itemsz = IndexTupleDSize(*itup);
|
2005-10-15 04:49:52 +02:00
|
|
|
itemsz = MAXALIGN(itemsz); /* be safe, PageAddItem will do this but we
|
|
|
|
* need to be consistent */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Acquire shared split lock so we can compute the target bucket safely
|
|
|
|
* (see README).
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2003-09-05 00:06:27 +02:00
|
|
|
_hash_getlock(rel, 0, HASH_SHARE);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
/* Read the metapage */
|
2007-05-03 18:45:58 +02:00
|
|
|
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
|
2003-09-05 00:06:27 +02:00
|
|
|
metap = (HashMetaPage) BufferGetPage(metabuf);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Check whether the item can fit on a hash page at all. (Eventually, we
|
|
|
|
* ought to try to apply TOAST methods if not.) Note that at this point,
|
|
|
|
* itemsz doesn't include the ItemId.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2003-09-05 00:06:27 +02:00
|
|
|
if (itemsz > HashMaxItemSize((Page) metap))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("index row size %lu exceeds hash maximum %lu",
|
2003-09-05 00:06:27 +02:00
|
|
|
(unsigned long) itemsz,
|
2005-08-10 23:36:46 +02:00
|
|
|
(unsigned long) HashMaxItemSize((Page) metap)),
|
2005-10-15 04:49:52 +02:00
|
|
|
errhint("Values larger than a buffer page cannot be indexed.")));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
/*
|
|
|
|
* Compute the target bucket number, and convert to block number.
|
|
|
|
*/
|
|
|
|
bucket = _hash_hashkey2bucket(hashkey,
|
|
|
|
metap->hashm_maxbucket,
|
|
|
|
metap->hashm_highmask,
|
|
|
|
metap->hashm_lowmask);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
blkno = BUCKET_TO_BLKNO(metap, bucket);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
/* release lock on metapage, but keep pin since we'll need it again */
|
|
|
|
_hash_chgbufaccess(rel, metabuf, HASH_READ, HASH_NOLOCK);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Acquire share lock on target bucket; then we can release split lock.
|
2003-09-05 00:06:27 +02:00
|
|
|
*/
|
|
|
|
_hash_getlock(rel, blkno, HASH_SHARE);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
_hash_droplock(rel, 0, HASH_SHARE);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
/* Fetch the primary bucket page for the bucket */
|
2007-05-03 18:45:58 +02:00
|
|
|
buf = _hash_getbuf(rel, blkno, HASH_WRITE, LH_BUCKET_PAGE);
|
1997-09-07 07:04:48 +02:00
|
|
|
page = BufferGetPage(buf);
|
1996-07-09 08:22:35 +02:00
|
|
|
pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
2003-09-05 00:06:27 +02:00
|
|
|
Assert(pageopaque->hasho_bucket == bucket);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
/* Do the insertion */
|
1997-09-07 07:04:48 +02:00
|
|
|
while (PageGetFreeSpace(page) < itemsz)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* no space on this page; check for an overflow page
|
|
|
|
*/
|
2004-08-29 07:07:03 +02:00
|
|
|
BlockNumber nextblkno = pageopaque->hasho_nextblkno;
|
2003-09-05 00:06:27 +02:00
|
|
|
|
|
|
|
if (BlockNumberIsValid(nextblkno))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* ovfl page exists; go get it. if it doesn't have room, we'll
|
|
|
|
* find out next pass through the loop test above.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2003-09-05 00:06:27 +02:00
|
|
|
_hash_relbuf(rel, buf);
|
2007-05-03 18:45:58 +02:00
|
|
|
buf = _hash_getbuf(rel, nextblkno, HASH_WRITE, LH_OVERFLOW_PAGE);
|
1997-09-07 07:04:48 +02:00
|
|
|
page = BufferGetPage(buf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* we're at the end of the bucket chain and we haven't found a
|
|
|
|
* page with enough room. allocate a new overflow page.
|
|
|
|
*/
|
2003-09-05 00:06:27 +02:00
|
|
|
|
|
|
|
/* release our write lock without modifying buffer */
|
|
|
|
_hash_chgbufaccess(rel, buf, HASH_READ, HASH_NOLOCK);
|
|
|
|
|
|
|
|
/* chain to a new overflow page */
|
|
|
|
buf = _hash_addovflpage(rel, metabuf, buf);
|
1997-09-07 07:04:48 +02:00
|
|
|
page = BufferGetPage(buf);
|
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
/* should fit now, given test above */
|
|
|
|
Assert(PageGetFreeSpace(page) >= itemsz);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
2007-05-03 18:45:58 +02:00
|
|
|
Assert(pageopaque->hasho_flag == LH_OVERFLOW_PAGE);
|
1997-09-07 07:04:48 +02:00
|
|
|
Assert(pageopaque->hasho_bucket == bucket);
|
|
|
|
}
|
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
/* found page with enough space, so add the item here */
|
2006-01-26 00:26:11 +01:00
|
|
|
(void) _hash_pgaddtup(rel, buf, itemsz, itup);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
/* write and release the modified page */
|
|
|
|
_hash_wrtbuf(rel, buf);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
/* We can drop the bucket lock now */
|
|
|
|
_hash_droplock(rel, blkno, HASH_SHARE);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* Write-lock the metapage so we can increment the tuple count. After
|
|
|
|
* incrementing it, check to see if it's time for a split.
|
2003-09-05 00:06:27 +02:00
|
|
|
*/
|
|
|
|
_hash_chgbufaccess(rel, metabuf, HASH_NOLOCK, HASH_WRITE);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
metap->hashm_ntuples += 1;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-09-05 00:06:27 +02:00
|
|
|
/* Make sure this stays in sync with _hash_expandtable() */
|
|
|
|
do_expand = metap->hashm_ntuples >
|
|
|
|
(double) metap->hashm_ffactor * (metap->hashm_maxbucket + 1);
|
|
|
|
|
|
|
|
/* Write out the metapage and drop lock, but keep pin */
|
|
|
|
_hash_chgbufaccess(rel, metabuf, HASH_WRITE, HASH_NOLOCK);
|
|
|
|
|
|
|
|
/* Attempt to split if a split is needed */
|
|
|
|
if (do_expand)
|
1997-09-07 07:04:48 +02:00
|
|
|
_hash_expandtable(rel, metabuf);
|
2003-09-05 00:06:27 +02:00
|
|
|
|
|
|
|
/* Finally drop our pin on the metapage */
|
|
|
|
_hash_dropbuf(rel, metabuf);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* _hash_pgaddtup() -- add a tuple to a particular page in the index.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2003-09-05 00:06:27 +02:00
|
|
|
* This routine adds the tuple to the page as requested; it does
|
|
|
|
* not write out the page. It is an error to call pgaddtup() without
|
|
|
|
* a write lock and pin.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static OffsetNumber
|
1996-07-09 08:22:35 +02:00
|
|
|
_hash_pgaddtup(Relation rel,
|
1997-09-07 07:04:48 +02:00
|
|
|
Buffer buf,
|
|
|
|
Size itemsize,
|
2006-01-26 00:26:11 +01:00
|
|
|
IndexTuple itup)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
OffsetNumber itup_off;
|
|
|
|
Page page;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-11-06 20:29:01 +01:00
|
|
|
_hash_checkpage(rel, buf, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
|
1997-09-07 07:04:48 +02:00
|
|
|
page = BufferGetPage(buf);
|
|
|
|
|
|
|
|
itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
|
2007-09-20 19:56:33 +02:00
|
|
|
if (PageAddItem(page, (Item) itup, itemsize, itup_off, false, false)
|
2001-03-07 22:20:26 +01:00
|
|
|
== InvalidOffsetNumber)
|
2003-07-21 22:29:40 +02:00
|
|
|
elog(ERROR, "failed to add index item to \"%s\"",
|
2001-03-07 22:20:26 +01:00
|
|
|
RelationGetRelationName(rel));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return itup_off;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|