nodeHashjoin.c 18.2 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * nodeHashjoin.c
4
 *	  Routines to handle hash join nodes
5
 *
B
Add:  
Bruce Momjian 已提交
6 7
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994, Regents of the University of California
8 9 10
 *
 *
 * IDENTIFICATION
11
 *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.33 2000/08/24 03:29:03 tgl Exp $
12 13 14
 *
 *-------------------------------------------------------------------------
 */
15
#include <sys/types.h>
16

B
Bruce Momjian 已提交
17
#include "postgres.h"
18 19

#include "executor/executor.h"
20 21
#include "executor/nodeHash.h"
#include "executor/nodeHashjoin.h"
B
Bruce Momjian 已提交
22
#include "optimizer/clauses.h"
23 24
#include "utils/memutils.h"

25

26
static TupleTableSlot *ExecHashJoinOuterGetTuple(Plan *node, Plan *parent,
B
Bruce Momjian 已提交
27
						  HashJoinState *hjstate);
28
static TupleTableSlot *ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
29
						  BufFile *file,
B
Bruce Momjian 已提交
30
						  TupleTableSlot *tupleSlot);
31
static int	ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable);
32
static int	ExecHashJoinNewBatch(HashJoinState *hjstate);
33 34


35
/* ----------------------------------------------------------------
36
 *		ExecHashJoin
37
 *
38 39 40 41
 *		This function implements the Hybrid Hashjoin algorithm.
 *		recursive partitioning remains to be added.
 *		Note: the relation we build hash table on is the inner
 *			  the other one is outer.
42 43
 * ----------------------------------------------------------------
 */
44
TupleTableSlot *				/* return: a tuple or NULL */
45
ExecHashJoin(HashJoin *node)
46
{
47 48 49 50 51 52 53 54
	HashJoinState *hjstate;
	EState	   *estate;
	Plan	   *outerNode;
	Hash	   *hashNode;
	List	   *hjclauses;
	Expr	   *clause;
	List	   *qual;
	ScanDirection dir;
55
	TupleTableSlot *inntuple;
56
	Node	   *outerVar;
57
	ExprContext *econtext;
58
	ExprDoneCond isDone;
59 60
	HashJoinTable hashtable;
	HeapTuple	curtuple;
61 62
	TupleTableSlot *outerTupleSlot;
	TupleTableSlot *innerTupleSlot;
63 64
	int			i;
	bool		hashPhaseDone;
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

	/* ----------------
	 *	get information from HashJoin node
	 * ----------------
	 */
	hjstate = node->hashjoinstate;
	hjclauses = node->hashclauses;
	clause = lfirst(hjclauses);
	estate = node->join.state;
	qual = node->join.qual;
	hashNode = (Hash *) innerPlan(node);
	outerNode = outerPlan(node);
	hashPhaseDone = node->hashdone;
	dir = estate->es_direction;

80
	/* -----------------
81
	 * get information from HashJoin state
82 83
	 * -----------------
	 */
84
	hashtable = hjstate->hj_HashTable;
85
	econtext = hjstate->jstate.cs_ExprContext;
86

87 88 89 90 91 92
	/* ----------------
	 *	Check to see if we're still projecting out tuples from a previous
	 *	join tuple (because there is a function-returning-set in the
	 *	projection expressions).  If so, try to project another one.
	 * ----------------
	 */
93 94 95 96 97
	if (hjstate->jstate.cs_TupFromTlist)
	{
		TupleTableSlot *result;

		result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
98
		if (isDone == ExprMultipleResult)
99
			return result;
100 101
		/* Done with that source tuple... */
		hjstate->jstate.cs_TupFromTlist = false;
102
	}
103

104 105 106 107 108 109 110 111
	/* ----------------
	 *	Reset per-tuple memory context to free any expression evaluation
	 *	storage allocated in the previous tuple cycle.  Note this can't
	 *	happen until we're done projecting out tuples from a join tuple.
	 * ----------------
	 */
	ResetExprContext(econtext);

112
	/* ----------------
113
	 *	if this is the first call, build the hash table for inner relation
114 115
	 * ----------------
	 */
116 117 118 119 120 121 122 123 124 125
	if (!hashPhaseDone)
	{							/* if the hash phase not completed */
		if (hashtable == NULL)
		{						/* if the hash table has not been created */
			/* ----------------
			 * create the hash table
			 * ----------------
			 */
			hashtable = ExecHashTableCreate(hashNode);
			hjstate->hj_HashTable = hashtable;
126
			hjstate->hj_InnerHashKey = hashNode->hashkey;
127 128 129 130 131

			/* ----------------
			 * execute the Hash node, to build the hash table
			 * ----------------
			 */
132
			hashNode->hashstate->hashtable = hashtable;
133 134 135
			innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node);
		}
		node->hashdone = true;
136 137 138 139
		/* ----------------
		 * Open temp files for outer batches, if needed.
		 * Note that file buffers are palloc'd in regular executor context.
		 * ----------------
140
		 */
141
		for (i = 0; i < hashtable->nbatch; i++)
142
			hashtable->outerBatchFile[i] = BufFileCreateTemp();
143
	}
