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.27 1999/10/13 15:02:25 tgl 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
			hashtable->outerBatchFile[i] = BufFileCreateTemp();
133
	}
134 135
	else if (hashtable == NULL)
		return NULL;
136

137
	/* ----------------
138
	 *	Now get an outer tuple and probe into the hash table for matches
139 140
	 * ----------------
	 */
141 142 143
	outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
	outerVar = get_leftop(clause);

144
	for (;;)
145
	{
B
Bruce Momjian 已提交
146

147
		/*
148
		 * if the current outer tuple is nil, get a new one
149
		 */
150
		if (TupIsNull(outerTupleSlot))
151
		{
152 153 154 155
			outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode,
													   (Plan *) node,
													   hjstate);
			if (TupIsNull(outerTupleSlot))
156
			{
B
Bruce Momjian 已提交
157

158
				/*
159
				 * when the last batch runs out, clean up and exit
160 161 162 163 164 165 166
				 */
				ExecHashTableDestroy(hashtable);
				hjstate->hj_HashTable = NULL;
				return NULL;
			}

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

175 176 177 178 179 180 181 182
			/* ----------------
			 *	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 已提交
183 184 185
				int			batch = ExecHashJoinGetBatch(hjstate->hj_CurBucketNo,
														 hashtable);

186
				if (batch > 0)
187
				{
B
Bruce Momjian 已提交
188

189
					/*
190 191
					 * Need to postpone this outer tuple to a later batch.
					 * Save it in the corresponding outer-batch file.
192
					 */
B
Bruce Momjian 已提交
193 194
					int			batchno = batch - 1;

195 196
					hashtable->outerBatchSize[batchno]++;
					ExecHashJoinSaveTuple(outerTupleSlot->val,
B
Bruce Momjian 已提交
197
									 hashtable->outerBatchFile[batchno]);
198
					ExecClearTuple(outerTupleSlot);
B
Bruce Momjian 已提交
199
					continue;	/* loop around for a new outer tuple */
200 201 202 203
				}
			}
		}

204 205
		/*
		 * OK, scan the selected hash bucket for matches
206
		 */
207
		for (;;)
208
		{
209 210 211 212 213
			curtuple = ExecScanHashBucket(hjstate,
										  hjclauses,
										  econtext);
			if (curtuple == NULL)
				break;			/* out of matches */
B
Bruce Momjian 已提交
214

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

		/* ----------------
245 246
		 *	 Now the current outer tuple has run out of matches,
		 *	 so we free it and loop around to get a new outer tuple.
247 248
		 * ----------------
		 */
249
		ExecClearTuple(outerTupleSlot);
250
	}
251 252 253
}

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

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

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

	node->hashjoinstate = hjstate;

	/* ----------------
281
	 *	Miscellaneous initialization
282 283 284 285 286 287 288 289 290
	 *
	 *		 +	assign node's base_id
	 *		 +	assign debugging hooks and
	 *		 +	create expression context for node
	 * ----------------
	 */
	ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent);
	ExecAssignExprContext(estate, &hjstate->jstate);

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

		hjstate->hj_HashTupleSlot = slot;
	}
324
	hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
325

326
/*
327
	hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode);
328
*/
329 330 331 332 333 334 335 336 337

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

	/* ----------------
338
	 *	initialize hash-specific info
339 340 341 342 343 344
	 * ----------------
	 */

	node->hashdone = false;

	hjstate->hj_HashTable = (HashJoinTable) NULL;
345 346
	hjstate->hj_CurBucketNo = 0;
	hjstate->hj_CurTuple = (HashJoinTuple) NULL;
347 348 349 350 351 352
	hjstate->hj_InnerHashKey = (Var *) NULL;

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

	return TRUE;
353 354 355
}

int
356
ExecCountSlotsHashJoin(HashJoin *node)
357
{
358
	return ExecCountSlotsNode(outerPlan(node)) +
359
	ExecCountSlotsNode(innerPlan(node)) +
360
	HASHJOIN_NSLOTS;
361 362 363
}

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

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

416 417 418
}

/* ----------------------------------------------------------------
419
 *		ExecHashJoinOuterGetTuple
420
 *
421 422 423
 *		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.
424 425 426 427
 * ----------------------------------------------------------------
 */

static TupleTableSlot *
428
ExecHashJoinOuterGetTuple(Plan *node, Plan *parent, HashJoinState *hjstate)
429
{
B
Bruce Momjian 已提交
430 431
	HashJoinTable hashtable = hjstate->hj_HashTable;
	int			curbatch = hashtable->curbatch;
432 433 434 435 436
	TupleTableSlot *slot;

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

440
		/*
B
Bruce Momjian 已提交
441 442
		 * We have just reached the end of the first pass. Try to switch
		 * to a saved batch.
443 444
		 */
		curbatch = ExecHashJoinNewBatch(hjstate);
445 446 447
	}

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

	/* Out of batches... */
	return NULL;
