OSDN Git Service

Revise hash join and hash aggregation code to use the same datatype-
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 22 Jun 2003 22:04:55 +0000 (22:04 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 22 Jun 2003 22:04:55 +0000 (22:04 +0000)
specific hash functions used by hash indexes, rather than the old
not-datatype-aware ComputeHashFunc routine.  This makes it safe to do
hash joining on several datatypes that previously couldn't use hashing.
The sets of datatypes that are hash indexable and hash joinable are now
exactly the same, whereas before each had some that weren't in the other.

27 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/xfunc.sgml
doc/src/sgml/xoper.sgml
src/backend/access/hash/hashfunc.c
src/backend/executor/execGrouping.c
src/backend/executor/nodeAgg.c
src/backend/executor/nodeHash.c
src/backend/executor/nodeHashjoin.c
src/backend/executor/nodeSubplan.c
src/backend/utils/adt/varchar.c
src/backend/utils/cache/catcache.c
src/backend/utils/cache/lsyscache.c
src/include/access/hash.h
src/include/catalog/catversion.h
src/include/catalog/pg_am.h
src/include/catalog/pg_amop.h
src/include/catalog/pg_amproc.h
src/include/catalog/pg_opclass.h
src/include/catalog/pg_operator.h
src/include/catalog/pg_proc.h
src/include/executor/executor.h
src/include/executor/hashjoin.h
src/include/executor/nodeHash.h
src/include/nodes/execnodes.h
src/include/utils/lsyscache.h
src/test/regress/expected/opr_sanity.out
src/test/regress/sql/opr_sanity.sql

index a8f7190..835739d 100644 (file)
@@ -1,6 +1,6 @@
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
- $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.71 2003/05/28 16:03:55 tgl Exp $
+ $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.72 2003/06/22 22:04:54 tgl Exp $
  -->
 
 <chapter id="catalogs">
       <entry><structfield>oprcanhash</structfield></entry>
       <entry><type>bool</type></entry>
       <entry></entry>
-      <entry>This operator supports hash joins.</entry>
+      <entry>This operator supports hash joins</entry>
      </row>
 
      <row>
index f6298a0..b64aa01 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.68 2003/05/29 20:40:36 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.69 2003/06/22 22:04:54 tgl Exp $
 -->
 
  <sect1 id="xfunc">
@@ -1442,11 +1442,10 @@ concat_text(PG_FUNCTION_ARGS)
       <listitem>
        <para>
         Always zero the bytes of your structures using
-        <function>memset</function> or <function>bzero</function>.
-        Several routines (such as the hash access method, hash joins,
-        and the sort algorithm) compute functions of the raw bits
-        contained in your structure.  Even if you initialize all
-        fields of your structure, there may be several bytes of
+       <function>memset</function>.  Without this, it's difficult to
+       support hash indexes or hash joins, as you must pick out only
+       the significant bits of your data structure to compute a hash.
+        Even if you initialize all fields of your structure, there may be
         alignment padding (holes in the structure) that may contain
         garbage values.
        </para>
index 22d2146..a2705eb 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/xoper.sgml,v 1.23 2003/04/10 01:22:45 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/xoper.sgml,v 1.24 2003/06/22 22:04:54 tgl Exp $
 -->
 
  <sect1 id="xoper">
@@ -315,46 +315,34 @@ table1.column1 OP table2.column2
      same hash code.  If two values get put in different hash buckets, the
      join will never compare them at all, implicitly assuming that the
      result of the join operator must be false.  So it never makes sense
-     to specify <literal>HASHES</literal> for operators that do not represent equality.
+     to specify <literal>HASHES</literal> for operators that do not represent
+     equality.
     </para>
 
     <para>
-     In fact, logical equality is not good enough either; the operator
-     had better represent pure bitwise equality, because the hash
-     function will be computed on the memory representation of the
-     values regardless of what the bits mean.  For example, the
-     polygon operator <literal>~=</literal>, which checks whether two
-     polygons are the same, is not bitwise equality, because two
-     polygons can be considered the same even if their vertices are
-     specified in a different order.  What this means is that a join
-     using <literal>~=</literal> between polygon fields would yield
-     different results if implemented as a hash join than if
-     implemented another way, because a large fraction of the pairs
-     that should match will hash to different values and will never be
-     compared by the hash join.  But if the optimizer chooses to use a
-     different kind of join, all the pairs that the operator
-     <literal>~=</literal> says are the same will be found.  We don't
-     want that kind of inconsistency, so we don't mark the polygon
-     operator <literal>~=</literal> as hashable.
+     To be marked <literal>HASHES</literal>, the join operator must appear
+     in a hash index operator class.  This is not enforced when you create
+     the operator, since of course the referencing operator class couldn't
+     exist yet.  But attempts to use the operator in hash joins will fail
+     at runtime if no such operator class exists.  The system needs the
+     operator class to find the datatype-specific hash function for the
+     operator's input datatype.  Of course, you must also supply a suitable
+     hash function before you can create the operator class.
     </para>
 
     <para>
-     There are also machine-dependent ways in which a hash join might fail
-     to do the right thing.  For example, if your data type
-     is a structure in which there may be uninteresting pad bits, it's unsafe
-     to mark the equality operator <literal>HASHES</>.  (Unless you write
-     your other operators and functions to ensure that the unused bits are always zero, which is the recommended strategy.)
-     Another example is that the floating-point data types are unsafe for hash
-     joins.  On machines that meet the <acronym>IEEE</> floating-point standard, negative
-     zero and positive zero are different values (different bit patterns) but
-     they are defined to compare equal.  So, if the equality operator on floating-point data types were marked
-     <literal>HASHES</>, a negative zero and a positive zero would probably not be matched up
-     by a hash join, but they would be matched up by any other join process.
-    </para>
-
-    <para>
-     The bottom line is that you should probably only use <literal>HASHES</literal> for
-     equality operators that are (or could be) implemented by <function>memcmp()</function>.
+     Care should be exercised when preparing a hash function, because there
+     are machine-dependent ways in which it might fail to do the right thing.
+     For example, if your data type is a structure in which there may be
+     uninteresting pad bits, you can't simply pass the whole structure to
+     <function>hash_any</>.  (Unless you write your other operators and
+     functions to ensure that the unused bits are always zero, which is the
+     recommended strategy.)
+     Another example is that on machines that meet the <acronym>IEEE</>
+     floating-point standard, negative zero and positive zero are different
+     values (different bit patterns) but they are defined to compare equal.
+     If a float value might contain negative zero then extra steps are needed
+     to ensure it generates the same hash value as positive zero.
     </para>
 
     <note>
index e6595de..a82b8b3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.35 2002/09/04 20:31:09 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.36 2003/06/22 22:04:54 tgl Exp $
  *
  * NOTES
  *       These functions are stored in pg_amproc.      For each operator class
@@ -22,6 +22,7 @@
 #include "access/hash.h"
 
 
+/* Note: this is used for both "char" and boolean datatypes */
 Datum
 hashchar(PG_FUNCTION_ARGS)
 {
@@ -58,6 +59,14 @@ hashfloat4(PG_FUNCTION_ARGS)
 {
        float4          key = PG_GETARG_FLOAT4(0);
 
+       /*
+        * On IEEE-float machines, minus zero and zero have different bit patterns
+        * but should compare as equal.  We must ensure that they have the same
+        * hash value, which is most easily done this way:
+        */
+       if (key == (float4) 0)
+               PG_RETURN_UINT32(0);
+
        return hash_any((unsigned char *) &key, sizeof(key));
 }
 
@@ -66,6 +75,14 @@ hashfloat8(PG_FUNCTION_ARGS)
 {
        float8          key = PG_GETARG_FLOAT8(0);
 
+       /*
+        * On IEEE-float machines, minus zero and zero have different bit patterns
+        * but should compare as equal.  We must ensure that they have the same
+        * hash value, which is most easily done this way:
+        */
+       if (key == (float8) 0)
+               PG_RETURN_UINT32(0);
+
        return hash_any((unsigned char *) &key, sizeof(key));
 }
 
@@ -77,11 +94,6 @@ hashoidvector(PG_FUNCTION_ARGS)
        return hash_any((unsigned char *) key, INDEX_MAX_KEYS * sizeof(Oid));
 }
 
-/*
- * Note: hashint2vector currently can't be used as a user hash table
- * hash function, because it has no pg_proc entry.     We only need it
- * for catcache indexing.
- */
 Datum
 hashint2vector(PG_FUNCTION_ARGS)
 {
@@ -102,6 +114,26 @@ hashname(PG_FUNCTION_ARGS)
        return hash_any((unsigned char *) key, keylen);
 }
 
+Datum
+hashtext(PG_FUNCTION_ARGS)
+{
+       text       *key = PG_GETARG_TEXT_P(0);
+       Datum           result;
+
+       /*
+        * Note: this is currently identical in behavior to hashvarlena,
+        * but it seems likely that we may need to do something different
+        * in non-C locales.  (See also hashbpchar, if so.)
+        */
+       result = hash_any((unsigned char *) VARDATA(key),
+                                         VARSIZE(key) - VARHDRSZ);
+
+       /* Avoid leaking memory for toasted inputs */
+       PG_FREE_IF_COPY(key, 0);
+
+       return result;
+}
+
 /*
  * hashvarlena() can be used for any varlena datatype in which there are
  * no non-significant bits, ie, distinct bitpatterns never compare as equal.
index 0d4d5ed..b41c75e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execGrouping.c,v 1.2 2003/01/12 04:03:34 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execGrouping.c,v 1.3 2003/06/22 22:04:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,6 +19,8 @@
 #include "executor/executor.h"
 #include "parser/parse_oper.h"
 #include "utils/memutils.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
 
 
 /*****************************************************************************
@@ -213,76 +215,46 @@ execTuplesMatchPrepare(TupleDesc tupdesc,
        return eqfunctions;
 }
 
-
-/*****************************************************************************
- *             Utility routines for hashing
- *****************************************************************************/
-
 /*
- * ComputeHashFunc
+ * execTuplesHashPrepare
+ *             Look up the equality and hashing functions needed for a TupleHashTable.
  *
- *             the hash function for hash joins (also used for hash aggregation)
- *
- *             XXX this probably ought to be replaced with datatype-specific
- *             hash functions, such as those already implemented for hash indexes.
+ * This is similar to execTuplesMatchPrepare, but we also need to find the
+ * hash functions associated with the equality operators.  *eqfunctions and
+ * *hashfunctions receive the palloc'd result arrays.
  */
-uint32
-ComputeHashFunc(Datum key, int typLen, bool byVal)
+void
+execTuplesHashPrepare(TupleDesc tupdesc,
+                                         int numCols,
+                                         AttrNumber *matchColIdx,
+                                         FmgrInfo **eqfunctions,
+                                         FmgrInfo **hashfunctions)
 {
-       unsigned char *k;
+       int                     i;
 
-       if (byVal)
-       {
-               /*
-                * 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;
 
index f2499cb..d0dd6b3 100644 (file)
@@ -45,7 +45,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.106 2003/06/06 15:04:01 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.107 2003/06/22 22:04:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -582,6 +582,7 @@ build_hash_table(AggState *aggstate)
        aggstate->hashtable = BuildTupleHashTable(node->numCols,
                                                                                          node->grpColIdx,
                                                                                          aggstate->eqfunctions,
+                                                                                         aggstate->hashfunctions,
                                                                                          node->numGroups,
                                                                                          entrysize,
                                                                                          aggstate->aggcontext,
@@ -1035,6 +1036,7 @@ ExecInitAgg(Agg *node, EState *estate)
        aggstate->aggs = NIL;
        aggstate->numaggs = 0;
        aggstate->eqfunctions = NULL;
+       aggstate->hashfunctions = NULL;
        aggstate->peragg = NULL;
        aggstate->agg_done = false;
        aggstate->pergroup = NULL;
@@ -1123,14 +1125,23 @@ ExecInitAgg(Agg *node, EState *estate)
        }
 
        /*
-        * If we are grouping, precompute fmgr lookup data for inner loop
+        * If we are grouping, precompute fmgr lookup data for inner loop.
+        * We need both equality and hashing functions to do it by hashing,
+        * but only equality if not hashing.
         */
        if (node->numCols > 0)
        {
-               aggstate->eqfunctions =
-                       execTuplesMatchPrepare(ExecGetScanType(&aggstate->ss),
-                                                                  node->numCols,
-                                                                  node->grpColIdx);
+               if (node->aggstrategy == AGG_HASHED)
+                       execTuplesHashPrepare(ExecGetScanType(&aggstate->ss),
+                                                                 node->numCols,
+                                                                 node->grpColIdx,
+                                                                 &aggstate->eqfunctions,
+                                                                 &aggstate->hashfunctions);
+               else
+                       aggstate->eqfunctions =
+                               execTuplesMatchPrepare(ExecGetScanType(&aggstate->ss),
+                                                                          node->numCols,
+                                                                          node->grpColIdx);
        }
 
        /*
index b338c89..f00cc28 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.75 2003/03/27 16:51:27 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.76 2003/06/22 22:04:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -192,7 +192,7 @@ ExecEndHash(HashState *node)
  * ----------------------------------------------------------------
  */
 HashJoinTable
-ExecHashTableCreate(Hash *node)
+ExecHashTableCreate(Hash *node, List *hashOperators)
 {
        HashJoinTable hashtable;
        Plan       *outerNode;
@@ -201,7 +201,7 @@ ExecHashTableCreate(Hash *node)
        int                     nbatch;
        int                     nkeys;
        int                     i;
-       List       *hk;
+       List       *ho;
        MemoryContext oldcxt;
 
        /*
@@ -237,17 +237,20 @@ ExecHashTableCreate(Hash *node)
        hashtable->outerBatchSize = NULL;
 
        /*
-        * Get info about the datatypes of the hash keys.
+        * Get info about the hash functions to be used for each hash key.
         */
-       nkeys = length(node->hashkeys);
-       hashtable->typLens = (int16 *) palloc(nkeys * sizeof(int16));
-       hashtable->typByVals = (bool *) palloc(nkeys * sizeof(bool));
+       nkeys = length(hashOperators);
+       hashtable->hashfunctions = (FmgrInfo *) palloc(nkeys * sizeof(FmgrInfo));
        i = 0;
-       foreach(hk, node->hashkeys)
+       foreach(ho, hashOperators)
        {
-               get_typlenbyval(exprType(lfirst(hk)),
-                                               &hashtable->typLens[i],
-                                               &hashtable->typByVals[i]);
+               Oid             hashfn;
+
+               hashfn = get_op_hash_function(lfirsto(ho));
+               if (!OidIsValid(hashfn))
+                       elog(ERROR, "Could not find hash function for hash operator %u",
+                                lfirsto(ho));
+               fmgr_info(hashfn, &hashtable->hashfunctions[i]);
                i++;
        }
 
@@ -520,7 +523,7 @@ ExecHashGetBucket(HashJoinTable hashtable,
 
        /*
         * We reset the eval context each time to reclaim any memory leaked in
-        * the hashkey expressions or ComputeHashFunc itself.
+        * the hashkey expressions.
         */
        ResetExprContext(econtext);
 
@@ -545,9 +548,11 @@ ExecHashGetBucket(HashJoinTable hashtable,
                 */
                if (!isNull)                    /* treat nulls as having hash key 0 */
                {
-                       hashkey ^= ComputeHashFunc(keyval,
-                                                                          (int) hashtable->typLens[i],
-                                                                          hashtable->typByVals[i]);
+                       uint32          hkey;
+
+                       hkey = DatumGetUInt32(FunctionCall1(&hashtable->hashfunctions[i],
+                                                                                               keyval));
+                       hashkey ^= hkey;
                }
 
                i++;
index 17585b2..9a0071f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.51 2003/05/30 20:23:10 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.52 2003/06/22 22:04:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -117,7 +117,8 @@ ExecHashJoin(HashJoinState *node)
                 * create the hash table
                 */
                Assert(hashtable == NULL);
-               hashtable = ExecHashTableCreate((Hash *) hashNode->ps.plan);
+               hashtable = ExecHashTableCreate((Hash *) hashNode->ps.plan,
+                                                                               node->hj_HashOperators);
                node->hj_HashTable = hashtable;
 
                /*
@@ -305,6 +306,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
        Plan       *outerNode;
        Hash       *hashNode;
        List       *hclauses;
+       List       *hoperators;
        List       *hcl;
 
        /*
@@ -406,8 +408,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
 
        /*
         * The planner already made a list of the inner hashkeys for us,
-        * but we also need a list of the outer hashkeys.  Each list of
-        * exprs must then be prepared for execution.
+        * but we also need a list of the outer hashkeys, as well as a list
+        * of the hash operator OIDs.  Both lists of exprs must then be prepared
+        * for execution.
         */
        hjstate->hj_InnerHashKeys = (List *)
                ExecInitExpr((Expr *) hashNode->hashkeys,
@@ -416,13 +419,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
                hjstate->hj_InnerHashKeys;
 
        hclauses = NIL;
+       hoperators = NIL;
        foreach(hcl, node->hashclauses)
        {
-               hclauses = lappend(hclauses, get_leftop(lfirst(hcl)));
+               OpExpr     *hclause = (OpExpr *) lfirst(hcl);
+
+               Assert(IsA(hclause, OpExpr));
+               hclauses = lappend(hclauses, get_leftop((Expr *) hclause));
+               hoperators = lappendo(hoperators, hclause->opno);
        }
        hjstate->hj_OuterHashKeys = (List *)
                ExecInitExpr((Expr *) hclauses,
                                         (PlanState *) hjstate);
+       hjstate->hj_HashOperators = hoperators;
 
        hjstate->js.ps.ps_OuterTupleSlot = NULL;
        hjstate->js.ps.ps_TupFromTlist = false;
index ff5d03f..82502c9 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.46 2003/06/06 15:04:01 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.47 2003/06/22 22:04:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -519,6 +519,7 @@ buildSubPlanHash(SubPlanState *node)
        node->hashtable = BuildTupleHashTable(ncols,
                                                                                  node->keyColIdx,
                                                                                  node->eqfunctions,
+                                                                                 node->hashfunctions,
                                                                                  nbuckets,
                                                                                  sizeof(TupleHashEntryData),
                                                                                  node->tablecxt,
@@ -537,6 +538,7 @@ buildSubPlanHash(SubPlanState *node)
                node->hashnulls = BuildTupleHashTable(ncols,
                                                                                          node->keyColIdx,
                                                                                          node->eqfunctions,
+                                                                                         node->hashfunctions,
                                                                                          nbuckets,
                                                                                          sizeof(TupleHashEntryData),
                                                                                          node->tablecxt,
@@ -700,6 +702,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
        node->innerecontext = NULL;
        node->keyColIdx = NULL;
        node->eqfunctions = NULL;
+       node->hashfunctions = NULL;
 
        /*
         * create an EState for the subplan
@@ -797,11 +800,12 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
                 * ExecTypeFromTL).
                 *
                 * We also extract the combining operators themselves to initialize
-                * the equality functions for the hash tables.
+                * the equality and hashing functions for the hash tables.
                 */
                lefttlist = righttlist = NIL;
                leftptlist = rightptlist = NIL;
                node->eqfunctions = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
+               node->hashfunctions = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
                i = 1;
                foreach(lexpr, node->exprs)
                {
@@ -811,6 +815,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
                        Expr       *expr;
                        TargetEntry *tle;
                        GenericExprState *tlestate;
+                       Oid                     hashfn;
 
                        Assert(IsA(fstate, FuncExprState));
                        Assert(IsA(opexpr, OpExpr));
@@ -850,6 +855,13 @@ ExecInitSubPlan(SubPlanState *node, EState *estate)
                        fmgr_info(opexpr->opfuncid, &node->eqfunctions[i-1]);
                        node->eqfunctions[i-1].fn_expr = (Node *) opexpr;
 
+                       /* Lookup the associated hash function */
+                       hashfn = get_op_hash_function(opexpr->opno);
+                       if (!OidIsValid(hashfn))
+                               elog(ERROR, "Could not find hash function for hash operator %u",
+                                        opexpr->opno);
+                       fmgr_info(hashfn, &node->hashfunctions[i-1]);
+
                        i++;
                }
 
index 5085c60..6e99c15 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.97 2003/05/26 00:11:27 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.98 2003/06/22 22:04:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -704,6 +704,8 @@ bpcharcmp(PG_FUNCTION_ARGS)
 /*
  * bpchar needs a specialized hash function because we want to ignore
  * trailing blanks in comparisons.
+ *
+ * XXX is there any need for locale-specific behavior here?
  */
 Datum
 hashbpchar(PG_FUNCTION_ARGS)
index 65d716f..8c0df3d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.103 2003/05/27 17:49:46 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.104 2003/06/22 22:04:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 /* 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);
index fcd9dc2..095c5e6 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.95 2003/05/26 00:11:27 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.96 2003/06/22 22:04:54 tgl Exp $
  *
  * NOTES
  *       Eventually, the index information should go through here, too.
 #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 ----------                                     */
 
 /*
index 1fadd8c..5834f92 100644 (file)
@@ -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);
 
index 209bd5f..f6c3855 100644 (file)
@@ -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
index 0807fb1..5186d9e 100644 (file)
@@ -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
index dbff38b..e2cb756 100644 (file)
@@ -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 */
index 0048d00..c2c37c1 100644 (file)
@@ -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 */
index f9de8fa..1c8844b 100644 (file)
@@ -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 */
index ae4fb6e..88d3d99 100644 (file)
@@ -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 ));
index d8ce41c..6cf727c 100644 (file)
@@ -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_ ));
index 707b8c9..04e6304 100644 (file)
@@ -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);
index a2d5f63..da7e0bf 100644 (file)
@@ -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
index da1113b..d6d7ea6 100644 (file)
@@ -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,
index 68a3bb9..4787929 100644 (file)
@@ -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 */
index 66b497a..878f544 100644 (file)
@@ -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);
index e050470..985e065 100644 (file)
@@ -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)
+
index 4b7bd7b..9cba48b 100644 (file)
@@ -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])
+);