1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-15 03:41:20 +03:00

Revise hash join and hash aggregation code to use the same datatype-

specific hash functions used by hash indexes, rather than the old
not-datatype-aware ComputeHashFunc routine.  This makes it safe to do
hash joining on several datatypes that previously couldn't use hashing.
The sets of datatypes that are hash indexable and hash joinable are now
exactly the same, whereas before each had some that weren't in the other.
This commit is contained in:
Tom Lane
2003-06-22 22:04:55 +00:00
parent 0dda75f6eb
commit bff0422b6c
27 changed files with 489 additions and 232 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.35 2002/09/04 20:31:09 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.36 2003/06/22 22:04:54 tgl Exp $
*
* NOTES
* These functions are stored in pg_amproc. For each operator class
@@ -22,6 +22,7 @@
#include "access/hash.h"
/* Note: this is used for both "char" and boolean datatypes */
Datum
hashchar(PG_FUNCTION_ARGS)
{
@@ -58,6 +59,14 @@ hashfloat4(PG_FUNCTION_ARGS)
{
float4 key = PG_GETARG_FLOAT4(0);
/*
* On IEEE-float machines, minus zero and zero have different bit patterns
* but should compare as equal. We must ensure that they have the same
* hash value, which is most easily done this way:
*/
if (key == (float4) 0)
PG_RETURN_UINT32(0);
return hash_any((unsigned char *) &key, sizeof(key));
}
@@ -66,6 +75,14 @@ hashfloat8(PG_FUNCTION_ARGS)
{
float8 key = PG_GETARG_FLOAT8(0);
/*
* On IEEE-float machines, minus zero and zero have different bit patterns
* but should compare as equal. We must ensure that they have the same
* hash value, which is most easily done this way:
*/
if (key == (float8) 0)
PG_RETURN_UINT32(0);
return hash_any((unsigned char *) &key, sizeof(key));
}
@@ -77,11 +94,6 @@ hashoidvector(PG_FUNCTION_ARGS)
return hash_any((unsigned char *) key, INDEX_MAX_KEYS * sizeof(Oid));
}
/*
* Note: hashint2vector currently can't be used as a user hash table
* hash function, because it has no pg_proc entry. We only need it
* for catcache indexing.
*/
Datum
hashint2vector(PG_FUNCTION_ARGS)
{
@@ -102,6 +114,26 @@ hashname(PG_FUNCTION_ARGS)
return hash_any((unsigned char *) key, keylen);
}
Datum
hashtext(PG_FUNCTION_ARGS)
{
text *key = PG_GETARG_TEXT_P(0);
Datum result;
/*
* Note: this is currently identical in behavior to hashvarlena,
* but it seems likely that we may need to do something different
* in non-C locales. (See also hashbpchar, if so.)
*/
result = hash_any((unsigned char *) VARDATA(key),
VARSIZE(key) - VARHDRSZ);
/* Avoid leaking memory for toasted inputs */
PG_FREE_IF_COPY(key, 0);
return result;
}
/*
* hashvarlena() can be used for any varlena datatype in which there are
* no non-significant bits, ie, distinct bitpatterns never compare as equal.

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execGrouping.c,v 1.2 2003/01/12 04:03:34 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execGrouping.c,v 1.3 2003/06/22 22:04:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,6 +19,8 @@
#include "executor/executor.h"
#include "parser/parse_oper.h"
#include "utils/memutils.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/*****************************************************************************
@@ -213,76 +215,46 @@ execTuplesMatchPrepare(TupleDesc tupdesc,
return eqfunctions;
}
/*****************************************************************************
* Utility routines for hashing
*****************************************************************************/
/*
* ComputeHashFunc
* execTuplesHashPrepare
* Look up the equality and hashing functions needed for a TupleHashTable.
*
* the hash function for hash joins (also used for hash aggregation)
*
* XXX this probably ought to be replaced with datatype-specific
* hash functions, such as those already implemented for hash indexes.
* This is similar to execTuplesMatchPrepare, but we also need to find the
* hash functions associated with the equality operators. *eqfunctions and
* *hashfunctions receive the palloc'd result arrays.
*/
uint32
ComputeHashFunc(Datum key, int typLen, bool byVal)
void
execTuplesHashPrepare(TupleDesc tupdesc,
int numCols,
AttrNumber *matchColIdx,
FmgrInfo **eqfunctions,
FmgrInfo **hashfunctions)
{
unsigned char *k;
int i;
if (byVal)
*eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
*hashfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
for (i = 0; i < numCols; i++)
{
/*
* If it's a by-value data type, just hash the whole Datum value.
* This assumes that datatypes narrower than Datum are
* consistently padded (either zero-extended or sign-extended, but
* not random bits) to fill Datum; see the XXXGetDatum macros in
* postgres.h. NOTE: it would not work to do hash_any(&key, len)
* since this would get the wrong bytes on a big-endian machine.
*/
k = (unsigned char *) &key;
typLen = sizeof(Datum);
}
else
{
if (typLen > 0)
{
/* fixed-width pass-by-reference type */
k = (unsigned char *) DatumGetPointer(key);
}
else if (typLen == -1)
{
/*
* It's a varlena type, so 'key' points to a "struct varlena".
* NOTE: VARSIZE returns the "real" data length plus the
* sizeof the "vl_len" attribute of varlena (the length
* information). 'key' points to the beginning of the varlena
* struct, so we have to use "VARDATA" to find the beginning
* of the "real" data. Also, we have to be careful to detoast
* the datum if it's toasted. (We don't worry about freeing
* the detoasted copy; that happens for free when the
* per-tuple memory context is reset in ExecHashGetBucket.)
*/
struct varlena *vkey = PG_DETOAST_DATUM(key);
AttrNumber att = matchColIdx[i];
Oid typid = tupdesc->attrs[att - 1]->atttypid;
Operator optup;
Oid eq_opr;
Oid eq_function;
Oid hash_function;
typLen = VARSIZE(vkey) - VARHDRSZ;
k = (unsigned char *) VARDATA(vkey);
}
else if (typLen == -2)
{
/* It's a null-terminated C string */
typLen = strlen(DatumGetCString(key)) + 1;
k = (unsigned char *) DatumGetPointer(key);
}
else
{
elog(ERROR, "ComputeHashFunc: Invalid typLen %d", typLen);
k = NULL; /* keep compiler quiet */
}
optup = equality_oper(typid, false);
eq_opr = oprid(optup);
eq_function = oprfuncid(optup);
ReleaseSysCache(optup);
hash_function = get_op_hash_function(eq_opr);
if (!OidIsValid(hash_function))
elog(ERROR, "Could not find hash function for hash operator %u",
eq_opr);
fmgr_info(eq_function, &(*eqfunctions)[i]);
fmgr_info(hash_function, &(*hashfunctions)[i]);
}
return DatumGetUInt32(hash_any(k, typLen));
}
@@ -299,19 +271,21 @@ ComputeHashFunc(Datum key, int typLen, bool byVal)
*
* numCols, keyColIdx: identify the tuple fields to use as lookup key
* eqfunctions: equality comparison functions to use
* hashfunctions: datatype-specific hashing functions to use
* nbuckets: number of buckets to make
* entrysize: size of each entry (at least sizeof(TupleHashEntryData))
* tablecxt: memory context in which to store table and table entries
* tempcxt: short-lived context for evaluation hash and comparison functions
*
* The eqfunctions array may be made with execTuplesMatchPrepare().
* The function arrays may be made with execTuplesHashPrepare().
*
* Note that keyColIdx and eqfunctions must be allocated in storage that
* will live as long as the hashtable does.
* Note that keyColIdx, eqfunctions, and hashfunctions must be allocated in
* storage that will live as long as the hashtable does.
*/
TupleHashTable
BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
FmgrInfo *eqfunctions,
FmgrInfo *hashfunctions,
int nbuckets, Size entrysize,
MemoryContext tablecxt, MemoryContext tempcxt)
{
@@ -328,6 +302,7 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
hashtable->numCols = numCols;
hashtable->keyColIdx = keyColIdx;
hashtable->eqfunctions = eqfunctions;
hashtable->hashfunctions = hashfunctions;
hashtable->tablecxt = tablecxt;
hashtable->tempcxt = tempcxt;
hashtable->entrysize = entrysize;
@@ -375,11 +350,15 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0);
attr = heap_getattr(tuple, att, tupdesc, &isNull);
if (isNull)
continue; /* treat nulls as having hash key 0 */
hashkey ^= ComputeHashFunc(attr,
(int) tupdesc->attrs[att - 1]->attlen,
tupdesc->attrs[att - 1]->attbyval);
if (!isNull) /* treat nulls as having hash key 0 */
{
uint32 hkey;
hkey = DatumGetUInt32(FunctionCall1(&hashtable->hashfunctions[i],
attr));
hashkey ^= hkey;
}
}
bucketno = hashkey % (uint32) hashtable->nbuckets;

