nodeHashjoin.c 17.7 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
10
 *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.26 1999/07/17 20:16:58 momjian Exp $
11 12 13
 *
 *-------------------------------------------------------------------------
 */
14
#include <sys/types.h>
15

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

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

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


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

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

79
	/* -----------------
80
	 * get information from HashJoin state
81 82
	 * -----------------
	 */
83
	hashtable = hjstate->hj_HashTable;
84

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

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

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

101
	/* ----------------
102
	 *	if this is the first call, build the hash table for inner relation
103 104
	 * ----------------
	 */
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
	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
			 * ----------------
			 */
122
			hashNode->hashstate->hashtable = hashtable;
123 124 125
			innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node);
		}
		node->hashdone = true;
126 127 128 129
		/* ----------------
		 * Open temp files for outer batches, if needed.
		 * Note that file buffers are palloc'd in regular executor context.
		 * ----------------
130
		 */
131
		for (i = 0; i < hashtable->nbatch; i++)
132
		{
B
Bruce Momjian 已提交
133 134
			File		tfile = OpenTemporaryFile();

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

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

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

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

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

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

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

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

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

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

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

220
			/*
221
			 * we've got a match, but still need to test qpqual
222
			 */
223 224 225 226 227 228 229 230 231 232 233 234 235
			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)
236
			{
237 238 239 240 241 242 243 244 245
				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;
246 247 248 249
			}
		}

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

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

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

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

	node->hashjoinstate = hjstate;

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

296
#define HASHJOIN_NSLOTS 2
297 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
	/* ----------------
	 *	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
	 * ----------------
	 */
	{
324
		HashState  *hashstate = hashNode->hashstate;
325
		TupleTableSlot *slot = hashstate->cstate.cs_ResultTupleSlot;
326 327 328

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

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

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

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

	node->hashdone = false;

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

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

	return TRUE;
358 359 360
}

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

/* ----------------------------------------------------------------
369
 *		ExecEndHashJoin
370
 *
371
 *		clean up routine for HashJoin node
372 373 374
 * ----------------------------------------------------------------
 */
void
375
ExecEndHashJoin(HashJoin *node)
376
{
377
	HashJoinState *hjstate;
378 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

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

421 422 423
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

560 561 562 563 564 565 566
	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]);
567 568 569 570 571

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

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

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

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

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

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

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

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

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

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

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

	if (!node->hashdone)
		return;
645

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

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

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

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

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