setrefs.c 40 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * setrefs.c
4 5
 *	  Post-processing of a completed plan tree: fix references to subplan
 *	  vars, and compute regproc values for operators
6
 *
7
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
Bruce Momjian's avatar
Add:  
Bruce Momjian committed
8
 * Portions Copyright (c) 1994, Regents of the University of California
9 10 11
 *
 *
 * IDENTIFICATION
12
 *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.114 2005/09/05 18:59:38 tgl Exp $
13 14 15 16 17
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

18
#include "nodes/makefuncs.h"
19 20 21
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/tlist.h"
22
#include "optimizer/var.h"
23
#include "parser/parse_expr.h"
24
#include "parser/parsetree.h"
25
#include "utils/lsyscache.h"
26

27

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
typedef struct
{
	Index		varno;			/* RT index of Var */
	AttrNumber	varattno;		/* attr number of Var */
	AttrNumber	resno;			/* TLE position of Var */
} tlist_vinfo;

typedef struct
{
	List	   *tlist;			/* underlying target list */
	int			num_vars;		/* number of plain Var tlist entries */
	bool		has_non_vars;	/* are there non-plain-Var entries? */
	/* array of num_vars entries: */
	tlist_vinfo	vars[1];		/* VARIABLE LENGTH ARRAY */
} indexed_tlist;				/* VARIABLE LENGTH STRUCT */

44 45
typedef struct
{
46
	List	   *rtable;
47 48
	indexed_tlist *outer_itlist;
	indexed_tlist *inner_itlist;
49 50
	Index		acceptable_rel;
} join_references_context;
51

52 53
typedef struct
{
54
	indexed_tlist *subplan_itlist;
55 56 57
	Index		subvarno;
} replace_vars_with_subplan_refs_context;

58 59 60 61 62
static Plan *set_subqueryscan_references(SubqueryScan *plan, List *rtable);
static bool trivial_subqueryscan(SubqueryScan *plan);
static void adjust_plan_varnos(Plan *plan, int rtoffset);
static void adjust_expr_varnos(Node *node, int rtoffset);
static bool adjust_expr_varnos_walker(Node *node, int *context);
63
static void fix_expr_references(Plan *plan, Node *node);
64
static bool fix_expr_references_walker(Node *node, void *context);
65
static void set_join_references(Join *join, List *rtable);
66 67
static void set_inner_join_references(Plan *inner_plan,
									  List *rtable,
68
									  indexed_tlist *outer_itlist);
69
static void set_uppernode_references(Plan *plan, Index subvarno);
70 71 72 73 74 75 76
static indexed_tlist *build_tlist_index(List *tlist);
static Var *search_indexed_tlist_for_var(Var *var,
										 indexed_tlist *itlist,
										 Index newvarno);
static Var *search_indexed_tlist_for_non_var(Node *node,
											 indexed_tlist *itlist,
											 Index newvarno);
77
static List *join_references(List *clauses,
78 79 80 81
							 List *rtable,
							 indexed_tlist *outer_itlist,
							 indexed_tlist *inner_itlist,
							 Index acceptable_rel);
82
static Node *join_references_mutator(Node *node,
83
						join_references_context *context);
84
static Node *replace_vars_with_subplan_refs(Node *node,
85 86
											indexed_tlist *subplan_itlist,
											Index subvarno);
87
static Node *replace_vars_with_subplan_refs_mutator(Node *node,
88
						replace_vars_with_subplan_refs_context *context);
89
static bool fix_opfuncids_walker(Node *node, void *context);
90
static void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
91

92 93

/*****************************************************************************
94 95 96
 *
 *		SUBPLAN REFERENCES
 *
97 98
 *****************************************************************************/

99
/*
100
 * set_plan_references
101
 *
102 103 104 105 106
 * This is the final processing pass of the planner/optimizer.  The plan
 * tree is complete; we just have to adjust some representational details
 * for the convenience of the executor.  We update Vars in upper plan nodes
 * to refer to the outputs of their subplans, and we compute regproc OIDs
 * for operators (ie, we look up the function that implements each op).
107
 *
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
 * We also perform one final optimization step, which is to delete
 * SubqueryScan plan nodes that aren't doing anything useful (ie, have
 * no qual and a no-op targetlist).  The reason for doing this last is that
 * it can't readily be done before set_plan_references, because it would
 * break set_uppernode_references: the Vars in the subquery's top tlist
 * won't match up with the Vars in the outer plan tree.  The SubqueryScan
 * serves a necessary function as a buffer between outer query and subquery
 * variable numbering ... but the executor doesn't care about that, only the
 * planner.
 *
 * set_plan_references recursively traverses the whole plan tree.
 *
 * The return value is normally the same Plan node passed in, but can be
 * different when the passed-in Plan is a SubqueryScan we decide isn't needed.
 *
 * Note: to delete a SubqueryScan, we have to renumber Vars in its child nodes
 * and append the modified subquery rangetable to the outer rangetable.
 * Therefore "rtable" is an in/out argument and really should be declared
 * "List **".  But in the interest of notational simplicity we don't do that.
 * (Since rtable can't be NIL if there's a SubqueryScan, the list header
 * address won't change when we append a subquery rangetable.)
129
 */
