1 /*-------------------------------------------------------------------------
4 * Routines to handle hash join nodes
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.98 2009/03/21 00:04:38 tgl Exp $
13 *-------------------------------------------------------------------------
18 #include "executor/executor.h"
19 #include "executor/hashjoin.h"
20 #include "executor/nodeHash.h"
21 #include "executor/nodeHashjoin.h"
22 #include "utils/memutils.h"
25 /* Returns true for JOIN_LEFT and JOIN_ANTI jointypes */
26 #define HASHJOIN_IS_OUTER(hjstate) ((hjstate)->hj_NullInnerTupleSlot != NULL)
28 static TupleTableSlot *ExecHashJoinOuterGetTuple(PlanState *outerNode,
29 HashJoinState *hjstate,
31 static TupleTableSlot *ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
34 TupleTableSlot *tupleSlot);
35 static int ExecHashJoinNewBatch(HashJoinState *hjstate);
38 /* ----------------------------------------------------------------
41 * This function implements the Hybrid Hashjoin algorithm.
43 * Note: the relation we build hash table on is the "inner"
44 * the other one is "outer".
45 * ----------------------------------------------------------------
47 TupleTableSlot * /* return: a tuple or NULL */
48 ExecHashJoin(HashJoinState *node)
55 TupleTableSlot *inntuple;
56 ExprContext *econtext;
58 HashJoinTable hashtable;
59 HashJoinTuple curtuple;
60 TupleTableSlot *outerTupleSlot;
65 * get information from HashJoin node
67 estate = node->js.ps.state;
68 joinqual = node->js.joinqual;
69 otherqual = node->js.ps.qual;
70 hashNode = (HashState *) innerPlanState(node);
71 outerNode = outerPlanState(node);
74 * get information from HashJoin state
76 hashtable = node->hj_HashTable;
77 econtext = node->js.ps.ps_ExprContext;
80 * Check to see if we're still projecting out tuples from a previous join
81 * tuple (because there is a function-returning-set in the projection
82 * expressions). If so, try to project another one.
84 if (node->js.ps.ps_TupFromTlist)
86 TupleTableSlot *result;
88 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
89 if (isDone == ExprMultipleResult)
91 /* Done with that source tuple... */
92 node->js.ps.ps_TupFromTlist = false;
96 * Reset per-tuple memory context to free any expression evaluation
97 * storage allocated in the previous tuple cycle. Note this can't happen
98 * until we're done projecting out tuples from a join tuple.
100 ResetExprContext(econtext);
103 * if this is the first call, build the hash table for inner relation
105 if (hashtable == NULL)
108 * If the outer relation is completely empty, we can quit without
109 * building the hash table. However, for an inner join it is only a
110 * win to check this when the outer relation's startup cost is less
111 * than the projected cost of building the hash table. Otherwise it's
112 * best to build the hash table first and see if the inner relation is
113 * empty. (When it's an outer join, we should always make this check,
114 * since we aren't going to be able to skip the join on the strength
115 * of an empty inner relation anyway.)
117 * If we are rescanning the join, we make use of information gained on
118 * the previous scan: don't bother to try the prefetch if the previous
119 * scan found the outer relation nonempty. This is not 100% reliable
120 * since with new parameters the outer relation might yield different
121 * results, but it's a good heuristic.
123 * The only way to make the check is to try to fetch a tuple from the
124 * outer plan node. If we succeed, we have to stash it away for later
125 * consumption by ExecHashJoinOuterGetTuple.
127 if (HASHJOIN_IS_OUTER(node) ||
128 (outerNode->plan->startup_cost < hashNode->ps.plan->total_cost &&
129 !node->hj_OuterNotEmpty))
131 node->hj_FirstOuterTupleSlot = ExecProcNode(outerNode);
132 if (TupIsNull(node->hj_FirstOuterTupleSlot))
134 node->hj_OuterNotEmpty = false;
138 node->hj_OuterNotEmpty = true;
141 node->hj_FirstOuterTupleSlot = NULL;
144 * create the hash table
146 hashtable = ExecHashTableCreate((Hash *) hashNode->ps.plan,
147 node->hj_HashOperators);
148 node->hj_HashTable = hashtable;
151 * execute the Hash node, to build the hash table
153 hashNode->hashtable = hashtable;
154 (void) MultiExecProcNode((PlanState *) hashNode);
157 * If the inner relation is completely empty, and we're not doing an
158 * outer join, we can quit without scanning the outer relation.
160 if (hashtable->totalTuples == 0 && !HASHJOIN_IS_OUTER(node))
164 * need to remember whether nbatch has increased since we began
165 * scanning the outer relation
167 hashtable->nbatch_outstart = hashtable->nbatch;
170 * Reset OuterNotEmpty for scan. (It's OK if we fetched a tuple
171 * above, because ExecHashJoinOuterGetTuple will immediately set it
174 node->hj_OuterNotEmpty = false;
178 * run the hash join process
183 * If we don't have an outer tuple, get the next one
185 if (node->hj_NeedNewOuter)
187 outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode,
190 if (TupIsNull(outerTupleSlot))
196 econtext->ecxt_outertuple = outerTupleSlot;
197 node->hj_NeedNewOuter = false;
198 node->hj_MatchedOuter = false;
201 * Now we have an outer tuple; find the corresponding bucket for
202 * this tuple in the main hash table or skew hash table.
204 node->hj_CurHashValue = hashvalue;
205 ExecHashGetBucketAndBatch(hashtable, hashvalue,
206 &node->hj_CurBucketNo, &batchno);
207 node->hj_CurSkewBucketNo = ExecHashGetSkewBucket(hashtable,
209 node->hj_CurTuple = NULL;
212 * Now we've got an outer tuple and the corresponding hash bucket,
213 * but it might not belong to the current batch, or it might
214 * match a skew bucket.
216 if (batchno != hashtable->curbatch &&
217 node->hj_CurSkewBucketNo == INVALID_SKEW_BUCKET_NO)
220 * Need to postpone this outer tuple to a later batch. Save it
221 * in the corresponding outer-batch file.
223 Assert(batchno > hashtable->curbatch);
224 ExecHashJoinSaveTuple(ExecFetchSlotMinimalTuple(outerTupleSlot),
226 &hashtable->outerBatchFile[batchno]);
227 node->hj_NeedNewOuter = true;
228 continue; /* loop around for a new outer tuple */
233 * OK, scan the selected hash bucket for matches
237 curtuple = ExecScanHashBucket(node, econtext);
238 if (curtuple == NULL)
239 break; /* out of matches */
242 * we've got a match, but still need to test non-hashed quals
244 inntuple = ExecStoreMinimalTuple(HJTUPLE_MINTUPLE(curtuple),
245 node->hj_HashTupleSlot,
246 false); /* don't pfree */
247 econtext->ecxt_innertuple = inntuple;
249 /* reset temp memory each time to avoid leaks from qual expr */
250 ResetExprContext(econtext);
253 * if we pass the qual, then save state for next call and have
254 * ExecProject form the projection, store it in the tuple table,
255 * and return the slot.
257 * Only the joinquals determine MatchedOuter status, but all quals
258 * must pass to actually return the tuple.
260 if (joinqual == NIL || ExecQual(joinqual, econtext, false))
262 node->hj_MatchedOuter = true;
264 /* In an antijoin, we never return a matched tuple */
265 if (node->js.jointype == JOIN_ANTI)
267 node->hj_NeedNewOuter = true;
268 break; /* out of loop over hash bucket */
272 * In a semijoin, we'll consider returning the first match,
273 * but after that we're done with this outer tuple.
275 if (node->js.jointype == JOIN_SEMI)
276 node->hj_NeedNewOuter = true;
278 if (otherqual == NIL || ExecQual(otherqual, econtext, false))
280 TupleTableSlot *result;
282 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
284 if (isDone != ExprEndResult)
286 node->js.ps.ps_TupFromTlist =
287 (isDone == ExprMultipleResult);
293 * If semijoin and we didn't return the tuple, we're still
294 * done with this outer tuple.
296 if (node->js.jointype == JOIN_SEMI)
297 break; /* out of loop over hash bucket */
302 * Now the current outer tuple has run out of matches, so check
303 * whether to emit a dummy outer-join tuple. If not, loop around to
304 * get a new outer tuple.
306 node->hj_NeedNewOuter = true;
308 if (!node->hj_MatchedOuter &&
309 HASHJOIN_IS_OUTER(node))
312 * We are doing an outer join and there were no join matches for
313 * this outer tuple. Generate a fake join tuple with nulls for
314 * the inner tuple, and return it if it passes the non-join quals.
316 econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
318 if (otherqual == NIL || ExecQual(otherqual, econtext, false))
321 * qualification was satisfied so we project and return the
322 * slot containing the result tuple using ExecProject().
324 TupleTableSlot *result;
326 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
328 if (isDone != ExprEndResult)
330 node->js.ps.ps_TupFromTlist =
331 (isDone == ExprMultipleResult);
339 /* ----------------------------------------------------------------
342 * Init routine for HashJoin node.
343 * ----------------------------------------------------------------
346 ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
348 HashJoinState *hjstate;
356 /* check for unsupported flags */
357 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
360 * create state structure
362 hjstate = makeNode(HashJoinState);
363 hjstate->js.ps.plan = (Plan *) node;
364 hjstate->js.ps.state = estate;
367 * Miscellaneous initialization
369 * create expression context for node
371 ExecAssignExprContext(estate, &hjstate->js.ps);
374 * initialize child expressions
376 hjstate->js.ps.targetlist = (List *)
377 ExecInitExpr((Expr *) node->join.plan.targetlist,
378 (PlanState *) hjstate);
379 hjstate->js.ps.qual = (List *)
380 ExecInitExpr((Expr *) node->join.plan.qual,
381 (PlanState *) hjstate);
382 hjstate->js.jointype = node->join.jointype;
383 hjstate->js.joinqual = (List *)
384 ExecInitExpr((Expr *) node->join.joinqual,
385 (PlanState *) hjstate);
386 hjstate->hashclauses = (List *)
387 ExecInitExpr((Expr *) node->hashclauses,
388 (PlanState *) hjstate);
391 * initialize child nodes
393 * Note: we could suppress the REWIND flag for the inner input, which
394 * would amount to betting that the hash will be a single batch. Not
395 * clear if this would be a win or not.
397 outerNode = outerPlan(node);
398 hashNode = (Hash *) innerPlan(node);
400 outerPlanState(hjstate) = ExecInitNode(outerNode, estate, eflags);
401 innerPlanState(hjstate) = ExecInitNode((Plan *) hashNode, estate, eflags);
403 #define HASHJOIN_NSLOTS 3
406 * tuple table initialization
408 ExecInitResultTupleSlot(estate, &hjstate->js.ps);
409 hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate);
411 /* note: HASHJOIN_IS_OUTER macro depends on this initialization */
412 switch (node->join.jointype)
419 hjstate->hj_NullInnerTupleSlot =
420 ExecInitNullTupleSlot(estate,
421 ExecGetResultType(innerPlanState(hjstate)));
424 elog(ERROR, "unrecognized join type: %d",
425 (int) node->join.jointype);
429 * now for some voodoo. our temporary tuple slot is actually the result
430 * tuple slot of the Hash node (which is our inner plan). we do this
431 * because Hash nodes don't return tuples via ExecProcNode() -- instead
432 * the hash join node uses ExecScanHashBucket() to get at the contents of
433 * the hash table. -cim 6/9/91
436 HashState *hashstate = (HashState *) innerPlanState(hjstate);
437 TupleTableSlot *slot = hashstate->ps.ps_ResultTupleSlot;
439 hjstate->hj_HashTupleSlot = slot;
443 * initialize tuple type and projection info
445 ExecAssignResultTypeFromTL(&hjstate->js.ps);
446 ExecAssignProjectionInfo(&hjstate->js.ps, NULL);
448 ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,
449 ExecGetResultType(outerPlanState(hjstate)));
452 * initialize hash-specific info
454 hjstate->hj_HashTable = NULL;
455 hjstate->hj_FirstOuterTupleSlot = NULL;
457 hjstate->hj_CurHashValue = 0;
458 hjstate->hj_CurBucketNo = 0;
459 hjstate->hj_CurSkewBucketNo = INVALID_SKEW_BUCKET_NO;
460 hjstate->hj_CurTuple = NULL;
463 * Deconstruct the hash clauses into outer and inner argument values, so
464 * that we can evaluate those subexpressions separately. Also make a list
465 * of the hash operator OIDs, in preparation for looking up the hash
471 foreach(l, hjstate->hashclauses)
473 FuncExprState *fstate = (FuncExprState *) lfirst(l);
476 Assert(IsA(fstate, FuncExprState));
477 hclause = (OpExpr *) fstate->xprstate.expr;
478 Assert(IsA(hclause, OpExpr));
479 lclauses = lappend(lclauses, linitial(fstate->args));
480 rclauses = lappend(rclauses, lsecond(fstate->args));
481 hoperators = lappend_oid(hoperators, hclause->opno);
483 hjstate->hj_OuterHashKeys = lclauses;
484 hjstate->hj_InnerHashKeys = rclauses;
485 hjstate->hj_HashOperators = hoperators;
486 /* child Hash node needs to evaluate inner hash keys, too */
487 ((HashState *) innerPlanState(hjstate))->hashkeys = rclauses;
489 hjstate->js.ps.ps_TupFromTlist = false;
490 hjstate->hj_NeedNewOuter = true;
491 hjstate->hj_MatchedOuter = false;
492 hjstate->hj_OuterNotEmpty = false;
498 ExecCountSlotsHashJoin(HashJoin *node)
500 return ExecCountSlotsNode(outerPlan(node)) +
501 ExecCountSlotsNode(innerPlan(node)) +
505 /* ----------------------------------------------------------------
508 * clean up routine for HashJoin node
509 * ----------------------------------------------------------------
512 ExecEndHashJoin(HashJoinState *node)
517 if (node->hj_HashTable)
519 ExecHashTableDestroy(node->hj_HashTable);
520 node->hj_HashTable = NULL;
524 * Free the exprcontext
526 ExecFreeExprContext(&node->js.ps);
529 * clean out the tuple table
531 ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
532 ExecClearTuple(node->hj_OuterTupleSlot);
533 ExecClearTuple(node->hj_HashTupleSlot);
538 ExecEndNode(outerPlanState(node));
539 ExecEndNode(innerPlanState(node));
543 * ExecHashJoinOuterGetTuple
545 * get the next outer tuple for hashjoin: either by
546 * executing a plan node in the first pass, or from
547 * the temp files for the hashjoin batches.
549 * Returns a null slot if no more outer tuples. On success, the tuple's
550 * hash value is stored at *hashvalue --- this is either originally computed,
551 * or re-read from the temp file.
553 static TupleTableSlot *
554 ExecHashJoinOuterGetTuple(PlanState *outerNode,
555 HashJoinState *hjstate,
558 HashJoinTable hashtable = hjstate->hj_HashTable;
559 int curbatch = hashtable->curbatch;
560 TupleTableSlot *slot;
562 if (curbatch == 0) /* if it is the first pass */
565 * Check to see if first outer tuple was already fetched by
566 * ExecHashJoin() and not used yet.
568 slot = hjstate->hj_FirstOuterTupleSlot;
569 if (!TupIsNull(slot))
570 hjstate->hj_FirstOuterTupleSlot = NULL;
572 slot = ExecProcNode(outerNode);
574 while (!TupIsNull(slot))
577 * We have to compute the tuple's hash value.
579 ExprContext *econtext = hjstate->js.ps.ps_ExprContext;
581 econtext->ecxt_outertuple = slot;
582 if (ExecHashGetHashValue(hashtable, econtext,
583 hjstate->hj_OuterHashKeys,
584 true, /* outer tuple */
585 HASHJOIN_IS_OUTER(hjstate),
588 /* remember outer relation is not empty for possible rescan */
589 hjstate->hj_OuterNotEmpty = true;
595 * That tuple couldn't match because of a NULL, so discard it and
596 * continue with the next one.
598 slot = ExecProcNode(outerNode);
602 * We have just reached the end of the first pass. Try to switch to a
605 curbatch = ExecHashJoinNewBatch(hjstate);
609 * Try to read from a temp file. Loop allows us to advance to new batches
610 * as needed. NOTE: nbatch could increase inside ExecHashJoinNewBatch, so
611 * don't try to optimize this loop.
613 while (curbatch < hashtable->nbatch)
615 slot = ExecHashJoinGetSavedTuple(hjstate,
616 hashtable->outerBatchFile[curbatch],
618 hjstate->hj_OuterTupleSlot);
619 if (!TupIsNull(slot))
621 curbatch = ExecHashJoinNewBatch(hjstate);
624 /* Out of batches... */
629 * ExecHashJoinNewBatch
630 * switch to a new hashjoin batch
632 * Returns the number of the new batch (1..nbatch-1), or nbatch if no more.
633 * We will never return a batch number that has an empty outer batch file.
636 ExecHashJoinNewBatch(HashJoinState *hjstate)
638 HashJoinTable hashtable = hjstate->hj_HashTable;
642 TupleTableSlot *slot;
646 nbatch = hashtable->nbatch;
647 curbatch = hashtable->curbatch;
652 * We no longer need the previous outer batch file; close it right
653 * away to free disk space.
655 if (hashtable->outerBatchFile[curbatch])
656 BufFileClose(hashtable->outerBatchFile[curbatch]);
657 hashtable->outerBatchFile[curbatch] = NULL;
659 else /* we just finished the first batch */
662 * Reset some of the skew optimization state variables, since we
663 * no longer need to consider skew tuples after the first batch.
664 * The memory context reset we are about to do will release the
665 * skew hashtable itself.
667 hashtable->skewEnabled = false;
668 hashtable->skewBucket = NULL;
669 hashtable->skewBucketNums = NULL;
670 hashtable->spaceUsedSkew = 0;
674 * We can always skip over any batches that are completely empty on both
675 * sides. We can sometimes skip over batches that are empty on only one
676 * side, but there are exceptions:
678 * 1. In an outer join, we have to process outer batches even if the inner
681 * 2. If we have increased nbatch since the initial estimate, we have to
682 * scan inner batches since they might contain tuples that need to be
683 * reassigned to later inner batches.
685 * 3. Similarly, if we have increased nbatch since starting the outer
686 * scan, we have to rescan outer batches in case they contain tuples that
687 * need to be reassigned.
690 while (curbatch < nbatch &&
691 (hashtable->outerBatchFile[curbatch] == NULL ||
692 hashtable->innerBatchFile[curbatch] == NULL))
694 if (hashtable->outerBatchFile[curbatch] &&
695 HASHJOIN_IS_OUTER(hjstate))
696 break; /* must process due to rule 1 */
697 if (hashtable->innerBatchFile[curbatch] &&
698 nbatch != hashtable->nbatch_original)
699 break; /* must process due to rule 2 */
700 if (hashtable->outerBatchFile[curbatch] &&
701 nbatch != hashtable->nbatch_outstart)
702 break; /* must process due to rule 3 */
703 /* We can ignore this batch. */
704 /* Release associated temp files right away. */
705 if (hashtable->innerBatchFile[curbatch])
706 BufFileClose(hashtable->innerBatchFile[curbatch]);
707 hashtable->innerBatchFile[curbatch] = NULL;
708 if (hashtable->outerBatchFile[curbatch])
709 BufFileClose(hashtable->outerBatchFile[curbatch]);
710 hashtable->outerBatchFile[curbatch] = NULL;
714 if (curbatch >= nbatch)
715 return curbatch; /* no more batches */
717 hashtable->curbatch = curbatch;
720 * Reload the hash table with the new inner batch (which could be empty)
722 ExecHashTableReset(hashtable);
724 innerFile = hashtable->innerBatchFile[curbatch];
726 if (innerFile != NULL)
728 if (BufFileSeek(innerFile, 0, 0L, SEEK_SET))
730 (errcode_for_file_access(),
731 errmsg("could not rewind hash-join temporary file: %m")));
733 while ((slot = ExecHashJoinGetSavedTuple(hjstate,
736 hjstate->hj_HashTupleSlot)))
739 * NOTE: some tuples may be sent to future batches. Also, it is
740 * possible for hashtable->nbatch to be increased here!
742 ExecHashTableInsert(hashtable, slot, hashvalue);
746 * after we build the hash table, the inner batch file is no longer
749 BufFileClose(innerFile);
750 hashtable->innerBatchFile[curbatch] = NULL;
754 * If there's no outer batch file, advance to next batch.
756 if (hashtable->outerBatchFile[curbatch] == NULL)
760 * Rewind outer batch file, so that we can start reading it.
762 if (BufFileSeek(hashtable->outerBatchFile[curbatch], 0, 0L, SEEK_SET))
764 (errcode_for_file_access(),
765 errmsg("could not rewind hash-join temporary file: %m")));
771 * ExecHashJoinSaveTuple
772 * save a tuple to a batch file.
774 * The data recorded in the file for each tuple is its hash value,
775 * then the tuple in MinimalTuple format.
777 * Note: it is important always to call this in the regular executor
778 * context, not in a shorter-lived context; else the temp file buffers
779 * will get messed up.
782 ExecHashJoinSaveTuple(MinimalTuple tuple, uint32 hashvalue,
785 BufFile *file = *fileptr;
790 /* First write to this batch file, so open it. */
791 file = BufFileCreateTemp(false);
795 written = BufFileWrite(file, (void *) &hashvalue, sizeof(uint32));
796 if (written != sizeof(uint32))
798 (errcode_for_file_access(),
799 errmsg("could not write to hash-join temporary file: %m")));
801 written = BufFileWrite(file, (void *) tuple, tuple->t_len);
802 if (written != tuple->t_len)
804 (errcode_for_file_access(),
805 errmsg("could not write to hash-join temporary file: %m")));
809 * ExecHashJoinGetSavedTuple
810 * read the next tuple from a batch file. Return NULL if no more.
812 * On success, *hashvalue is set to the tuple's hash value, and the tuple
813 * itself is stored in the given slot.
815 static TupleTableSlot *
816 ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
819 TupleTableSlot *tupleSlot)
826 * Since both the hash value and the MinimalTuple length word are uint32,
827 * we can read them both in one BufFileRead() call without any type
830 nread = BufFileRead(file, (void *) header, sizeof(header));
831 if (nread == 0) /* end of file */
833 ExecClearTuple(tupleSlot);
836 if (nread != sizeof(header))
838 (errcode_for_file_access(),
839 errmsg("could not read from hash-join temporary file: %m")));
840 *hashvalue = header[0];
841 tuple = (MinimalTuple) palloc(header[1]);
842 tuple->t_len = header[1];
843 nread = BufFileRead(file,
844 (void *) ((char *) tuple + sizeof(uint32)),
845 header[1] - sizeof(uint32));
846 if (nread != header[1] - sizeof(uint32))
848 (errcode_for_file_access(),
849 errmsg("could not read from hash-join temporary file: %m")));
850 return ExecStoreMinimalTuple(tuple, tupleSlot, true);
855 ExecReScanHashJoin(HashJoinState *node, ExprContext *exprCtxt)
858 * In a multi-batch join, we currently have to do rescans the hard way,
859 * primarily because batch temp files may have already been released. But
860 * if it's a single-batch join, and there is no parameter change for the
861 * inner subnode, then we can just re-use the existing hash table without
864 if (node->hj_HashTable != NULL)
866 if (node->hj_HashTable->nbatch == 1 &&
867 ((PlanState *) node)->righttree->chgParam == NULL)
870 * okay to reuse the hash table; needn't rescan inner, either.
872 * What we do need to do is reset our state about the emptiness of
873 * the outer relation, so that the new scan of the outer will
874 * update it correctly if it turns out to be empty this time.
875 * (There's no harm in clearing it now because ExecHashJoin won't
876 * need the info. In the other cases, where the hash table
877 * doesn't exist or we are destroying it, we leave this state
878 * alone because ExecHashJoin will need it the first time
881 node->hj_OuterNotEmpty = false;
885 /* must destroy and rebuild hash table */
886 ExecHashTableDestroy(node->hj_HashTable);
887 node->hj_HashTable = NULL;
890 * if chgParam of subnode is not null then plan will be re-scanned
891 * by first ExecProcNode.
893 if (((PlanState *) node)->righttree->chgParam == NULL)
894 ExecReScan(((PlanState *) node)->righttree, exprCtxt);
898 /* Always reset intra-tuple state */
899 node->hj_CurHashValue = 0;
900 node->hj_CurBucketNo = 0;
901 node->hj_CurSkewBucketNo = INVALID_SKEW_BUCKET_NO;
902 node->hj_CurTuple = NULL;
904 node->js.ps.ps_TupFromTlist = false;
905 node->hj_NeedNewOuter = true;
906 node->hj_MatchedOuter = false;
907 node->hj_FirstOuterTupleSlot = NULL;
910 * if chgParam of subnode is not null then plan will be re-scanned by
911 * first ExecProcNode.
913 if (((PlanState *) node)->lefttree->chgParam == NULL)
914 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);