463 464 465
}

/* ----------------------------------------------------------------
466
 *		ExecHashJoinGetSavedTuple
467
 *
468
 *		read the next tuple from a tmp file
469 470 471 472
 * ----------------------------------------------------------------
 */

static TupleTableSlot *
473
ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
474
						  BufFile *file,
475
						  TupleTableSlot *tupleSlot)
476
{
B
Bruce Momjian 已提交
477 478 479
	HeapTupleData htup;
	size_t		nread;
	HeapTuple	heapTuple;
480 481 482 483 484 485 486 487

	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 已提交
488 489
	heapTuple->t_data = (HeapTupleHeader)
		((char *) heapTuple + HEAPTUPLESIZE);
490 491 492 493
	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);
494 495 496
}

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

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

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

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

542
	if (newbatch > nbatch)
543
		return newbatch;		/* no more batches */
544

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

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

554
	if (BufFileSeek(innerFile, 0, 0L, SEEK_SET))
555 556 557 558 559 560
		elog(ERROR, "Failed to rewind hash temp file");

	/*
	 * Reload the hash table with the new inner batch
	 */
	ExecHashTableReset(hashtable, innerBatchSize[newbatch - 1]);
561 562 563 564 565

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

	while ((slot = ExecHashJoinGetSavedTuple(hjstate,
566 567
											 innerFile,
											 hjstate->hj_HashTupleSlot))
568 569 570
		   && !TupIsNull(slot))
	{
		econtext->ecxt_innertuple = slot;
571
		ExecHashTableInsert(hashtable, econtext, innerhashkey);
572 573 574
	}

	/*
B
Bruce Momjian 已提交
575 576
	 * after we build the hash table, the inner batch file is no longer
	 * needed
577
	 */
578 579
	BufFileClose(innerFile);
	hashtable->innerBatchFile[newbatch - 1] = NULL;
580

581
	hashtable->curbatch = newbatch;
582 583 584 585
	return newbatch;
}

/* ----------------------------------------------------------------
586
 *		ExecHashJoinGetBatch
587
 *
588 589 590 591
 *		determine the batch number for a bucketno
 *		+----------------+-------+-------+ ... +-------+
 *		0			  nbuckets						 totalbuckets
 * batch		 0			 1		 2	   ...
592 593
 * ----------------------------------------------------------------
 */
594
static int
595
ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable)
596
{
597
	int			b;
598

599
	if (bucketno < hashtable->nbuckets || hashtable->nbatch == 0)
600 601
		return 0;

602 603
	b = (hashtable->nbatch * (bucketno - hashtable->nbuckets)) /
		(hashtable->totalbuckets - hashtable->nbuckets);
604
	return b + 1;
605 606 607
}

/* ----------------------------------------------------------------
608
 *		ExecHashJoinSaveTuple
609
 *
610 611 612 613 614
 *		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.
615 616 617
 * ----------------------------------------------------------------
 */

618
void
619
ExecHashJoinSaveTuple(HeapTuple heapTuple,
620
					  BufFile *file)
621
{
B
Bruce Momjian 已提交
622
	size_t		written;
623 624 625 626 627 628 629

	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");
630
}
V
Vadim B. Mikheev 已提交
631 632 633 634

void
ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
{
635
	HashJoinState *hjstate = node->hashjoinstate;
V
Vadim B. Mikheev 已提交
636 637 638

	if (!node->hashdone)
		return;
639

V
Vadim B. Mikheev 已提交
640
	node->hashdone = false;
641 642 643 644

	/*
	 * Unfortunately, currently we have to destroy hashtable in all
	 * cases...
V
Vadim B. Mikheev 已提交
645 646 647 648 649 650
	 */
	if (hjstate->hj_HashTable)
	{
		ExecHashTableDestroy(hjstate->hj_HashTable);
		hjstate->hj_HashTable = NULL;
	}
651 652 653

	hjstate->hj_CurBucketNo = 0;
	hjstate->hj_CurTuple = (HashJoinTuple) NULL;
V
Vadim B. Mikheev 已提交
654 655 656 657
	hjstate->hj_InnerHashKey = (Var *) NULL;

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

	/*
	 * if chgParam of subnodes is not null then plans will be re-scanned
	 * by first ExecProcNode.
V
Vadim B. Mikheev 已提交
662
	 */
663 664 665 666
	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 已提交
667
}