130
Plan *
131
set_plan_references(Plan *plan, List *rtable)
132
{
Bruce Momjian's avatar
Bruce Momjian committed
133
	ListCell   *l;
134

135
	if (plan == NULL)
136
		return NULL;
137

138 139 140 141 142 143
	/*
	 * Plan-type-specific fixes
	 */
	switch (nodeTag(plan))
	{
		case T_SeqScan:
144 145
			fix_expr_references(plan, (Node *) plan->targetlist);
			fix_expr_references(plan, (Node *) plan->qual);
146 147
			break;
		case T_IndexScan:
148 149 150
			fix_expr_references(plan, (Node *) plan->targetlist);
			fix_expr_references(plan, (Node *) plan->qual);
			fix_expr_references(plan,
151
								(Node *) ((IndexScan *) plan)->indexqual);
152
			fix_expr_references(plan,
153
							(Node *) ((IndexScan *) plan)->indexqualorig);
154
			break;
155 156 157 158 159
		case T_BitmapIndexScan:
			/* no need to fix targetlist and qual */
			Assert(plan->targetlist == NIL);
			Assert(plan->qual == NIL);
			fix_expr_references(plan,
160
							(Node *) ((BitmapIndexScan *) plan)->indexqual);
161
			fix_expr_references(plan,
162
						(Node *) ((BitmapIndexScan *) plan)->indexqualorig);
163 164 165 166 167 168 169
			break;
		case T_BitmapHeapScan:
			fix_expr_references(plan, (Node *) plan->targetlist);
			fix_expr_references(plan, (Node *) plan->qual);
			fix_expr_references(plan,
						(Node *) ((BitmapHeapScan *) plan)->bitmapqualorig);
			break;
170 171 172
		case T_TidScan:
			fix_expr_references(plan, (Node *) plan->targetlist);
			fix_expr_references(plan, (Node *) plan->qual);
173 174
			fix_expr_references(plan,
								(Node *) ((TidScan *) plan)->tideval);
175
			break;
176
		case T_SubqueryScan:
177 178
			/* Needs special treatment, see comments below */
			return set_subqueryscan_references((SubqueryScan *) plan, rtable);
179
		case T_FunctionScan:
180 181 182 183 184 185
			{
				RangeTblEntry *rte;

				fix_expr_references(plan, (Node *) plan->targetlist);
				fix_expr_references(plan, (Node *) plan->qual);
				rte = rt_fetch(((FunctionScan *) plan)->scan.scanrelid,
186
							   rtable);
187 188 189
				Assert(rte->rtekind == RTE_FUNCTION);
				fix_expr_references(plan, rte->funcexpr);
			}
190
			break;
191
		case T_NestLoop:
192
			set_join_references((Join *) plan, rtable);
193 194
			fix_expr_references(plan, (Node *) plan->targetlist);
			fix_expr_references(plan, (Node *) plan->qual);
195
			fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
196 197
			break;
		case T_MergeJoin:
198
			set_join_references((Join *) plan, rtable);
199 200
			fix_expr_references(plan, (Node *) plan->targetlist);
			fix_expr_references(plan, (Node *) plan->qual);
201
			fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
202
			fix_expr_references(plan,
203
							(Node *) ((MergeJoin *) plan)->mergeclauses);
204 205
			break;
		case T_HashJoin:
206
			set_join_references((Join *) plan, rtable);
207 208
			fix_expr_references(plan, (Node *) plan->targetlist);
			fix_expr_references(plan, (Node *) plan->qual);
209
			fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
210
			fix_expr_references(plan,
211
							  (Node *) ((HashJoin *) plan)->hashclauses);
212
			break;
213
		case T_Hash:
214 215 216
		case T_Material:
		case T_Sort:
		case T_Unique:
217
		case T_SetOp:
218 219 220

			/*
			 * These plan types don't actually bother to evaluate their
221 222 223 224 225 226 227 228
			 * targetlists (because they just return their unmodified
			 * input tuples).  The optimizer is lazy about creating really
			 * valid targetlists for them --- it tends to just put in a
			 * pointer to the child plan node's tlist.  Hence, we leave
			 * the tlist alone.  In particular, we do not want to process
			 * subplans in the tlist, since we will likely end up reprocessing
			 * subplans that also appear in lower levels of the plan tree!
			 *
229 230
			 * Since these plan types don't check quals either, we should
			 * not find any qual expression attached to them.
231
			 */
232
			Assert(plan->qual == NIL);
233
			break;
234
		case T_Limit:
Bruce Momjian's avatar
Bruce Momjian committed
235

236
			/*
Bruce Momjian's avatar
Bruce Momjian committed
237 238 239
			 * Like the plan types above, Limit doesn't evaluate its tlist
			 * or quals.  It does have live expressions for limit/offset,
			 * however.
240
			 */
241
			Assert(plan->qual == NIL);
242 243 244
			fix_expr_references(plan, ((Limit *) plan)->limitOffset);
			fix_expr_references(plan, ((Limit *) plan)->limitCount);
			break;
245 246 247
		case T_Agg:
		case T_Group:
			set_uppernode_references(plan, (Index) 0);
248 249
			fix_expr_references(plan, (Node *) plan->targetlist);
			fix_expr_references(plan, (Node *) plan->qual);
250 251
			break;
		case T_Result:
252 253 254

			/*
			 * Result may or may not have a subplan; no need to fix up
255 256 257 258 259 260
			 * subplan references if it hasn't got one...
			 *
			 * XXX why does Result use a different subvarno from Agg/Group?
			 */
			if (plan->lefttree != NULL)
				set_uppernode_references(plan, (Index) OUTER);
261 262 263
			fix_expr_references(plan, (Node *) plan->targetlist);
			fix_expr_references(plan, (Node *) plan->qual);
			fix_expr_references(plan, ((Result *) plan)->resconstantqual);
264 265
			break;
		case T_Append:
Bruce Momjian's avatar
Bruce Momjian committed
266

267 268
			/*
			 * Append, like Sort et al, doesn't actually evaluate its
269 270 271
			 * targetlist or check quals, and we haven't bothered to give it
			 * its own tlist copy. So, don't fix targetlist/qual. But do
			 * recurse into child plans.
272
			 */
273
			Assert(plan->qual == NIL);
274
			foreach(l, ((Append *) plan)->appendplans)
275
				lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
276
			break;
277
		case T_BitmapAnd:
278 279 280
			/* BitmapAnd works like Append, but has no tlist */
			Assert(plan->targetlist == NIL);
			Assert(plan->qual == NIL);
281
			foreach(l, ((BitmapAnd *) plan)->bitmapplans)
282
				lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
283 284
			break;
		case T_BitmapOr:
285 286 287
			/* BitmapOr works like Append, but has no tlist */
			Assert(plan->targetlist == NIL);
			Assert(plan->qual == NIL);
288
			foreach(l, ((BitmapOr *) plan)->bitmapplans)
289
				lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
290
			break;
291
		default:
292 293
			elog(ERROR, "unrecognized node type: %d",
				 (int) nodeTag(plan));
294 295
			break;
	}
296

297
	/*
298
	 * Now recurse into child plans and initplans, if any
299
	 *
300
	 * NOTE: it is essential that we recurse into child plans AFTER we set
301 302
	 * subplan references in this plan's tlist and quals.  If we did the
	 * reference-adjustments bottom-up, then we would fail to match this
303
	 * plan's var nodes against the already-modified nodes of the
304
	 * children.  Fortunately, that consideration doesn't apply to SubPlan
305
	 * nodes; else we'd need two passes over the expression trees.
306
	 */
307 308
	plan->lefttree = set_plan_references(plan->lefttree, rtable);
	plan->righttree = set_plan_references(plan->righttree, rtable);
Bruce Momjian's avatar
Bruce Momjian committed
309

310
	foreach(l, plan->initPlan)
311
	{
312
		SubPlan    *sp = (SubPlan *) lfirst(l);
313

314
		Assert(IsA(sp, SubPlan));
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
		sp->plan = set_plan_references(sp->plan, sp->rtable);
	}

	return plan;
}

