aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeHash.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeHash.c')
-rw-r--r--src/backend/executor/nodeHash.c52
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.