nodeHashjoin.c 17.8 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * nodeHashjoin.c
4
 *	  Routines to handle hash join nodes
5 6 7 8 9
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
B
Bruce Momjian 已提交
10
 *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.25 1999/07/16 04:58:49 momjian Exp $
11 12 13
 *
 *-------------------------------------------------------------------------
 */
14
#include <sys/types.h>
15 16
#include <string.h>

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
static TupleTableSlot *ExecHashJoinOuterGetTuple(Plan *node, Plan *parent,
B
Bruce Momjian 已提交
25
						  HashJoinState *hjstate);
26
static TupleTableSlot *ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
27
						  BufFile *file,
B
Bruce Momjian 已提交
28
						  TupleTableSlot *tupleSlot);
29
static int	ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable);
30
static int	ExecHashJoinNewBatch(HashJoinState *hjstate);
31 32


33
/* ----------------------------------------------------------------
34
 *		ExecHashJoin
35
 *
36 37 38 39
 *		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.
40 41
 * ----------------------------------------------------------------
 */
42
TupleTableSlot *				/* return: a tuple or NULL */
43
ExecHashJoin(HashJoin *node)
44
{
45 46 47 48 49 50 51 52
	HashJoinState *hjstate;
	EState	   *estate;
	Plan	   *outerNode;
	Hash	   *hashNode;
	List	   *hjclauses;
	Expr	   *clause;
	List	   *qual;
	ScanDirection dir;
53
	TupleTableSlot *inntuple;
54 55 56 57 58
	Var		   *outerVar;
	ExprContext *econtext;
	HashJoinTable hashtable;
	HeapTuple	curtuple;
	bool		qualResult;
59 60
	TupleTableSlot *outerTupleSlot;
	TupleTableSlot *innerTupleSlot;
61 62 63
	Var		   *innerhashkey;
	int			i;
	bool		hashPhaseDone;
64 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

86 87 88
	/* --------------------
	 * initialize expression context
	 * --------------------
89
	 */
90 91 92 93 94
	econtext = hjstate->jstate.cs_ExprContext;

	if (hjstate->jstate.cs_TupFromTlist)
	{
		TupleTableSlot *result;
95
		bool		isDone;
96 97 98 99

		result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
		if (!isDone)
			return result;
100
	}
101

102
	/* ----------------
103
	 *	if this is the first call, build the hash table for inner relation
104 105
	 * ----------------
	 */
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
	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;
			innerhashkey = hashNode->hashkey;
			hjstate->hj_InnerHashKey = innerhashkey;

			/* ----------------
			 * execute the Hash node, to build the hash table
			 * ----------------
			 */
123
			hashNode->hashstate->hashtable = hashtable;
124 125 126
			innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node);
		}
		node->hashdone = true;
127 128 129 130
		/* ----------------
		 * Open temp files for outer batches, if needed.
		 * Note that file buffers are palloc'd in regular executor context.
		 * ----------------
131
		 */
132
		for (i = 0; i < hashtable->nbatch; i++)
133
		{
B
Bruce Momjian 已提交
134 135
			File		tfile = OpenTemporaryFile();

136 137
			Assert(tfile >= 0);
			hashtable->outerBatchFile[i] = BufFileCreate(tfile);
138 139
		}
	}
140 141
	else if (hashtable == NULL)
		return NULL;
142

143
	/* ----------------
144
	 *	Now get an outer tuple and probe into the hash table for matches
145 146
	 * ----------------
	 */
147 148 149
	outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
	outerVar = get_leftop(clause);

150
	for (;;)
151
	{
B
Bruce Momjian 已提交
152

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

164
				/*
165
				 * when the last batch runs out, clean up and exit
166 167 168 169 170 171 172
				 */
				ExecHashTableDestroy(hashtable);
				hjstate->hj_HashTable = NULL;
				return NULL;
			}

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

181 182 183 184 185 186 187 188
			/* ----------------
			 *	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 已提交
189 190 191
				int			batch = ExecHashJoinGetBatch(hjstate->hj_CurBucketNo,
														 hashtable);

192
				if (batch > 0)
193
				{
B
Bruce Momjian 已提交
194

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

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

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

221
			/*
222
			 * we've got a match, but still need to test qpqual
223
			 */
224 225 226 227 228 229 230 231 232 233 234 235 236
			inntuple = ExecStoreTuple(curtuple,
									  hjstate->hj_HashTupleSlot,
									  InvalidBuffer,
									  false);	/* don't pfree this tuple */
			econtext->ecxt_innertuple = inntuple;
			qualResult = ExecQual(qual, econtext);
			/* ----------------
			 * 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.
			 * ----------------
			 */
			if (qualResult)
