]> granicus.if.org Git - postgresql/commitdiff
Upgrade planner and executor to allow multiple hash keys for a hash join,
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 30 Nov 2002 00:08:22 +0000 (00:08 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 30 Nov 2002 00:08:22 +0000 (00:08 +0000)
instead of only one.  This should speed up planning (only one hash path
to consider for a given pair of relations) as well as allow more effective
hashing, when there are multiple hashable joinclauses.

14 files changed:
src/backend/executor/nodeHash.c
src/backend/executor/nodeHashjoin.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/path/joinpath.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/util/pathnode.c
src/include/executor/hashjoin.h
src/include/executor/nodeHash.h
src/include/nodes/execnodes.h
src/include/nodes/plannodes.h
src/include/nodes/relation.h

index 57faf0622cbd5e122966c10f78ced32e24f29b19..c2c3ab66644af41e00d74c3479579edc82352988 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.67 2002/11/06 22:31:23 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.68 2002/11/30 00:08:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,7 +45,7 @@ ExecHash(Hash *node)
        EState     *estate;
        HashState  *hashstate;
        Plan       *outerNode;
-       Node       *hashkey;
+       List       *hashkeys;
        HashJoinTable hashtable;
        TupleTableSlot *slot;
        ExprContext *econtext;
@@ -79,7 +79,7 @@ ExecHash(Hash *node)
        /*
         * set expression context
         */
-       hashkey = node->hashkey;
+       hashkeys = node->hashkeys;
        econtext = hashstate->cstate.cs_ExprContext;
 
        /*
@@ -91,7 +91,7 @@ ExecHash(Hash *node)
                if (TupIsNull(slot))
                        break;
                econtext->ecxt_innertuple = slot;
-               ExecHashTableInsert(hashtable, econtext, hashkey);
+               ExecHashTableInsert(hashtable, econtext, hashkeys);
                ExecClearTuple(slot);
        }
 
@@ -212,7 +212,9 @@ ExecHashTableCreate(Hash *node)
        int                     totalbuckets;
        int                     nbuckets;
        int                     nbatch;
+       int                     nkeys;
        int                     i;
+       List       *hk;
        MemoryContext oldcxt;
 
        /*
@@ -248,11 +250,19 @@ ExecHashTableCreate(Hash *node)
        hashtable->outerBatchSize = NULL;
 
        /*
-        * Get info about the datatype of the hash key.
+        * Get info about the datatypes of the hash keys.
         */
-       get_typlenbyval(exprType(node->hashkey),
-                                       &hashtable->typLen,
-                                       &hashtable->typByVal);
+       nkeys = length(node->hashkeys);
+       hashtable->typLens = (int16 *) palloc(nkeys * sizeof(int16));
+       hashtable->typByVals = (bool *) palloc(nkeys * sizeof(bool));
+       i = 0;
+       foreach(hk, node->hashkeys)
+       {
+               get_typlenbyval(exprType(lfirst(hk)),
+                                               &hashtable->typLens[i],
+                                               &hashtable->typByVals[i]);
+               i++;
+       }
 
        /*
         * Create temporary memory contexts in which to keep the hashtable
@@ -465,9 +475,9 @@ ExecHashTableDestroy(HashJoinTable hashtable)
 void
 ExecHashTableInsert(HashJoinTable hashtable,
                                        ExprContext *econtext,
-                                       Node *hashkey)
+                                       List *hashkeys)
 {
-       int                     bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
+       int                     bucketno = ExecHashGetBucket(hashtable, econtext, hashkeys);
        TupleTableSlot *slot = econtext->ecxt_innertuple;
        HeapTuple       heapTuple = slot->val;
 
@@ -522,44 +532,55 @@ ExecHashTableInsert(HashJoinTable hashtable,
 int
 ExecHashGetBucket(HashJoinTable hashtable,
                                  ExprContext *econtext,
-                                 Node *hashkey)
+                                 List *hashkeys)
 {
+       uint32          hashkey = 0;
        int                     bucketno;
-       Datum           keyval;
-       bool            isNull;
+       List       *hk;
+       int                     i = 0;
        MemoryContext oldContext;
 
        /*
         * We reset the eval context each time to reclaim any memory leaked in
-        * the hashkey expression or ComputeHashFunc itself.
+        * the hashkey expressions or ComputeHashFunc itself.
         */
        ResetExprContext(econtext);
 
        oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
 
-       /*
-        * Get the join attribute value of the tuple
-        */
-       keyval = ExecEvalExpr(hashkey, econtext, &isNull, NULL);
-
-       /*
-        * Compute the hash function
-        */
-       if (isNull)
-               bucketno = 0;
-       else
+       foreach(hk, hashkeys)
        {
-               bucketno = ComputeHashFunc(keyval,
-                                                                  (int) hashtable->typLen,
-                                                                  hashtable->typByVal)
-                       % (uint32) hashtable->totalbuckets;
+               Datum           keyval;
+               bool            isNull;
+
+               /* rotate hashkey left 1 bit at each step */
+               hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0);
+
+               /*
+                * Get the join attribute value of the tuple
+                */
+               keyval = ExecEvalExpr(lfirst(hk), econtext, &isNull, NULL);
+
+               /*
+                * Compute the hash function
+                */
+               if (!isNull)                    /* treat nulls as having hash key 0 */
+               {
+                       hashkey ^= ComputeHashFunc(keyval,
+                                                                          (int) hashtable->typLens[i],
+                                                                          hashtable->typByVals[i]);
+               }
+
+               i++;
        }
 
