diff options
Diffstat (limited to 'src')
24 files changed, 460 insertions, 190 deletions
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c index e6595de0727..a82b8b32d55 100644 --- a/src/backend/access/hash/hashfunc.c +++ b/src/backend/access/hash/hashfunc.c @@ -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. diff --git a/src/backend/executor/execGrouping.c b/src/backend/executor/execGrouping.c index 0d4d5ed20f3..b41c75e926c 100644 --- a/src/backend/executor/execGrouping.c +++ b/src/backend/executor/execGrouping.c @@ -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) - { - /* - * 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 + *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo)); + *hashfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo)); + + for (i = 0; i < numCols; i++) { - 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); - - 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 */ - } + AttrNumber att = matchColIdx[i]; + Oid typid = tupdesc->attrs[att - 1]->atttypid; + Operator optup; + Oid eq_opr; + Oid eq_function; + Oid hash_function; + + 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; diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index f2499cb4e5e..d0dd6b31c99 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -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); } /* diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index b338c8961e2..f00cc28684d 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -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++; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 17585b2f0fc..9a0071f0180 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -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; diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index ff5d03faf8c..82502c985e8 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -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++; } diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c index 5085c6025c2..6e99c15e369 100644 --- a/src/backend/utils/adt/varchar.c +++ b/src/backend/utils/adt/varchar.c @@ -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) diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 65d716f6ba5..8c0df3dfc2c 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -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); diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index fcd9dc2f59b..095c5e6a8aa 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -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 ---------- */ /* diff --git a/src/include/access/hash.h b/src/include/access/hash.h index 1fadd8c9e37..5834f921889 100644 --- a/src/include/access/hash.h +++ b/src/include/access/hash.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: hash.h,v 1.47 2002/06/20 20:29:42 momjian Exp $ + * $Id: hash.h,v 1.48 2003/06/22 22:04:54 tgl Exp $ * * NOTES * modeled after Margo Seltzer's hash implementation for unix. @@ -251,9 +251,11 @@ extern Datum hashbulkdelete(PG_FUNCTION_ARGS); /* * Datatype-specific hash functions in hashfunc.c. * + * These support both hash indexes and hash joins. + * * NOTE: some of these are also used by catcache operations, without * any direct connection to hash indexes. Also, the common hash_any - * routine is also used by dynahash tables and hash joins. + * routine is also used by dynahash tables. */ extern Datum hashchar(PG_FUNCTION_ARGS); extern Datum hashint2(PG_FUNCTION_ARGS); @@ -265,6 +267,7 @@ extern Datum hashfloat8(PG_FUNCTION_ARGS); extern Datum hashoidvector(PG_FUNCTION_ARGS); extern Datum hashint2vector(PG_FUNCTION_ARGS); extern Datum hashname(PG_FUNCTION_ARGS); +extern Datum hashtext(PG_FUNCTION_ARGS); extern Datum hashvarlena(PG_FUNCTION_ARGS); extern Datum hash_any(register const unsigned char *k, register int keylen); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 209bd5ff242..f6c3855bb41 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.198 2003/06/06 15:04:02 tgl Exp $ + * $Id: catversion.h,v 1.199 2003/06/22 22:04:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200306051 +#define CATALOG_VERSION_NO 200306221 #endif diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h index 0807fb1a398..5186d9ef5eb 100644 --- a/src/include/catalog/pg_am.h +++ b/src/include/catalog/pg_am.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_am.h,v 1.25 2003/03/10 22:28:19 tgl Exp $ + * $Id: pg_am.h,v 1.26 2003/06/22 22:04:55 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -107,6 +107,7 @@ DESCR("b-tree index access method"); #define BTREE_AM_OID 403 DATA(insert OID = 405 ( hash PGUID 1 1 0 f f f t hashgettuple hashinsert hashbeginscan hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete - hashcostestimate )); DESCR("hash index access method"); +#define HASH_AM_OID 405 DATA(insert OID = 783 ( gist PGUID 100 7 0 f t f f gistgettuple gistinsert gistbeginscan gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete - gistcostestimate )); DESCR("GiST index access method"); #define GIST_AM_OID 783 diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index dbff38b3c39..e2cb7560e17 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -16,7 +16,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_amop.h,v 1.49 2003/05/26 00:11:27 tgl Exp $ + * $Id: pg_amop.h,v 1.50 2003/06/22 22:04:55 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -465,5 +465,27 @@ DATA(insert ( 2001 1 f 1550 )); DATA(insert ( 2004 1 f 98 )); /* timestamp_ops */ DATA(insert ( 2040 1 f 2060 )); +/* bool_ops */ +DATA(insert ( 2222 1 f 91 )); +/* bytea_ops */ +DATA(insert ( 2223 1 f 1955 )); +/* int2vector_ops */ +DATA(insert ( 2224 1 f 386 )); +/* xid_ops */ +DATA(insert ( 2225 1 f 352 )); +/* cid_ops */ +DATA(insert ( 2226 1 f 385 )); +/* abstime_ops */ +DATA(insert ( 2227 1 f 560 )); +/* reltime_ops */ +DATA(insert ( 2228 1 f 566 )); +/* text_pattern_ops */ +DATA(insert ( 2229 1 f 2316 )); +/* varchar_pattern_ops */ +DATA(insert ( 2230 1 f 2316 )); +/* bpchar_pattern_ops */ +DATA(insert ( 2231 1 f 2328 )); +/* name_pattern_ops */ +DATA(insert ( 2232 1 f 2334 )); #endif /* PG_AMOP_H */ diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index 0048d000cdf..c2c37c13613 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -14,7 +14,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_amproc.h,v 1.37 2003/05/26 00:11:27 tgl Exp $ + * $Id: pg_amproc.h,v 1.38 2003/06/22 22:04:55 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -127,11 +127,22 @@ DATA(insert ( 1985 1 399 )); DATA(insert ( 1987 1 455 )); DATA(insert ( 1990 1 453 )); DATA(insert ( 1992 1 457 )); -DATA(insert ( 1995 1 456 )); +DATA(insert ( 1995 1 400 )); DATA(insert ( 1997 1 452 )); DATA(insert ( 1999 1 452 )); DATA(insert ( 2001 1 1696 )); -DATA(insert ( 2004 1 456 )); +DATA(insert ( 2004 1 400 )); DATA(insert ( 2040 1 452 )); +DATA(insert ( 2222 1 454 )); +DATA(insert ( 2223 1 456 )); +DATA(insert ( 2224 1 398 )); +DATA(insert ( 2225 1 450 )); +DATA(insert ( 2226 1 450 )); +DATA(insert ( 2227 1 450 )); +DATA(insert ( 2228 1 450 )); +DATA(insert ( 2229 1 456 )); +DATA(insert ( 2230 1 456 )); +DATA(insert ( 2231 1 456 )); +DATA(insert ( 2232 1 455 )); #endif /* PG_AMPROC_H */ diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index f9de8fa28c8..1c8844b2fec 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -26,7 +26,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_opclass.h,v 1.50 2003/05/28 16:04:00 tgl Exp $ + * $Id: pg_opclass.h,v 1.51 2003/06/22 22:04:55 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -155,5 +155,16 @@ DATA(insert OID = 2097 ( 403 bpchar_pattern_ops PGNSP PGUID 1042 f 0 )); #define BPCHAR_PATTERN_BTREE_OPS_OID 2097 DATA(insert OID = 2098 ( 403 name_pattern_ops PGNSP PGUID 19 f 0 )); #define NAME_PATTERN_BTREE_OPS_OID 2098 +DATA(insert OID = 2222 ( 405 bool_ops PGNSP PGUID 16 t 0 )); +DATA(insert OID = 2223 ( 405 bytea_ops PGNSP PGUID 17 t 0 )); +DATA(insert OID = 2224 ( 405 int2vector_ops PGNSP PGUID 22 t 0 )); +DATA(insert OID = 2225 ( 405 xid_ops PGNSP PGUID 28 t 0 )); +DATA(insert OID = 2226 ( 405 cid_ops PGNSP PGUID 29 t 0 )); +DATA(insert OID = 2227 ( 405 abstime_ops PGNSP PGUID 702 t 0 )); +DATA(insert OID = 2228 ( 405 reltime_ops PGNSP PGUID 703 t 0 )); +DATA(insert OID = 2229 ( 405 text_pattern_ops PGNSP PGUID 25 f 0 )); +DATA(insert OID = 2230 ( 405 varchar_pattern_ops PGNSP PGUID 1043 f 0 )); +DATA(insert OID = 2231 ( 405 bpchar_pattern_ops PGNSP PGUID 1042 f 0 )); +DATA(insert OID = 2232 ( 405 name_pattern_ops PGNSP PGUID 19 f 0 )); #endif /* PG_OPCLASS_H */ diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index ae4fb6e04bb..88d3d998a05 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_operator.h,v 1.114 2003/05/26 00:11:27 tgl Exp $ + * $Id: pg_operator.h,v 1.115 2003/06/22 22:04:55 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -122,10 +122,10 @@ DATA(insert OID = 374 ( "||" PGNSP PGUID b f 2283 2277 2277 0 0 0 0 0 0 ar DATA(insert OID = 375 ( "||" PGNSP PGUID b f 2277 2277 2277 0 0 0 0 0 0 array_cat - - )); DATA(insert OID = 352 ( "=" PGNSP PGUID b t 28 28 16 352 0 0 0 0 0 xideq eqsel eqjoinsel )); -DATA(insert OID = 353 ( "=" PGNSP PGUID b t 28 23 16 0 0 0 0 0 0 xideqint4 eqsel eqjoinsel )); +DATA(insert OID = 353 ( "=" PGNSP PGUID b f 28 23 16 0 0 0 0 0 0 xideqint4 eqsel eqjoinsel )); DATA(insert OID = 385 ( "=" PGNSP PGUID b t 29 29 16 385 0 0 0 0 0 cideq eqsel eqjoinsel )); DATA(insert OID = 386 ( "=" PGNSP PGUID b t 22 22 16 386 0 0 0 0 0 int2vectoreq eqsel eqjoinsel )); -DATA(insert OID = 387 ( "=" PGNSP PGUID b t 27 27 16 387 0 0 0 0 0 tideq eqsel eqjoinsel )); +DATA(insert OID = 387 ( "=" PGNSP PGUID b f 27 27 16 387 0 0 0 0 0 tideq eqsel eqjoinsel )); #define TIDEqualOperator 387 DATA(insert OID = 388 ( "!" PGNSP PGUID r f 20 0 20 0 0 0 0 0 0 int8fac - - )); DATA(insert OID = 389 ( "!!" PGNSP PGUID l f 0 20 20 0 0 0 0 0 0 int8fac - - )); @@ -287,7 +287,7 @@ DATA(insert OID = 616 ( "<->" PGNSP PGUID b f 601 628 701 0 0 0 0 0 0 DATA(insert OID = 617 ( "<->" PGNSP PGUID b f 601 603 701 0 0 0 0 0 0 dist_sb - - )); DATA(insert OID = 618 ( "<->" PGNSP PGUID b f 600 602 701 0 0 0 0 0 0 dist_ppath - - )); -DATA(insert OID = 620 ( "=" PGNSP PGUID b f 700 700 16 620 621 622 622 622 623 float4eq eqsel eqjoinsel )); +DATA(insert OID = 620 ( "=" PGNSP PGUID b t 700 700 16 620 621 622 622 622 623 float4eq eqsel eqjoinsel )); DATA(insert OID = 621 ( "<>" PGNSP PGUID b f 700 700 16 621 620 0 0 0 0 float4ne neqsel neqjoinsel )); DATA(insert OID = 622 ( "<" PGNSP PGUID b f 700 700 16 623 625 0 0 0 0 float4lt scalarltsel scalarltjoinsel )); DATA(insert OID = 623 ( ">" PGNSP PGUID b f 700 700 16 622 624 0 0 0 0 float4gt scalargtsel scalargtjoinsel )); @@ -325,7 +325,7 @@ DATA(insert OID = 665 ( "<=" PGNSP PGUID b f 25 25 16 667 666 0 0 0 0 text_l DATA(insert OID = 666 ( ">" PGNSP PGUID b f 25 25 16 664 665 0 0 0 0 text_gt scalargtsel scalargtjoinsel )); DATA(insert OID = 667 ( ">=" PGNSP PGUID b f 25 25 16 665 664 0 0 0 0 text_ge scalargtsel scalargtjoinsel )); -DATA(insert OID = 670 ( "=" PGNSP PGUID b f 701 701 16 670 671 672 672 672 674 float8eq eqsel eqjoinsel )); +DATA(insert OID = 670 ( "=" PGNSP PGUID b t 701 701 16 670 671 672 672 672 674 float8eq eqsel eqjoinsel )); DATA(insert OID = 671 ( "<>" PGNSP PGUID b f 701 701 16 671 670 0 0 0 0 float8ne neqsel neqjoinsel )); DATA(insert OID = 672 ( "<" PGNSP PGUID b f 701 701 16 674 675 0 0 0 0 float8lt scalarltsel scalarltjoinsel )); DATA(insert OID = 673 ( "<=" PGNSP PGUID b f 701 701 16 675 674 0 0 0 0 float8le scalarltsel scalarltjoinsel )); @@ -403,7 +403,7 @@ DATA(insert OID = 843 ( "*" PGNSP PGUID b f 790 700 790 845 0 0 0 0 DATA(insert OID = 844 ( "/" PGNSP PGUID b f 790 700 790 0 0 0 0 0 0 cash_div_flt4 - - )); DATA(insert OID = 845 ( "*" PGNSP PGUID b f 700 790 790 843 0 0 0 0 0 flt4_mul_cash - - )); -DATA(insert OID = 900 ( "=" PGNSP PGUID b t 790 790 16 900 901 902 902 902 903 cash_eq eqsel eqjoinsel )); +DATA(insert OID = 900 ( "=" PGNSP PGUID b f 790 790 16 900 901 902 902 902 903 cash_eq eqsel eqjoinsel )); DATA(insert OID = 901 ( "<>" PGNSP PGUID b f 790 790 16 901 900 0 0 0 0 cash_ne neqsel neqjoinsel )); DATA(insert OID = 902 ( "<" PGNSP PGUID b f 790 790 16 903 905 0 0 0 0 cash_lt scalarltsel scalarltjoinsel )); DATA(insert OID = 903 ( ">" PGNSP PGUID b f 790 790 16 902 904 0 0 0 0 cash_gt scalargtsel scalargtjoinsel )); @@ -431,7 +431,7 @@ DATA(insert OID = 969 ( "@@" PGNSP PGUID l f 0 601 600 0 0 0 0 0 0 lse DATA(insert OID = 970 ( "@@" PGNSP PGUID l f 0 602 600 0 0 0 0 0 0 path_center - - )); DATA(insert OID = 971 ( "@@" PGNSP PGUID l f 0 604 600 0 0 0 0 0 0 poly_center - - )); -DATA(insert OID = 1054 ( "=" PGNSP PGUID b f 1042 1042 16 1054 1057 1058 1058 1058 1060 bpchareq eqsel eqjoinsel )); +DATA(insert OID = 1054 ( "=" PGNSP PGUID b t 1042 1042 16 1054 1057 1058 1058 1058 1060 bpchareq eqsel eqjoinsel )); DATA(insert OID = 1055 ( "~" PGNSP PGUID b f 1042 25 16 0 1056 0 0 0 0 bpcharregexeq regexeqsel regexeqjoinsel )); #define OID_BPCHAR_REGEXEQ_OP 1055 DATA(insert OID = 1056 ( "!~" PGNSP PGUID b f 1042 25 16 0 1055 0 0 0 0 bpcharregexne regexnesel regexnejoinsel )); @@ -455,7 +455,7 @@ DATA(insert OID = 1100 ( "+" PGNSP PGUID b f 1082 23 1082 0 0 0 0 0 0 date_ DATA(insert OID = 1101 ( "-" PGNSP PGUID b f 1082 23 1082 0 0 0 0 0 0 date_mii - - )); /* time operators */ -DATA(insert OID = 1108 ( "=" PGNSP PGUID b f 1083 1083 16 1108 1109 1110 1110 1110 1112 time_eq eqsel eqjoinsel )); +DATA(insert OID = 1108 ( "=" PGNSP PGUID b t 1083 1083 16 1108 1109 1110 1110 1110 1112 time_eq eqsel eqjoinsel )); DATA(insert OID = 1109 ( "<>" PGNSP PGUID b f 1083 1083 16 1109 1108 0 0 0 0 time_ne neqsel neqjoinsel )); DATA(insert OID = 1110 ( "<" PGNSP PGUID b f 1083 1083 16 1112 1113 0 0 0 0 time_lt scalarltsel scalarltjoinsel )); DATA(insert OID = 1111 ( "<=" PGNSP PGUID b f 1083 1083 16 1113 1112 0 0 0 0 time_le scalarltsel scalarltjoinsel )); @@ -465,7 +465,7 @@ DATA(insert OID = 1269 ( "-" PGNSP PGUID b f 1186 1083 1083 0 0 0 0 0 0 inte /* timetz operators */ DATA(insert OID = 1295 ( "-" PGNSP PGUID b f 1186 1266 1266 0 0 0 0 0 0 interval_mi_timetz - - )); -DATA(insert OID = 1550 ( "=" PGNSP PGUID b f 1266 1266 16 1550 1551 1552 1552 1552 1554 timetz_eq eqsel eqjoinsel )); +DATA(insert OID = 1550 ( "=" PGNSP PGUID b t 1266 1266 16 1550 1551 1552 1552 1552 1554 timetz_eq eqsel eqjoinsel )); DATA(insert OID = 1551 ( "<>" PGNSP PGUID b f 1266 1266 16 1551 1550 0 0 0 0 timetz_ne neqsel neqjoinsel )); DATA(insert OID = 1552 ( "<" PGNSP PGUID b f 1266 1266 16 1554 1555 0 0 0 0 timetz_lt scalarltsel scalarltjoinsel )); DATA(insert OID = 1553 ( "<=" PGNSP PGUID b f 1266 1266 16 1555 1554 0 0 0 0 timetz_le scalarltsel scalarltjoinsel )); @@ -522,7 +522,7 @@ DATA(insert OID = 1234 ( "~*" PGNSP PGUID b f 1042 25 16 0 1235 0 0 0 0 DATA(insert OID = 1235 ( "!~*" PGNSP PGUID b f 1042 25 16 0 1234 0 0 0 0 bpcharicregexne icregexnesel icregexnejoinsel )); /* timestamptz operators */ -DATA(insert OID = 1320 ( "=" PGNSP PGUID b f 1184 1184 16 1320 1321 1322 1322 1322 1324 timestamptz_eq eqsel eqjoinsel )); +DATA(insert OID = 1320 ( "=" PGNSP PGUID b t 1184 1184 16 1320 1321 1322 1322 1322 1324 timestamptz_eq eqsel eqjoinsel )); DATA(insert OID = 1321 ( "<>" PGNSP PGUID b f 1184 1184 16 1321 1320 0 0 0 0 timestamptz_ne neqsel neqjoinsel )); DATA(insert OID = 1322 ( "<" PGNSP PGUID b f 1184 1184 16 1324 1325 0 0 0 0 timestamptz_lt scalarltsel scalarltjoinsel )); DATA(insert OID = 1323 ( "<=" PGNSP PGUID b f 1184 1184 16 1325 1324 0 0 0 0 timestamptz_le scalarltsel scalarltjoinsel )); @@ -533,7 +533,7 @@ DATA(insert OID = 1328 ( "-" PGNSP PGUID b f 1184 1184 1186 0 0 0 0 0 0 tim DATA(insert OID = 1329 ( "-" PGNSP PGUID b f 1184 1186 1184 0 0 0 0 0 0 timestamptz_mi_span - - )); /* interval operators */ -DATA(insert OID = 1330 ( "=" PGNSP PGUID b f 1186 1186 16 1330 1331 1332 1332 1332 1334 interval_eq eqsel eqjoinsel )); +DATA(insert OID = 1330 ( "=" PGNSP PGUID b t 1186 1186 16 1330 1331 1332 1332 1332 1334 interval_eq eqsel eqjoinsel )); DATA(insert OID = 1331 ( "<>" PGNSP PGUID b f 1186 1186 16 1331 1330 0 0 0 0 interval_ne neqsel neqjoinsel )); DATA(insert OID = 1332 ( "<" PGNSP PGUID b f 1186 1186 16 1334 1335 0 0 0 0 interval_lt scalarltsel scalarltjoinsel )); DATA(insert OID = 1333 ( "<=" PGNSP PGUID b f 1186 1186 16 1335 1334 0 0 0 0 interval_le scalarltsel scalarltjoinsel )); @@ -630,7 +630,7 @@ DATA(insert OID = 1616 ( "=" PGNSP PGUID b f 628 628 16 1616 0 0 0 0 0 line DATA(insert OID = 1617 ( "#" PGNSP PGUID b f 628 628 600 1617 0 0 0 0 0 line_interpt - - )); /* MAC type */ -DATA(insert OID = 1220 ( "=" PGNSP PGUID b f 829 829 16 1220 1221 1222 1222 1222 1224 macaddr_eq eqsel eqjoinsel )); +DATA(insert OID = 1220 ( "=" PGNSP PGUID b t 829 829 16 1220 1221 1222 1222 1222 1224 macaddr_eq eqsel eqjoinsel )); DATA(insert OID = 1221 ( "<>" PGNSP PGUID b f 829 829 16 1221 1220 0 0 0 0 macaddr_ne neqsel neqjoinsel )); DATA(insert OID = 1222 ( "<" PGNSP PGUID b f 829 829 16 1224 1225 0 0 0 0 macaddr_lt scalarltsel scalarltjoinsel )); DATA(insert OID = 1223 ( "<=" PGNSP PGUID b f 829 829 16 1225 1224 0 0 0 0 macaddr_le scalarltsel scalarltjoinsel )); @@ -638,7 +638,7 @@ DATA(insert OID = 1224 ( ">" PGNSP PGUID b f 829 829 16 1222 1223 0 0 DATA(insert OID = 1225 ( ">=" PGNSP PGUID b f 829 829 16 1223 1222 0 0 0 0 macaddr_ge scalargtsel scalargtjoinsel )); /* INET type */ -DATA(insert OID = 1201 ( "=" PGNSP PGUID b f 869 869 16 1201 1202 1203 1203 1203 1205 network_eq eqsel eqjoinsel )); +DATA(insert OID = 1201 ( "=" PGNSP PGUID b t 869 869 16 1201 1202 1203 1203 1203 1205 network_eq eqsel eqjoinsel )); DATA(insert OID = 1202 ( "<>" PGNSP PGUID b f 869 869 16 1202 1201 0 0 0 0 network_ne neqsel neqjoinsel )); DATA(insert OID = 1203 ( "<" PGNSP PGUID b f 869 869 16 1205 1206 0 0 0 0 network_lt scalarltsel scalarltjoinsel )); DATA(insert OID = 1204 ( "<=" PGNSP PGUID b f 869 869 16 1206 1205 0 0 0 0 network_le scalarltsel scalarltjoinsel )); @@ -654,7 +654,7 @@ DATA(insert OID = 934 ( ">>=" PGNSP PGUID b f 869 869 16 932 0 0 0 0 #define OID_INET_SUPEQ_OP 934 /* CIDR type */ -DATA(insert OID = 820 ( "=" PGNSP PGUID b f 650 650 16 820 821 822 822 822 824 network_eq eqsel eqjoinsel )); +DATA(insert OID = 820 ( "=" PGNSP PGUID b t 650 650 16 820 821 822 822 822 824 network_eq eqsel eqjoinsel )); DATA(insert OID = 821 ( "<>" PGNSP PGUID b f 650 650 16 821 820 0 0 0 0 network_ne neqsel neqjoinsel )); DATA(insert OID = 822 ( "<" PGNSP PGUID b f 650 650 16 824 825 0 0 0 0 network_lt scalarltsel scalarltjoinsel )); DATA(insert OID = 823 ( "<=" PGNSP PGUID b f 650 650 16 825 824 0 0 0 0 network_le scalarltsel scalarltjoinsel )); @@ -778,7 +778,7 @@ DATA(insert OID = 2017 ( "!~~" PGNSP PGUID b f 17 17 16 0 2016 0 0 0 0 DATA(insert OID = 2018 ( "||" PGNSP PGUID b f 17 17 17 0 0 0 0 0 0 byteacat - - )); /* timestamp operators */ -DATA(insert OID = 2060 ( "=" PGNSP PGUID b f 1114 1114 16 2060 2061 2062 2062 2062 2064 timestamp_eq eqsel eqjoinsel )); +DATA(insert OID = 2060 ( "=" PGNSP PGUID b t 1114 1114 16 2060 2061 2062 2062 2062 2064 timestamp_eq eqsel eqjoinsel )); DATA(insert OID = 2061 ( "<>" PGNSP PGUID b f 1114 1114 16 2061 2060 0 0 0 0 timestamp_ne neqsel neqjoinsel )); DATA(insert OID = 2062 ( "<" PGNSP PGUID b f 1114 1114 16 2064 2065 0 0 0 0 timestamp_lt scalarltsel scalarltjoinsel )); DATA(insert OID = 2063 ( "<=" PGNSP PGUID b f 1114 1114 16 2065 2064 0 0 0 0 timestamp_le scalarltsel scalarltjoinsel )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index d8ce41c6a8e..6cf727cc64b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.303 2003/06/11 09:23:55 petere Exp $ + * $Id: pg_proc.h,v 1.304 2003/06/22 22:04:55 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -836,10 +836,14 @@ DATA(insert OID = 454 ( hashchar PGNSP PGUID 12 f f t f i 1 23 "18" hashch DESCR("hash"); DATA(insert OID = 455 ( hashname PGNSP PGUID 12 f f t f i 1 23 "19" hashname - _null_ )); DESCR("hash"); +DATA(insert OID = 400 ( hashtext PGNSP PGUID 12 f f t f i 1 23 "25" hashtext - _null_ )); +DESCR("hash"); DATA(insert OID = 456 ( hashvarlena PGNSP PGUID 12 f f t f i 1 23 "2281" hashvarlena - _null_ )); DESCR("hash any varlena type"); DATA(insert OID = 457 ( hashoidvector PGNSP PGUID 12 f f t f i 1 23 "30" hashoidvector - _null_ )); DESCR("hash"); +DATA(insert OID = 398 ( hashint2vector PGNSP PGUID 12 f f t f i 1 23 "22" hashint2vector - _null_ )); +DESCR("hash"); DATA(insert OID = 399 ( hashmacaddr PGNSP PGUID 12 f f t f i 1 23 "829" hashmacaddr - _null_ )); DESCR("hash"); DATA(insert OID = 458 ( text_larger PGNSP PGUID 12 f f t f i 2 25 "25 25" text_larger - _null_ )); diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 707b8c9fa62..04e630451f2 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: executor.h,v 1.94 2003/05/06 20:26:28 tgl Exp $ + * $Id: executor.h,v 1.95 2003/06/22 22:04:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -57,9 +57,14 @@ extern bool execTuplesUnequal(HeapTuple tuple1, extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc, int numCols, AttrNumber *matchColIdx); -extern uint32 ComputeHashFunc(Datum key, int typLen, bool byVal); +extern void execTuplesHashPrepare(TupleDesc tupdesc, + int numCols, + AttrNumber *matchColIdx, + FmgrInfo **eqfunctions, + FmgrInfo **hashfunctions); extern TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx, FmgrInfo *eqfunctions, + FmgrInfo *hashfunctions, int nbuckets, Size entrysize, MemoryContext tablecxt, MemoryContext tempcxt); diff --git a/src/include/executor/hashjoin.h b/src/include/executor/hashjoin.h index a2d5f633fcd..da7e0bf98c4 100644 --- a/src/include/executor/hashjoin.h +++ b/src/include/executor/hashjoin.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: hashjoin.h,v 1.27 2002/11/30 00:08:20 tgl Exp $ + * $Id: hashjoin.h,v 1.28 2003/06/22 22:04:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -69,13 +69,12 @@ typedef struct HashTableData * file */ /* - * Info about the datatypes being hashed. We assume that the inner and - * outer sides of each hashclause are the same type, or at least - * binary-compatible types. Each of these fields points to an array - * of the same length as the number of hash keys. + * Info about the datatype-specific hash functions for the datatypes + * being hashed. We assume that the inner and outer sides of each + * hashclause are the same type, or at least share the same hash function. + * This is an array of the same length as the number of hash keys. */ - int16 *typLens; - bool *typByVals; + FmgrInfo *hashfunctions; /* lookup data for hash functions */ /* * During 1st scan of inner relation, we get tuples from executor. If diff --git a/src/include/executor/nodeHash.h b/src/include/executor/nodeHash.h index da1113b32da..d6d7ea627ea 100644 --- a/src/include/executor/nodeHash.h +++ b/src/include/executor/nodeHash.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: nodeHash.h,v 1.29 2003/01/10 23:54:24 tgl Exp $ + * $Id: nodeHash.h,v 1.30 2003/06/22 22:04:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,7 +22,7 @@ extern TupleTableSlot *ExecHash(HashState *node); extern void ExecEndHash(HashState *node); extern void ExecReScanHash(HashState *node, ExprContext *exprCtxt); -extern HashJoinTable ExecHashTableCreate(Hash *node); +extern HashJoinTable ExecHashTableCreate(Hash *node, List *hashOperators); extern void ExecHashTableDestroy(HashJoinTable hashtable); extern void ExecHashTableInsert(HashJoinTable hashtable, ExprContext *econtext, diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 68a3bb9b1cd..47879296c0e 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execnodes.h,v 1.98 2003/05/28 16:04:00 tgl Exp $ + * $Id: execnodes.h,v 1.99 2003/06/22 22:04:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -353,6 +353,7 @@ typedef struct TupleHashTableData int numCols; /* number of columns in lookup key */ AttrNumber *keyColIdx; /* attr numbers of key columns */ FmgrInfo *eqfunctions; /* lookup data for comparison functions */ + FmgrInfo *hashfunctions; /* lookup data for hash functions */ MemoryContext tablecxt; /* memory context containing table */ MemoryContext tempcxt; /* context for function evaluations */ Size entrysize; /* actual size to make each hash entry */ @@ -521,6 +522,7 @@ typedef struct SubPlanState ExprContext *innerecontext; /* working context for comparisons */ AttrNumber *keyColIdx; /* control data for hash tables */ FmgrInfo *eqfunctions; /* comparison functions for hash tables */ + FmgrInfo *hashfunctions; /* lookup data for hash functions */ } SubPlanState; /* ---------------- @@ -900,6 +902,7 @@ typedef struct MergeJoinState * unless OuterTupleSlot is nonempty!) * hj_OuterHashKeys the outer hash keys in the hashjoin condition * hj_InnerHashKeys the inner hash keys in the hashjoin condition + * hj_HashOperators the join operators in the hashjoin condition * hj_OuterTupleSlot tuple slot for outer tuples * hj_HashTupleSlot tuple slot for hashed tuples * hj_NullInnerTupleSlot prepared null tuple for left outer joins @@ -917,6 +920,7 @@ typedef struct HashJoinState HashJoinTuple hj_CurTuple; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ + List *hj_HashOperators; /* list of operator OIDs */ TupleTableSlot *hj_OuterTupleSlot; TupleTableSlot *hj_HashTupleSlot; TupleTableSlot *hj_NullInnerTupleSlot; @@ -992,6 +996,7 @@ typedef struct AggState List *aggs; /* all Aggref nodes in targetlist & quals */ int numaggs; /* length of list (could be zero!) */ FmgrInfo *eqfunctions; /* per-grouping-field equality fns */ + FmgrInfo *hashfunctions; /* per-grouping-field hash fns */ AggStatePerAgg peragg; /* per-Aggref information */ MemoryContext aggcontext; /* memory context for long-lived data */ ExprContext *tmpcontext; /* econtext for input expressions */ diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 66b497a98b1..878f5445c2a 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: lsyscache.h,v 1.70 2003/05/26 00:11:28 tgl Exp $ + * $Id: lsyscache.h,v 1.71 2003/06/22 22:04:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +18,7 @@ extern bool op_in_opclass(Oid opno, Oid opclass); extern bool op_requires_recheck(Oid opno, Oid opclass); extern Oid get_opclass_member(Oid opclass, int16 strategy); +extern Oid get_op_hash_function(Oid opno); extern char *get_attname(Oid relid, AttrNumber attnum); extern AttrNumber get_attnum(Oid relid, const char *attname); extern Oid get_atttype(Oid relid, AttrNumber attnum); @@ -25,6 +26,7 @@ extern int32 get_atttypmod(Oid relid, AttrNumber attnum); extern void get_atttypetypmod(Oid relid, AttrNumber attnum, Oid *typid, int32 *typmod); extern bool opclass_is_btree(Oid opclass); +extern bool opclass_is_hash(Oid opclass); extern RegProcedure get_opcode(Oid opno); extern char *get_opname(Oid opno); extern bool op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp); diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index e0504706f3c..985e06595e7 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -436,9 +436,6 @@ WHERE p1.oprlsortop != p1.oprrsortop AND -- Hashing only works on simple equality operators "type = sametype", -- since the hash itself depends on the bitwise representation of the type. -- Check that allegedly hashable operators look like they might be "=". --- NOTE: as of 7.3, this search finds xideqint4. Since we do not mark --- xid and int4 as binary-equivalent in pg_cast, there's no easy way to --- recognize that case as OK; just leave that tuple in the expected output. SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE p1.oprcanhash AND NOT @@ -447,8 +444,7 @@ WHERE p1.oprcanhash AND NOT p1.oprcom = p1.oid); oid | oprname -----+--------- - 353 | = -(1 row) +(0 rows) -- In 6.5 we accepted hashable array equality operators when the array element -- type is hashable. However, what we actually need to make hashjoin work on @@ -474,6 +470,17 @@ WHERE p1.oprcanhash AND p1.oprcode = p2.oid AND p2.proname = 'array_eq'; -----+--------- (0 rows) +-- Hashable operators should appear as members of hash index opclasses. +SELECT p1.oid, p1.oprname +FROM pg_operator AS p1 +WHERE p1.oprcanhash AND NOT EXISTS + (SELECT 1 FROM pg_opclass op JOIN pg_amop p ON op.oid = amopclaid + WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'hash') AND + amopopr = p1.oid); + oid | oprname +-----+--------- +(0 rows) + -- Check that each operator defined in pg_operator matches its oprcode entry -- in pg_proc. Easiest to do this separately for each oprkind. SELECT p1.oid, p1.oprname, p2.oid, p2.proname @@ -793,3 +800,24 @@ WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') -----------+-----------+-----+---------+--------- (0 rows) +-- For hash we can also do a little better: the support routines must be +-- of the form hash(something) returns int4. Ideally we'd check that the +-- opcintype is binary-coercible to the function's input, but there are +-- enough cases where that fails that I'll just leave out the check for now. +SELECT p1.amopclaid, p1.amprocnum, + p2.oid, p2.proname, + p3.opcname +FROM pg_amproc AS p1, pg_proc AS p2, pg_opclass AS p3 +WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'hash') + AND p1.amopclaid = p3.oid AND p1.amproc = p2.oid AND + (opckeytype != 0 + OR amprocnum != 1 + OR proretset + OR prorettype != 23 + OR pronargs != 1 +-- OR NOT physically_coercible(opcintype, proargtypes[0]) +); + amopclaid | amprocnum | oid | proname | opcname +-----------+-----------+-----+---------+--------- +(0 rows) + diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 4b7bd7b4dd5..9cba48b4296 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -364,10 +364,6 @@ WHERE p1.oprlsortop != p1.oprrsortop AND -- since the hash itself depends on the bitwise representation of the type. -- Check that allegedly hashable operators look like they might be "=". --- NOTE: as of 7.3, this search finds xideqint4. Since we do not mark --- xid and int4 as binary-equivalent in pg_cast, there's no easy way to --- recognize that case as OK; just leave that tuple in the expected output. - SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE p1.oprcanhash AND NOT @@ -398,6 +394,16 @@ SELECT p1.oid, p1.oprname FROM pg_operator AS p1, pg_proc AS p2 WHERE p1.oprcanhash AND p1.oprcode = p2.oid AND p2.proname = 'array_eq'; +-- Hashable operators should appear as members of hash index opclasses. + +SELECT p1.oid, p1.oprname +FROM pg_operator AS p1 +WHERE p1.oprcanhash AND NOT EXISTS + (SELECT 1 FROM pg_opclass op JOIN pg_amop p ON op.oid = amopclaid + WHERE opcamid = (SELECT oid FROM pg_am WHERE amname = 'hash') AND + amopopr = p1.oid); + + -- Check that each operator defined in pg_operator matches its oprcode entry -- in pg_proc. Easiest to do this separately for each oprkind. @@ -665,3 +671,22 @@ WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree') OR pronargs != 2 OR NOT binary_coercible(opcintype, proargtypes[0]) OR proargtypes[0] != proargtypes[1]); + +-- For hash we can also do a little better: the support routines must be +-- of the form hash(something) returns int4. Ideally we'd check that the +-- opcintype is binary-coercible to the function's input, but there are +-- enough cases where that fails that I'll just leave out the check for now. + +SELECT p1.amopclaid, p1.amprocnum, + p2.oid, p2.proname, + p3.opcname +FROM pg_amproc AS p1, pg_proc AS p2, pg_opclass AS p3 +WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'hash') + AND p1.amopclaid = p3.oid AND p1.amproc = p2.oid AND + (opckeytype != 0 + OR amprocnum != 1 + OR proretset + OR prorettype != 23 + OR pronargs != 1 +-- OR NOT physically_coercible(opcintype, proargtypes[0]) +); |