237
			{
238 239 240 241 242 243 244 245 246
				ProjectionInfo *projInfo;
				TupleTableSlot *result;
				bool		isDone;

				hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
				projInfo = hjstate->jstate.cs_ProjInfo;
				result = ExecProject(projInfo, &isDone);
				hjstate->jstate.cs_TupFromTlist = !isDone;
				return result;
247 248 249 250
			}
		}

		/* ----------------
251 252
		 *	 Now the current outer tuple has run out of matches,
		 *	 so we free it and loop around to get a new outer tuple.
253 254
		 * ----------------
		 */
255
		ExecClearTuple(outerTupleSlot);
256
	}
257 258 259
}

/* ----------------------------------------------------------------
260
 *		ExecInitHashJoin
261
 *
262
 *		Init routine for HashJoin node.
263 264
 * ----------------------------------------------------------------
 */
265
bool							/* return: initialization status */
266
ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
267
{
268 269 270
	HashJoinState *hjstate;
	Plan	   *outerNode;
	Hash	   *hashNode;
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286

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

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

	node->hashjoinstate = hjstate;

	/* ----------------
287
	 *	Miscellaneous initialization
288 289 290 291 292 293 294 295 296
	 *
	 *		 +	assign node's base_id
	 *		 +	assign debugging hooks and
	 *		 +	create expression context for node
	 * ----------------
	 */
	ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent);
	ExecAssignExprContext(estate, &hjstate->jstate);

297
#define HASHJOIN_NSLOTS 2
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
	/* ----------------
	 *	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
	 * ----------------
	 */
	{
325
		HashState  *hashstate = hashNode->hashstate;
326
		TupleTableSlot *slot = hashstate->cstate.cs_ResultTupleSlot;
327 328 329

		hjstate->hj_HashTupleSlot = slot;
	}
330
	hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
331

332
/*
333
	hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode);
334
*/
335 336 337 338 339 340 341 342 343

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

	/* ----------------
344
	 *	initialize hash-specific info
345 346 347 348 349 350
	 * ----------------
	 */

	node->hashdone = false;

	hjstate->hj_HashTable = (HashJoinTable) NULL;
351 352
	hjstate->hj_CurBucketNo = 0;
	hjstate->hj_CurTuple = (HashJoinTuple) NULL;
353 354 355 356 357 358
	hjstate->hj_InnerHashKey = (Var *) NULL;

	hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
	hjstate->jstate.cs_TupFromTlist = (bool) false;

	return TRUE;
359 360 361
}

int
362
ExecCountSlotsHashJoin(HashJoin *node)
363
{
364
	return ExecCountSlotsNode(outerPlan(node)) +
365
	ExecCountSlotsNode(innerPlan(node)) +
366
	HASHJOIN_NSLOTS;
367 368 369
}

/* ----------------------------------------------------------------
370
 *		ExecEndHashJoin
371
 *
372
 *		clean up routine for HashJoin node
373 374 375
 * ----------------------------------------------------------------
 */
void
376
ExecEndHashJoin(HashJoin *node)
377
{
378
	HashJoinState *hjstate;
379 380 381 382 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 410 411 412 413 414 415 416 417 418 419 420 421

	/* ----------------
	 *	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);

	/* ----------------
	 * 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);

422 423 424
}

/* ----------------------------------------------------------------
425
 *		ExecHashJoinOuterGetTuple
426
 *
427 428 429
 *		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.
430 431 432 433
 * ----------------------------------------------------------------
 */

static TupleTableSlot *
434
ExecHashJoinOuterGetTuple(Plan *node, Plan *parent, HashJoinState *hjstate)
435
{
B
Bruce Momjian 已提交
436 437
	HashJoinTable hashtable = hjstate->hj_HashTable;
	int			curbatch = hashtable->curbatch;
438 439 440 441 442
	TupleTableSlot *slot;

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

446
		/*
B
Bruce Momjian 已提交
447 448
		 * We have just reached the end of the first pass. Try to switch
		 * to a saved batch.
449 450
		 */
		curbatch = ExecHashJoinNewBatch(hjstate);
451 452 453
	}

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

	/* Out of batches... */
	return NULL;
469 470 471
}

/* ----------------------------------------------------------------
472
 *		ExecHashJoinGetSavedTuple
473
 *
474
 *		read the next tuple from a tmp file
475 476 477 478
 * ----------------------------------------------------------------
 */

static TupleTableSlot *
479
ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
480
						  BufFile *file,
481
						  TupleTableSlot *tupleSlot)
482
{
B
Bruce Momjian 已提交
483 484 485
	HeapTupleData htup;
	size_t		nread;
	HeapTuple	heapTuple;
486 487 488 489 490 491 492 493

	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));