144 145
	else if (hashtable == NULL)
		return NULL;
146

147
	/* ----------------
148
	 *	Now get an outer tuple and probe into the hash table for matches
149 150
	 * ----------------
	 */
151
	outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
152
	outerVar = (Node *) get_leftop(clause);
153

154
	for (;;)
155
	{
B
Bruce Momjian 已提交
156

157
		/*
158
		 * if the current outer tuple is nil, get a new one
159
		 */
160
		if (TupIsNull(outerTupleSlot))
161
		{
162 163 164 165
			outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode,
													   (Plan *) node,
													   hjstate);
			if (TupIsNull(outerTupleSlot))
166
			{
B
Bruce Momjian 已提交
167

168
				/*
169
				 * when the last batch runs out, clean up and exit
170 171 172 173 174 175 176
				 */
				ExecHashTableDestroy(hashtable);
				hjstate->hj_HashTable = NULL;
				return NULL;
			}

			/*
B
Bruce Momjian 已提交
177 178
			 * now we have an outer tuple, find the corresponding bucket
			 * for this tuple from the hash table
179
			 */
180 181 182 183
			econtext->ecxt_outertuple = outerTupleSlot;
			hjstate->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext,
														outerVar);
			hjstate->hj_CurTuple = NULL;
184

185 186 187 188 189 190 191 192
			/* ----------------
			 *	Now we've got an outer tuple and the corresponding hash bucket,
			 *	but this tuple may not belong to the current batch.
			 *	This need only be checked in the first pass.
			 * ----------------
			 */
			if (hashtable->curbatch == 0)
			{
B
Bruce Momjian 已提交
193 194 195
				int			batch = ExecHashJoinGetBatch(hjstate->hj_CurBucketNo,
														 hashtable);

196
				if (batch > 0)
197
				{
B
Bruce Momjian 已提交
198

199
					/*
200 201
					 * Need to postpone this outer tuple to a later batch.
					 * Save it in the corresponding outer-batch file.
202
					 */
B
Bruce Momjian 已提交
203 204
					int			batchno = batch - 1;

205 206
					hashtable->outerBatchSize[batchno]++;
					ExecHashJoinSaveTuple(outerTupleSlot->val,
B
Bruce Momjian 已提交
207
									 hashtable->outerBatchFile[batchno]);
208
					ExecClearTuple(outerTupleSlot);
B
Bruce Momjian 已提交
209
					continue;	/* loop around for a new outer tuple */
210 211 212 213
				}
			}
		}

214 215
		/*
		 * OK, scan the selected hash bucket for matches
216
		 */
217
		for (;;)