+       bucketno = hashkey % (uint32) hashtable->totalbuckets;
+
 #ifdef HJDEBUG
        if (bucketno >= hashtable->nbuckets)
-               printf("hash(%ld) = %d SAVED\n", (long) keyval, bucketno);
+               printf("hash(%u) = %d SAVED\n", hashkey, bucketno);
        else
-               printf("hash(%ld) = %d\n", (long) keyval, bucketno);
+               printf("hash(%u) = %d\n", hashkey, bucketno);
 #endif
 
        MemoryContextSwitchTo(oldContext);
index f1484c4a0545769f2f46b80fff147e2b01298168..8f0e700ac35bfe345cd61c3a239bc56a8e381dc7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.41 2002/09/02 02:47:02 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.42 2002/11/30 00:08:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,12 +48,11 @@ ExecHashJoin(HashJoin *node)
        Plan       *outerNode;
        Hash       *hashNode;
        List       *hjclauses;
-       Expr       *clause;
+       List       *outerkeys;
        List       *joinqual;
        List       *otherqual;
        ScanDirection dir;
        TupleTableSlot *inntuple;
-       Node       *outerVar;
        ExprContext *econtext;
        ExprDoneCond isDone;
        HashJoinTable hashtable;
@@ -68,7 +67,6 @@ ExecHashJoin(HashJoin *node)
         */
        hjstate = node->hashjoinstate;
        hjclauses = node->hashclauses;
-       clause = lfirst(hjclauses);
        estate = node->join.plan.state;
        joinqual = node->join.joinqual;
        otherqual = node->join.plan.qual;
@@ -81,6 +79,7 @@ ExecHashJoin(HashJoin *node)
         * get information from HashJoin state
         */
        hashtable = hjstate->hj_HashTable;
+       outerkeys = hjstate->hj_OuterHashKeys;
        econtext = hjstate->jstate.cs_ExprContext;
 
        /*
@@ -119,7 +118,6 @@ ExecHashJoin(HashJoin *node)
                         */
                        hashtable = ExecHashTableCreate(hashNode);
                        hjstate->hj_HashTable = hashtable;
-                       hjstate->hj_InnerHashKey = hashNode->hashkey;
 
                        /*
                         * execute the Hash node, to build the hash table
@@ -143,7 +141,6 @@ ExecHashJoin(HashJoin *node)
         * Now get an outer tuple and probe into the hash table for matches
         */
        outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
