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.29 2000/01/19 23:54:55 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
	Var		   *outerVar;
	ExprContext *econtext;
	HashJoinTable hashtable;
	HeapTuple	curtuple;
57 58
	TupleTableSlot *outerTupleSlot;
	TupleTableSlot *innerTupleSlot;
59 60 61
	Var		   *innerhashkey;
	int			i;
	bool		hashPhaseDone;
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

214
			/*
215
			 * we've got a match, but still need to test qpqual
216
			 */
217 218 219 220 221 222 223 224 225 226 227
			inntuple = ExecStoreTuple(curtuple,
									  hjstate->hj_HashTupleSlot,
									  InvalidBuffer,
									  false);	/* don't pfree this tuple */
			econtext->ecxt_innertuple = inntuple;
			/* ----------------
			 * 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.
			 * ----------------
			 */
228
			if (ExecQual(qual, econtext, false))
229
			{
230 231 232 233 234 235 236 237 238
				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;
239 240 241 242
			}
		}

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

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

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

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

	node->hashjoinstate = hjstate;

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

289
#define HASHJOIN_NSLOTS 2
290 291 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
	/* ----------------
	 *	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
	 * ----------------
	 */
	{
317
		HashState  *hashstate = hashNode->hashstate;
318
		TupleTableSlot *slot = hashstate->cstate.cs_ResultTupleSlot;
319 320 321

		hjstate->hj_HashTupleSlot = slot;
	}
322
	hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
323

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

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

	/* ----------------
336
	 *	initialize hash-specific info
337 338 339 340 341 342
	 * ----------------
	 */

	node->hashdone = false;

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

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

	return TRUE;
351 352 353
}

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

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

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

414 415 416
}

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

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

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

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

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

	/* Out of batches... */
	return NULL;
461 462 463
}

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

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

	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));
486
	heapTuple->t_datamcxt = CurrentMemoryContext;
B
Bruce Momjian 已提交
487 488
	heapTuple->t_data = (HeapTupleHeader)
		((char *) heapTuple + HEAPTUPLESIZE);
489 490 491 492
	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);
493 494 495
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	if (!node->hashdone)
		return;
638

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

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

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

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

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