218
		{
219 220 221 222 223
			curtuple = ExecScanHashBucket(hjstate,
										  hjclauses,
										  econtext);
			if (curtuple == NULL)
				break;			/* out of matches */
B
Bruce Momjian 已提交
224

225
			/*
226
			 * we've got a match, but still need to test qpqual
227
			 */
228 229 230 231 232
			inntuple = ExecStoreTuple(curtuple,
									  hjstate->hj_HashTupleSlot,
									  InvalidBuffer,
									  false);	/* don't pfree this tuple */
			econtext->ecxt_innertuple = inntuple;
233 234 235 236

			/* reset temp memory each time to avoid leaks from qpqual */
			ResetExprContext(econtext);

237 238 239 240 241 242
			/* ----------------
			 * if we pass the qual, then save state for next call and
			 * have ExecProject form the projection, store it
			 * in the tuple table, and return the slot.
			 * ----------------
			 */
243
			if (ExecQual(qual, econtext, false))
244
			{
245 246 247
				TupleTableSlot *result;

				hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
248 249 250 251 252 253
				result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
				if (isDone != ExprEndResult)
				{
					hjstate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
					return result;
				}
254 255 256 257
			}
		}

		/* ----------------
258 259
		 *	 Now the current outer tuple has run out of matches,
		 *	 so we free it and loop around to get a new outer tuple.
260 261
		 * ----------------
		 */
262
		ExecClearTuple(outerTupleSlot);
263
	}
264 265 266
}

/* ----------------------------------------------------------------
267
 *		ExecInitHashJoin
268
 *
269
 *		Init routine for HashJoin node.
270 271
 * ----------------------------------------------------------------
 */
272
bool							/* return: initialization status */
273
ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
274
{
275 276 277
	HashJoinState *hjstate;
	Plan	   *outerNode;
	Hash	   *hashNode;
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293

	/* ----------------
	 *	assign the node's execution state
	 * ----------------
	 */
	node->join.state = estate;

	/* ----------------
	 * create state structure
	 * ----------------
	 */
	hjstate = makeNode(HashJoinState);

	node->hashjoinstate = hjstate;

	/* ----------------
294
	 *	Miscellaneous initialization
295 296 297 298 299 300
	 *
	 *		 +	create expression context for node
	 * ----------------
	 */
	ExecAssignExprContext(estate, &hjstate->jstate);

301
#define HASHJOIN_NSLOTS 2
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
	/* ----------------
	 *	tuple table initialization
	 * ----------------
	 */
	ExecInitResultTupleSlot(estate, &hjstate->jstate);
	ExecInitOuterTupleSlot(estate, hjstate);

	/* ----------------
	 * initializes child nodes
	 * ----------------
	 */
	outerNode = outerPlan((Plan *) node);
	hashNode = (Hash *) innerPlan((Plan *) node);

	ExecInitNode(outerNode, estate, (Plan *) node);
	ExecInitNode((Plan *) hashNode, estate, (Plan *) node);

	/* ----------------
	 *	now for some voodoo.  our temporary tuple slot
	 *	is actually the result tuple slot of the Hash node
	 *	(which is our inner plan).	we do this because Hash
	 *	nodes don't return tuples via ExecProcNode() -- instead
	 *	the hash join node uses ExecScanHashBucket() to get
	 *	at the contents of the hash table.	-cim 6/9/91
	 * ----------------
	 */
	{
329
		HashState  *hashstate = hashNode->hashstate;
330
		TupleTableSlot *slot = hashstate->cstate.cs_ResultTupleSlot;
331 332 333

		hjstate->hj_HashTupleSlot = slot;
	}
334
	hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
335

336
/*
337
	hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode);
338
*/
339 340 341 342 343 344 345 346 347

	/* ----------------
	 *	initialize tuple type and projection info
	 * ----------------
	 */
	ExecAssignResultTypeFromTL((Plan *) node, &hjstate->jstate);
	ExecAssignProjectionInfo((Plan *) node, &hjstate->jstate);

	/* ----------------
348
	 *	initialize hash-specific info
349 350 351 352 353 354
	 * ----------------
	 */

	node->hashdone = false;

	hjstate->hj_HashTable = (HashJoinTable) NULL;
355 356
	hjstate->hj_CurBucketNo = 0;
	hjstate->hj_CurTuple = (HashJoinTuple) NULL;
357
	hjstate->hj_InnerHashKey = (Node *) NULL;
358 359

	hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
360
	hjstate->jstate.cs_TupFromTlist = false;
361 362

	return TRUE;
363 364 365
}