-       outerVar = (Node *) get_leftop(clause);
 
        for (;;)
        {
@@ -175,7 +172,7 @@ ExecHashJoin(HashJoin *node)
                         * for this tuple from the hash table
                         */
                        hjstate->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext,
-                                                                                                               outerVar);
+                                                                                                               outerkeys);
                        hjstate->hj_CurTuple = NULL;
 
                        /*
@@ -308,6 +305,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
        HashJoinState *hjstate;
        Plan       *outerNode;
        Hash       *hashNode;
+       List       *hcl;
 
        /*
         * assign the node's execution state
@@ -391,7 +389,18 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
        hjstate->hj_HashTable = (HashJoinTable) NULL;
        hjstate->hj_CurBucketNo = 0;
        hjstate->hj_CurTuple = (HashJoinTuple) NULL;
-       hjstate->hj_InnerHashKey = (Node *) NULL;
+
+       /*
+        * The planner already made a list of the inner hashkeys for us,
+        * but we also need a list of the outer hashkeys.
+        */
+       hjstate->hj_InnerHashKeys = hashNode->hashkeys;
+       hjstate->hj_OuterHashKeys = NIL;
+       foreach(hcl, node->hashclauses)
+       {
+               hjstate->hj_OuterHashKeys = lappend(hjstate->hj_OuterHashKeys,
+                                                                                       get_leftop(lfirst(hcl)));
+       }
 
        hjstate->jstate.cs_OuterTupleSlot = NULL;
        hjstate->jstate.cs_TupFromTlist = false;
@@ -555,7 +564,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate)
        BufFile    *innerFile;
        TupleTableSlot *slot;
        ExprContext *econtext;
-       Node       *innerhashkey;
+       List       *innerhashkeys;
 
        if (newbatch > 1)
        {
@@ -603,7 +612,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate)
        ExecHashTableReset(hashtable, innerBatchSize[newbatch - 1]);
 
        econtext = hjstate->jstate.cs_ExprContext;
-       innerhashkey = hjstate->hj_InnerHashKey;
+       innerhashkeys = hjstate->hj_InnerHashKeys;
 
        while ((slot = ExecHashJoinGetSavedTuple(hjstate,
                                                                                         innerFile,
@@ -611,7 +620,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate)
                   && !TupIsNull(slot))
        {
                econtext->ecxt_innertuple = slot;
-               ExecHashTableInsert(hashtable, econtext, innerhashkey);
+               ExecHashTableInsert(hashtable, econtext, innerhashkeys);
        }
 
        /*
@@ -694,7 +703,6 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
 
        hjstate->hj_CurBucketNo = 0;
        hjstate->hj_CurTuple = (HashJoinTuple) NULL;
-       hjstate->hj_InnerHashKey = (Node *) NULL;
 
        hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
        hjstate->jstate.cs_TupFromTlist = false;
index a678d6326b1942da3ceb3c3117301152a505ed66..7798913cde517ff0f1d351db1d32922239c814d2 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.223 2002/11/25 21:29:36 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.224 2002/11/30 00:08:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -429,7 +429,6 @@ _copyHashJoin(HashJoin *from)
         * copy remainder of node
         */
        COPY_NODE_FIELD(hashclauses);
-       COPY_SCALAR_FIELD(hashjoinop);
 
        /* subPlan list must point to subplans in the new subtree, not the old */
        FIX_SUBPLAN_LINKS(join.plan.subPlan, hashclauses);
@@ -593,9 +592,9 @@ _copyHash(Hash *from)
        /*
         * copy remainder of node
         */
-       COPY_NODE_FIELD(hashkey);
+       COPY_NODE_FIELD(hashkeys);
 
-       /* XXX could the hashkey contain subplans?  Not at present... */
+       /* XXX could the hashkeys contain subplans?  Not at present... */
 
        return newnode;
 }
index 11572a4ebad0afe0527e5fd509d5d81174bdcd68..528148f02f0dce56e6298e521d0ce2f296e81212 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.183 2002/11/25 21:29:36 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.184 2002/11/30 00:08:16 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -538,7 +538,6 @@ _outHashJoin(StringInfo str, HashJoin *node)
        _outJoinPlanInfo(str, (Join *) node);
 
        WRITE_NODE_FIELD(hashclauses);
-       WRITE_OID_FIELD(hashjoinop);
 }
 
 static void
@@ -634,7 +633,7 @@ _outHash(StringInfo str, Hash *node)
 
        _outPlanInfo(str, (Plan *) node);
 
