Upgrade planner and executor to allow multiple hash keys for a hash join,
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.
This commit is contained in:
parent
f68f11928d
commit
ddb2d78de0
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
EState *estate;
|
||||||
HashState *hashstate;
|
HashState *hashstate;
|
||||||
Plan *outerNode;
|
Plan *outerNode;
|
||||||
Node *hashkey;
|
List *hashkeys;
|
||||||
HashJoinTable hashtable;
|
HashJoinTable hashtable;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
@ -79,7 +79,7 @@ ExecHash(Hash *node)
|
|||||||
/*
|
/*
|
||||||
* set expression context
|
* set expression context
|
||||||
*/
|
*/
|
||||||
hashkey = node->hashkey;
|
hashkeys = node->hashkeys;
|
||||||
econtext = hashstate->cstate.cs_ExprContext;
|
econtext = hashstate->cstate.cs_ExprContext;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -91,7 +91,7 @@ ExecHash(Hash *node)
|
|||||||
if (TupIsNull(slot))
|
if (TupIsNull(slot))
|
||||||
break;
|
break;
|
||||||
econtext->ecxt_innertuple = slot;
|
econtext->ecxt_innertuple = slot;
|
||||||
ExecHashTableInsert(hashtable, econtext, hashkey);
|
ExecHashTableInsert(hashtable, econtext, hashkeys);
|
||||||
ExecClearTuple(slot);
|
ExecClearTuple(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +212,9 @@ ExecHashTableCreate(Hash *node)
|
|||||||
int totalbuckets;
|
int totalbuckets;
|
||||||
int nbuckets;
|
int nbuckets;
|
||||||
int nbatch;
|
int nbatch;
|
||||||
|
int nkeys;
|
||||||
int i;
|
int i;
|
||||||
|
List *hk;
|
||||||
MemoryContext oldcxt;
|
MemoryContext oldcxt;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -248,11 +250,19 @@ ExecHashTableCreate(Hash *node)
|
|||||||
hashtable->outerBatchSize = NULL;
|
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),
|
nkeys = length(node->hashkeys);
|
||||||
&hashtable->typLen,
|
hashtable->typLens = (int16 *) palloc(nkeys * sizeof(int16));
|
||||||
&hashtable->typByVal);
|
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
|
* Create temporary memory contexts in which to keep the hashtable
|
||||||
@ -465,9 +475,9 @@ ExecHashTableDestroy(HashJoinTable hashtable)
|
|||||||
void
|
void
|
||||||
ExecHashTableInsert(HashJoinTable hashtable,
|
ExecHashTableInsert(HashJoinTable hashtable,
|
||||||
ExprContext *econtext,
|
ExprContext *econtext,
|
||||||
Node *hashkey)
|
List *hashkeys)
|
||||||
{
|
{
|
||||||
int bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
|
int bucketno = ExecHashGetBucket(hashtable, econtext, hashkeys);
|
||||||
TupleTableSlot *slot = econtext->ecxt_innertuple;
|
TupleTableSlot *slot = econtext->ecxt_innertuple;
|
||||||
HeapTuple heapTuple = slot->val;
|
HeapTuple heapTuple = slot->val;
|
||||||
|
|
||||||
@ -522,44 +532,55 @@ ExecHashTableInsert(HashJoinTable hashtable,
|
|||||||
int
|
int
|
||||||
ExecHashGetBucket(HashJoinTable hashtable,
|
ExecHashGetBucket(HashJoinTable hashtable,
|
||||||
ExprContext *econtext,
|
ExprContext *econtext,
|
||||||
Node *hashkey)
|
List *hashkeys)
|
||||||
{
|
{
|
||||||
|
uint32 hashkey = 0;
|
||||||
int bucketno;
|
int bucketno;
|
||||||
Datum keyval;
|
List *hk;
|
||||||
bool isNull;
|
int i = 0;
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We reset the eval context each time to reclaim any memory leaked in
|
* 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);
|
ResetExprContext(econtext);
|
||||||
|
|
||||||
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
||||||
|
|
||||||
|
foreach(hk, hashkeys)
|
||||||
|
{
|
||||||
|
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
|
* Get the join attribute value of the tuple
|
||||||
*/
|
*/
|
||||||
keyval = ExecEvalExpr(hashkey, econtext, &isNull, NULL);
|
keyval = ExecEvalExpr(lfirst(hk), econtext, &isNull, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute the hash function
|
* Compute the hash function
|
||||||
*/
|
*/
|
||||||
if (isNull)
|
if (!isNull) /* treat nulls as having hash key 0 */
|
||||||
bucketno = 0;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
bucketno = ComputeHashFunc(keyval,
|
hashkey ^= ComputeHashFunc(keyval,
|
||||||
(int) hashtable->typLen,
|
(int) hashtable->typLens[i],
|
||||||
hashtable->typByVal)
|
hashtable->typByVals[i]);
|
||||||
% (uint32) hashtable->totalbuckets;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bucketno = hashkey % (uint32) hashtable->totalbuckets;
|
||||||
|
|
||||||
#ifdef HJDEBUG
|
#ifdef HJDEBUG
|
||||||
if (bucketno >= hashtable->nbuckets)
|
if (bucketno >= hashtable->nbuckets)
|
||||||
printf("hash(%ld) = %d SAVED\n", (long) keyval, bucketno);
|
printf("hash(%u) = %d SAVED\n", hashkey, bucketno);
|
||||||
else
|
else
|
||||||
printf("hash(%ld) = %d\n", (long) keyval, bucketno);
|
printf("hash(%u) = %d\n", hashkey, bucketno);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldContext);
|
MemoryContextSwitchTo(oldContext);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
Plan *outerNode;
|
||||||
Hash *hashNode;
|
Hash *hashNode;
|
||||||
List *hjclauses;
|
List *hjclauses;
|
||||||
Expr *clause;
|
List *outerkeys;
|
||||||
List *joinqual;
|
List *joinqual;
|
||||||
List *otherqual;
|
List *otherqual;
|
||||||
ScanDirection dir;
|
ScanDirection dir;
|
||||||
TupleTableSlot *inntuple;
|
TupleTableSlot *inntuple;
|
||||||
Node *outerVar;
|
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
ExprDoneCond isDone;
|
ExprDoneCond isDone;
|
||||||
HashJoinTable hashtable;
|
HashJoinTable hashtable;
|
||||||
@ -68,7 +67,6 @@ ExecHashJoin(HashJoin *node)
|
|||||||
*/
|
*/
|
||||||
hjstate = node->hashjoinstate;
|
hjstate = node->hashjoinstate;
|
||||||
hjclauses = node->hashclauses;
|
hjclauses = node->hashclauses;
|
||||||
clause = lfirst(hjclauses);
|
|
||||||
estate = node->join.plan.state;
|
estate = node->join.plan.state;
|
||||||
joinqual = node->join.joinqual;
|
joinqual = node->join.joinqual;
|
||||||
otherqual = node->join.plan.qual;
|
otherqual = node->join.plan.qual;
|
||||||
@ -81,6 +79,7 @@ ExecHashJoin(HashJoin *node)
|
|||||||
* get information from HashJoin state
|
* get information from HashJoin state
|
||||||
*/
|
*/
|
||||||
hashtable = hjstate->hj_HashTable;
|
hashtable = hjstate->hj_HashTable;
|
||||||
|
outerkeys = hjstate->hj_OuterHashKeys;
|
||||||
econtext = hjstate->jstate.cs_ExprContext;
|
econtext = hjstate->jstate.cs_ExprContext;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -119,7 +118,6 @@ ExecHashJoin(HashJoin *node)
|
|||||||
*/
|
*/
|
||||||
hashtable = ExecHashTableCreate(hashNode);
|
hashtable = ExecHashTableCreate(hashNode);
|
||||||
hjstate->hj_HashTable = hashtable;
|
hjstate->hj_HashTable = hashtable;
|
||||||
hjstate->hj_InnerHashKey = hashNode->hashkey;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* execute the Hash node, to build the hash table
|
* 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
|
* Now get an outer tuple and probe into the hash table for matches
|
||||||
*/
|
*/
|
||||||
outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
|
outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
|
||||||
outerVar = (Node *) get_leftop(clause);
|
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@ -175,7 +172,7 @@ ExecHashJoin(HashJoin *node)
|
|||||||
* for this tuple from the hash table
|
* for this tuple from the hash table
|
||||||
*/
|
*/
|
||||||
hjstate->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext,
|
hjstate->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext,
|
||||||
outerVar);
|
outerkeys);
|
||||||
hjstate->hj_CurTuple = NULL;
|
hjstate->hj_CurTuple = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -308,6 +305,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
|
|||||||
HashJoinState *hjstate;
|
HashJoinState *hjstate;
|
||||||
Plan *outerNode;
|
Plan *outerNode;
|
||||||
Hash *hashNode;
|
Hash *hashNode;
|
||||||
|
List *hcl;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* assign the node's execution state
|
* assign the node's execution state
|
||||||
@ -391,7 +389,18 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
|
|||||||
hjstate->hj_HashTable = (HashJoinTable) NULL;
|
hjstate->hj_HashTable = (HashJoinTable) NULL;
|
||||||
hjstate->hj_CurBucketNo = 0;
|
hjstate->hj_CurBucketNo = 0;
|
||||||
hjstate->hj_CurTuple = (HashJoinTuple) NULL;
|
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_OuterTupleSlot = NULL;
|
||||||
hjstate->jstate.cs_TupFromTlist = false;
|
hjstate->jstate.cs_TupFromTlist = false;
|
||||||
@ -555,7 +564,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate)
|
|||||||
BufFile *innerFile;
|
BufFile *innerFile;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
Node *innerhashkey;
|
List *innerhashkeys;
|
||||||
|
|
||||||
if (newbatch > 1)
|
if (newbatch > 1)
|
||||||
{
|
{
|
||||||
@ -603,7 +612,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate)
|
|||||||
ExecHashTableReset(hashtable, innerBatchSize[newbatch - 1]);
|
ExecHashTableReset(hashtable, innerBatchSize[newbatch - 1]);
|
||||||
|
|
||||||
econtext = hjstate->jstate.cs_ExprContext;
|
econtext = hjstate->jstate.cs_ExprContext;
|
||||||
innerhashkey = hjstate->hj_InnerHashKey;
|
innerhashkeys = hjstate->hj_InnerHashKeys;
|
||||||
|
|
||||||
while ((slot = ExecHashJoinGetSavedTuple(hjstate,
|
while ((slot = ExecHashJoinGetSavedTuple(hjstate,
|
||||||
innerFile,
|
innerFile,
|
||||||
@ -611,7 +620,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate)
|
|||||||
&& !TupIsNull(slot))
|
&& !TupIsNull(slot))
|
||||||
{
|
{
|
||||||
econtext->ecxt_innertuple = 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_CurBucketNo = 0;
|
||||||
hjstate->hj_CurTuple = (HashJoinTuple) NULL;
|
hjstate->hj_CurTuple = (HashJoinTuple) NULL;
|
||||||
hjstate->hj_InnerHashKey = (Node *) NULL;
|
|
||||||
|
|
||||||
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
|
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
|
||||||
hjstate->jstate.cs_TupFromTlist = false;
|
hjstate->jstate.cs_TupFromTlist = false;
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 remainder of node
|
||||||
*/
|
*/
|
||||||
COPY_NODE_FIELD(hashclauses);
|
COPY_NODE_FIELD(hashclauses);
|
||||||
COPY_SCALAR_FIELD(hashjoinop);
|
|
||||||
|
|
||||||
/* subPlan list must point to subplans in the new subtree, not the old */
|
/* subPlan list must point to subplans in the new subtree, not the old */
|
||||||
FIX_SUBPLAN_LINKS(join.plan.subPlan, hashclauses);
|
FIX_SUBPLAN_LINKS(join.plan.subPlan, hashclauses);
|
||||||
@ -593,9 +592,9 @@ _copyHash(Hash *from)
|
|||||||
/*
|
/*
|
||||||
* copy remainder of node
|
* 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;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* Every node type that can appear in stored rules' parsetrees *must*
|
* Every node type that can appear in stored rules' parsetrees *must*
|
||||||
@ -538,7 +538,6 @@ _outHashJoin(StringInfo str, HashJoin *node)
|
|||||||
_outJoinPlanInfo(str, (Join *) node);
|
_outJoinPlanInfo(str, (Join *) node);
|
||||||
|
|
||||||
WRITE_NODE_FIELD(hashclauses);
|
WRITE_NODE_FIELD(hashclauses);
|
||||||
WRITE_OID_FIELD(hashjoinop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -634,7 +633,7 @@ _outHash(StringInfo str, Hash *node)
|
|||||||
|
|
||||||
_outPlanInfo(str, (Plan *) node);
|
_outPlanInfo(str, (Plan *) node);
|
||||||
|
|
||||||
WRITE_NODE_FIELD(hashkey);
|
WRITE_NODE_FIELD(hashkeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* 'outer_path' is the path for the outer relation
|
||||||
* 'inner_path' is the path for the inner relation
|
* 'inner_path' is the path for the inner relation
|
||||||
* 'restrictlist' are the RestrictInfo nodes to be applied at the join
|
* '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)
|
* (this should be a subset of the restrictlist)
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
@ -838,10 +838,8 @@ cost_hashjoin(Path *path, Query *root,
|
|||||||
double innerbytes = relation_byte_size(inner_path->parent->rows,
|
double innerbytes = relation_byte_size(inner_path->parent->rows,
|
||||||
inner_path->parent->width);
|
inner_path->parent->width);
|
||||||
long hashtablebytes = SortMem * 1024L;
|
long hashtablebytes = SortMem * 1024L;
|
||||||
RestrictInfo *restrictinfo;
|
|
||||||
Var *left,
|
|
||||||
*right;
|
|
||||||
Selectivity innerbucketsize;
|
Selectivity innerbucketsize;
|
||||||
|
List *hcl;
|
||||||
|
|
||||||
if (!enable_hashjoin)
|
if (!enable_hashjoin)
|
||||||
startup_cost += disable_cost;
|
startup_cost += disable_cost;
|
||||||
@ -856,17 +854,27 @@ cost_hashjoin(Path *path, Query *root,
|
|||||||
run_cost += cpu_operator_cost * outer_path->parent->rows;
|
run_cost += cpu_operator_cost * outer_path->parent->rows;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine bucketsize fraction for inner relation. First we have to
|
* Determine bucketsize fraction for inner relation. We use the
|
||||||
* figure out which side of the hashjoin clause is the inner side.
|
* smallest bucketsize estimated for any individual hashclause;
|
||||||
|
* this is undoubtedly conservative.
|
||||||
*/
|
*/
|
||||||
Assert(length(hashclauses) == 1);
|
innerbucketsize = 1.0;
|
||||||
Assert(IsA(lfirst(hashclauses), RestrictInfo));
|
foreach(hcl, hashclauses)
|
||||||
restrictinfo = (RestrictInfo *) lfirst(hashclauses);
|
{
|
||||||
|
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(hcl);
|
||||||
|
Var *left,
|
||||||
|
*right;
|
||||||
|
Selectivity thisbucketsize;
|
||||||
|
|
||||||
|
Assert(IsA(restrictinfo, RestrictInfo));
|
||||||
/* these must be OK, since check_hashjoinable accepted the clause */
|
/* these must be OK, since check_hashjoinable accepted the clause */
|
||||||
left = get_leftop(restrictinfo->clause);
|
left = get_leftop(restrictinfo->clause);
|
||||||
right = get_rightop(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
|
* Since we tend to visit the same clauses over and over when planning
|
||||||
* a large query, we cache the bucketsize estimate in the RestrictInfo
|
* a large query, we cache the bucketsize estimate in the RestrictInfo
|
||||||
* node to avoid repeated lookups of statistics.
|
* node to avoid repeated lookups of statistics.
|
||||||
@ -874,27 +882,31 @@ cost_hashjoin(Path *path, Query *root,
|
|||||||
if (VARISRELMEMBER(right->varno, inner_path->parent))
|
if (VARISRELMEMBER(right->varno, inner_path->parent))
|
||||||
{
|
{
|
||||||
/* righthand side is inner */
|
/* righthand side is inner */
|
||||||
innerbucketsize = restrictinfo->right_bucketsize;
|
thisbucketsize = restrictinfo->right_bucketsize;
|
||||||
if (innerbucketsize < 0)
|
if (thisbucketsize < 0)
|
||||||
{
|
{
|
||||||
/* not cached yet */
|
/* not cached yet */
|
||||||
innerbucketsize = estimate_hash_bucketsize(root, right);
|
thisbucketsize = estimate_hash_bucketsize(root, right);
|
||||||
restrictinfo->right_bucketsize = innerbucketsize;
|
restrictinfo->right_bucketsize = thisbucketsize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Assert(VARISRELMEMBER(left->varno, inner_path->parent));
|
Assert(VARISRELMEMBER(left->varno, inner_path->parent));
|
||||||
/* lefthand side is inner */
|
/* lefthand side is inner */
|
||||||
innerbucketsize = restrictinfo->left_bucketsize;
|
thisbucketsize = restrictinfo->left_bucketsize;
|
||||||
if (innerbucketsize < 0)
|
if (thisbucketsize < 0)
|
||||||
{
|
{
|
||||||
/* not cached yet */
|
/* not cached yet */
|
||||||
innerbucketsize = estimate_hash_bucketsize(root, left);
|
thisbucketsize = estimate_hash_bucketsize(root, left);
|
||||||
restrictinfo->left_bucketsize = innerbucketsize;
|
restrictinfo->left_bucketsize = thisbucketsize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (innerbucketsize > thisbucketsize)
|
||||||
|
innerbucketsize = thisbucketsize;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The number of tuple comparisons needed is the number of outer
|
* The number of tuple comparisons needed is the number of outer
|
||||||
* tuples times the typical number of tuples in a hash bucket, which
|
* tuples times the typical number of tuples in a hash bucket, which
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* hash_inner_and_outer
|
||||||
* Create hashjoin join paths by explicitly hashing both the outer and
|
* 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
|
* 'joinrel' is the join relation
|
||||||
* 'outerrel' is the outer join relation
|
* 'outerrel' is the outer join relation
|
||||||
@ -719,6 +719,7 @@ hash_inner_and_outer(Query *root,
|
|||||||
JoinType jointype)
|
JoinType jointype)
|
||||||
{
|
{
|
||||||
bool isouterjoin;
|
bool isouterjoin;
|
||||||
|
List *hashclauses;
|
||||||
List *i;
|
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
|
* Scan the join's restrictinfo list to find hashjoinable clauses that
|
||||||
* are usable with this pair of sub-relations. Since we currently
|
* are usable with this pair of sub-relations.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
hashclauses = NIL;
|
||||||
foreach(i, restrictlist)
|
foreach(i, restrictlist)
|
||||||
{
|
{
|
||||||
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
|
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
|
||||||
Var *left,
|
Var *left,
|
||||||
*right;
|
*right;
|
||||||
List *hashclauses;
|
|
||||||
|
|
||||||
if (restrictinfo->hashjoinoperator == InvalidOid)
|
if (restrictinfo->hashjoinoperator == InvalidOid)
|
||||||
continue; /* not hashjoinable */
|
continue; /* not hashjoinable */
|
||||||
@ -768,6 +767,12 @@ hash_inner_and_outer(Query *root,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if clause is usable with these input rels.
|
* 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) &&
|
if (VARISRELMEMBER(left->varno, outerrel) &&
|
||||||
VARISRELMEMBER(right->varno, innerrel))
|
VARISRELMEMBER(right->varno, innerrel))
|
||||||
@ -782,9 +787,12 @@ hash_inner_and_outer(Query *root,
|
|||||||
else
|
else
|
||||||
continue; /* no good for these input relations */
|
continue; /* no good for these input relations */
|
||||||
|
|
||||||
/* always a one-element list of hash clauses */
|
hashclauses = lappend(hashclauses, restrictinfo);
|
||||||
hashclauses = makeList1(restrictinfo);
|
}
|
||||||
|
|
||||||
|
/* If we found any usable hashclauses, make a path */
|
||||||
|
if (hashclauses)
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* We consider both the cheapest-total-cost and
|
* We consider both the cheapest-total-cost and
|
||||||
* cheapest-startup-cost outer paths. There's no need to consider
|
* cheapest-startup-cost outer paths. There's no need to consider
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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,
|
List *hashclauses,
|
||||||
Plan *lefttree, Plan *righttree,
|
Plan *lefttree, Plan *righttree,
|
||||||
JoinType jointype);
|
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,
|
static MergeJoin *make_mergejoin(List *tlist,
|
||||||
List *joinclauses, List *otherclauses,
|
List *joinclauses, List *otherclauses,
|
||||||
List *mergeclauses,
|
List *mergeclauses,
|
||||||
@ -910,14 +910,9 @@ create_hashjoin_plan(Query *root,
|
|||||||
List *hashclauses;
|
List *hashclauses;
|
||||||
HashJoin *join_plan;
|
HashJoin *join_plan;
|
||||||
Hash *hash_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);
|
hashclauses = get_actual_clauses(best_path->path_hashclauses);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -950,13 +945,20 @@ create_hashjoin_plan(Query *root,
|
|||||||
inner_tlist,
|
inner_tlist,
|
||||||
(Index) 0));
|
(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.
|
* 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,
|
join_plan = make_hashjoin(tlist,
|
||||||
joinclauses,
|
joinclauses,
|
||||||
otherclauses,
|
otherclauses,
|
||||||
@ -1511,7 +1513,7 @@ make_hashjoin(List *tlist,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Hash *
|
static Hash *
|
||||||
make_hash(List *tlist, Node *hashkey, Plan *lefttree)
|
make_hash(List *tlist, List *hashkeys, Plan *lefttree)
|
||||||
{
|
{
|
||||||
Hash *node = makeNode(Hash);
|
Hash *node = makeNode(Hash);
|
||||||
Plan *plan = &node->plan;
|
Plan *plan = &node->plan;
|
||||||
@ -1528,7 +1530,7 @@ make_hash(List *tlist, Node *hashkey, Plan *lefttree)
|
|||||||
plan->qual = NULL;
|
plan->qual = NULL;
|
||||||
plan->lefttree = lefttree;
|
plan->lefttree = lefttree;
|
||||||
plan->righttree = NULL;
|
plan->righttree = NULL;
|
||||||
node->hashkey = hashkey;
|
node->hashkeys = hashkeys;
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
break;
|
||||||
|
|
||||||
case T_Hash:
|
case T_Hash:
|
||||||
finalize_primnode(((Hash *) plan)->hashkey,
|
finalize_primnode((Node *) ((Hash *) plan)->hashkeys,
|
||||||
&results);
|
&results);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* 'outer_path' is the cheapest outer path
|
||||||
* 'inner_path' is the cheapest inner path
|
* 'inner_path' is the cheapest inner path
|
||||||
* 'restrict_clauses' are the RestrictInfo nodes to apply at the join
|
* '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)
|
* (this should be a subset of the restrict_clauses list)
|
||||||
*/
|
*/
|
||||||
HashPath *
|
HashPath *
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 */
|
* file */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Info about the datatype being hashed. We assume that the inner and
|
* Info about the datatypes being hashed. We assume that the inner and
|
||||||
* outer sides of the hash are the same type, or at least
|
* outer sides of each hashclause are the same type, or at least
|
||||||
* binary-compatible types.
|
* binary-compatible types. Each of these fields points to an array
|
||||||
|
* of the same length as the number of hash keys.
|
||||||
*/
|
*/
|
||||||
int16 typLen;
|
int16 *typLens;
|
||||||
bool typByVal;
|
bool *typByVals;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* During 1st scan of inner relation, we get tuples from executor. If
|
* During 1st scan of inner relation, we get tuples from executor. If
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 ExecHashTableDestroy(HashJoinTable hashtable);
|
||||||
extern void ExecHashTableInsert(HashJoinTable hashtable,
|
extern void ExecHashTableInsert(HashJoinTable hashtable,
|
||||||
ExprContext *econtext,
|
ExprContext *econtext,
|
||||||
Node *hashkey);
|
List *hashkeys);
|
||||||
extern int ExecHashGetBucket(HashJoinTable hashtable,
|
extern int ExecHashGetBucket(HashJoinTable hashtable,
|
||||||
ExprContext *econtext,
|
ExprContext *econtext,
|
||||||
Node *hashkey);
|
List *hashkeys);
|
||||||
extern HeapTuple ExecScanHashBucket(HashJoinState *hjstate, List *hjclauses,
|
extern HeapTuple ExecScanHashBucket(HashJoinState *hjstate, List *hjclauses,
|
||||||
ExprContext *econtext);
|
ExprContext *econtext);
|
||||||
extern void ExecHashTableReset(HashJoinTable hashtable, long ntuples);
|
extern void ExecHashTableReset(HashJoinTable hashtable, long ntuples);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* tuple, or NULL if starting search
|
||||||
* (CurBucketNo and CurTuple are meaningless
|
* (CurBucketNo and CurTuple are meaningless
|
||||||
* unless OuterTupleSlot is nonempty!)
|
* 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_OuterTupleSlot tuple slot for outer tuples
|
||||||
* hj_HashTupleSlot tuple slot for hashed tuples
|
* hj_HashTupleSlot tuple slot for hashed tuples
|
||||||
* hj_NullInnerTupleSlot prepared null tuple for left outer joins
|
* hj_NullInnerTupleSlot prepared null tuple for left outer joins
|
||||||
@ -633,7 +634,8 @@ typedef struct HashJoinState
|
|||||||
HashJoinTable hj_HashTable;
|
HashJoinTable hj_HashTable;
|
||||||
int hj_CurBucketNo;
|
int hj_CurBucketNo;
|
||||||
HashJoinTuple hj_CurTuple;
|
HashJoinTuple hj_CurTuple;
|
||||||
Node *hj_InnerHashKey;
|
List *hj_OuterHashKeys;
|
||||||
|
List *hj_InnerHashKeys;
|
||||||
TupleTableSlot *hj_OuterTupleSlot;
|
TupleTableSlot *hj_OuterTupleSlot;
|
||||||
TupleTableSlot *hj_HashTupleSlot;
|
TupleTableSlot *hj_HashTupleSlot;
|
||||||
TupleTableSlot *hj_NullInnerTupleSlot;
|
TupleTableSlot *hj_NullInnerTupleSlot;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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;
|
Join join;
|
||||||
List *hashclauses;
|
List *hashclauses;
|
||||||
Oid hashjoinop;
|
|
||||||
HashJoinState *hashjoinstate;
|
HashJoinState *hashjoinstate;
|
||||||
} HashJoin;
|
} HashJoin;
|
||||||
|
|
||||||
@ -443,7 +442,7 @@ typedef struct Limit
|
|||||||
typedef struct Hash
|
typedef struct Hash
|
||||||
{
|
{
|
||||||
Plan plan;
|
Plan plan;
|
||||||
Node *hashkey;
|
List *hashkeys;
|
||||||
HashState *hashstate;
|
HashState *hashstate;
|
||||||
} Hash;
|
} Hash;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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.
|
* A hashjoin path has these fields.
|
||||||
*
|
*
|
||||||
* The remarks above for mergeclauses apply for hashclauses as well.
|
* 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
|
* Hashjoin does not care what order its inputs appear in, so we have
|
||||||
* no need for sortkeys.
|
* no need for sortkeys.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user