int
366
ExecCountSlotsHashJoin(HashJoin *node)
367
{
368
	return ExecCountSlotsNode(outerPlan(node)) +
369
	ExecCountSlotsNode(innerPlan(node)) +
370
	HASHJOIN_NSLOTS;
371 372 373
}

/* ----------------------------------------------------------------
374
 *		ExecEndHashJoin
375
 *
376
 *		clean up routine for HashJoin node
377 378 379
 * ----------------------------------------------------------------
 */
void
380
ExecEndHashJoin(HashJoin *node)
381
{
382
	HashJoinState *hjstate;
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409

	/* ----------------
	 *	get info from the HashJoin state
	 * ----------------
	 */
	hjstate = node->hashjoinstate;

	/* ----------------
	 * free hash table in case we end plan before all tuples are retrieved
	 * ---------------
	 */
	if (hjstate->hj_HashTable)
	{
		ExecHashTableDestroy(hjstate->hj_HashTable);
		hjstate->hj_HashTable = NULL;
	}

	/* ----------------
	 *	Free the projection info and the scan attribute info
	 *
	 *	Note: we don't ExecFreeResultType(hjstate)
	 *		  because the rule manager depends on the tupType
	 *		  returned by ExecMain().  So for now, this
	 *		  is freed at end-transaction time.  -cim 6/2/91
	 * ----------------
	 */
	ExecFreeProjectionInfo(&hjstate->jstate);
410
	ExecFreeExprContext(&hjstate->jstate);
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426

	/* ----------------
	 * clean up subtrees
	 * ----------------
	 */
	ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
	ExecEndNode(innerPlan((Plan *) node), (Plan *) node);

	/* ----------------
	 *	clean out the tuple table
	 * ----------------
	 */
	ExecClearTuple(hjstate->jstate.cs_ResultTupleSlot);
	ExecClearTuple(hjstate->hj_OuterTupleSlot);
	ExecClearTuple(hjstate->hj_HashTupleSlot);

427 428 429
}

/* ----------------------------------------------------------------
430
 *		ExecHashJoinOuterGetTuple
431
 *
432 433 434
 *		get the next outer tuple for hashjoin: either by
 *		executing a plan node as in the first pass, or from
 *		the tmp files for the hashjoin batches.
435 436 437 438
 * ----------------------------------------------------------------
 */

static TupleTableSlot *
439
ExecHashJoinOuterGetTuple(Plan *node, Plan *parent, HashJoinState *hjstate)
440
{
B
Bruce Momjian 已提交
441 442
	HashJoinTable hashtable = hjstate->hj_HashTable;
	int			curbatch = hashtable->curbatch;
443 444 445 446 447
	TupleTableSlot *slot;

	if (curbatch == 0)
	{							/* if it is the first pass */
		slot = ExecProcNode(node, parent);
B
Bruce Momjian 已提交
448
		if (!TupIsNull(slot))
449
			return slot;
B
Bruce Momjian 已提交
450

451
		/*
B
Bruce Momjian 已提交
452 453
		 * We have just reached the end of the first pass. Try to switch
		 * to a saved batch.
454 455
		 */
		curbatch = ExecHashJoinNewBatch(hjstate);
456 457 458
	}

	/*
B
Bruce Momjian 已提交
459 460
	 * Try to read from a temp file. Loop allows us to advance to new
	 * batch as needed.
461
	 */
462 463 464
	while (curbatch <= hashtable->nbatch)
	{
		slot = ExecHashJoinGetSavedTuple(hjstate,
B
Bruce Momjian 已提交
465
								 hashtable->outerBatchFile[curbatch - 1],
466
										 hjstate->hj_OuterTupleSlot);
B
Bruce Momjian 已提交
467
		if (!TupIsNull(slot))
468 469 470 471 472 473
			return slot;
		curbatch = ExecHashJoinNewBatch(hjstate);
	}

	/* Out of batches... */
	return NULL;
474 475 476
}

/* ----------------------------------------------------------------
477
 *		ExecHashJoinGetSavedTuple
478
 *
479
 *		read the next tuple from a tmp file
480 481 482 483
 * ----------------------------------------------------------------
 */

static TupleTableSlot *
484
ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
485
						  BufFile *file,