View File

@@ -45,7 +45,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.106 2003/06/06 15:04:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.107 2003/06/22 22:04:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -582,6 +582,7 @@ build_hash_table(AggState *aggstate)
aggstate->hashtable = BuildTupleHashTable(node->numCols,
node->grpColIdx,
aggstate->eqfunctions,
aggstate->hashfunctions,
node->numGroups,
entrysize,
aggstate->aggcontext,
@@ -1035,6 +1036,7 @@ ExecInitAgg(Agg *node, EState *estate)
aggstate->aggs = NIL;
aggstate->numaggs = 0;
aggstate->eqfunctions = NULL;
aggstate->hashfunctions = NULL;
aggstate->peragg = NULL;
aggstate->agg_done = false;
aggstate->pergroup = NULL;
@@ -1123,14 +1125,23 @@ ExecInitAgg(Agg *node, EState *estate)
}
/*
* If we are grouping, precompute fmgr lookup data for inner loop
* If we are grouping, precompute fmgr lookup data for inner loop.
* We need both equality and hashing functions to do it by hashing,
* but only equality if not hashing.
*/
if (node->numCols > 0)
{
aggstate->eqfunctions =
execTuplesMatchPrepare(ExecGetScanType(&aggstate->ss),
node->numCols,
node->grpColIdx);
if (node->aggstrategy == AGG_HASHED)
execTuplesHashPrepare(ExecGetScanType(&aggstate->ss),
node->numCols,
node->grpColIdx,
&aggstate->eqfunctions,
&aggstate->hashfunctions);
else
aggstate->eqfunctions =
execTuplesMatchPrepare(ExecGetScanType(&aggstate->ss),
node->numCols,
node->grpColIdx);
}
/*

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.75 2003/03/27 16:51:27 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.76 2003/06/22 22:04:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -192,7 +192,7 @@ ExecEndHash(HashState *node)
* ----------------------------------------------------------------
*/
HashJoinTable
ExecHashTableCreate(Hash *node)
ExecHashTableCreate(Hash *node, List *hashOperators)
{
HashJoinTable hashtable;
Plan *outerNode;
@@ -201,7 +201,7 @@ ExecHashTableCreate(Hash *node)
int nbatch;
int nkeys;
int i;
List *hk;
List *ho;
MemoryContext oldcxt;
/*
@@ -237,17 +237,20 @@ ExecHashTableCreate(Hash *node)
hashtable->outerBatchSize = NULL;
/*
* Get info about the datatypes of the hash keys.
* Get info about the hash functions to be used for each hash key.
*/
nkeys = length(node->hashkeys);
hashtable->typLens = (int16 *) palloc(nkeys * sizeof(int16));
hashtable->typByVals = (bool *) palloc(nkeys * sizeof(bool));
nkeys = length(hashOperators);
hashtable->hashfunctions = (FmgrInfo *) palloc(nkeys * sizeof(FmgrInfo));
i = 0;
foreach(hk, node->hashkeys)
foreach(ho, hashOperators)
{
get_typlenbyval(exprType(lfirst(hk)),
&hashtable->typLens[i],
&hashtable->typByVals[i]);
Oid hashfn;
hashfn = get_op_hash_function(lfirsto(ho));
if (!OidIsValid(hashfn))
elog(ERROR, "Could not find hash function for hash operator %u",
lfirsto(ho));
fmgr_info(hashfn, &hashtable->hashfunctions[i]);
i++;
}
@@ -520,7 +523,7 @@ ExecHashGetBucket(HashJoinTable hashtable,
/*
* We reset the eval context each time to reclaim any memory leaked in
* the hashkey expressions or ComputeHashFunc itself.
* the hashkey expressions.
*/
ResetExprContext(econtext);
@@ -545,9 +548,11 @@ ExecHashGetBucket(HashJoinTable hashtable,
*/
if (!isNull) /* treat nulls as having hash key 0 */
{
hashkey ^= ComputeHashFunc(keyval,
(int) hashtable->typLens[i],
hashtable->typByVals[i]);
uint32 hkey;
hkey = DatumGetUInt32(FunctionCall1(&hashtable->hashfunctions[i],
keyval));
hashkey ^= hkey;
}
i++;

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.51 2003/05/30 20:23:10 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.52 2003/06/22 22:04:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -117,7 +117,8 @@ ExecHashJoin(HashJoinState *node)
* create the hash table
*/
Assert(hashtable == NULL);
hashtable = ExecHashTableCreate((Hash *) hashNode->ps.plan);
hashtable = ExecHashTableCreate((Hash *) hashNode->ps.plan,
node->hj_HashOperators);
node->hj_HashTable = hashtable;
/*
@@ -305,6 +306,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
Plan *outerNode;
Hash *hashNode;
List *hclauses;
List *hoperators;
List *hcl;
/*
@@ -406,8 +408,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
/*
* The planner already made a list of the inner hashkeys for us,
* but we also need a list of the outer hashkeys. Each list of
* exprs must then be prepared for execution.
* but we also need a list of the outer hashkeys, as well as a list
* of the hash operator OIDs. Both lists of exprs must then be prepared
* for execution.
*/
hjstate->hj_InnerHashKeys = (List *)
ExecInitExpr((Expr *) hashNode->hashkeys,
@@ -416,13 +419,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
hjstate->hj_InnerHashKeys;
hclauses = NIL;
hoperators = NIL;
foreach(hcl, node->hashclauses)
{
hclauses = lappend(hclauses, get_leftop(lfirst(hcl)));
OpExpr *hclause = (OpExpr *) lfirst(hcl);
Assert(IsA(hclause, OpExpr));
hclauses = lappend(hclauses, get_leftop((Expr *) hclause));
hoperators = lappendo(hoperators, hclause->opno);
}
hjstate->hj_OuterHashKeys = (List *)
ExecInitExpr((Expr *) hclauses,
(PlanState *) hjstate);
hjstate->hj_HashOperators = hoperators;
hjstate->js.ps.ps_OuterTupleSlot = NULL;
hjstate->js.ps.ps_TupFromTlist = false;

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.46 2003/06/06 15:04:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.47 2003/06/22 22:04:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -519,6 +519,7 @@ buildSubPlanHash(SubPlanState *node)
node->hashtable = BuildTupleHashTable(ncols,
node->keyColIdx,
node->eqfunctions,
node->hashfunctions,
nbuckets,
sizeof(TupleHashEntryData),
node->tablecxt,
@@ -537,6 +538,7 @@ buildSubPlanHash(SubPlanState *node)
node->hashnulls = BuildTupleHashTable(ncols,
node->keyColIdx,
node->eqfunctions,
node->hashfunctions,
nbuckets,
sizeof(TupleHashEntryData),
node->tablecxt,
@@ -700,6 +702,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
node->innerecontext = NULL;
node->keyColIdx = NULL;
node->eqfunctions = NULL;
node->hashfunctions = NULL;
/*
* create an EState for the subplan
@@ -797,11 +800,12 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
* ExecTypeFromTL).
*
* We also extract the combining operators themselves to initialize
* the equality functions for the hash tables.
* the equality and hashing functions for the hash tables.
*/
lefttlist = righttlist = NIL;
leftptlist = rightptlist = NIL;
node->eqfunctions = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
node->hashfunctions = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
i = 1;
foreach(lexpr, node->exprs)
{
@@ -811,6 +815,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
Expr *expr;
TargetEntry *tle;
GenericExprState *tlestate;
Oid hashfn;
Assert(IsA(fstate, FuncExprState));
Assert(IsA(opexpr, OpExpr));
@@ -850,6 +855,13 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
fmgr_info(opexpr->opfuncid, &node->eqfunctions[i-1]);
node->eqfunctions[i-1].fn_expr = (Node *) opexpr;
/* Lookup the associated hash function */
hashfn = get_op_hash_function(opexpr->opno);
if (!OidIsValid(hashfn))
elog(ERROR, "Could not find hash function for hash operator %u",
opexpr->opno);
fmgr_info(hashfn, &node->hashfunctions[i-1]);
i++;
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.97 2003/05/26 00:11:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.98 2003/06/22 22:04:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -704,6 +704,8 @@ bpcharcmp(PG_FUNCTION_ARGS)
/*
* bpchar needs a specialized hash function because we want to ignore
* trailing blanks in comparisons.
*
* XXX is there any need for locale-specific behavior here?
*/
Datum
hashbpchar(PG_FUNCTION_ARGS)

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.103 2003/05/27 17:49:46 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.104 2003/06/22 22:04:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -81,22 +81,6 @@
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
/*
* EQPROC is used in CatalogCacheInitializeCache to find the equality
* functions for system types that are used as cache key fields.
* See also GetCCHashFunc, which should support the same set of types.
*
* XXX this should be replaced by catalog lookups,
* but that seems to pose considerable risk of circularity...
*/
static const Oid eqproc[] = {
F_BOOLEQ, InvalidOid, F_CHAREQ, F_NAMEEQ, InvalidOid,
F_INT2EQ, F_INT2VECTOREQ, F_INT4EQ, F_OIDEQ, F_TEXTEQ,
F_OIDEQ, InvalidOid, InvalidOid, InvalidOid, F_OIDVECTOREQ
};
#define EQPROC(SYSTEMTYPEOID) eqproc[(SYSTEMTYPEOID)-BOOLOID]
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
ScanKey cur_skey);
@@ -119,24 +103,46 @@ static HeapTuple build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys);
* internal support functions
*/
static PGFunction
GetCCHashFunc(Oid keytype)
/*
* Look up the hash and equality functions for system types that are used
* as cache key fields.
*
* XXX this should be replaced by catalog lookups,
* but that seems to pose considerable risk of circularity...
*/
static void
GetCCHashEqFuncs(Oid keytype, PGFunction *hashfunc, RegProcedure *eqfunc)
{
switch (keytype)
{
case BOOLOID:
*hashfunc = hashchar;
*eqfunc = F_BOOLEQ;
break;
case CHAROID:
return hashchar;
*hashfunc = hashchar;
*eqfunc = F_CHAREQ;
break;
case NAMEOID:
return hashname;
*hashfunc = hashname;
*eqfunc = F_NAMEEQ;
break;
case INT2OID:
return hashint2;
*hashfunc = hashint2;
*eqfunc = F_INT2EQ;
break;
case INT2VECTOROID:
return hashint2vector;
*hashfunc = hashint2vector;
*eqfunc = F_INT2VECTOREQ;
break;
case INT4OID:
return hashint4;
*hashfunc = hashint4;
*eqfunc = F_INT4EQ;
break;
case TEXTOID:
return hashvarlena;
*hashfunc = hashtext;
*eqfunc = F_TEXTEQ;
break;
case OIDOID:
case REGPROCOID:
case REGPROCEDUREOID:
@@ -144,13 +150,17 @@ GetCCHashFunc(Oid keytype)
case REGOPERATOROID:
case REGCLASSOID:
case REGTYPEOID:
return hashoid;
*hashfunc = hashoid;
*eqfunc = F_OIDEQ;
break;
case OIDVECTOROID:
return hashoidvector;
*hashfunc = hashoidvector;
*eqfunc = F_OIDVECTOREQ;
break;
default:
elog(FATAL, "GetCCHashFunc: type %u unsupported as catcache key",
elog(FATAL, "GetCCHashEqFuncs: type %u unsupported as catcache key",
keytype);
return (PGFunction) NULL;
break;
}
}
@@ -941,16 +951,16 @@ CatalogCacheInitializeCache(CatCache *cache)
keytype = OIDOID;
}
cache->cc_hashfunc[i] = GetCCHashFunc(keytype);
GetCCHashEqFuncs(keytype,
&cache->cc_hashfunc[i],
&cache->cc_skey[i].sk_procedure);
cache->cc_isname[i] = (keytype == NAMEOID);
/*
* If GetCCHashFunc liked the type, safe to index into eqproc[]
* Do equality-function lookup (we assume this won't need a catalog
* lookup for any supported type)
*/
cache->cc_skey[i].sk_procedure = EQPROC(keytype);
/* Do function lookup */
fmgr_info_cxt(cache->cc_skey[i].sk_procedure,
&cache->cc_skey[i].sk_func,
CacheMemoryContext);

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.95 2003/05/26 00:11:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.96 2003/06/22 22:04:54 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -16,8 +16,10 @@
#include "postgres.h"
#include "miscadmin.h"
#include "access/hash.h"
#include "access/tupmacs.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
@@ -28,6 +30,7 @@
#include "nodes/makefuncs.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -106,6 +109,72 @@ get_opclass_member(Oid opclass, int16 strategy)
return result;
}
/*
* get_op_hash_function
* Get the OID of the datatype-specific hash function associated with
* a hashable equality operator.
*
* Returns InvalidOid if no hash function can be found. (This indicates
* that the operator should not have been marked oprcanhash.)
*/
Oid
get_op_hash_function(Oid opno)
{
CatCList *catlist;
int i;
HeapTuple tuple;
Oid opclass = InvalidOid;
/*
* Search pg_amop to see if the target operator is registered as the "="
* operator of any hash opclass. If the operator is registered in
* multiple opclasses, assume we can use the associated hash function
* from any one.
*/
catlist = SearchSysCacheList(AMOPOPID, 1,
ObjectIdGetDatum(opno),
0, 0, 0);
for (i = 0; i < catlist->n_members; i++)
{
Form_pg_amop aform;
tuple = &catlist->members[i]->tuple;
aform = (Form_pg_amop) GETSTRUCT(tuple);
if (aform->amopstrategy == HTEqualStrategyNumber &&
opclass_is_hash(aform->amopclaid))
{
opclass = aform->amopclaid;
break;
}
}
ReleaseSysCacheList(catlist);
if (OidIsValid(opclass))
{
/* Found a suitable opclass, get its hash support function */
tuple = SearchSysCache(AMPROCNUM,
ObjectIdGetDatum(opclass),
Int16GetDatum(HASHPROC),
0, 0);
if (HeapTupleIsValid(tuple))
{
Form_pg_amproc aform = (Form_pg_amproc) GETSTRUCT(tuple);
RegProcedure result;
result = aform->amproc;
ReleaseSysCache(tuple);
Assert(RegProcedureIsValid(result));
return result;
}
}
/* Didn't find a match... */
return InvalidOid;
}
/* ---------- ATTRIBUTE CACHES ---------- */
@@ -284,6 +353,31 @@ opclass_is_btree(Oid opclass)
return result;
}
/*
* opclass_is_hash
*
* Returns TRUE iff the specified opclass is associated with the
* hash index access method.
*/
bool
opclass_is_hash(Oid opclass)
{
HeapTuple tp;
Form_pg_opclass cla_tup;
bool result;
tp = SearchSysCache(CLAOID,
ObjectIdGetDatum(opclass),
0, 0, 0);
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for opclass %u", opclass);
cla_tup = (Form_pg_opclass) GETSTRUCT(tp);
result = (cla_tup->opcamid == HASH_AM_OID);
ReleaseSysCache(tp);
return result;
}
/* ---------- OPERATOR CACHE ---------- */
/*