B
Bruce Momjian 已提交
494 495
	heapTuple->t_data = (HeapTupleHeader)
		((char *) heapTuple + HEAPTUPLESIZE);
496 497 498 499
	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);
500 501 502
}

/* ----------------------------------------------------------------
503
 *		ExecHashJoinNewBatch
504
 *
505
 *		switch to a new hashjoin batch
506 507
 * ----------------------------------------------------------------
 */
508
static int
509
ExecHashJoinNewBatch(HashJoinState *hjstate)
510
{
511 512 513 514 515
	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 已提交
516
	BufFile    *innerFile;
517
	TupleTableSlot *slot;
518
	ExprContext *econtext;
519
	Var		   *innerhashkey;
520 521 522

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

524
		/*
B
Bruce Momjian 已提交
525 526
		 * We no longer need the previous outer batch file; close it right
		 * away to free disk space.
527
		 */
528 529
		BufFileClose(hashtable->outerBatchFile[newbatch - 2]);
		hashtable->outerBatchFile[newbatch - 2] = NULL;
530 531 532
	}

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

548
	if (newbatch > nbatch)
549
		return newbatch;		/* no more batches */
550

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

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

561 562 563 564 565 566 567
	if (BufFileSeek(innerFile, 0L, SEEK_SET) != 0L)
		elog(ERROR, "Failed to rewind hash temp file");

	/*
	 * Reload the hash table with the new inner batch
	 */
	ExecHashTableReset(hashtable, innerBatchSize[newbatch - 1]);
568 569 570 571 572

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

	while ((slot = ExecHashJoinGetSavedTuple(hjstate,
573 574
											 innerFile,
											 hjstate->hj_HashTupleSlot))
575 576 577
		   && !TupIsNull(slot))
	{
		econtext->ecxt_innertuple = slot;
578
		ExecHashTableInsert(hashtable, econtext, innerhashkey);
579 580 581
	}

	/*
B
Bruce Momjian 已提交
582 583
	 * after we build the hash table, the inner batch file is no longer
	 * needed
584
	 */
585 586
	BufFileClose(innerFile);
	hashtable->innerBatchFile[newbatch - 1] = NULL;
587

588
	hashtable->curbatch = newbatch;
589 590 591 592
	return newbatch;
}

/* ----------------------------------------------------------------
593
 *		ExecHashJoinGetBatch
594
 *
595 596 597 598
 *		determine the batch number for a bucketno
 *		+----------------+-------+-------+ ... +-------+
 *		0			  nbuckets						 totalbuckets
 * batch		 0			 1		 2	   ...
599 600
 * ----------------------------------------------------------------
 */
601
static int
602
ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable)
603
{
604
	int			b;
605

606
	if (bucketno < hashtable->nbuckets || hashtable->nbatch == 0)
607 608
		return 0;

609 610
	b = (hashtable->nbatch * (bucketno - hashtable->nbuckets)) /
		(hashtable->totalbuckets - hashtable->nbuckets);
611
	return b + 1;
612 613 614
}

/* ----------------------------------------------------------------
615
 *		ExecHashJoinSaveTuple
616
 *
617 618 619 620 621
 *		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.
622 623 624
 * ----------------------------------------------------------------
 */

625
void
626
ExecHashJoinSaveTuple(HeapTuple heapTuple,
627
					  BufFile *file)
628
{
B
Bruce Momjian 已提交
629
	size_t		written;
630 631 632 633 634 635 636

	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");
637
}
V
Vadim B. Mikheev 已提交
638 639 640 641

void
ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
{
642
	HashJoinState *hjstate = node->hashjoinstate;
V
Vadim B. Mikheev 已提交
643 644 645

	if (!node->hashdone)
		return;
646

V
Vadim B. Mikheev 已提交
647
	node->hashdone = false;
648 649 650 651

	/*
	 * Unfortunately, currently we have to destroy hashtable in all
	 * cases...
V
Vadim B. Mikheev 已提交
652 653 654 655 656 657
	 */
	if (hjstate->hj_HashTable)
	{
		ExecHashTableDestroy(hjstate->hj_HashTable);
		hjstate->hj_HashTable = NULL;
	}
658 659 660

	hjstate->hj_CurBucketNo = 0;
	hjstate->hj_CurTuple = (HashJoinTuple) NULL;
V
Vadim B. Mikheev 已提交
661 662 663 664
	hjstate->hj_InnerHashKey = (Var *) NULL;

	hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
	hjstate->jstate.cs_TupFromTlist = (bool) false;
665 666 667 668

	/*
	 * if chgParam of subnodes is not null then plans will be re-scanned
	 * by first ExecProcNode.
V
Vadim B. Mikheev 已提交
669
	 */
670 671 672 673
	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 已提交
674
}