486
						  TupleTableSlot *tupleSlot)
487
{
B
Bruce Momjian 已提交
488 489 490
	HeapTupleData htup;
	size_t		nread;
	HeapTuple	heapTuple;
491 492 493 494 495 496 497 498

	nread = BufFileRead(file, (void *) &htup, sizeof(HeapTupleData));
	if (nread == 0)
		return NULL;			/* end of file */
	if (nread != sizeof(HeapTupleData))
		elog(ERROR, "Read from hashjoin temp file failed");
	heapTuple = palloc(HEAPTUPLESIZE + htup.t_len);
	memcpy((char *) heapTuple, (char *) &htup, sizeof(HeapTupleData));
499
	heapTuple->t_datamcxt = CurrentMemoryContext;
B
Bruce Momjian 已提交
500 501
	heapTuple->t_data = (HeapTupleHeader)
		((char *) heapTuple + HEAPTUPLESIZE);
502 503 504 505
	nread = BufFileRead(file, (void *) heapTuple->t_data, htup.t_len);
	if (nread != (size_t) htup.t_len)
		elog(ERROR, "Read from hashjoin temp file failed");
	return ExecStoreTuple(heapTuple, tupleSlot, InvalidBuffer, true);
506 507 508
}

/* ----------------------------------------------------------------
509
 *		ExecHashJoinNewBatch
510
 *
511
 *		switch to a new hashjoin batch
512 513
 * ----------------------------------------------------------------
 */
514
static int
515
ExecHashJoinNewBatch(HashJoinState *hjstate)
516
{
517 518 519 520 521
	HashJoinTable hashtable = hjstate->hj_HashTable;
	int			nbatch = hashtable->nbatch;
	int			newbatch = hashtable->curbatch + 1;
	long	   *innerBatchSize = hashtable->innerBatchSize;
	long	   *outerBatchSize = hashtable->outerBatchSize;
B
Bruce Momjian 已提交
522
	BufFile    *innerFile;
523
	TupleTableSlot *slot;
524
	ExprContext *econtext;
525
	Node	   *innerhashkey;
526 527 528

	if (newbatch > 1)
	{
B
Bruce Momjian 已提交
529

530
		/*
B
Bruce Momjian 已提交
531 532
		 * We no longer need the previous outer batch file; close it right
		 * away to free disk space.
533
		 */
534 535
		BufFileClose(hashtable->outerBatchFile[newbatch - 2]);
		hashtable->outerBatchFile[newbatch - 2] = NULL;
536 537 538
	}

	/* --------------
539 540
	 *	We can skip over any batches that are empty on either side.
	 *	Release associated temp files right away.
541
	 * --------------
542
	 */
543 544 545
	while (newbatch <= nbatch &&
		   (innerBatchSize[newbatch - 1] == 0L ||
			outerBatchSize[newbatch - 1] == 0L))
546
	{
547 548 549 550
		BufFileClose(hashtable->innerBatchFile[newbatch - 1]);
		hashtable->innerBatchFile[newbatch - 1] = NULL;
		BufFileClose(hashtable->outerBatchFile[newbatch - 1]);
		hashtable->outerBatchFile[newbatch - 1] = NULL;
551
		newbatch++;
552
	}
553

554
	if (newbatch > nbatch)
555
		return newbatch;		/* no more batches */
556

557
	/*
B
Bruce Momjian 已提交
558 559
	 * Rewind inner and outer batch files for this batch, so that we can
	 * start reading them.
560
	 */
561
	if (BufFileSeek(hashtable->outerBatchFile[newbatch - 1], 0, 0L, SEEK_SET))
562 563 564
		elog(ERROR, "Failed to rewind hash temp file");

	innerFile = hashtable->innerBatchFile[newbatch - 1];
565

566
	if (BufFileSeek(innerFile, 0, 0L, SEEK_SET))
567 568 569 570 571 572
		elog(ERROR, "Failed to rewind hash temp file");

	/*
	 * Reload the hash table with the new inner batch
	 */
	ExecHashTableReset(hashtable, innerBatchSize[newbatch - 1]);
573 574 575 576 577

	econtext = hjstate->jstate.cs_ExprContext;
	innerhashkey = hjstate->hj_InnerHashKey;

	while ((slot = ExecHashJoinGetSavedTuple(hjstate,
578 579
											 innerFile,
											 hjstate->hj_HashTupleSlot))
580 581 582
		   && !TupIsNull(slot))
	{
		econtext->ecxt_innertuple = slot;
583
		ExecHashTableInsert(hashtable, econtext, innerhashkey);
584 585 586
	}

	/*
B
Bruce Momjian 已提交
587 588
	 * after we build the hash table, the inner batch file is no longer
	 * needed
589
	 */
590 591
	BufFileClose(innerFile);
	hashtable->innerBatchFile[newbatch - 1] = NULL;
592

593
	hashtable->curbatch = newbatch;
594 595 596 597
	return newbatch;
}