/*
 * set_subqueryscan_references
 *		Do set_plan_references processing on a SubqueryScan
 *
 * We try to strip out the SubqueryScan entirely; if we can't, we have
 * to do the normal processing on it.
 */
static Plan *
set_subqueryscan_references(SubqueryScan *plan, List *rtable)
{
	Plan	   *result;
	RangeTblEntry *rte;
	ListCell   *l;

	/* First, recursively process the subplan */
	rte = rt_fetch(plan->scan.scanrelid, rtable);
	Assert(rte->rtekind == RTE_SUBQUERY);
	plan->subplan = set_plan_references(plan->subplan,
										rte->subquery->rtable);

	/*
	 * We have to process any initplans too; set_plan_references can't do
	 * it for us because of the possibility of double-processing.
	 */
	foreach(l, plan->scan.plan.initPlan)
	{
		SubPlan    *sp = (SubPlan *) lfirst(l);

		Assert(IsA(sp, SubPlan));
		sp->plan = set_plan_references(sp->plan, sp->rtable);
	}

	if (trivial_subqueryscan(plan))
	{
		/*
		 * We can omit the SubqueryScan node and just pull up the subplan.
		 * We have to merge its rtable into the outer rtable, which means
		 * adjusting varnos throughout the subtree.
		 */
		int		rtoffset = list_length(rtable);
		List   *sub_rtable;

		sub_rtable = copyObject(rte->subquery->rtable);
		range_table_walker(sub_rtable,
						   adjust_expr_varnos_walker,
						   (void *) &rtoffset,
						   QTW_IGNORE_RT_SUBQUERIES);
		rtable = list_concat(rtable, sub_rtable);

370 371 372 373 374 375
		/*
		 * we have to copy the subplan to make sure there are no duplicately
		 * linked nodes in it, else adjust_plan_varnos might increment some
		 * varnos twice
		 */
		result = copyObject(plan->subplan);
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 416

		adjust_plan_varnos(result, rtoffset);

		result->initPlan = list_concat(plan->scan.plan.initPlan,
									   result->initPlan);
	}
	else
	{
		/*
		 * Keep the SubqueryScan node.  We have to do the processing that
		 * set_plan_references would otherwise have done on it.  Notice
		 * we do not do set_uppernode_references() here, because a
		 * SubqueryScan will always have been created with correct
		 * references to its subplan's outputs to begin with.
		 */
		result = (Plan *) plan;

		fix_expr_references(result, (Node *) result->targetlist);
		fix_expr_references(result, (Node *) result->qual);
	}

	return result;
}

/*
 * trivial_subqueryscan
 *		Detect whether a SubqueryScan can be deleted from the plan tree.
 *
 * We can delete it if it has no qual to check and the targetlist just
 * regurgitates the output of the child plan.
 */
static bool
trivial_subqueryscan(SubqueryScan *plan)
{
	int			attrno;
	ListCell   *lp,
			   *lc;

	if (plan->scan.plan.qual != NIL)
		return false;

417 418 419 420
	if (list_length(plan->scan.plan.targetlist) !=
		list_length(plan->subplan->targetlist))
		return false;			/* tlists not same length */

421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
	attrno = 1;
	forboth(lp, plan->scan.plan.targetlist, lc, plan->subplan->targetlist)
	{
		TargetEntry *ptle = (TargetEntry *) lfirst(lp);
		TargetEntry *ctle = (TargetEntry *) lfirst(lc);
		Var		   *var = (Var *) ptle->expr;

		if (ptle->resjunk != ctle->resjunk)
			return false;		/* tlist doesn't match junk status */
		if (!var || !IsA(var, Var))
			return false;		/* tlist item not a Var */
		Assert(var->varno == plan->scan.scanrelid);
		Assert(var->varlevelsup == 0);
		if (var->varattno != attrno)
			return false;		/* out of order */
		attrno++;
	}

	return true;
}

/*
 * adjust_plan_varnos
 *		Offset varnos and other rangetable indexes in a plan tree by rtoffset.
 */
