diff options
Diffstat (limited to 'src/backend/executor/nodeHash.c')
-rw-r--r-- | src/backend/executor/nodeHash.c | 52 |
1 files changed, 44 insertions, 8 deletions
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index c304d930a28..c7119933e5b 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.93 2005/04/16 20:07:35 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.94 2005/06/15 07:27:44 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -37,14 +37,22 @@ static void ExecHashIncreaseNumBatches(HashJoinTable hashtable); /* ---------------------------------------------------------------- * ExecHash * - * stub for pro forma compliance + * produce the first tuple from our child node (and _only_ the + * first tuple). This is of limited general use -- it does not + * hash its output, and produces only a single tuple. It is + * provided so that hash join can probe the inner hash input to + * determine whether it is empty without needing to build the + * entire hash table first, which is what MultiExecHash() would + * do. * ---------------------------------------------------------------- */ TupleTableSlot * ExecHash(HashState *node) { - elog(ERROR, "Hash node does not support ExecProcNode call convention"); - return NULL; + if (TupIsNull(node->firstTuple)) + node->firstTuple = ExecProcNode(outerPlanState(node)); + + return node->firstTuple; } /* ---------------------------------------------------------------- @@ -63,6 +71,7 @@ MultiExecHash(HashState *node) TupleTableSlot *slot; ExprContext *econtext; uint32 hashvalue; + bool cleared_first_tuple = false; /* must provide our own instrumentation support */ if (node->ps.instrument) @@ -85,9 +94,19 @@ MultiExecHash(HashState *node) */ for (;;) { - slot = ExecProcNode(outerNode); - if (TupIsNull(slot)) - break; + /* use and clear the tuple produced by ExecHash(), if any */ + if (!TupIsNull(node->firstTuple)) + { + slot = node->firstTuple; + node->firstTuple = NULL; + cleared_first_tuple = true; + } + else + { + slot = ExecProcNode(outerNode); + if (TupIsNull(slot)) + break; + } hashtable->totalTuples += 1; /* We have to compute the hash value */ econtext->ecxt_innertuple = slot; @@ -97,7 +116,19 @@ MultiExecHash(HashState *node) /* must provide our own instrumentation support */ if (node->ps.instrument) - InstrStopNodeMulti(node->ps.instrument, hashtable->totalTuples); + { + /* + * XXX: kludge -- if ExecHash() was invoked, we've already + * included the tuple that it produced in the row output count + * for this node, so subtract 1 from the # of hashed tuples. + */ + if (cleared_first_tuple) + InstrStopNodeMulti(node->ps.instrument, + hashtable->totalTuples - 1); + else + InstrStopNodeMulti(node->ps.instrument, + hashtable->totalTuples); + } /* * We do not return the hash table directly because it's not a subtype @@ -130,6 +161,7 @@ ExecInitHash(Hash *node, EState *estate) hashstate->ps.state = estate; hashstate->hashtable = NULL; hashstate->hashkeys = NIL; /* will be set by parent HashJoin */ + hashstate->firstTuple = NULL; /* * Miscellaneous initialization @@ -189,6 +221,8 @@ ExecEndHash(HashState *node) { PlanState *outerPlan; + node->firstTuple = NULL; + /* * free exprcontext */ @@ -830,6 +864,8 @@ ExecHashTableReset(HashJoinTable hashtable) void ExecReScanHash(HashState *node, ExprContext *exprCtxt) { + node->firstTuple = NULL; + /* * if chgParam of subnode is not null then plan will be re-scanned by * first ExecProcNode. |