/* ----------------------------------------------------------------
598
 *		ExecHashJoinGetBatch
599
 *
600 601 602 603
 *		determine the batch number for a bucketno
 *		+----------------+-------+-------+ ... +-------+
 *		0			  nbuckets						 totalbuckets
 * batch		 0			 1		 2	   ...
604 605
 * ----------------------------------------------------------------
 */
606
static int
607
ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable)
608
{
609
	int			b;
610

611
	if (bucketno < hashtable->nbuckets || hashtable->nbatch == 0)
612 613
		return 0;

614 615
	b = (hashtable->nbatch * (bucketno - hashtable->nbuckets)) /
		(hashtable->totalbuckets - hashtable->nbuckets);
616
	return b + 1;
617 618 619
}

/* ----------------------------------------------------------------
620
 *		ExecHashJoinSaveTuple
621
 *
622 623 624 625 626
 *		save a tuple to a tmp file.
 *
 * The data recorded in the file for each tuple is an image of its
 * HeapTupleData (with meaningless t_data pointer) followed by the
 * HeapTupleHeader and tuple data.
627 628 629
 * ----------------------------------------------------------------
 */

630
void
631
ExecHashJoinSaveTuple(HeapTuple heapTuple,
632
					  BufFile *file)
633
{
B
Bruce Momjian 已提交
634
	size_t		written;
635 636 637 638 639 640 641

	written = BufFileWrite(file, (void *) heapTuple, sizeof(HeapTupleData));
	if (written != sizeof(HeapTupleData))
		elog(ERROR, "Write to hashjoin temp file failed");
	written = BufFileWrite(file, (void *) heapTuple->t_data, heapTuple->t_len);
	if (written != (size_t) heapTuple->t_len)
		elog(ERROR, "Write to hashjoin temp file failed");
642
}
V
Vadim B. Mikheev 已提交
643 644 645 646

void
ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
{
647
	HashJoinState *hjstate = node->hashjoinstate;
V
Vadim B. Mikheev 已提交
648 649 650

	if (!node->hashdone)
		return;
651

V
Vadim B. Mikheev 已提交
652
	node->hashdone = false;
653 654 655 656

	/*
	 * Unfortunately, currently we have to destroy hashtable in all
	 * cases...
V
Vadim B. Mikheev 已提交
657 658 659 660 661 662
	 */
	if (hjstate->hj_HashTable)
	{
		ExecHashTableDestroy(hjstate->hj_HashTable);
		hjstate->hj_HashTable = NULL;
	}
663 664 665

	hjstate->hj_CurBucketNo = 0;
	hjstate->hj_CurTuple = (HashJoinTuple) NULL;
666
	hjstate->hj_InnerHashKey = (Node *) NULL;
V
Vadim B. Mikheev 已提交
667 668

	hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
669
	hjstate->jstate.cs_TupFromTlist = false;
670 671 672 673

	/*
	 * if chgParam of subnodes is not null then plans will be re-scanned
	 * by first ExecProcNode.
V
Vadim B. Mikheev 已提交
674
	 */
675 676 677 678
	if (((Plan *) node)->lefttree->chgParam == NULL)
		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
	if (((Plan *) node)->righttree->chgParam == NULL)
		ExecReScan(((Plan *) node)->righttree, exprCtxt, (Plan *) node);
V
Vadim B. Mikheev 已提交
679
}