static void
adjust_plan_varnos(Plan *plan, int rtoffset)
{
	ListCell   *l;

	if (plan == NULL)
		return;

	/*
	 * Plan-type-specific fixes
	 */
	switch (nodeTag(plan))
	{
		case T_SeqScan:
			((SeqScan *) plan)->scanrelid += rtoffset;
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			adjust_expr_varnos((Node *) plan->qual, rtoffset);
			break;
		case T_IndexScan:
			((IndexScan *) plan)->scan.scanrelid += rtoffset;
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			adjust_expr_varnos((Node *) plan->qual, rtoffset);
			adjust_expr_varnos((Node *) ((IndexScan *) plan)->indexqual,
							   rtoffset);
			adjust_expr_varnos((Node *) ((IndexScan *) plan)->indexqualorig,
							   rtoffset);
			break;
		case T_BitmapIndexScan:
			((BitmapIndexScan *) plan)->scan.scanrelid += rtoffset;
			/* no need to fix targetlist and qual */
			Assert(plan->targetlist == NIL);
			Assert(plan->qual == NIL);
			adjust_expr_varnos((Node *) ((BitmapIndexScan *) plan)->indexqual,
							   rtoffset);
			adjust_expr_varnos((Node *) ((BitmapIndexScan *) plan)->indexqualorig,
							   rtoffset);
			break;
		case T_BitmapHeapScan:
			((BitmapHeapScan *) plan)->scan.scanrelid += rtoffset;
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			adjust_expr_varnos((Node *) plan->qual, rtoffset);
			adjust_expr_varnos((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig,
							   rtoffset);
			break;
		case T_TidScan:
			((TidScan *) plan)->scan.scanrelid += rtoffset;
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			adjust_expr_varnos((Node *) plan->qual, rtoffset);
			adjust_expr_varnos((Node *) ((TidScan *) plan)->tideval,
							   rtoffset);
			break;
		case T_SubqueryScan:
			((SubqueryScan *) plan)->scan.scanrelid += rtoffset;
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			adjust_expr_varnos((Node *) plan->qual, rtoffset);
			/* we should not recurse into the subquery! */
			break;
		case T_FunctionScan:
			((FunctionScan *) plan)->scan.scanrelid += rtoffset;
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			adjust_expr_varnos((Node *) plan->qual, rtoffset);
			/* rte was already fixed by set_subqueryscan_references */
			break;
		case T_NestLoop:
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			adjust_expr_varnos((Node *) plan->qual, rtoffset);
			adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset);
			break;
		case T_MergeJoin:
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			adjust_expr_varnos((Node *) plan->qual, rtoffset);
			adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset);
			adjust_expr_varnos((Node *) ((MergeJoin *) plan)->mergeclauses,
							   rtoffset);
			break;
		case T_HashJoin:
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			adjust_expr_varnos((Node *) plan->qual, rtoffset);
			adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset);
			adjust_expr_varnos((Node *) ((HashJoin *) plan)->hashclauses,
							   rtoffset);
			break;
		case T_Hash:
		case T_Material:
		case T_Sort:
		case T_Unique:
		case T_SetOp:

			/*
			 * Even though the targetlist won't be used by the executor,
			 * we fix it up for possible use by EXPLAIN (not to mention
			 * ease of debugging --- wrong varnos are very confusing).
			 */
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			Assert(plan->qual == NIL);
			break;
		case T_Limit:

			/*
			 * Like the plan types above, Limit doesn't evaluate its tlist
			 * or quals.  It does have live expressions for limit/offset,
			 * however.
			 */
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			Assert(plan->qual == NIL);
			adjust_expr_varnos(((Limit *) plan)->limitOffset, rtoffset);
			adjust_expr_varnos(((Limit *) plan)->limitCount, rtoffset);
			break;
		case T_Agg:
		case T_Group:
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			adjust_expr_varnos((Node *) plan->qual, rtoffset);
			break;
		case T_Result:
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			adjust_expr_varnos((Node *) plan->qual, rtoffset);
			adjust_expr_varnos(((Result *) plan)->resconstantqual, rtoffset);
			break;
		case T_Append:
			adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
			Assert(plan->qual == NIL);
			foreach(l, ((Append *) plan)->appendplans)
				adjust_plan_varnos((Plan *) lfirst(l), rtoffset);
			break;
		case T_BitmapAnd:
			/* BitmapAnd works like Append, but has no tlist */
			Assert(plan->targetlist == NIL);
			Assert(plan->qual == NIL);
			foreach(l, ((BitmapAnd *) plan)->bitmapplans)
				adjust_plan_varnos((Plan *) lfirst(l), rtoffset);
			break;
		case T_BitmapOr:
			/* BitmapOr works like Append, but has no tlist */
			Assert(plan->targetlist == NIL);
			Assert(plan->qual == NIL);
			foreach(l, ((BitmapOr *) plan)->bitmapplans)
				adjust_plan_varnos((Plan *) lfirst(l), rtoffset);
			break;
		default:
			elog(ERROR, "unrecognized node type: %d",
				 (int) nodeTag(plan));
			break;
	}

	/*
	 * Now recurse into child plans.
	 *
	 * We don't need to (and in fact mustn't) recurse into subqueries,
	 * so no need to examine initPlan list.
	 */
	adjust_plan_varnos(plan->lefttree, rtoffset);
	adjust_plan_varnos(plan->righttree, rtoffset);
}

/*
 * adjust_expr_varnos
 *		Offset varnos of Vars in an expression by rtoffset.
 *
 * This is different from the rewriter's OffsetVarNodes in that it has to
 * work on an already-planned expression tree; in particular, we should not
 * disturb INNER and OUTER references.  On the other hand, we don't have to
 * recurse into subqueries nor deal with outer-level Vars, so it's pretty
 * simple.
 */
static void
adjust_expr_varnos(Node *node, int rtoffset)
{
	/* This tree walk requires no special setup, so away we go... */
	adjust_expr_varnos_walker(node, &rtoffset);
}

static bool
adjust_expr_varnos_walker(Node *node, int *context)
{
	if (node == NULL)
		return false;
	if (IsA(node, Var))
	{
		Var		   *var = (Var *) node;

		Assert(var->varlevelsup == 0);
		if (var->varno > 0 && var->varno != INNER && var->varno != OUTER)
			var->varno += *context;
		if (var->varnoold > 0)
			var->varnoold += *context;
		return false;
632
	}
633 634
	return expression_tree_walker(node, adjust_expr_varnos_walker,
								  (void *) context);
635 636
}

637 638 639 640
/*
 * fix_expr_references
 *	  Do final cleanup on expressions (targetlists or quals).
 *
641 642
 * This consists of looking up operator opcode info for OpExpr nodes
 * and recursively performing set_plan_references on subplans.
643 644
 *
 * The Plan argument is currently unused, but might be needed again someday.
645 646 647 648
 */
static void
fix_expr_references(Plan *plan, Node *node)
{
649 650 651 652 653 654 655 656 657
	/* This tree walk requires no special setup, so away we go... */
	fix_expr_references_walker(node, NULL);
}

