aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/hash/hashfunc.c44
-rw-r--r--src/backend/executor/execGrouping.c123
-rw-r--r--src/backend/executor/nodeAgg.c23
-rw-r--r--src/backend/executor/nodeHash.c35
-rw-r--r--src/backend/executor/nodeHashjoin.c19
-rw-r--r--src/backend/executor/nodeSubplan.c16
-rw-r--r--src/backend/utils/adt/varchar.c4
-rw-r--r--src/backend/utils/cache/catcache.c78
-rw-r--r--src/backend/utils/cache/lsyscache.c96
-rw-r--r--src/include/access/hash.h7
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/pg_am.h3
-rw-r--r--src/include/catalog/pg_amop.h24
-rw-r--r--src/include/catalog/pg_amproc.h17
-rw-r--r--src/include/catalog/pg_opclass.h13
-rw-r--r--src/include/catalog/pg_operator.h30
-rw-r--r--src/include/catalog/pg_proc.h6
-rw-r--r--src/include/executor/executor.h9
-rw-r--r--src/include/executor/hashjoin.h13
-rw-r--r--src/include/executor/nodeHash.h4
-rw-r--r--src/include/nodes/execnodes.h7
-rw-r--r--src/include/utils/lsyscache.h4
-rw-r--r--src/test/regress/expected/opr_sanity.out38
-rw-r--r--src/test/regress/sql/opr_sanity.sql33
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])
+);