1 /*-------------------------------------------------------------------------
4 * Routines to handle hash join nodes
6 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/executor/nodeHashjoin.c
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"
26 * States of the ExecHashJoin state machine
28 #define HJ_BUILD_HASHTABLE 1
29 #define HJ_NEED_NEW_OUTER 2
30 #define HJ_SCAN_BUCKET 3
31 #define HJ_FILL_OUTER_TUPLE 4
32 #define HJ_FILL_INNER_TUPLES 5
33 #define HJ_NEED_NEW_BATCH 6
35 /* Returns true if doing null-fill on outer relation */
36 #define HJ_FILL_OUTER(hjstate) ((hjstate)->hj_NullInnerTupleSlot != NULL)
37 /* Returns true if doing null-fill on inner relation */
38 #define HJ_FILL_INNER(hjstate) ((hjstate)->hj_NullOuterTupleSlot != NULL)
40 static TupleTableSlot *ExecHashJoinOuterGetTuple(PlanState *outerNode,
41 HashJoinState *hjstate,
43 static TupleTableSlot *ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
46 TupleTableSlot *tupleSlot);
47 static bool ExecHashJoinNewBatch(HashJoinState *hjstate);
50 /* ----------------------------------------------------------------
53 * This function implements the Hybrid Hashjoin algorithm.
55 * Note: the relation we build hash table on is the "inner"
56 * the other one is "outer".
57 * ----------------------------------------------------------------
59 TupleTableSlot * /* return: a tuple or NULL */
60 ExecHashJoin(HashJoinState *node)
66 ExprContext *econtext;
68 HashJoinTable hashtable;
69 TupleTableSlot *outerTupleSlot;
74 * get information from HashJoin node
76 joinqual = node->js.joinqual;
77 otherqual = node->js.ps.qual;
78 hashNode = (HashState *) innerPlanState(node);
79 outerNode = outerPlanState(node);
80 hashtable = node->hj_HashTable;
81 econtext = node->js.ps.ps_ExprContext;
84 * Check to see if we're still projecting out tuples from a previous join
85 * tuple (because there is a function-returning-set in the projection
86 * expressions). If so, try to project another one.
88 if (node->js.ps.ps_TupFromTlist)
90 TupleTableSlot *result;
92 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
93 if (isDone == ExprMultipleResult)
95 /* Done with that source tuple... */
96 node->js.ps.ps_TupFromTlist = false;
100 * Reset per-tuple memory context to free any expression evaluation
101 * storage allocated in the previous tuple cycle. Note this can't happen
102 * until we're done projecting out tuples from a join tuple.
104 ResetExprContext(econtext);
107 * run the hash join state machine
111 switch (node->hj_JoinState)
113 case HJ_BUILD_HASHTABLE:
116 * First time through: build hash table for inner relation.
118 Assert(hashtable == NULL);
121 * If the outer relation is completely empty, and it's not
122 * right/full join, we can quit without building the hash
123 * table. However, for an inner join it is only a win to
124 * check this when the outer relation's startup cost is less
125 * than the projected cost of building the hash table.
126 * Otherwise it's best to build the hash table first and see
127 * if the inner relation is empty. (When it's a left join, we
128 * should always make this check, since we aren't going to be
129 * able to skip the join on the strength of an empty inner
132 * If we are rescanning the join, we make use of information
133 * gained on the previous scan: don't bother to try the
134 * prefetch if the previous scan found the outer relation
135 * nonempty. This is not 100% reliable since with new
136 * parameters the outer relation might yield different
137 * results, but it's a good heuristic.
139 * The only way to make the check is to try to fetch a tuple
140 * from the outer plan node. If we succeed, we have to stash
141 * it away for later consumption by ExecHashJoinOuterGetTuple.
143 if (HJ_FILL_INNER(node))
145 /* no chance to not build the hash table */
146 node->hj_FirstOuterTupleSlot = NULL;
148 else if (HJ_FILL_OUTER(node) ||
149 (outerNode->plan->startup_cost < hashNode->ps.plan->total_cost &&
150 !node->hj_OuterNotEmpty))
152 node->hj_FirstOuterTupleSlot = ExecProcNode(outerNode);
153 if (TupIsNull(node->hj_FirstOuterTupleSlot))
155 node->hj_OuterNotEmpty = false;
159 node->hj_OuterNotEmpty = true;
162 node->hj_FirstOuterTupleSlot = NULL;
165 * create the hash table
167 hashtable = ExecHashTableCreate((Hash *) hashNode->ps.plan,
168 node->hj_HashOperators,
169 HJ_FILL_INNER(node));
170 node->hj_HashTable = hashtable;
173 * execute the Hash node, to build the hash table
175 hashNode->hashtable = hashtable;
176 (void) MultiExecProcNode((PlanState *) hashNode);
179 * If the inner relation is completely empty, and we're not
180 * doing a left outer join, we can quit without scanning the
183 if (hashtable->totalTuples == 0 && !HJ_FILL_OUTER(node))
187 * need to remember whether nbatch has increased since we
188 * began scanning the outer relation
190 hashtable->nbatch_outstart = hashtable->nbatch;
193 * Reset OuterNotEmpty for scan. (It's OK if we fetched a
194 * tuple above, because ExecHashJoinOuterGetTuple will
195 * immediately set it again.)
197 node->hj_OuterNotEmpty = false;
199 node->hj_JoinState = HJ_NEED_NEW_OUTER;
203 case HJ_NEED_NEW_OUTER:
206 * We don't have an outer tuple, try to get the next one
208 outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode,
211 if (TupIsNull(outerTupleSlot))
213 /* end of batch, or maybe whole join */
214 if (HJ_FILL_INNER(node))
216 /* set up to scan for unmatched inner tuples */
217 ExecPrepHashTableForUnmatched(node);
218 node->hj_JoinState = HJ_FILL_INNER_TUPLES;
221 node->hj_JoinState = HJ_NEED_NEW_BATCH;
225 econtext->ecxt_outertuple = outerTupleSlot;
226 node->hj_MatchedOuter = false;
229 * Find the corresponding bucket for this tuple in the main
230 * hash table or skew hash table.
232 node->hj_CurHashValue = hashvalue;
233 ExecHashGetBucketAndBatch(hashtable, hashvalue,
234 &node->hj_CurBucketNo, &batchno);
235 node->hj_CurSkewBucketNo = ExecHashGetSkewBucket(hashtable,
237 node->hj_CurTuple = NULL;
240 * The tuple might not belong to the current batch (where
241 * "current batch" includes the skew buckets if any).
243 if (batchno != hashtable->curbatch &&
244 node->hj_CurSkewBucketNo == INVALID_SKEW_BUCKET_NO)
247 * Need to postpone this outer tuple to a later batch.
248 * Save it in the corresponding outer-batch file.
250 Assert(batchno > hashtable->curbatch);
251 ExecHashJoinSaveTuple(ExecFetchSlotMinimalTuple(outerTupleSlot),
253 &hashtable->outerBatchFile[batchno]);
254 /* Loop around, staying in HJ_NEED_NEW_OUTER state */
258 /* OK, let's scan the bucket for matches */
259 node->hj_JoinState = HJ_SCAN_BUCKET;
266 * Scan the selected hash bucket for matches to current outer
268 if (!ExecScanHashBucket(node, econtext))
270 /* out of matches; check for possible outer-join fill */
271 node->hj_JoinState = HJ_FILL_OUTER_TUPLE;
276 * We've got a match, but still need to test non-hashed quals.
277 * ExecScanHashBucket already set up all the state needed to
280 * If we pass the qual, then save state for next call and have
281 * ExecProject form the projection, store it in the tuple
282 * table, and return the slot.
284 * Only the joinquals determine tuple match status, but all
285 * quals must pass to actually return the tuple.
287 if (joinqual == NIL || ExecQual(joinqual, econtext, false))
289 node->hj_MatchedOuter = true;
290 HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
292 /* In an antijoin, we never return a matched tuple */
293 if (node->js.jointype == JOIN_ANTI)
295 node->hj_JoinState = HJ_NEED_NEW_OUTER;
300 * In a semijoin, we'll consider returning the first
301 * match, but after that we're done with this outer tuple.
303 if (node->js.jointype == JOIN_SEMI)
304 node->hj_JoinState = HJ_NEED_NEW_OUTER;
306 if (otherqual == NIL ||
307 ExecQual(otherqual, econtext, false))
309 TupleTableSlot *result;
311 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
313 if (isDone != ExprEndResult)
315 node->js.ps.ps_TupFromTlist =
316 (isDone == ExprMultipleResult);
323 case HJ_FILL_OUTER_TUPLE:
326 * The current outer tuple has run out of matches, so check
327 * whether to emit a dummy outer-join tuple. Whether we emit
328 * one or not, the next state is NEED_NEW_OUTER.
330 node->hj_JoinState = HJ_NEED_NEW_OUTER;
332 if (!node->hj_MatchedOuter &&
336 * Generate a fake join tuple with nulls for the inner
337 * tuple, and return it if it passes the non-join quals.
339 econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
341 if (otherqual == NIL ||
342 ExecQual(otherqual, econtext, false))
344 TupleTableSlot *result;
346 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
348 if (isDone != ExprEndResult)
350 node->js.ps.ps_TupFromTlist =
351 (isDone == ExprMultipleResult);
358 case HJ_FILL_INNER_TUPLES:
361 * We have finished a batch, but we are doing right/full join,
362 * so any unmatched inner tuples in the hashtable have to be
363 * emitted before we continue to the next batch.
365 if (!ExecScanHashTableForUnmatched(node, econtext))
367 /* no more unmatched tuples */
368 node->hj_JoinState = HJ_NEED_NEW_BATCH;
373 * Generate a fake join tuple with nulls for the outer tuple,
374 * and return it if it passes the non-join quals.
376 econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
378 if (otherqual == NIL ||
379 ExecQual(otherqual, econtext, false))
381 TupleTableSlot *result;
383 result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
385 if (isDone != ExprEndResult)
387 node->js.ps.ps_TupFromTlist =
388 (isDone == ExprMultipleResult);
394 case HJ_NEED_NEW_BATCH:
397 * Try to advance to next batch. Done if there are no more.
399 if (!ExecHashJoinNewBatch(node))
400 return NULL; /* end of join */
401 node->hj_JoinState = HJ_NEED_NEW_OUTER;
405 elog(ERROR, "unrecognized hashjoin state: %d",
406 (int) node->hj_JoinState);
411 /* ----------------------------------------------------------------
414 * Init routine for HashJoin node.
415 * ----------------------------------------------------------------
418 ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
420 HashJoinState *hjstate;
428 /* check for unsupported flags */
429 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
432 * create state structure
434 hjstate = makeNode(HashJoinState);
435 hjstate->js.ps.plan = (Plan *) node;
436 hjstate->js.ps.state = estate;
439 * Miscellaneous initialization
441 * create expression context for node
443 ExecAssignExprContext(estate, &hjstate->js.ps);
446 * initialize child expressions
448 hjstate->js.ps.targetlist = (List *)
449 ExecInitExpr((Expr *) node->join.plan.targetlist,
450 (PlanState *) hjstate);
451 hjstate->js.ps.qual = (List *)
452 ExecInitExpr((Expr *) node->join.plan.qual,
453 (PlanState *) hjstate);
454 hjstate->js.jointype = node->join.jointype;
455 hjstate->js.joinqual = (List *)
456 ExecInitExpr((Expr *) node->join.joinqual,
457 (PlanState *) hjstate);
458 hjstate->hashclauses = (List *)
459 ExecInitExpr((Expr *) node->hashclauses,
460 (PlanState *) hjstate);
463 * initialize child nodes
465 * Note: we could suppress the REWIND flag for the inner input, which
466 * would amount to betting that the hash will be a single batch. Not
467 * clear if this would be a win or not.
469 outerNode = outerPlan(node);
470 hashNode = (Hash *) innerPlan(node);
472 outerPlanState(hjstate) = ExecInitNode(outerNode, estate, eflags);
473 innerPlanState(hjstate) = ExecInitNode((Plan *) hashNode, estate, eflags);
476 * tuple table initialization
478 ExecInitResultTupleSlot(estate, &hjstate->js.ps);
479 hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate);
481 /* set up null tuples for outer joins, if needed */
482 switch (node->join.jointype)
489 hjstate->hj_NullInnerTupleSlot =
490 ExecInitNullTupleSlot(estate,
491 ExecGetResultType(innerPlanState(hjstate)));
494 hjstate->hj_NullOuterTupleSlot =
495 ExecInitNullTupleSlot(estate,
496 ExecGetResultType(outerPlanState(hjstate)));
499 hjstate->hj_NullOuterTupleSlot =
500 ExecInitNullTupleSlot(estate,
501 ExecGetResultType(outerPlanState(hjstate)));
502 hjstate->hj_NullInnerTupleSlot =
503 ExecInitNullTupleSlot(estate,
504 ExecGetResultType(innerPlanState(hjstate)));
507 elog(ERROR, "unrecognized join type: %d",
508 (int) node->join.jointype);
512 * now for some voodoo. our temporary tuple slot is actually the result
513 * tuple slot of the Hash node (which is our inner plan). we can do this
514 * because Hash nodes don't return tuples via ExecProcNode() -- instead
515 * the hash join node uses ExecScanHashBucket() to get at the contents of
516 * the hash table. -cim 6/9/91
519 HashState *hashstate = (HashState *) innerPlanState(hjstate);
520 TupleTableSlot *slot = hashstate->ps.ps_ResultTupleSlot;
522 hjstate->hj_HashTupleSlot = slot;
526 * initialize tuple type and projection info
528 ExecAssignResultTypeFromTL(&hjstate->js.ps);
529 ExecAssignProjectionInfo(&hjstate->js.ps, NULL);
531 ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,
532 ExecGetResultType(outerPlanState(hjstate)));
535 * initialize hash-specific info
537 hjstate->hj_HashTable = NULL;
538 hjstate->hj_FirstOuterTupleSlot = NULL;
540 hjstate->hj_CurHashValue = 0;
541 hjstate->hj_CurBucketNo = 0;
542 hjstate->hj_CurSkewBucketNo = INVALID_SKEW_BUCKET_NO;
543 hjstate->hj_CurTuple = NULL;
546 * Deconstruct the hash clauses into outer and inner argument values, so
547 * that we can evaluate those subexpressions separately. Also make a list
548 * of the hash operator OIDs, in preparation for looking up the hash
554 foreach(l, hjstate->hashclauses)
556 FuncExprState *fstate = (FuncExprState *) lfirst(l);
559 Assert(IsA(fstate, FuncExprState));
560 hclause = (OpExpr *) fstate->xprstate.expr;
561 Assert(IsA(hclause, OpExpr));
562 lclauses = lappend(lclauses, linitial(fstate->args));
563 rclauses = lappend(rclauses, lsecond(fstate->args));
564 hoperators = lappend_oid(hoperators, hclause->opno);
566 hjstate->hj_OuterHashKeys = lclauses;
567 hjstate->hj_InnerHashKeys = rclauses;
568 hjstate->hj_HashOperators = hoperators;
569 /* child Hash node needs to evaluate inner hash keys, too */
570 ((HashState *) innerPlanState(hjstate))->hashkeys = rclauses;
572 hjstate->js.ps.ps_TupFromTlist = false;
573 hjstate->hj_JoinState = HJ_BUILD_HASHTABLE;
574 hjstate->hj_MatchedOuter = false;
575 hjstate->hj_OuterNotEmpty = false;
580 /* ----------------------------------------------------------------
583 * clean up routine for HashJoin node
584 * ----------------------------------------------------------------
587 ExecEndHashJoin(HashJoinState *node)
592 if (node->hj_HashTable)
594 ExecHashTableDestroy(node->hj_HashTable);
595 node->hj_HashTable = NULL;
599 * Free the exprcontext
601 ExecFreeExprContext(&node->js.ps);
604 * clean out the tuple table
606 ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
607 ExecClearTuple(node->hj_OuterTupleSlot);
608 ExecClearTuple(node->hj_HashTupleSlot);
613 ExecEndNode(outerPlanState(node));
614 ExecEndNode(innerPlanState(node));
618 * ExecHashJoinOuterGetTuple
620 * get the next outer tuple for hashjoin: either by
621 * executing the outer plan node in the first pass, or from
622 * the temp files for the hashjoin batches.
624 * Returns a null slot if no more outer tuples (within the current batch).
626 * On success, the tuple's hash value is stored at *hashvalue --- this is
627 * either originally computed, or re-read from the temp file.
629 static TupleTableSlot *
630 ExecHashJoinOuterGetTuple(PlanState *outerNode,
631 HashJoinState *hjstate,
634 HashJoinTable hashtable = hjstate->hj_HashTable;
635 int curbatch = hashtable->curbatch;
636 TupleTableSlot *slot;
638 if (curbatch == 0) /* if it is the first pass */
641 * Check to see if first outer tuple was already fetched by
642 * ExecHashJoin() and not used yet.
644 slot = hjstate->hj_FirstOuterTupleSlot;
645 if (!TupIsNull(slot))
646 hjstate->hj_FirstOuterTupleSlot = NULL;
648 slot = ExecProcNode(outerNode);
650 while (!TupIsNull(slot))
653 * We have to compute the tuple's hash value.
655 ExprContext *econtext = hjstate->js.ps.ps_ExprContext;
657 econtext->ecxt_outertuple = slot;
658 if (ExecHashGetHashValue(hashtable, econtext,
659 hjstate->hj_OuterHashKeys,
660 true, /* outer tuple */
661 HJ_FILL_OUTER(hjstate),
664 /* remember outer relation is not empty for possible rescan */
665 hjstate->hj_OuterNotEmpty = true;
671 * That tuple couldn't match because of a NULL, so discard it and
672 * continue with the next one.
674 slot = ExecProcNode(outerNode);
677 else if (curbatch < hashtable->nbatch)
679 BufFile *file = hashtable->outerBatchFile[curbatch];
682 * In outer-join cases, we could get here even though the batch file
688 slot = ExecHashJoinGetSavedTuple(hjstate,
691 hjstate->hj_OuterTupleSlot);
692 if (!TupIsNull(slot))
696 /* End of this batch */
701 * ExecHashJoinNewBatch
702 * switch to a new hashjoin batch
704 * Returns true if successful, false if there are no more batches.
707 ExecHashJoinNewBatch(HashJoinState *hjstate)
709 HashJoinTable hashtable = hjstate->hj_HashTable;
713 TupleTableSlot *slot;
716 nbatch = hashtable->nbatch;
717 curbatch = hashtable->curbatch;
722 * We no longer need the previous outer batch file; close it right
723 * away to free disk space.
725 if (hashtable->outerBatchFile[curbatch])
726 BufFileClose(hashtable->outerBatchFile[curbatch]);
727 hashtable->outerBatchFile[curbatch] = NULL;
729 else /* we just finished the first batch */
732 * Reset some of the skew optimization state variables, since we no
733 * longer need to consider skew tuples after the first batch. The
734 * memory context reset we are about to do will release the skew
737 hashtable->skewEnabled = false;
738 hashtable->skewBucket = NULL;
739 hashtable->skewBucketNums = NULL;
740 hashtable->nSkewBuckets = 0;
741 hashtable->spaceUsedSkew = 0;
745 * We can always skip over any batches that are completely empty on both
746 * sides. We can sometimes skip over batches that are empty on only one
747 * side, but there are exceptions:
749 * 1. In a left/full outer join, we have to process outer batches even if
750 * the inner batch is empty. Similarly, in a right/full outer join, we
751 * have to process inner batches even if the outer batch is empty.
753 * 2. If we have increased nbatch since the initial estimate, we have to
754 * scan inner batches since they might contain tuples that need to be
755 * reassigned to later inner batches.
757 * 3. Similarly, if we have increased nbatch since starting the outer
758 * scan, we have to rescan outer batches in case they contain tuples that
759 * need to be reassigned.
762 while (curbatch < nbatch &&
763 (hashtable->outerBatchFile[curbatch] == NULL ||
764 hashtable->innerBatchFile[curbatch] == NULL))
766 if (hashtable->outerBatchFile[curbatch] &&
767 HJ_FILL_OUTER(hjstate))
768 break; /* must process due to rule 1 */
769 if (hashtable->innerBatchFile[curbatch] &&
770 HJ_FILL_INNER(hjstate))
771 break; /* must process due to rule 1 */
772 if (hashtable->innerBatchFile[curbatch] &&
773 nbatch != hashtable->nbatch_original)
774 break; /* must process due to rule 2 */
775 if (hashtable->outerBatchFile[curbatch] &&
776 nbatch != hashtable->nbatch_outstart)
777 break; /* must process due to rule 3 */
778 /* We can ignore this batch. */
779 /* Release associated temp files right away. */
780 if (hashtable->innerBatchFile[curbatch])
781 BufFileClose(hashtable->innerBatchFile[curbatch]);
782 hashtable->innerBatchFile[curbatch] = NULL;
783 if (hashtable->outerBatchFile[curbatch])
784 BufFileClose(hashtable->outerBatchFile[curbatch]);
785 hashtable->outerBatchFile[curbatch] = NULL;
789 if (curbatch >= nbatch)
790 return false; /* no more batches */
792 hashtable->curbatch = curbatch;
795 * Reload the hash table with the new inner batch (which could be empty)
797 ExecHashTableReset(hashtable);
799 innerFile = hashtable->innerBatchFile[curbatch];
801 if (innerFile != NULL)
803 if (BufFileSeek(innerFile, 0, 0L, SEEK_SET))
805 (errcode_for_file_access(),
806 errmsg("could not rewind hash-join temporary file: %m")));
808 while ((slot = ExecHashJoinGetSavedTuple(hjstate,
811 hjstate->hj_HashTupleSlot)))
814 * NOTE: some tuples may be sent to future batches. Also, it is
815 * possible for hashtable->nbatch to be increased here!
817 ExecHashTableInsert(hashtable, slot, hashvalue);
821 * after we build the hash table, the inner batch file is no longer
824 BufFileClose(innerFile);
825 hashtable->innerBatchFile[curbatch] = NULL;
829 * Rewind outer batch file (if present), so that we can start reading it.
831 if (hashtable->outerBatchFile[curbatch] != NULL)
833 if (BufFileSeek(hashtable->outerBatchFile[curbatch], 0, 0L, SEEK_SET))
835 (errcode_for_file_access(),
836 errmsg("could not rewind hash-join temporary file: %m")));
843 * ExecHashJoinSaveTuple
844 * save a tuple to a batch file.
846 * The data recorded in the file for each tuple is its hash value,
847 * then the tuple in MinimalTuple format.
849 * Note: it is important always to call this in the regular executor
850 * context, not in a shorter-lived context; else the temp file buffers
851 * will get messed up.
854 ExecHashJoinSaveTuple(MinimalTuple tuple, uint32 hashvalue,
857 BufFile *file = *fileptr;
862 /* First write to this batch file, so open it. */
863 file = BufFileCreateTemp(false);
867 written = BufFileWrite(file, (void *) &hashvalue, sizeof(uint32));
868 if (written != sizeof(uint32))
870 (errcode_for_file_access(),
871 errmsg("could not write to hash-join temporary file: %m")));
873 written = BufFileWrite(file, (void *) tuple, tuple->t_len);
874 if (written != tuple->t_len)
876 (errcode_for_file_access(),
877 errmsg("could not write to hash-join temporary file: %m")));
881 * ExecHashJoinGetSavedTuple
882 * read the next tuple from a batch file. Return NULL if no more.
884 * On success, *hashvalue is set to the tuple's hash value, and the tuple
885 * itself is stored in the given slot.
887 static TupleTableSlot *
888 ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
891 TupleTableSlot *tupleSlot)
898 * Since both the hash value and the MinimalTuple length word are uint32,
899 * we can read them both in one BufFileRead() call without any type
902 nread = BufFileRead(file, (void *) header, sizeof(header));
903 if (nread == 0) /* end of file */
905 ExecClearTuple(tupleSlot);
908 if (nread != sizeof(header))
910 (errcode_for_file_access(),
911 errmsg("could not read from hash-join temporary file: %m")));
912 *hashvalue = header[0];
913 tuple = (MinimalTuple) palloc(header[1]);
914 tuple->t_len = header[1];
915 nread = BufFileRead(file,
916 (void *) ((char *) tuple + sizeof(uint32)),
917 header[1] - sizeof(uint32));
918 if (nread != header[1] - sizeof(uint32))
920 (errcode_for_file_access(),
921 errmsg("could not read from hash-join temporary file: %m")));
922 return ExecStoreMinimalTuple(tuple, tupleSlot, true);
927 ExecReScanHashJoin(HashJoinState *node)
930 * In a multi-batch join, we currently have to do rescans the hard way,
931 * primarily because batch temp files may have already been released. But
932 * if it's a single-batch join, and there is no parameter change for the
933 * inner subnode, then we can just re-use the existing hash table without
936 if (node->hj_HashTable != NULL)
938 if (node->hj_HashTable->nbatch == 1 &&
939 node->js.ps.righttree->chgParam == NULL)
942 * Okay to reuse the hash table; needn't rescan inner, either.
944 * However, if it's a right/full join, we'd better reset the
945 * inner-tuple match flags contained in the table.
947 if (HJ_FILL_INNER(node))
948 ExecHashTableResetMatchFlags(node->hj_HashTable);
951 * Also, we need to reset our state about the emptiness of the
952 * outer relation, so that the new scan of the outer will update
953 * it correctly if it turns out to be empty this time. (There's no
954 * harm in clearing it now because ExecHashJoin won't need the
955 * info. In the other cases, where the hash table doesn't exist
956 * or we are destroying it, we leave this state alone because
957 * ExecHashJoin will need it the first time through.)
959 node->hj_OuterNotEmpty = false;
961 /* ExecHashJoin can skip the BUILD_HASHTABLE step */
962 node->hj_JoinState = HJ_NEED_NEW_OUTER;
966 /* must destroy and rebuild hash table */
967 ExecHashTableDestroy(node->hj_HashTable);
968 node->hj_HashTable = NULL;
969 node->hj_JoinState = HJ_BUILD_HASHTABLE;
972 * if chgParam of subnode is not null then plan will be re-scanned
973 * by first ExecProcNode.
975 if (node->js.ps.righttree->chgParam == NULL)
976 ExecReScan(node->js.ps.righttree);
980 /* Always reset intra-tuple state */
981 node->hj_CurHashValue = 0;
982 node->hj_CurBucketNo = 0;
983 node->hj_CurSkewBucketNo = INVALID_SKEW_BUCKET_NO;
984 node->hj_CurTuple = NULL;
986 node->js.ps.ps_TupFromTlist = false;
987 node->hj_MatchedOuter = false;
988 node->hj_FirstOuterTupleSlot = NULL;
991 * if chgParam of subnode is not null then plan will be re-scanned by
992 * first ExecProcNode.
994 if (node->js.ps.lefttree->chgParam == NULL)
995 ExecReScan(node->js.ps.lefttree);