static bool
fix_expr_references_walker(Node *node, void *context)
{
	if (node == NULL)
		return false;
658 659 660
	if (IsA(node, OpExpr))
		set_opfuncid((OpExpr *) node);
	else if (IsA(node, DistinctExpr))
Bruce Momjian's avatar
Bruce Momjian committed
661
		set_opfuncid((OpExpr *) node);	/* rely on struct equivalence */
662 663
	else if (IsA(node, ScalarArrayOpExpr))
		set_sa_opfuncid((ScalarArrayOpExpr *) node);
664
	else if (IsA(node, NullIfExpr))
Bruce Momjian's avatar
Bruce Momjian committed
665
		set_opfuncid((OpExpr *) node);	/* rely on struct equivalence */
666
	else if (IsA(node, SubPlan))
667
	{
Bruce Momjian's avatar
Bruce Momjian committed
668
		SubPlan    *sp = (SubPlan *) node;
669

670
		sp->plan = set_plan_references(sp->plan, sp->rtable);
671 672
	}
	return expression_tree_walker(node, fix_expr_references_walker, context);
673 674
}

675
/*
676
 * set_join_references
677 678 679 680
 *	  Modifies the target list and quals of a join node to reference its
 *	  subplans, by setting the varnos to OUTER or INNER and setting attno
 *	  values to the result domain number of either the corresponding outer
 *	  or inner join tuple item.
681
 *
682 683
 * In the case of a nestloop with inner indexscan, we will also need to
 * apply the same transformation to any outer vars appearing in the
684
 * quals of the child indexscan.  set_inner_join_references does that.
685
 *
686 687
 *	'join' is a join plan node
 *	'rtable' is the associated range table
688 689
 */
static void
690
set_join_references(Join *join, List *rtable)
691
{
692 693
	Plan	   *outer_plan = join->plan.lefttree;
	Plan	   *inner_plan = join->plan.righttree;
694 695
	indexed_tlist *outer_itlist;
	indexed_tlist *inner_itlist;
696

697 698
	outer_itlist = build_tlist_index(outer_plan->targetlist);
	inner_itlist = build_tlist_index(inner_plan->targetlist);
699

700
	/* All join plans have tlist, qual, and joinqual */
701
	join->plan.targetlist = join_references(join->plan.targetlist,
702
											rtable,
703 704 705
											outer_itlist,
											inner_itlist,
											(Index) 0);
706 707
	join->plan.qual = join_references(join->plan.qual,
									  rtable,
708 709 710
									  outer_itlist,
									  inner_itlist,
									  (Index) 0);
711 712
	join->joinqual = join_references(join->joinqual,
									 rtable,
713 714 715
									 outer_itlist,
									 inner_itlist,
									 (Index) 0);
716 717 718 719

	/* Now do join-type-specific stuff */
	if (IsA(join, NestLoop))
	{
720 721 722
		/* This processing is split out to handle possible recursion */
		set_inner_join_references(inner_plan,
								  rtable,
723
								  outer_itlist);
724 725 726 727 728 729 730
	}
	else if (IsA(join, MergeJoin))
	{
		MergeJoin  *mj = (MergeJoin *) join;

		mj->mergeclauses = join_references(mj->mergeclauses,
										   rtable,
731 732 733
										   outer_itlist,
										   inner_itlist,
										   (Index) 0);
734 735 736 737 738 739 740
	}
	else if (IsA(join, HashJoin))
	{
		HashJoin   *hj = (HashJoin *) join;

		hj->hashclauses = join_references(hj->hashclauses,
										  rtable,
741 742 743
										  outer_itlist,
										  inner_itlist,
										  (Index) 0);
744
	}
745 746 747

	pfree(outer_itlist);
	pfree(inner_itlist);
748 749
}

750 751 752 753 754 755 756 757 758 759 760
/*
 * set_inner_join_references
 *		Handle join references appearing in an inner indexscan's quals
 *
 * To handle bitmap-scan plan trees, we have to be able to recurse down
 * to the bottom BitmapIndexScan nodes, so this is split out as a separate
 * function.
 */
static void
set_inner_join_references(Plan *inner_plan,
						  List *rtable,
761
						  indexed_tlist *outer_itlist)