-       WRITE_NODE_FIELD(hashkey);
+       WRITE_NODE_FIELD(hashkeys);
 }
 
 static void
index 6cf8b2af4b5f3db0dd1bc90c88d1651c42da3b1a..fbdeea414c274c442240fdd8a4a67374db954cfb 100644 (file)
@@ -42,7 +42,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.91 2002/11/21 00:42:19 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.92 2002/11/30 00:08:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -819,7 +819,7 @@ cost_mergejoin(Path *path, Query *root,
  * 'outer_path' is the path for the outer relation
  * 'inner_path' is the path for the inner relation
  * 'restrictlist' are the RestrictInfo nodes to be applied at the join
- * 'hashclauses' is a list of the hash join clause (always a 1-element list)
+ * 'hashclauses' are the RestrictInfo nodes to use as hash clauses
  *             (this should be a subset of the restrictlist)
  */
 void
@@ -838,10 +838,8 @@ cost_hashjoin(Path *path, Query *root,
        double          innerbytes = relation_byte_size(inner_path->parent->rows,
                                                                                          inner_path->parent->width);
        long            hashtablebytes = SortMem * 1024L;
-       RestrictInfo *restrictinfo;
-       Var                *left,
-                          *right;
        Selectivity innerbucketsize;
+       List       *hcl;
 
        if (!enable_hashjoin)
                startup_cost += disable_cost;
@@ -856,43 +854,57 @@ cost_hashjoin(Path *path, Query *root,
        run_cost += cpu_operator_cost * outer_path->parent->rows;
 
        /*
-        * Determine bucketsize fraction for inner relation.  First we have to
-        * figure out which side of the hashjoin clause is the inner side.
+        * Determine bucketsize fraction for inner relation.  We use the
+        * smallest bucketsize estimated for any individual hashclause;
+        * this is undoubtedly conservative.
         */
-       Assert(length(hashclauses) == 1);
-       Assert(IsA(lfirst(hashclauses), RestrictInfo));
-       restrictinfo = (RestrictInfo *) lfirst(hashclauses);
-       /* these must be OK, since check_hashjoinable accepted the clause */
-       left = get_leftop(restrictinfo->clause);
-       right = get_rightop(restrictinfo->clause);
-
-       /*
-        * Since we tend to visit the same clauses over and over when planning
-        * a large query, we cache the bucketsize estimate in the RestrictInfo
-        * node to avoid repeated lookups of statistics.
-        */
-       if (VARISRELMEMBER(right->varno, inner_path->parent))
+       innerbucketsize = 1.0;
+       foreach(hcl, hashclauses)
        {
-               /* righthand side is inner */
-               innerbucketsize = restrictinfo->right_bucketsize;
-               if (innerbucketsize < 0)
+               RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(hcl);
+               Var                *left,
+                                  *right;
+               Selectivity thisbucketsize;
+
+               Assert(IsA(restrictinfo, RestrictInfo));
+               /* these must be OK, since check_hashjoinable accepted the clause */
+               left = get_leftop(restrictinfo->clause);
+               right = get_rightop(restrictinfo->clause);
+
+               /*
+                * First we have to figure out which side of the hashjoin clause is the
+                * inner side.
+                *
+                * Since we tend to visit the same clauses over and over when planning
+                * a large query, we cache the bucketsize estimate in the RestrictInfo
+                * node to avoid repeated lookups of statistics.
+                */
+               if (VARISRELMEMBER(right->varno, inner_path->parent))
                {
-                       /* not cached yet */
-                       innerbucketsize = estimate_hash_bucketsize(root, right);
-                       restrictinfo->right_bucketsize = innerbucketsize;
+                       /* righthand side is inner */
+                       thisbucketsize = restrictinfo->right_bucketsize;
+                       if (thisbucketsize < 0)
+                       {
+                               /* not cached yet */
+                               thisbucketsize = estimate_hash_bucketsize(root, right);
+                               restrictinfo->right_bucketsize = thisbucketsize;
+                       }
                }
-       }
-       else
-       {
-               Assert(VARISRELMEMBER(left->varno, inner_path->parent));
-               /* lefthand side is inner */
-               innerbucketsize = restrictinfo->left_bucketsize;
-               if (innerbucketsize < 0)
+               else
                {
-                       /* not cached yet */
-                       innerbucketsize = estimate_hash_bucketsize(root, left);
-                       restrictinfo->left_bucketsize = innerbucketsize;
+                       Assert(VARISRELMEMBER(left->varno, inner_path->parent));
+                       /* lefthand side is inner */
+                       thisbucketsize = restrictinfo->left_bucketsize;
+                       if (thisbucketsize < 0)
+                       {
+                               /* not cached yet */
+                               thisbucketsize = estimate_hash_bucketsize(root, left);
+                               restrictinfo->left_bucketsize = thisbucketsize;
+                       }
                }
+
+               if (innerbucketsize > thisbucketsize)
+                       innerbucketsize = thisbucketsize;
        }
 
        /*
index ac5d4a72d456330226918b161e401300b7a35c11..6069a34d879e7d3ad4579ee50c9ea2318430d017 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.72 2002/11/24 21:52:14 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.73 2002/11/30 00:08:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -701,7 +701,7 @@ match_unsorted_inner(Query *root,
 /*
  * hash_inner_and_outer
  *       Create hashjoin join paths by explicitly hashing both the outer and
- *       inner join relations of each available hash clause.
+ *       inner keys of each available hash clause.
  *
  * 'joinrel' is the join relation
  * 'outerrel' is the outer join relation
@@ -719,6 +719,7 @@ hash_inner_and_outer(Query *root,
                                         JoinType jointype)
 {
        bool            isouterjoin;
+       List       *hashclauses;
        List       *i;
 
        /*
@@ -737,20 +738,18 @@ hash_inner_and_outer(Query *root,
        }
 
        /*
+        * We need to build only one hashpath for any given pair of outer and
+        * inner relations; all of the hashable clauses will be used as keys.
+        *
         * Scan the join's restrictinfo list to find hashjoinable clauses that
-        * are usable with this pair of sub-relations.  Since we currently
-        * accept only var-op-var clauses as hashjoinable, we need only check
-        * the membership of the vars to determine whether a particular clause
-        * can be used with this pair of sub-relations.  This code would need
-        * to be upgraded if we wanted to allow more-complex expressions in
-        * hash joins.
+        * are usable with this pair of sub-relations.
         */
+       hashclauses = NIL;
        foreach(i, restrictlist)
        {
                RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
                Var                *left,
                                   *right;
-               List       *hashclauses;
 
                if (restrictinfo->hashjoinoperator == InvalidOid)
                        continue;                       /* not hashjoinable */
@@ -768,6 +767,12 @@ hash_inner_and_outer(Query *root,
 
                /*
                 * Check if clause is usable with these input rels.
+                *
+                * Since we currently accept only var-op-var clauses as hashjoinable,
+                * we need only check the membership of the vars to determine whether
+                * a particular clause can be used with this pair of sub-relations.
+                * This code would need to be upgraded if we wanted to allow
+                * more-complex expressions in hash joins.
                 */
                if (VARISRELMEMBER(left->varno, outerrel) &&
                        VARISRELMEMBER(right->varno, innerrel))
@@ -782,9 +787,12 @@ hash_inner_and_outer(Query *root,
                else
                        continue;                       /* no good for these input relations */
 
-               /* always a one-element list of hash clauses */
-               hashclauses = makeList1(restrictinfo);
+               hashclauses = lappend(hashclauses, restrictinfo);
+       }
 
+       /* If we found any usable hashclauses, make a path */
+       if (hashclauses)
+       {
                /*
                 * We consider both the cheapest-total-cost and
                 * cheapest-startup-cost outer paths.  There's no need to consider
index b393252542fc81316f940fecf12bb355f6d744b6..d43e3271fbf604d4c4d11883d5e263a4cf4ab271 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.124 2002/11/21 00:42:19 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.125 2002/11/30 00:08:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -91,7 +91,7 @@ static HashJoin *make_hashjoin(List *tlist,
                          List *hashclauses,
                          Plan *lefttree, Plan *righttree,
                          JoinType jointype);
-static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree);
+static Hash *make_hash(List *tlist, List *hashkeys, Plan *lefttree);
 static MergeJoin *make_mergejoin(List *tlist,
                           List *joinclauses, List *otherclauses,
                           List *mergeclauses,
@@ -910,14 +910,9 @@ create_hashjoin_plan(Query *root,
        List       *hashclauses;
        HashJoin   *join_plan;
        Hash       *hash_plan;
-       Node       *innerhashkey;
+       List       *innerhashkeys;
+       List       *hcl;
 
-       /*
-        * NOTE: there will always be exactly one hashclause in the list
-        * best_path->path_hashclauses (cf. hash_inner_and_outer()). We
-        * represent it as a list anyway, for convenience with routines that
-        * want to work on lists of clauses.
-        */
        hashclauses = get_actual_clauses(best_path->path_hashclauses);
 
        /*
@@ -950,13 +945,20 @@ create_hashjoin_plan(Query *root,
                                                                                           inner_tlist,
                                                                                           (Index) 0));
 
-       /* Now the righthand op of the sole hashclause is the inner hash key. */
-       innerhashkey = (Node *) get_rightop(lfirst(hashclauses));
+       /*
+        * Extract the inner hash keys (right-hand operands of the hashclauses)
+        * to put in the Hash node.
+        */
+       innerhashkeys = NIL;
+       foreach(hcl, hashclauses)
+       {
+               innerhashkeys = lappend(innerhashkeys, get_rightop(lfirst(hcl)));
+       }
 
        /*
         * Build the hash node and hash join node.
         */
-       hash_plan = make_hash(inner_tlist, innerhashkey, inner_plan);
+       hash_plan = make_hash(inner_tlist, innerhashkeys, inner_plan);
        join_plan = make_hashjoin(tlist,
                                                          joinclauses,
                                                          otherclauses,
@@ -1511,7 +1513,7 @@ make_hashjoin(List *tlist,
 }
 
 static Hash *
-make_hash(List *tlist, Node *hashkey, Plan *lefttree)
+make_hash(List *tlist, List *hashkeys, Plan *lefttree)
 {
        Hash       *node = makeNode(Hash);
        Plan       *plan = &node->plan;
@@ -1528,7 +1530,7 @@ make_hash(List *tlist, Node *hashkey, Plan *lefttree)
        plan->qual = NULL;
        plan->lefttree = lefttree;
        plan->righttree = NULL;
-       node->hashkey = hashkey;
+       node->hashkeys = hashkeys;
 
        return node;
 }
index 5b171fb819a20babcc31e48250bdf0364a59a686..61476a656041f70c7d7167a829f8165075cf86b3 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.56 2002/11/26 03:01:58 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.57 2002/11/30 00:08:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -677,7 +677,7 @@ SS_finalize_plan(Plan *plan, List *rtable)
                        break;
 
                case T_Hash:
-                       finalize_primnode(((Hash *) plan)->hashkey,
+                       finalize_primnode((Node *) ((Hash *) plan)->hashkeys,
                                                          &results);
                        break;
 
index e99435a6edf91c48e5dda41ecd375906cfc4580b..98227355605ad980ca62cb5a12484d6b0155cc86 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.80 2002/11/24 21:52:14 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.81 2002/11/30 00:08:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -616,7 +616,7 @@ create_mergejoin_path(Query *root,
  * 'outer_path' is the cheapest outer path
  * 'inner_path' is the cheapest inner path
  * 'restrict_clauses' are the RestrictInfo nodes to apply at the join
- * 'hashclauses' is a list of the hash join clause (always a 1-element list)
+ * 'hashclauses' are the RestrictInfo nodes to use as hash clauses
  *             (this should be a subset of the restrict_clauses list)
  */
 HashPath *
index 1869feae08b79c1f34339ee4ed336e48b1579c93..a2d5f633fcd198559586672ea166a4805ee36943 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.26 2002/06/20 20:29:49 momjian Exp $
+ * $Id: hashjoin.h,v 1.27 2002/11/30 00:08:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -69,12 +69,13 @@ typedef struct HashTableData
                                                                 * file */
 
        /*
-        * Info about the datatype being hashed.  We assume that the inner and
-        * outer sides of the hash are the same type, or at least
-        * binary-compatible types.
+        * 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.
         */
-       int16           typLen;
-       bool            typByVal;
+       int16      *typLens;
+       bool       *typByVals;
 
        /*
         * During 1st scan of inner relation, we get tuples from executor. If
index 8bea51e8af05ee635c97103d81af14a94614dab0..654906cd3c27140b635b80fe91813f2b11fad39a 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.25 2002/11/06 22:31:24 tgl Exp $
+ * $Id: nodeHash.h,v 1.26 2002/11/30 00:08:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,10 +24,10 @@ extern HashJoinTable ExecHashTableCreate(Hash *node);
 extern void ExecHashTableDestroy(HashJoinTable hashtable);
 extern void ExecHashTableInsert(HashJoinTable hashtable,
                                        ExprContext *econtext,
-                                       Node *hashkey);
+                                       List *hashkeys);
 extern int ExecHashGetBucket(HashJoinTable hashtable,
                                  ExprContext *econtext,
-                                 Node *hashkey);
+                                 List *hashkeys);
 extern HeapTuple ExecScanHashBucket(HashJoinState *hjstate, List *hjclauses,
                                   ExprContext *econtext);
 extern void ExecHashTableReset(HashJoinTable hashtable, long ntuples);
index 6ee39b98182174ed2773c0cd1e52732699f5a7d6..544510746d653649e35e8206bde5a97286948c5e 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.80 2002/11/25 21:29:42 tgl Exp $
+ * $Id: execnodes.h,v 1.81 2002/11/30 00:08:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -618,7 +618,8 @@ typedef struct MergeJoinState
  *                                                             tuple, or NULL if starting search
  *                                                             (CurBucketNo and CurTuple are meaningless
  *                                                              unless OuterTupleSlot is nonempty!)
- *             hj_InnerHashKey                 the inner hash key in the hashjoin condition
+ *             hj_OuterHashKeys                the outer hash keys in the hashjoin condition
+ *             hj_InnerHashKeys                the inner hash keys 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
@@ -633,7 +634,8 @@ typedef struct HashJoinState
        HashJoinTable hj_HashTable;
        int                     hj_CurBucketNo;
        HashJoinTuple hj_CurTuple;
-       Node       *hj_InnerHashKey;
+       List       *hj_OuterHashKeys;
+       List       *hj_InnerHashKeys;
        TupleTableSlot *hj_OuterTupleSlot;
        TupleTableSlot *hj_HashTupleSlot;
        TupleTableSlot *hj_NullInnerTupleSlot;
index 0cf9d0bac913defb16e190a76e59d0d4e4003b76..6a6ac415f9f79b3c05840803c391c9b4215f53d6 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: plannodes.h,v 1.60 2002/11/06 22:31:24 tgl Exp $
+ * $Id: plannodes.h,v 1.61 2002/11/30 00:08:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -318,7 +318,6 @@ typedef struct HashJoin
 {
        Join            join;
        List       *hashclauses;
-       Oid                     hashjoinop;
        HashJoinState *hashjoinstate;
 } HashJoin;
 
@@ -443,7 +442,7 @@ typedef struct Limit
 typedef struct Hash
 {
        Plan            plan;
-       Node       *hashkey;
+       List       *hashkeys;
        HashState  *hashstate;
 } Hash;
 
index a3f0e36c76698e4faaecb6c616f82b043205ce28..4c06224eccec2465fbf9ca1a6d68605cdac21744 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: relation.h,v 1.70 2002/11/27 20:52:04 tgl Exp $
+ * $Id: relation.h,v 1.71 2002/11/30 00:08:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -464,8 +464,6 @@ typedef struct MergePath
  * A hashjoin path has these fields.
  *
  * The remarks above for mergeclauses apply for hashclauses as well.
- * (But note that path_hashclauses will always be a one-element list,
- * since we only hash on one hashable clause.)
  *
  * Hashjoin does not care what order its inputs appear in, so we have
  * no need for sortkeys.