
Hashing for aggregation purposes still needs work, so it's not time to mark any cross-type operators as hashable for general use, but these cases work if the operators are so marked by hand in the system catalogs.
221 lines
5.3 KiB
C
221 lines
5.3 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* hashutil.c
|
|
* Utility code for Postgres hash implementation.
|
|
*
|
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/access/hash/hashutil.c,v 1.51 2007/01/30 01:33:36 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/genam.h"
|
|
#include "access/hash.h"
|
|
#include "access/reloptions.h"
|
|
#include "executor/execdebug.h"
|
|
#include "utils/lsyscache.h"
|
|
|
|
|
|
/*
|
|
* _hash_checkqual -- does the index tuple satisfy the scan conditions?
|
|
*/
|
|
bool
|
|
_hash_checkqual(IndexScanDesc scan, IndexTuple itup)
|
|
{
|
|
TupleDesc tupdesc = RelationGetDescr(scan->indexRelation);
|
|
ScanKey key = scan->keyData;
|
|
int scanKeySize = scan->numberOfKeys;
|
|
|
|
IncrIndexProcessed();
|
|
|
|
while (scanKeySize > 0)
|
|
{
|
|
Datum datum;
|
|
bool isNull;
|
|
Datum test;
|
|
|
|
datum = index_getattr(itup,
|
|
key->sk_attno,
|
|
tupdesc,
|
|
&isNull);
|
|
|
|
/* assume sk_func is strict */
|
|
if (isNull)
|
|
return false;
|
|
if (key->sk_flags & SK_ISNULL)
|
|
return false;
|
|
|
|
test = FunctionCall2(&key->sk_func, datum, key->sk_argument);
|
|
|
|
if (!DatumGetBool(test))
|
|
return false;
|
|
|
|
key++;
|
|
scanKeySize--;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* _hash_datum2hashkey -- given a Datum, call the index's hash procedure
|
|
*
|
|
* The Datum is assumed to be of the index's column type, so we can use the
|
|
* "primary" hash procedure that's tracked for us by the generic index code.
|
|
*/
|
|
uint32
|
|
_hash_datum2hashkey(Relation rel, Datum key)
|
|
{
|
|
FmgrInfo *procinfo;
|
|
|
|
/* XXX assumes index has only one attribute */
|
|
procinfo = index_getprocinfo(rel, 1, HASHPROC);
|
|
|
|
return DatumGetUInt32(FunctionCall1(procinfo, key));
|
|
}
|
|
|
|
/*
|
|
* _hash_datum2hashkey_type -- given a Datum of a specified type,
|
|
* hash it in a fashion compatible with this index
|
|
*
|
|
* This is much more expensive than _hash_datum2hashkey, so use it only in
|
|
* cross-type situations.
|
|
*/
|
|
uint32
|
|
_hash_datum2hashkey_type(Relation rel, Datum key, Oid keytype)
|
|
{
|
|
RegProcedure hash_proc;
|
|
|
|
/* XXX assumes index has only one attribute */
|
|
hash_proc = get_opfamily_proc(rel->rd_opfamily[0],
|
|
keytype,
|
|
keytype,
|
|
HASHPROC);
|
|
if (!RegProcedureIsValid(hash_proc))
|
|
elog(ERROR, "missing support function %d(%u,%u) for index \"%s\"",
|
|
HASHPROC, keytype, keytype,
|
|
RelationGetRelationName(rel));
|
|
|
|
return DatumGetUInt32(OidFunctionCall1(hash_proc, key));
|
|
}
|
|
|
|
/*
|
|
* _hash_hashkey2bucket -- determine which bucket the hashkey maps to.
|
|
*/
|
|
Bucket
|
|
_hash_hashkey2bucket(uint32 hashkey, uint32 maxbucket,
|
|
uint32 highmask, uint32 lowmask)
|
|
{
|
|
Bucket bucket;
|
|
|
|
bucket = hashkey & highmask;
|
|
if (bucket > maxbucket)
|
|
bucket = bucket & lowmask;
|
|
|
|
return bucket;
|
|
}
|
|
|
|
/*
|
|
* _hash_log2 -- returns ceil(lg2(num))
|
|
*/
|
|
uint32
|
|
_hash_log2(uint32 num)
|
|
{
|
|
uint32 i,
|
|
limit;
|
|
|
|
limit = 1;
|
|
for (i = 0; limit < num; limit <<= 1, i++)
|
|
;
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* _hash_checkpage -- sanity checks on the format of all hash pages
|
|
*/
|
|
void
|
|
_hash_checkpage(Relation rel, Buffer buf, int flags)
|
|
{
|
|
Page page = BufferGetPage(buf);
|
|
|
|
/*
|
|
* ReadBuffer verifies that every newly-read page passes
|
|
* PageHeaderIsValid, which means it either contains a reasonably sane
|
|
* page header or is all-zero. We have to defend against the all-zero
|
|
* case, however.
|
|
*/
|
|
if (PageIsNew(page))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INDEX_CORRUPTED),
|
|
errmsg("index \"%s\" contains unexpected zero page at block %u",
|
|
RelationGetRelationName(rel),
|
|
BufferGetBlockNumber(buf)),
|
|
errhint("Please REINDEX it.")));
|
|
|
|
/*
|
|
* Additionally check that the special area looks sane.
|
|
*/
|
|
if (((PageHeader) (page))->pd_special !=
|
|
(BLCKSZ - MAXALIGN(sizeof(HashPageOpaqueData))))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INDEX_CORRUPTED),
|
|
errmsg("index \"%s\" contains corrupted page at block %u",
|
|
RelationGetRelationName(rel),
|
|
BufferGetBlockNumber(buf)),
|
|
errhint("Please REINDEX it.")));
|
|
|
|
if (flags)
|
|
{
|
|
HashPageOpaque opaque = (HashPageOpaque) PageGetSpecialPointer(page);
|
|
|
|
if ((opaque->hasho_flag & flags) == 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INDEX_CORRUPTED),
|
|
errmsg("index \"%s\" contains corrupted page at block %u",
|
|
RelationGetRelationName(rel),
|
|
BufferGetBlockNumber(buf)),
|
|
errhint("Please REINDEX it.")));
|
|
}
|
|
|
|
/*
|
|
* When checking the metapage, also verify magic number and version.
|
|
*/
|
|
if (flags == LH_META_PAGE)
|
|
{
|
|
HashMetaPage metap = (HashMetaPage) page;
|
|
|
|
if (metap->hashm_magic != HASH_MAGIC)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INDEX_CORRUPTED),
|
|
errmsg("index \"%s\" is not a hash index",
|
|
RelationGetRelationName(rel))));
|
|
|
|
if (metap->hashm_version != HASH_VERSION)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INDEX_CORRUPTED),
|
|
errmsg("index \"%s\" has wrong hash version",
|
|
RelationGetRelationName(rel)),
|
|
errhint("Please REINDEX it.")));
|
|
}
|
|
}
|
|
|
|
Datum
|
|
hashoptions(PG_FUNCTION_ARGS)
|
|
{
|
|
Datum reloptions = PG_GETARG_DATUM(0);
|
|
bool validate = PG_GETARG_BOOL(1);
|
|
bytea *result;
|
|
|
|
result = default_reloptions(reloptions, validate,
|
|
HASH_MIN_FILLFACTOR,
|
|
HASH_DEFAULT_FILLFACTOR);
|
|
if (result)
|
|
PG_RETURN_BYTEA_P(result);
|
|
PG_RETURN_NULL();
|
|
}
|