762 763 764 765 766 767 768 769 770 771
{
	if (IsA(inner_plan, IndexScan))
	{
		/*
		 * An index is being used to reduce the number of tuples
		 * scanned in the inner relation.  If there are join clauses
		 * being used with the index, we must update their outer-rel
		 * var nodes to refer to the outer side of the join.
		 */
		IndexScan  *innerscan = (IndexScan *) inner_plan;
772
		List	   *indexqualorig = innerscan->indexqualorig;
773

774 775
		/* No work needed if indexqual refers only to its own rel... */
		if (NumRelids((Node *) indexqualorig) > 1)
776 777 778 779
		{
			Index		innerrel = innerscan->scan.scanrelid;

			/* only refs to outer vars get changed in the inner qual */
780 781
			innerscan->indexqualorig = join_references(indexqualorig,
													   rtable,
782 783 784
													   outer_itlist,
													   NULL,
													   innerrel);
785 786
			innerscan->indexqual = join_references(innerscan->indexqual,
												   rtable,
787 788 789
												   outer_itlist,
												   NULL,
												   innerrel);
790 791 792 793

			/*
			 * We must fix the inner qpqual too, if it has join
			 * clauses (this could happen if special operators are
794
			 * involved: some indexquals may get rechecked as qpquals).
795 796 797 798
			 */
			if (NumRelids((Node *) inner_plan->qual) > 1)
				inner_plan->qual = join_references(inner_plan->qual,
												   rtable,
799 800 801
												   outer_itlist,
												   NULL,
												   innerrel);
802 803 804 805 806 807 808 809
		}
	}
	else if (IsA(inner_plan, BitmapIndexScan))
	{
		/*
		 * Same, but index is being used within a bitmap plan.
		 */
		BitmapIndexScan *innerscan = (BitmapIndexScan *) inner_plan;
810
		List	   *indexqualorig = innerscan->indexqualorig;
811

812 813
		/* No work needed if indexqual refers only to its own rel... */
		if (NumRelids((Node *) indexqualorig) > 1)
814 815 816 817
		{
			Index		innerrel = innerscan->scan.scanrelid;

			/* only refs to outer vars get changed in the inner qual */
818 819
			innerscan->indexqualorig = join_references(indexqualorig,
													   rtable,
820 821 822
													   outer_itlist,
													   NULL,
													   innerrel);
823 824
			innerscan->indexqual = join_references(innerscan->indexqual,
												   rtable,
825 826 827
												   outer_itlist,
												   NULL,
												   innerrel);
828 829 830 831 832 833 834 835 836
			/* no need to fix inner qpqual */
			Assert(inner_plan->qual == NIL);
		}
	}
	else if (IsA(inner_plan, BitmapHeapScan))
	{
		/*
		 * The inner side is a bitmap scan plan.  Fix the top node,
		 * and recurse to get the lower nodes.
837 838 839
		 *
		 * Note: create_bitmap_scan_plan removes clauses from bitmapqualorig
		 * if they are duplicated in qpqual, so must test these independently.
840 841
		 */
		BitmapHeapScan *innerscan = (BitmapHeapScan *) inner_plan;
842
		Index		innerrel = innerscan->scan.scanrelid;
843 844
		List	   *bitmapqualorig = innerscan->bitmapqualorig;

845
		/* only refs to outer vars get changed in the inner qual */
846 847
		if (NumRelids((Node *) bitmapqualorig) > 1)
			innerscan->bitmapqualorig = join_references(bitmapqualorig,
848 849 850 851
														rtable,
														outer_itlist,
														NULL,
														innerrel);
852

853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868
		/*
		 * We must fix the inner qpqual too, if it has join
		 * clauses (this could happen if special operators are
		 * involved: some indexquals may get rechecked as qpquals).
		 */
		if (NumRelids((Node *) inner_plan->qual) > 1)
			inner_plan->qual = join_references(inner_plan->qual,
											   rtable,
											   outer_itlist,
											   NULL,
											   innerrel);

		/* Now recurse */
		set_inner_join_references(inner_plan->lefttree,
								  rtable,
								  outer_itlist);
869 870 871 872 873 874 875 876 877 878 879
	}
	else if (IsA(inner_plan, BitmapAnd))
	{
		/* All we need do here is recurse */
		BitmapAnd *innerscan = (BitmapAnd *) inner_plan;
		ListCell *l;

		foreach(l, innerscan->bitmapplans)
		{
			set_inner_join_references((Plan *) lfirst(l),
									  rtable,
880
									  outer_itlist);
881 882 883 884 885 886 887 888 889 890 891 892
		}
	}
	else if (IsA(inner_plan, BitmapOr))
	{
		/* All we need do here is recurse */
		BitmapOr *innerscan = (BitmapOr *) inner_plan;
		ListCell *l;

		foreach(l, innerscan->bitmapplans)
		{
			set_inner_join_references((Plan *) lfirst(l),
									  rtable,
893
									  outer_itlist);
894 895 896 897 898 899 900 901 902
		}
	}
	else if (IsA(inner_plan, TidScan))
	{
		TidScan    *innerscan = (TidScan *) inner_plan;
		Index		innerrel = innerscan->scan.scanrelid;

		innerscan->tideval = join_references(innerscan->tideval,
											 rtable,
903 904 905
											 outer_itlist,
											 NULL,
											 innerrel);
906 907 908
	}
}

909
/*
910 911 912
 * set_uppernode_references
 *	  Update the targetlist and quals of an upper-level plan node
 *	  to refer to the tuples returned by its lefttree subplan.
913
 *
914
 * This is used for single-input plan types like Agg, Group, Result.
915 916 917 918 919
 *
 * In most cases, we have to match up individual Vars in the tlist and
 * qual expressions with elements of the subplan's tlist (which was
 * generated by flatten_tlist() from these selfsame expressions, so it
 * should have all the required variables).  There is an important exception,
920 921 922 923
 * however: GROUP BY and ORDER BY expressions will have been pushed into the
 * subplan tlist unflattened.  If these values are also needed in the output
 * then we want to reference the subplan tlist element rather than recomputing
 * the expression.
924 925
 */
static void
926
set_uppernode_references(Plan *plan, Index subvarno)
927
{
928
	Plan	   *subplan = plan->lefttree;
929 930
	indexed_tlist *subplan_itlist;
	List	   *output_targetlist;
931
	ListCell   *l;
932

933
	if (subplan != NULL)
934
		subplan_itlist = build_tlist_index(subplan->targetlist);
935
	else
936
		subplan_itlist = build_tlist_index(NIL);
937 938 939 940 941 942 943

	output_targetlist = NIL;
	foreach(l, plan->targetlist)
	{
		TargetEntry *tle = (TargetEntry *) lfirst(l);
		Node	   *newexpr;

944
		newexpr = replace_vars_with_subplan_refs((Node *) tle->expr,
945 946
												 subplan_itlist,
												 subvarno);
947 948 949
		tle = flatCopyTargetEntry(tle);
		tle->expr = (Expr *) newexpr;
		output_targetlist = lappend(output_targetlist, tle);
950 951
	}
	plan->targetlist = output_targetlist;
952 953 954

	plan->qual = (List *)
		replace_vars_with_subplan_refs((Node *) plan->qual,
955 956 957 958
									   subplan_itlist,
									   subvarno);

	pfree(subplan_itlist);
959 960
}

961
/*
962
 * build_tlist_index --- build an index data structure for a child tlist
963
 *
964 965 966 967 968 969 970 971 972
 * In most cases, subplan tlists will be "flat" tlists with only Vars,
 * so we try to optimize that case by extracting information about Vars
 * in advance.  Matching a parent tlist to a child is still an O(N^2)
 * operation, but at least with a much smaller constant factor than plain
 * tlist_member() searches.
 *
 * The result of this function is an indexed_tlist struct to pass to
 * search_indexed_tlist_for_var() or search_indexed_tlist_for_non_var().
 * When done, the indexed_tlist may be freed with a single pfree().
973
 */
974 975
static indexed_tlist *
build_tlist_index(List *tlist)
976
{
977 978
	indexed_tlist *itlist;
	tlist_vinfo *vinfo;
979
	ListCell   *l;
980

981 982 983 984 985 986 987 988 989 990
	/* Create data structure with enough slots for all tlist entries */
	itlist = (indexed_tlist *)
		palloc(offsetof(indexed_tlist, vars) +
			   list_length(tlist) * sizeof(tlist_vinfo));

	itlist->tlist = tlist;
	itlist->has_non_vars = false;

	/* Find the Vars and fill in the index array */
	vinfo = itlist->vars;
991 992 993 994
	foreach(l, tlist)
	{
		TargetEntry *tle = (TargetEntry *) lfirst(l);

995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
		if (tle->expr && IsA(tle->expr, Var))
		{
			Var	   *var = (Var *) tle->expr;

			vinfo->varno = var->varno;
			vinfo->varattno = var->varattno;
			vinfo->resno = tle->resno;
			vinfo++;
		}
		else
			itlist->has_non_vars = true;
	}

	itlist->num_vars = (vinfo - itlist->vars);

	return itlist;
}

/*
 * search_indexed_tlist_for_var --- find a Var in an indexed tlist
 *
 * If a match is found, return a copy of the given Var with suitably
 * modified varno/varattno (to wit, newvarno and the resno of the TLE entry).
 * If no match, return NULL.
 */
static Var *
search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, Index newvarno)
{
	Index		varno = var->varno;
	AttrNumber	varattno = var->varattno;
	tlist_vinfo *vinfo;
	int			i;

	vinfo = itlist->vars;
	i = itlist->num_vars;
	while (i-- > 0)
	{
		if (vinfo->varno == varno && vinfo->varattno == varattno)
		{
			/* Found a match */
			Var		   *newvar = (Var *) copyObject(var);

			newvar->varno = newvarno;
			newvar->varattno = vinfo->resno;
			return newvar;
		}
		vinfo++;
1042
	}
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
	return NULL;				/* no match */
}

/*
 * search_indexed_tlist_for_non_var --- find a non-Var in an indexed tlist
 *
 * If a match is found, return a Var constructed to reference the tlist item.
 * If no match, return NULL.
 *
 * NOTE: it is a waste of time to call this if !itlist->has_non_vars
 */
static Var *
search_indexed_tlist_for_non_var(Node *node,
								 indexed_tlist *itlist, Index newvarno)
{
	TargetEntry *tle;

	tle = tlist_member(node, itlist->tlist);
	if (tle)
	{
		/* Found a matching subplan output expression */
		Var		   *newvar;

		newvar = makeVar(newvarno,
						 tle->resno,
						 exprType((Node *) tle->expr),
						 exprTypmod((Node *) tle->expr),
						 0);
		newvar->varnoold = 0;		/* wasn't ever a plain Var */
		newvar->varoattno = 0;
		return newvar;
	}
	return NULL;				/* no match */
1076 1077
}

1078
/*
1079
 * join_references
1080 1081 1082
 *	   Creates a new set of targetlist entries or join qual clauses by
 *	   changing the varno/varattno values of variables in the clauses
 *	   to reference target list values from the outer and inner join
1083
 *	   relation target lists.
1084 1085 1086 1087 1088 1089 1090 1091 1092
 *
 * This is used in two different scenarios: a normal join clause, where
 * all the Vars in the clause *must* be replaced by OUTER or INNER references;
 * and an indexscan being used on the inner side of a nestloop join.
 * In the latter case we want to replace the outer-relation Vars by OUTER
 * references, but not touch the Vars of the inner relation.
 *
 * For a normal join, acceptable_rel should be zero so that any failure to
 * match a Var will be reported as an error.  For the indexscan case,
1093
 * pass inner_itlist = NULL and acceptable_rel = the ID of the inner relation.
1094 1095
 *
 * 'clauses' is the targetlist or list of join clauses
1096
 * 'rtable' is the current range table
1097 1098 1099
 * 'outer_itlist' is the indexed target list of the outer join relation
 * 'inner_itlist' is the indexed target list of the inner join relation,
 *		or NULL
1100 1101
 * 'acceptable_rel' is either zero or the rangetable index of a relation
 *		whose Vars may appear in the clause without provoking an error.
1102
 *
1103
 * Returns the new expression tree.  The original clause structure is
1104
 * not modified.
1105
 */
1106
static List *
1107
join_references(List *clauses,
1108
				List *rtable,
1109 1110 1111
				indexed_tlist *outer_itlist,
				indexed_tlist *inner_itlist,
				Index acceptable_rel)
1112
{
1113
	join_references_context context;
Bruce Momjian's avatar
Bruce Momjian committed
1114

1115
	context.rtable = rtable;
1116 1117
	context.outer_itlist = outer_itlist;
	context.inner_itlist = inner_itlist;
1118 1119
	context.acceptable_rel = acceptable_rel;
	return (List *) join_references_mutator((Node *) clauses, &context);
1120 1121
}

1122
static Node *
1123 1124
join_references_mutator(Node *node,
						join_references_context *context)
1125
{
1126 1127
	Var		   *newvar;

1128 1129 1130 1131 1132
	if (node == NULL)
		return NULL;
	if (IsA(node, Var))
	{
		Var		   *var = (Var *) node;
1133

1134
		/* First look for the var in the input tlists */
1135 1136 1137 1138
		newvar = search_indexed_tlist_for_var(var,
											  context->outer_itlist,
											  OUTER);
		if (newvar)
1139
			return (Node *) newvar;
1140
		if (context->inner_itlist)
1141
		{
1142 1143 1144 1145 1146
			newvar = search_indexed_tlist_for_var(var,
												  context->inner_itlist,
												  INNER);
			if (newvar)
				return (Node *) newvar;
1147
		}
1148

1149 1150 1151 1152 1153
		/* Return the Var unmodified, if it's for acceptable_rel */
		if (var->varno == context->acceptable_rel)
			return (Node *) copyObject(var);

		/* No referent found for Var */
1154
		elog(ERROR, "variable not found in subplan target lists");
1155
	}
1156
	/* Try matching more complex expressions too, if tlists have any */
1157
	if (context->outer_itlist->has_non_vars)
1158
	{
1159 1160 1161 1162
		newvar = search_indexed_tlist_for_non_var(node,
												  context->outer_itlist,
												  OUTER);
		if (newvar)
1163
			return (Node *) newvar;
1164 1165 1166 1167 1168 1169 1170
	}
	if (context->inner_itlist && context->inner_itlist->has_non_vars)
	{
		newvar = search_indexed_tlist_for_non_var(node,
												  context->inner_itlist,
												  INNER);
		if (newvar)
1171 1172
			return (Node *) newvar;
	}
1173 1174 1175
	return expression_tree_mutator(node,
								   join_references_mutator,
								   (void *) context);
1176 1177
}

1178
/*
1179
 * replace_vars_with_subplan_refs
1180 1181 1182
 *		This routine modifies an expression tree so that all Var nodes
 *		reference target nodes of a subplan.  It is used to fix up
 *		target and qual expressions of non-join upper-level plan nodes.
1183
 *
1184 1185 1186
 * An error is raised if no matching var can be found in the subplan tlist
 * --- so this routine should only be applied to nodes whose subplans'
 * targetlists were generated via flatten_tlist() or some such method.
1187
 *
1188
 * If itlist->has_non_vars is true, then we try to match whole subexpressions
1189 1190 1191 1192 1193 1194
 * against elements of the subplan tlist, so that we can avoid recomputing
 * expressions that were already computed by the subplan.  (This is relatively
 * expensive, so we don't want to try it in the common case where the
 * subplan tlist is just a flattened list of Vars.)
 *
 * 'node': the tree to be fixed (a target item or qual)
1195
 * 'subplan_itlist': indexed target list for subplan
1196
 * 'subvarno': varno to be assigned to all Vars
1197
 *
1198 1199 1200
 * The resulting tree is a copy of the original in which all Var nodes have
 * varno = subvarno, varattno = resno of corresponding subplan target.
 * The original tree is not modified.
1201
 */
1202 1203
static Node *
replace_vars_with_subplan_refs(Node *node,
1204 1205
							   indexed_tlist *subplan_itlist,
							   Index subvarno)
1206
{
1207
	replace_vars_with_subplan_refs_context context;
1208

1209
	context.subplan_itlist = subplan_itlist;
1210
	context.subvarno = subvarno;
1211
	return replace_vars_with_subplan_refs_mutator(node, &context);
1212
}
Bruce Momjian's avatar
Bruce Momjian committed
1213

1214 1215
static Node *
replace_vars_with_subplan_refs_mutator(Node *node,
1216
						 replace_vars_with_subplan_refs_context *context)
1217
{
1218 1219
	Var		   *newvar;

1220
	if (node == NULL)
1221
		return NULL;
1222 1223 1224
	if (IsA(node, Var))
	{
		Var		   *var = (Var *) node;
1225

1226 1227 1228 1229
		newvar = search_indexed_tlist_for_var(var,
											  context->subplan_itlist,
											  context->subvarno);
		if (!newvar)
1230
			elog(ERROR, "variable not found in subplan target list");
1231 1232
		return (Node *) newvar;
	}
1233
	/* Try matching more complex expressions too, if tlist has any */
1234
	if (context->subplan_itlist->has_non_vars)
1235
	{
1236 1237 1238 1239
		newvar = search_indexed_tlist_for_non_var(node,
												  context->subplan_itlist,
												  context->subvarno);
		if (newvar)
1240 1241
			return (Node *) newvar;
	}
1242 1243 1244
	return expression_tree_mutator(node,
								   replace_vars_with_subplan_refs_mutator,
								   (void *) context);
1245 1246 1247
}

/*****************************************************************************
1248
 *					OPERATOR REGPROC LOOKUP
1249 1250
 *****************************************************************************/

1251
/*
1252 1253
 * fix_opfuncids
 *	  Calculate opfuncid field from opno for each OpExpr node in given tree.
1254
 *	  The given tree can be anything expression_tree_walker handles.
1255
 *
1256 1257 1258
 * The argument is modified in-place.  (This is OK since we'd want the
 * same change for any node, even if it gets visited more than once due to
 * shared structure.)
1259
 */
1260
void
1261
fix_opfuncids(Node *node)
1262
{
1263
	/* This tree walk requires no special setup, so away we go... */
1264
	fix_opfuncids_walker(node, NULL);
1265
}
Bruce Momjian's avatar
Bruce Momjian committed
1266

1267
static bool
1268
fix_opfuncids_walker(Node *node, void *context)
1269 1270 1271
{
	if (node == NULL)
		return false;
1272 1273 1274
	if (IsA(node, OpExpr))
		set_opfuncid((OpExpr *) node);
	else if (IsA(node, DistinctExpr))
Bruce Momjian's avatar
Bruce Momjian committed
1275
		set_opfuncid((OpExpr *) node);	/* rely on struct equivalence */
1276 1277
	else if (IsA(node, ScalarArrayOpExpr))
		set_sa_opfuncid((ScalarArrayOpExpr *) node);
1278
	else if (IsA(node, NullIfExpr))
Bruce Momjian's avatar
Bruce Momjian committed
1279
		set_opfuncid((OpExpr *) node);	/* rely on struct equivalence */
1280
	return expression_tree_walker(node, fix_opfuncids_walker, context);
1281
}
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291

/*
 * set_opfuncid
 *		Set the opfuncid (procedure OID) in an OpExpr node,
 *		if it hasn't been set already.
 *
 * Because of struct equivalence, this can also be used for
 * DistinctExpr and NullIfExpr nodes.
 */
void
1292
set_opfuncid(OpExpr *opexpr)
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302
{
	if (opexpr->opfuncid == InvalidOid)
		opexpr->opfuncid = get_opcode(opexpr->opno);
}

/*
 * set_sa_opfuncid
 *		As above, for ScalarArrayOpExpr nodes.
 */
static void
1303
set_sa_opfuncid(ScalarArrayOpExpr *opexpr)
1304 1305 1306 1307
{
	if (opexpr->opfuncid == InvalidOid)
		opexpr->opfuncid = get_opcode(opexpr->opno);
}