rewriteHandler.c 52.6 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * rewriteHandler.c
4
 *
Bruce Momjian's avatar
Add:  
Bruce Momjian committed
5 6
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994, Regents of the University of California
7 8 9
 *
 *
 * IDENTIFICATION
10
 *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.72 2000/04/20 00:31:49 tgl Exp $
11 12 13 14 15
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

Bruce Momjian's avatar
Bruce Momjian committed
16
#include "access/heapam.h"
17
#include "catalog/pg_operator.h"
18
#include "catalog/pg_type.h"
Bruce Momjian's avatar
Bruce Momjian committed
19
#include "miscadmin.h"
20
#include "nodes/makefuncs.h"
Bruce Momjian's avatar
Bruce Momjian committed
21 22
#include "optimizer/clauses.h"
#include "optimizer/prep.h"
23
#include "optimizer/var.h"
Bruce Momjian's avatar
Bruce Momjian committed
24
#include "parser/analyze.h"
25
#include "parser/parse_expr.h"
Bruce Momjian's avatar
Bruce Momjian committed
26
#include "parser/parse_relation.h"
27
#include "parser/parse_oper.h"
Bruce Momjian's avatar
Bruce Momjian committed
28 29 30 31 32 33 34
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "parser/parsetree.h"
#include "rewrite/locks.h"
#include "rewrite/rewriteManip.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
35 36


37 38 39 40 41 42 43 44 45 46 47 48 49
extern void CheckSelectForUpdate(Query *rule_action);	/* in analyze.c */


/* macros borrowed from expression_tree_mutator */

#define FLATCOPY(newnode, node, nodetype)  \
	( (newnode) = makeNode(nodetype), \
	  memcpy((newnode), (node), sizeof(nodetype)) )

#define MUTATE(newfield, oldfield, fieldtype, mutator, context)  \
		( (newfield) = (fieldtype) mutator((Node *) (oldfield), (context)) )


50 51 52 53 54 55 56
static RewriteInfo *gatherRewriteMeta(Query *parsetree,
				  Query *rule_action,
				  Node *rule_qual,
				  int rt_index,
				  CmdType event,
				  bool *instead_flag);
static bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up);
57
static bool attribute_used(Node *node, int rt_index, int attno,
58
			   int sublevels_up);
59
static bool modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
60
						   int sublevels_up, int new_sublevels_up);
61
static Node *modifyAggrefDropQual(Node *node, Node *targetNode);
62
static SubLink *modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree);
63
static Node *modifyAggrefQual(Node *node, Query *parsetree);
64
static Query *fireRIRrules(Query *parsetree);
65 66
static Query *Except_Intersect_Rewrite(Query *parsetree);
static void check_targetlists_are_compatible(List *prev_target,
67
								 List *current_target);
68 69
static void create_intersect_list(Node *ptr, List **intersect_list);
static Node *intersect_tree_analyze(Node *tree, Node *first_select,
70
					   Node *parsetree);
71 72 73

/*
 * gatherRewriteMeta -
74 75 76
 *	  Gather meta information about parsetree, and rule. Fix rule body
 *	  and qualifier so that they can be mixed with the parsetree and
 *	  maintain semantic validity
77 78
 */
static RewriteInfo *
79 80 81
gatherRewriteMeta(Query *parsetree,
				  Query *rule_action,
				  Node *rule_qual,
82 83
				  int rt_index,
				  CmdType event,
84
				  bool *instead_flag)
85
{
86 87 88
	RewriteInfo *info;
	int			rt_length;
	int			result_reln;
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

	info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
	info->rt_index = rt_index;
	info->event = event;
	info->instead_flag = *instead_flag;
	info->rule_action = (Query *) copyObject(rule_action);
	info->rule_qual = (Node *) copyObject(rule_qual);
	if (info->rule_action == NULL)
		info->nothing = TRUE;
	else
	{
		info->nothing = FALSE;
		info->action = info->rule_action->commandType;
		info->current_varno = rt_index;
		info->rt = parsetree->rtable;
		rt_length = length(info->rt);
Bruce Momjian's avatar
Bruce Momjian committed
105
		info->rt = nconc(info->rt, copyObject(info->rule_action->rtable));
106 107

		info->new_varno = PRS2_NEW_VARNO + rt_length;
Bruce Momjian's avatar
Bruce Momjian committed
108 109 110
		OffsetVarNodes(info->rule_action->qual, rt_length, 0);
		OffsetVarNodes((Node *) info->rule_action->targetList, rt_length, 0);
		OffsetVarNodes(info->rule_qual, rt_length, 0);
111
		ChangeVarNodes((Node *) info->rule_action->qual,
112
					   PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
113
		ChangeVarNodes((Node *) info->rule_action->targetList,
114
					   PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
115
		ChangeVarNodes(info->rule_qual,
116
					   PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
117 118 119 120 121 122 123 124

		/*
		 * bug here about replace CURRENT  -- sort of replace current is
		 * deprecated now so this code shouldn't really need to be so
		 * clutzy but.....
		 */
		if (info->action != CMD_SELECT)
		{						/* i.e update XXXXX */
125
			int			new_result_reln = 0;
126 127 128 129

			result_reln = info->rule_action->resultRelation;
			switch (result_reln)
			{
130 131 132 133 134 135 136
				case PRS2_CURRENT_VARNO:
					new_result_reln = rt_index;
					break;
				case PRS2_NEW_VARNO:	/* XXX */
				default:
					new_result_reln = result_reln + rt_length;
					break;
137 138 139 140 141
			}
			info->rule_action->resultRelation = new_result_reln;
		}
	}
	return info;
142 143 144 145
}


/*
146 147 148
 * rangeTableEntry_used -
 *	we need to process a RTE for RIR rules only if it is
 *	referenced somewhere in var nodes of the query.
149
 */
150

151 152
typedef struct
{
153 154 155 156
	int			rt_index;
	int			sublevels_up;
} rangeTableEntry_used_context;

157
static bool
158 159
rangeTableEntry_used_walker(Node *node,
							rangeTableEntry_used_context *context)
160
{
161
	if (node == NULL)
162 163
		return false;
	if (IsA(node, Var))
Bruce Momjian's avatar
Bruce Momjian committed
164
	{
165
		Var		   *var = (Var *) node;
166

167 168 169 170
		if (var->varlevelsup == context->sublevels_up &&
			var->varno == context->rt_index)
			return true;
		return false;
171
	}
172 173
	if (IsA(node, SubLink))
	{
174

175
		/*
176 177
		 * Standard expression_tree_walker will not recurse into
		 * subselect, but here we must do so.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
		 */
		SubLink    *sub = (SubLink *) node;

		if (rangeTableEntry_used_walker((Node *) (sub->lefthand), context))
			return true;
		if (rangeTableEntry_used((Node *) (sub->subselect),
								 context->rt_index,
								 context->sublevels_up + 1))
			return true;
		return false;
	}
	if (IsA(node, Query))
	{
		/* Reach here after recursing down into subselect above... */
		Query	   *qry = (Query *) node;

		if (rangeTableEntry_used_walker((Node *) (qry->targetList), context))
			return true;
		if (rangeTableEntry_used_walker((Node *) (qry->qual), context))
			return true;
		if (rangeTableEntry_used_walker((Node *) (qry->havingQual), context))
			return true;
		return false;
	}
	return expression_tree_walker(node, rangeTableEntry_used_walker,
								  (void *) context);
}

static bool
rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
{
	rangeTableEntry_used_context context;
210

211 212 213
	context.rt_index = rt_index;
	context.sublevels_up = sublevels_up;
	return rangeTableEntry_used_walker(node, &context);
214
}
215 216


217 218 219 220
/*
 * attribute_used -
 *	Check if a specific attribute number of a RTE is used
 *	somewhere in the query
221
 */
222

223 224
typedef struct
{
225 226 227 228 229
	int			rt_index;
	int			attno;
	int			sublevels_up;
} attribute_used_context;

230
static bool
231 232
attribute_used_walker(Node *node,
					  attribute_used_context *context)
233
{
234
	if (node == NULL)
235 236
		return false;
	if (IsA(node, Var))
Bruce Momjian's avatar
Bruce Momjian committed
237
	{
238
		Var		   *var = (Var *) node;
239

240 241 242 243 244 245 246 247
		if (var->varlevelsup == context->sublevels_up &&
			var->varno == context->rt_index &&
			var->varattno == context->attno)
			return true;
		return false;
	}
	if (IsA(node, SubLink))
	{
248

249
		/*
250 251
		 * Standard expression_tree_walker will not recurse into
		 * subselect, but here we must do so.
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
		 */
		SubLink    *sub = (SubLink *) node;

		if (attribute_used_walker((Node *) (sub->lefthand), context))
			return true;
		if (attribute_used((Node *) (sub->subselect),
						   context->rt_index,
						   context->attno,
						   context->sublevels_up + 1))
			return true;
		return false;
	}
	if (IsA(node, Query))
	{
		/* Reach here after recursing down into subselect above... */
		Query	   *qry = (Query *) node;

		if (attribute_used_walker((Node *) (qry->targetList), context))
			return true;
		if (attribute_used_walker((Node *) (qry->qual), context))
			return true;
		if (attribute_used_walker((Node *) (qry->havingQual), context))
			return true;
		return false;
276
	}
277 278 279 280 281 282 283 284
	return expression_tree_walker(node, attribute_used_walker,
								  (void *) context);
}

static bool
attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
{
	attribute_used_context context;
285

286 287 288 289
	context.rt_index = rt_index;
	context.attno = attno;
	context.sublevels_up = sublevels_up;
	return attribute_used_walker(node, &context);
290
}
291

292

293
/*
Bruce Momjian's avatar
Bruce Momjian committed
294
 * modifyAggrefChangeVarnodes -
295
 *	Change the var nodes in a sublink created for an aggregate column
296 297
 *	used in the qualification to point to the correct local RTE.
 *
298 299 300 301
 * XXX if we still need this after redoing querytree design, it should
 * be combined with ChangeVarNodes, which is the same thing except for
 * not having the option to adjust the vars' varlevelsup.
 *
302
 * NOTE: although this has the form of a walker, we cheat and modify the
303
 * Var nodes in-place.	The given expression tree should have been copied
304
 * earlier to ensure that no unwanted side-effects occur!
305 306
 */

307 308
typedef struct
{
309 310 311
	int			rt_index;
	int			new_index;
	int			sublevels_up;
312
	int			new_sublevels_up;
313
} modifyAggrefChangeVarnodes_context;
314

315 316
static bool
modifyAggrefChangeVarnodes_walker(Node *node,
317
							 modifyAggrefChangeVarnodes_context *context)
318 319 320 321
{
	if (node == NULL)
		return false;
	if (IsA(node, Var))
Bruce Momjian's avatar
Bruce Momjian committed
322
	{
323
		Var		   *var = (Var *) node;
324

325 326 327 328 329
		if (var->varlevelsup == context->sublevels_up &&
			var->varno == context->rt_index)
		{
			var->varno = context->new_index;
			var->varnoold = context->new_index;
330
			var->varlevelsup = context->new_sublevels_up;
331 332 333 334 335
		}
		return false;
	}
	if (IsA(node, SubLink))
	{
336

337
		/*
338 339
		 * Standard expression_tree_walker will not recurse into
		 * subselect, but here we must do so.
340 341 342 343 344 345 346 347 348
		 */
		SubLink    *sub = (SubLink *) node;

		if (modifyAggrefChangeVarnodes_walker((Node *) (sub->lefthand),
											  context))
			return true;
		if (modifyAggrefChangeVarnodes((Node *) (sub->subselect),
									   context->rt_index,
									   context->new_index,
349 350
									   context->sublevels_up + 1,
									   context->new_sublevels_up + 1))
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
			return true;
		return false;
	}
	if (IsA(node, Query))
	{
		/* Reach here after recursing down into subselect above... */
		Query	   *qry = (Query *) node;

		if (modifyAggrefChangeVarnodes_walker((Node *) (qry->targetList),
											  context))
			return true;
		if (modifyAggrefChangeVarnodes_walker((Node *) (qry->qual),
											  context))
			return true;
		if (modifyAggrefChangeVarnodes_walker((Node *) (qry->havingQual),
											  context))
			return true;
		return false;
	}
	return expression_tree_walker(node, modifyAggrefChangeVarnodes_walker,
								  (void *) context);
}
373

374 375
static bool
modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
376
						   int sublevels_up, int new_sublevels_up)
377 378
{
	modifyAggrefChangeVarnodes_context context;
379

380 381 382
	context.rt_index = rt_index;
	context.new_index = new_index;
	context.sublevels_up = sublevels_up;
383
	context.new_sublevels_up = new_sublevels_up;
384
	return modifyAggrefChangeVarnodes_walker(node, &context);
385 386 387 388
}


/*
Bruce Momjian's avatar
Bruce Momjian committed
389
 * modifyAggrefDropQual -
390 391
 *	remove the pure aggref clause from a qualification
 *
392 393 394
 * targetNode is an Aggref node somewhere within the given expression tree.
 * Find the boolean operator that's presumably somewhere above it, and replace
 * that whole operator expression with a constant TRUE.  (This is NOT really
395
 * quite the right thing, but it handles simple cases.	This whole set of
396 397 398
 * Aggref-in-qual routines needs to be thrown away when we can do subselects
 * in FROM.)
 *
399 400 401 402 403 404
 * The return tree is a modified copy of the given tree; the given tree
 * is not altered.
 *
 * Note: we don't recurse into subselects looking for targetNode; that's
 * not necessary in the current usage, since in fact targetNode will be
 * within the same select level as the given toplevel node.
405
 */
406 407
static Node *
modifyAggrefDropQual(Node *node, Node *targetNode)
408 409
{
	if (node == NULL)
410 411
		return NULL;
	if (node == targetNode)
412 413 414 415 416
	{
		/* Oops, it's not inside an Expr we can rearrange... */
		elog(ERROR, "Cannot handle aggregate function inserted at this place in WHERE clause");
	}
	if (IsA(node, Expr))
Bruce Momjian's avatar
Bruce Momjian committed
417
	{
418
		Expr	   *expr = (Expr *) node;
419
		List	   *i;
420

421 422 423 424 425 426 427 428 429 430 431 432 433
		foreach(i, expr->args)
		{
			if (((Node *) lfirst(i)) == targetNode)
			{
				/* Found the parent expression containing the Aggref */
				if (expr->typeOid != BOOLOID)
					elog(ERROR,
						 "aggregate function in qual must be argument of boolean operator");
				return (Node *) makeConst(BOOLOID, 1, (Datum) true,
										  false, true, false, false);
			}
		}
		/* else this isn't the expr we want, keep going */
434
	}
435 436
	return expression_tree_mutator(node, modifyAggrefDropQual,
								   (void *) targetNode);
437 438 439
}

/*
Bruce Momjian's avatar
Bruce Momjian committed
440
 * modifyAggrefMakeSublink -
441 442 443 444
 *	Create a sublink node for a qualification expression that
 *	uses an aggregate column of a view
 */
static SubLink *
445
modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree)
446
{
447
	List	   *aggVarNos;
448

449
	/* rte points to old structure: */
450
	RangeTblEntry *rte;
451

452 453 454
	/* these point to newly-created structures: */
	Query	   *subquery;
	SubLink    *sublink;
Bruce Momjian's avatar
Bruce Momjian committed
455 456
	TargetEntry *tle;
	Resdom	   *resdom;
457

458 459 460 461
	aggVarNos = pull_varnos(aggref->target);
	if (length(aggVarNos) != 1)
		elog(ERROR, "rewrite: aggregates of views only allowed on single tables for now");
	rte = rt_fetch(lfirsti(aggVarNos), parsetree->rtable);
462

Bruce Momjian's avatar
Bruce Momjian committed
463 464
	resdom = makeNode(Resdom);
	resdom->resno = 1;
465
	resdom->restype = aggref->aggtype;
466 467
	resdom->restypmod = -1;
	resdom->resname = pstrdup("<noname>");
Bruce Momjian's avatar
Bruce Momjian committed
468
	resdom->reskey = 0;
469
	resdom->reskeyop = 0;
Bruce Momjian's avatar
Bruce Momjian committed
470
	resdom->resjunk = false;
471

472
	tle = makeNode(TargetEntry);
Bruce Momjian's avatar
Bruce Momjian committed
473
	tle->resdom = resdom;
474
	tle->expr = copyObject(aggref);		/* make a modifiable copy! */
475 476

	subquery = makeNode(Query);
477 478

	sublink = makeNode(SubLink);
Bruce Momjian's avatar
Bruce Momjian committed
479
	sublink->subLinkType = EXPR_SUBLINK;
480
	sublink->useor = false;
481 482
	sublink->lefthand = NIL;
	sublink->oper = NIL;
Bruce Momjian's avatar
Bruce Momjian committed
483 484 485 486 487 488 489 490 491
	sublink->subselect = (Node *) subquery;

	subquery->commandType = CMD_SELECT;
	subquery->utilityStmt = NULL;
	subquery->resultRelation = 0;
	subquery->into = NULL;
	subquery->isPortal = FALSE;
	subquery->isBinary = FALSE;
	subquery->isTemp = FALSE;
492
	subquery->unionall = FALSE;
493 494
	subquery->distinctClause = NIL;
	subquery->sortClause = NIL;
495
	subquery->rtable = lcons(copyObject(rte), NIL);
496 497
	subquery->targetList = lcons(tle, NIL);
	subquery->qual = modifyAggrefDropQual((Node *) parsetree->qual,
498
										  (Node *) aggref);
499

500
	/*
501 502 503
	 * If there are still aggs in the subselect's qual, give up. Recursing
	 * would be a bad idea --- we'd likely produce an infinite recursion.
	 * This whole technique is a crock, really...
504
	 */
505
	if (checkExprHasAggs(subquery->qual))
506
		elog(ERROR, "Cannot handle multiple aggregate functions in WHERE clause");
507 508 509
	subquery->groupClause = NIL;
	subquery->havingQual = NULL;
	subquery->hasAggs = TRUE;
510
	subquery->hasSubLinks = checkExprHasSubLink(subquery->qual);
511
	subquery->unionClause = NULL;
512

513
	/* Increment all varlevelsup fields in the new subquery */
514
	IncrementVarSublevelsUp((Node *) subquery, 1, 0);
515

516 517
	/*
	 * Replace references to the target table with correct local varno, 1.
518 519
	 * Note that because of previous line, these references have
	 * varlevelsup = 1, which must be changed to 0.
520
	 */
521 522 523
	modifyAggrefChangeVarnodes((Node *) subquery,
							   lfirsti(aggVarNos), 1,
							   1, 0);
524

525 526
	return sublink;
}
527 528


529 530 531 532 533 534 535 536 537 538 539 540 541 542
/*
 * modifyAggrefQual -
 *	Search for qualification expressions that contain aggregate
 *	functions and substitute them by sublinks. These expressions
 *	originally come from qualifications that use aggregate columns
 *	of a view.
 *
 *	The return value is a modified copy of the given expression tree.
 */
static Node *
modifyAggrefQual(Node *node, Query *parsetree)
{
	if (node == NULL)
		return NULL;
543 544
	if (IsA(node, Aggref))
	{
545 546 547 548
		SubLink    *sub = modifyAggrefMakeSublink((Aggref *) node, parsetree);

		parsetree->hasSubLinks = true;
		return (Node *) sub;
549
	}
550

551 552 553
	/*
	 * Otherwise, fall through and copy the expr normally.
	 *
554 555
	 * We do NOT recurse into subselects in this routine.  It's sufficient to
	 * get rid of aggregates that are in the qual expression proper.
556 557 558
	 */
	return expression_tree_mutator(node, modifyAggrefQual,
								   (void *) parsetree);
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
}


static Node *
FindMatchingTLEntry(List *tlist, char *e_attname)
{
	List	   *i;

	foreach(i, tlist)
	{
		TargetEntry *tle = lfirst(i);
		char	   *resname;

		resname = tle->resdom->resname;
		if (!strcmp(e_attname, resname))
			return (tle->expr);
	}
	return NULL;
}


static Node *
make_null(Oid type)
{
	Const	   *c = makeNode(Const);

	c->consttype = type;
	c->constlen = get_typlen(type);
	c->constvalue = PointerGetDatum(NULL);
	c->constisnull = true;
	c->constbyval = get_typbyval(type);
	return (Node *) c;
}


594 595 596 597
/*
 * apply_RIR_view
 *	Replace Vars matching a given RT index with copies of TL expressions.
 */
598

599 600 601 602 603 604 605
typedef struct
{
	int			rt_index;
	int			sublevels_up;
	RangeTblEntry *rte;
	List	   *tlist;
	int		   *modified;
606
} apply_RIR_view_context;
607

608 609 610 611 612 613 614 615 616
static Node *
apply_RIR_view_mutator(Node *node,
					   apply_RIR_view_context *context)
{
	if (node == NULL)
		return NULL;
	if (IsA(node, Var))
	{
		Var		   *var = (Var *) node;
617

618 619 620 621
		if (var->varlevelsup == context->sublevels_up &&
			var->varno == context->rt_index)
		{
			Node	   *expr;
622

623 624 625 626
			if (var->varattno < 0)
				elog(ERROR, "system column %s not available - %s is a view",
					 get_attname(context->rte->relid, var->varattno),
					 context->rte->relname);
627

628 629 630 631
			expr = FindMatchingTLEntry(context->tlist,
									   get_attname(context->rte->relid,
												   var->varattno));
			if (expr == NULL)
632
			{
633 634
				/* XXX shouldn't this be an error condition? */
				return make_null(var->vartype);
635 636
			}

637
			/* Make a copy of the tlist item to return */
638
			expr = copyObject(expr);
639
			/* Adjust varlevelsup if tlist item is from higher query level */
640
			if (var->varlevelsup > 0)
641 642
				IncrementVarSublevelsUp(expr, var->varlevelsup, 0);

643 644 645 646 647
			*(context->modified) = true;
			return (Node *) expr;
		}
		/* otherwise fall through to copy the var normally */
	}
648

649 650 651 652 653 654
	/*
	 * Since expression_tree_mutator won't touch subselects, we have to
	 * handle them specially.
	 */
	if (IsA(node, SubLink))
	{
655 656
		SubLink    *sublink = (SubLink *) node;
		SubLink    *newnode;
657 658 659 660 661 662 663 664 665 666 667 668

		FLATCOPY(newnode, sublink, SubLink);
		MUTATE(newnode->lefthand, sublink->lefthand, List *,
			   apply_RIR_view_mutator, context);
		context->sublevels_up++;
		MUTATE(newnode->subselect, sublink->subselect, Node *,
			   apply_RIR_view_mutator, context);
		context->sublevels_up--;
		return (Node *) newnode;
	}
	if (IsA(node, Query))
	{
669 670
		Query	   *query = (Query *) node;
		Query	   *newnode;
671 672 673 674 675 676 677 678 679 680 681 682 683

		FLATCOPY(newnode, query, Query);
		MUTATE(newnode->targetList, query->targetList, List *,
			   apply_RIR_view_mutator, context);
		MUTATE(newnode->qual, query->qual, Node *,
			   apply_RIR_view_mutator, context);
		MUTATE(newnode->havingQual, query->havingQual, Node *,
			   apply_RIR_view_mutator, context);
		return (Node *) newnode;
	}
	return expression_tree_mutator(node, apply_RIR_view_mutator,
								   (void *) context);
}
684

685 686 687 688
static Node *
apply_RIR_view(Node *node, int rt_index, RangeTblEntry *rte, List *tlist,
			   int *modified, int sublevels_up)
{
689
	apply_RIR_view_context context;
690

691 692 693 694 695
	context.rt_index = rt_index;
	context.sublevels_up = sublevels_up;
	context.rte = rte;
	context.tlist = tlist;
	context.modified = modified;
696

697
	return apply_RIR_view_mutator(node, &context);
698 699 700
}


701
static Query *
702 703 704 705 706
ApplyRetrieveRule(Query *parsetree,
				  RewriteRule *rule,
				  int rt_index,
				  int relation_level,
				  Relation relation,
707
				  bool relWasInJoinSet)
708 709 710 711
{
	Query	   *rule_action = NULL;
	Node	   *rule_qual;
	List	   *rtable,
712
			   *addedrtable,
713
			   *l;
714 715
	int			nothing,
				rt_length;
716
	int			modified = false;
717
	int			badsql = false;
718 719 720 721 722 723 724 725

	rule_qual = rule->qual;
	if (rule->actions)
	{
		if (length(rule->actions) > 1)	/* ??? because we don't handle
										 * rules with more than one
										 * action? -ay */

726
			return parsetree;
727 728 729 730 731 732 733
		rule_action = copyObject(lfirst(rule->actions));
		nothing = FALSE;
	}
	else
		nothing = TRUE;

	rtable = copyObject(parsetree->rtable);
734
	rt_length = length(rtable); /* original length, not counting rule */
735

736 737
	addedrtable = copyObject(rule_action->rtable);

738 739
	/*
	 * If the original rel wasn't in the join set, none of its spawn is.
740 741
	 * If it was, then leave the spawn's flags as they are.
	 */
742
	if (!relWasInJoinSet)
743 744 745 746
	{
		foreach(l, addedrtable)
		{
			RangeTblEntry *rte = lfirst(l);
747

748 749
			rte->inJoinSet = false;
		}
750 751
	}

752
	rtable = nconc(rtable, addedrtable);
753 754
	parsetree->rtable = rtable;

755
	/* FOR UPDATE of view... */
Bruce Momjian's avatar
Bruce Momjian committed
756
	foreach(l, parsetree->rowMark)
757
	{
Bruce Momjian's avatar
Bruce Momjian committed
758
		if (((RowMark *) lfirst(l))->rti == rt_index)
759 760
			break;
	}
Bruce Momjian's avatar
Bruce Momjian committed
761
	if (l != NULL)				/* oh, hell -:) */
762
	{
Bruce Momjian's avatar
Bruce Momjian committed
763
		RowMark    *newrm;
764 765 766
		Index		rti = 1;
		List	   *l2;

767
		CheckSelectForUpdate(rule_action);
Bruce Momjian's avatar
Bruce Momjian committed
768 769 770 771 772 773

		/*
		 * We believe that rt_index is VIEW - nothing should be marked for
		 * VIEW, but ACL check must be done. As for real tables of VIEW -
		 * their rows must be marked, but we have to skip ACL check for
		 * them.
774
		 */
Bruce Momjian's avatar
Bruce Momjian committed
775
		((RowMark *) lfirst(l))->info &= ~ROW_MARK_FOR_UPDATE;
776

Bruce Momjian's avatar
Bruce Momjian committed
777
		foreach(l2, rule_action->rtable)
778
		{
Bruce Momjian's avatar
Bruce Momjian committed
779

780
			/*
Bruce Momjian's avatar
Bruce Momjian committed
781 782
			 * RTable of VIEW has two entries of VIEW itself - we use
			 * relid to skip them.
783
			 */
Bruce Momjian's avatar
Bruce Momjian committed
784
			if (relation->rd_id != ((RangeTblEntry *) lfirst(l2))->relid)
785 786 787 788 789 790 791 792 793 794 795
			{
				newrm = makeNode(RowMark);
				newrm->rti = rti + rt_length;
				newrm->info = ROW_MARK_FOR_UPDATE;
				lnext(l) = lcons(newrm, lnext(l));
				l = lnext(l);
			}
			rti++;
		}
	}

796
	rule_action->rtable = rtable;
Bruce Momjian's avatar
Bruce Momjian committed
797
	OffsetVarNodes((Node *) rule_qual, rt_length, 0);
Bruce Momjian's avatar
Bruce Momjian committed
798
	OffsetVarNodes((Node *) rule_action, rt_length, 0);
799

Bruce Momjian's avatar
Bruce Momjian committed
800
	ChangeVarNodes((Node *) rule_qual,
801
				   PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
Bruce Momjian's avatar
Bruce Momjian committed
802
	ChangeVarNodes((Node *) rule_action,
803 804 805 806
				   PRS2_CURRENT_VARNO + rt_length, rt_index, 0);

	if (relation_level)
	{
807
		RangeTblEntry *rte = rt_fetch(rt_index, rtable);
808 809 810 811

		parsetree = (Query *) apply_RIR_view((Node *) parsetree,
											 rt_index, rte,
											 rule_action->targetList,
812
											 &modified, 0);
813 814 815
		rule_action = (Query *) apply_RIR_view((Node *) rule_action,
											   rt_index, rte,
											   rule_action->targetList,
816 817 818 819 820
											   &modified, 0);
		/* always apply quals of relation-level rules, whether we found a
		 * var to substitute or not.
		 */
		modified = true;
821 822 823
	}
	else
	{
Bruce Momjian's avatar
Bruce Momjian committed
824
		HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
825 826
							   rt_index, rule->attrno, &modified, &badsql);
		/* quals will be inserted only if we found uses of the attribute */
Bruce Momjian's avatar
Bruce Momjian committed
827
	}
828
	if (modified && !badsql)
Bruce Momjian's avatar
Bruce Momjian committed
829 830 831 832 833 834 835
	{
		AddQual(parsetree, rule_action->qual);
		AddGroupClause(parsetree, rule_action->groupClause,
					   rule_action->targetList);
		AddHavingQual(parsetree, rule_action->havingQual);
		parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
		parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks);
836
	}
837 838

	return parsetree;
839 840 841
}


842 843 844 845 846
/*
 * fireRIRonSubselect -
 *	Apply fireRIRrules() to each subselect found in the given tree.
 *
 * NOTE: although this has the form of a walker, we cheat and modify the
847
 * SubLink nodes in-place.	It is caller's responsibility to ensure that
848 849 850 851
 * no unwanted side-effects occur!
 */
static bool
fireRIRonSubselect(Node *node, void *context)
852 853
{
	if (node == NULL)
854 855
		return false;
	if (IsA(node, SubLink))
Bruce Momjian's avatar
Bruce Momjian committed
856
	{
857 858 859 860 861 862 863 864 865
		SubLink    *sub = (SubLink *) node;
		Query	   *qry;

		/* Process lefthand args */
		if (fireRIRonSubselect((Node *) (sub->lefthand), context))
			return true;
		/* Do what we came for */
		qry = fireRIRrules((Query *) (sub->subselect));
		sub->subselect = (Node *) qry;
866
		/* Need not recurse into subselect, because fireRIRrules did it */
867 868 869 870
		return false;
	}
	if (IsA(node, Query))
	{
871
		/* Reach here when called from fireRIRrules */
872 873 874 875 876 877 878 879 880
		Query	   *qry = (Query *) node;

		if (fireRIRonSubselect((Node *) (qry->targetList), context))
			return true;
		if (fireRIRonSubselect((Node *) (qry->qual), context))
			return true;
		if (fireRIRonSubselect((Node *) (qry->havingQual), context))
			return true;
		return false;
881
	}
882 883
	return expression_tree_walker(node, fireRIRonSubselect,
								  (void *) context);
884 885 886 887 888 889 890 891 892 893
}


/*
 * fireRIRrules -
 *	Apply all RIR rules on each rangetable entry in a query
 */
static Query *
fireRIRrules(Query *parsetree)
{
Bruce Momjian's avatar
Bruce Momjian committed
894 895
	int			rt_index;
	RangeTblEntry *rte;
896
	Relation	rel;
Bruce Momjian's avatar
Bruce Momjian committed
897 898 899 900
	List	   *locks;
	RuleLock   *rules;
	RewriteRule *rule;
	RewriteRule RIRonly;
901
	bool		relWasInJoinSet;
Bruce Momjian's avatar
Bruce Momjian committed
902 903
	int			i;
	List	   *l;
904

905 906 907
	/*
	 * don't try to convert this into a foreach loop, because rtable list
	 * can get changed each time through...
908
	 */
909
	rt_index = 0;
Bruce Momjian's avatar
Bruce Momjian committed
910 911
	while (rt_index < length(parsetree->rtable))
	{
912 913
		++rt_index;

914
		rte = rt_fetch(rt_index, parsetree->rtable);
915

916
		/*
917 918 919 920 921
		 * If the table is not referenced in the query, then we ignore it.
		 * This prevents infinite expansion loop due to new rtable entries
		 * inserted by expansion of a rule. A table is referenced if it is
		 * part of the join set (a source table), or is the result table,
		 * or is referenced by any Var nodes.
922
		 */
923
		if (!rte->inJoinSet && rt_index != parsetree->resultRelation &&
924
			!rangeTableEntry_used((Node *) parsetree, rt_index, 0))
925
			continue;
Bruce Momjian's avatar
Bruce Momjian committed
926

927
		rel = heap_openr(rte->relname, AccessShareLock);
928 929
		rules = rel->rd_rules;
		if (rules == NULL)
Bruce Momjian's avatar
Bruce Momjian committed
930
		{
931
			heap_close(rel, AccessShareLock);
932 933 934
			continue;
		}

935 936
		relWasInJoinSet = rte->inJoinSet;		/* save before possibly
												 * clearing */
937 938 939 940

		/*
		 * Collect the RIR rules that we must apply
		 */
941
		locks = NIL;
Bruce Momjian's avatar
Bruce Momjian committed
942 943
		for (i = 0; i < rules->numLocks; i++)
		{
944 945 946
			rule = rules->rules[i];
			if (rule->event != CMD_SELECT)
				continue;
Bruce Momjian's avatar
Bruce Momjian committed
947

948 949 950
			if (rule->attrno > 0)
			{
				/* per-attr rule; do we need it? */
951 952 953
				if (!attribute_used((Node *) parsetree,
									rt_index,
									rule->attrno, 0))
954 955 956 957
					continue;
			}
			else
			{
958 959 960 961 962

				/*
				 * Rel-wide ON SELECT DO INSTEAD means this is a view.
				 * Remove the view from the planner's join target set, or
				 * we'll get no rows out because view itself is empty!
963 964 965 966
				 */
				if (rule->isInstead)
					rte->inJoinSet = false;
			}
967 968 969 970 971 972 973 974 975 976 977 978

			locks = lappend(locks, rule);
		}

		/*
		 * Check permissions
		 */
		checkLockPerms(locks, parsetree, rt_index);

		/*
		 * Now apply them
		 */
Bruce Momjian's avatar
Bruce Momjian committed
979 980
		foreach(l, locks)
		{
981 982
			rule = lfirst(l);

Bruce Momjian's avatar
Bruce Momjian committed
983 984 985 986
			RIRonly.event = rule->event;
			RIRonly.attrno = rule->attrno;
			RIRonly.qual = rule->qual;
			RIRonly.actions = rule->actions;
987

988 989 990 991 992
			parsetree = ApplyRetrieveRule(parsetree,
										  &RIRonly,
										  rt_index,
										  RIRonly.attrno == -1,
										  rel,
993
										  relWasInJoinSet);
994 995
		}

996
		heap_close(rel, AccessShareLock);
997 998
	}

999 1000
	if (parsetree->hasAggs)
		parsetree->qual = modifyAggrefQual(parsetree->qual, parsetree);
1001

1002 1003 1004
	if (parsetree->hasSubLinks)
		fireRIRonSubselect((Node *) parsetree, NULL);

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
	return parsetree;
}


/*
 * idea is to fire regular rules first, then qualified instead
 * rules and unqualified instead rules last. Any lemming is counted for.
 */
static List *
orderRules(List *locks)
{
	List	   *regular = NIL;
	List	   *instead_rules = NIL;
	List	   *instead_qualified = NIL;
	List	   *i;

	foreach(i, locks)
	{
		RewriteRule *rule_lock = (RewriteRule *) lfirst(i);

		if (rule_lock->isInstead)
		{
			if (rule_lock->qual == NULL)
				instead_rules = lappend(instead_rules, rule_lock);
			else
				instead_qualified = lappend(instead_qualified, rule_lock);
1031
		}
1032 1033
		else
			regular = lappend(regular, rule_lock);
1034
	}
1035 1036
	regular = nconc(regular, instead_qualified);
	return nconc(regular, instead_rules);
1037 1038
}

1039 1040


1041
static Query *
1042 1043 1044
CopyAndAddQual(Query *parsetree,
			   List *actions,
			   Node *rule_qual,
1045 1046
			   int rt_index,
			   CmdType event)
1047
{
1048 1049 1050
	Query	   *new_tree = (Query *) copyObject(parsetree);
	Node	   *new_qual = NULL;
	Query	   *rule_action = NULL;
1051 1052 1053 1054 1055 1056 1057

	if (actions)
		rule_action = lfirst(actions);
	if (rule_qual != NULL)
		new_qual = (Node *) copyObject(rule_qual);
	if (rule_action != NULL)
	{
1058 1059
		List	   *rtable;
		int			rt_length;
1060 1061 1062

		rtable = new_tree->rtable;
		rt_length = length(rtable);
Bruce Momjian's avatar
Bruce Momjian committed
1063
		rtable = nconc(rtable, copyObject(rule_action->rtable));
1064
		new_tree->rtable = rtable;
Bruce Momjian's avatar
Bruce Momjian committed
1065
		OffsetVarNodes(new_qual, rt_length, 0);
1066
		ChangeVarNodes(new_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
1067 1068 1069 1070 1071
	}
	/* XXX -- where current doesn't work for instead nothing.... yet */
	AddNotQual(new_tree, new_qual);

	return new_tree;
1072 1073 1074
}


1075

1076
/*
1077
 *	fireRules -
1078 1079 1080 1081 1082
 *	   Iterate through rule locks applying rules.
 *	   All rules create their own parsetrees. Instead rules
 *	   with rule qualification save the original parsetree
 *	   and add their negated qualification to it. Real instead
 *	   rules finally throw away the original parsetree.
1083
 *
1084
 *	   remember: reality is for dead birds -- glass
1085 1086
 *
 */
1087
static List *
1088
fireRules(Query *parsetree,
1089 1090
		  int rt_index,
		  CmdType event,
1091 1092 1093
		  bool *instead_flag,
		  List *locks,
		  List **qual_products)
1094
{
1095 1096 1097
	RewriteInfo *info;
	List	   *results = NIL;
	List	   *i;
1098 1099 1100

	/* choose rule to fire from list of rules */
	if (locks == NIL)
1101
		return NIL;
1102

1103
	locks = orderRules(locks);	/* real instead rules last */
1104 1105
	foreach(i, locks)
	{
1106 1107 1108 1109 1110
		RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
		Node	   *qual,
				   *event_qual;
		List	   *actions;
		List	   *r;
1111

1112
		/*
1113 1114 1115 1116 1117 1118
		 * Instead rules change the resultRelation of the query. So the
		 * permission checks on the initial resultRelation would never be
		 * done (this is normally done in the executor deep down). So we
		 * must do it here. The result relations resulting from earlier
		 * rewrites are already checked against the rules eventrelation
		 * owner (during matchLocks) and have the skipAcl flag set.
1119
		 */
1120 1121 1122 1123
		if (rule_lock->isInstead &&
			parsetree->commandType != CMD_SELECT)
		{
			RangeTblEntry *rte;
1124 1125 1126
			int32		acl_rc;
			int32		reqperm;

1127 1128
			switch (parsetree->commandType)
			{
1129 1130 1131 1132 1133 1134 1135
				case CMD_INSERT:
					reqperm = ACL_AP;
					break;
				default:
					reqperm = ACL_WR;
					break;
			}
1136

1137
			rte = rt_fetch(parsetree->resultRelation, parsetree->rtable);
1138 1139
			if (!rte->skipAcl)
			{
1140
				acl_rc = pg_aclcheck(rte->relname,
1141 1142 1143
									 GetPgUserName(), reqperm);
				if (acl_rc != ACLCHECK_OK)
				{
1144
					elog(ERROR, "%s: %s",
1145 1146
						 rte->relname,
						 aclcheck_error_strings[acl_rc]);
1147 1148 1149 1150
				}
			}
		}

1151 1152 1153 1154
		/* multiple rule action time */
		*instead_flag = rule_lock->isInstead;
		event_qual = rule_lock->qual;
		actions = rule_lock->actions;
1155 1156 1157 1158
		if (event_qual != NULL && *instead_flag)
		{
			Query	   *qual_product;
			RewriteInfo qual_info;
1159 1160 1161 1162 1163

			/* ----------
			 * If there are instead rules with qualifications,
			 * the original query is still performed. But all
			 * the negated rule qualifications of the instead
1164
			 * rules are added so it does its actions only
1165 1166 1167 1168 1169 1170 1171
			 * in cases where the rule quals of all instead
			 * rules are false. Think of it as the default
			 * action in a case. We save this in *qual_products
			 * so deepRewriteQuery() can add it to the query
			 * list after we mangled it up enough.
			 * ----------
			 */
1172
			if (*qual_products == NIL)
1173
				qual_product = parsetree;
1174 1175
			else
				qual_product = (Query *) nth(0, *qual_products);
1176

1177
			MemSet(&qual_info, 0, sizeof(qual_info));
1178
			qual_info.event = qual_product->commandType;
1179
			qual_info.current_varno = rt_index;
1180
			qual_info.new_varno = length(qual_product->rtable) + 2;
1181

1182 1183 1184 1185 1186 1187 1188
			qual_product = CopyAndAddQual(qual_product,
										  actions,
										  event_qual,
										  rt_index,
										  event);

			qual_info.rule_action = qual_product;
1189 1190 1191 1192 1193 1194 1195

			if (event == CMD_INSERT || event == CMD_UPDATE)
				FixNew(&qual_info, qual_product);

			*qual_products = lappend(NIL, qual_product);
		}

1196 1197
		foreach(r, actions)
		{
1198 1199
			Query	   *rule_action = lfirst(r);
			Node	   *rule_qual = copyObject(event_qual);
1200

1201 1202 1203
			if (rule_action->commandType == CMD_NOTHING)
				continue;

Bruce Momjian's avatar
Bruce Momjian committed
1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
			/*--------------------------------------------------
			 * We copy the qualifications of the parsetree
			 * to the action and vice versa. So force
			 * hasSubLinks if one of them has it.
			 *
			 * As of 6.4 only parsetree qualifications can
			 * have sublinks. If this changes, we must make
			 * this a node lookup at the end of rewriting.
			 *
			 * Jan
			 *--------------------------------------------------
			 */
			if (parsetree->hasSubLinks && !rule_action->hasSubLinks)
			{
				rule_action = copyObject(rule_action);
				rule_action->hasSubLinks = TRUE;
			}
			if (!parsetree->hasSubLinks && rule_action->hasSubLinks)
				parsetree->hasSubLinks = TRUE;

1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
			/*--------------------------------------------------
			 * Step 1:
			 *	  Rewrite current.attribute or current to tuple variable
			 *	  this appears to be done in parser?
			 *--------------------------------------------------
			 */
			info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
									 rt_index, event, instead_flag);

			/* handle escapable cases, or those handled by other code */
			if (info->nothing)
			{
				if (*instead_flag)
					return NIL;
				else
					continue;
			}

			if (info->action == info->event &&
				info->event == CMD_SELECT)
				continue;

			/*
1247
			 * Event Qualification forces copying of parsetree and
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271
			 * splitting into two queries one w/rule_qual, one w/NOT
			 * rule_qual. Also add user query qual onto rule action
			 */
			qual = parsetree->qual;
			AddQual(info->rule_action, qual);

			if (info->rule_qual != NULL)
				AddQual(info->rule_action, info->rule_qual);

			/*--------------------------------------------------
			 * Step 2:
			 *	  Rewrite new.attribute w/ right hand side of target-list
			 *	  entry for appropriate field name in insert/update
			 *--------------------------------------------------
			 */
			if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE))
				FixNew(info, parsetree);

			/*--------------------------------------------------
			 * Step 3:
			 *	  rewriting due to retrieve rules
			 *--------------------------------------------------
			 */
			info->rule_action->rtable = info->rt;
Bruce Momjian's avatar
Bruce Momjian committed
1272

1273
			/*
Bruce Momjian's avatar
Bruce Momjian committed
1274 1275 1276
			 * ProcessRetrieveQuery(info->rule_action, info->rt,
			 * &orig_instead_flag, TRUE);
			 */
1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287

			/*--------------------------------------------------
			 * Step 4
			 *	  Simplify? hey, no algorithm for simplification... let
			 *	  the planner do it.
			 *--------------------------------------------------
			 */
			results = lappend(results, info->rule_action);

			pfree(info);
		}
1288 1289 1290 1291 1292 1293

		/* ----------
		 * If this was an unqualified instead rule,
		 * throw away an eventually saved 'default' parsetree
		 * ----------
		 */
1294
		if (event_qual == NULL && *instead_flag)
1295
			*qual_products = NIL;
1296 1297
	}
	return results;
1298 1299
}

1300 1301


1302
static List *
1303
RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
1304
{
1305
	CmdType		event;
Bruce Momjian's avatar
Bruce Momjian committed
1306 1307 1308
	List	   *product_queries = NIL;
	int			result_relation = 0;
	RangeTblEntry *rt_entry;
1309
	Relation	rt_entry_relation = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
1310
	RuleLock   *rt_entry_locks = NULL;
1311

1312 1313 1314 1315
	Assert(parsetree != NULL);

	event = parsetree->commandType;

1316
	/*
Bruce Momjian's avatar
Bruce Momjian committed
1317 1318
	 * SELECT rules are handled later when we have all the queries that
	 * should get executed
1319 1320 1321 1322 1323 1324 1325
	 */
	if (event == CMD_SELECT)
		return NIL;

	/*
	 * Utilities aren't rewritten at all - why is this here?
	 */
1326 1327
	if (event == CMD_UTILITY)
		return NIL;
1328

1329
	/*
Bruce Momjian's avatar
Bruce Momjian committed
1330
	 * the statement is an update, insert or delete - fire rules on it.
1331
	 */
1332
	result_relation = parsetree->resultRelation;
1333
	rt_entry = rt_fetch(result_relation, parsetree->rtable);
1334
	rt_entry_relation = heap_openr(rt_entry->relname, AccessShareLock);
1335
	rt_entry_locks = rt_entry_relation->rd_rules;
1336
	heap_close(rt_entry_relation, AccessShareLock);
1337

1338
	if (rt_entry_locks != NULL)
1339
	{
1340
		List	   *locks = matchLocks(event, rt_entry_locks, result_relation, parsetree);
1341

1342
		product_queries = fireRules(parsetree,
Bruce Momjian's avatar
Bruce Momjian committed
1343 1344 1345 1346 1347
									result_relation,
									event,
									instead_flag,
									locks,
									qual_products);
1348
	}
1349

1350
	return product_queries;
1351 1352
}

1353

1354 1355 1356 1357 1358
/*
 * to avoid infinite recursion, we restrict the number of times a query
 * can be rewritten. Detecting cycles is left for the reader as an excercise.
 */
#ifndef REWRITE_INVOKE_MAX
1359
#define REWRITE_INVOKE_MAX		10
1360 1361
#endif

1362
static int	numQueryRewriteInvoked = 0;
1363 1364 1365

/*
 * deepRewriteQuery -
1366
 *	  rewrites the query and apply the rules again on the queries rewritten
1367
 */
1368
static List *
1369
deepRewriteQuery(Query *parsetree)
1370
{
1371 1372 1373 1374 1375
	List	   *n;
	List	   *rewritten = NIL;
	List	   *result = NIL;
	bool		instead;
	List	   *qual_products = NIL;
1376

1377 1378


1379 1380
	if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
	{
1381
		elog(ERROR, "query rewritten %d times, may contain cycles",
1382 1383 1384 1385 1386
			 numQueryRewriteInvoked - 1);
	}

	instead = FALSE;
	result = RewriteQuery(parsetree, &instead, &qual_products);
1387

1388 1389
	foreach(n, result)
	{
1390 1391
		Query	   *pt = lfirst(n);
		List	   *newstuff = NIL;
1392 1393 1394 1395 1396

		newstuff = deepRewriteQuery(pt);
		if (newstuff != NIL)
			rewritten = nconc(rewritten, newstuff);
	}
1397 1398 1399 1400 1401 1402

	/* ----------
	 * qual_products are the original query with the negated
	 * rule qualification of an instead rule
	 * ----------
	 */
1403 1404 1405
	if (qual_products != NIL)
		rewritten = nconc(rewritten, qual_products);

1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418
	/* ----------
	 * The original query is appended last if not instead
	 * because update and delete rule actions might not do
	 * anything if they are invoked after the update or
	 * delete is performed. The command counter increment
	 * between the query execution makes the deleted (and
	 * maybe the updated) tuples disappear so the scans
	 * for them in the rule actions cannot find them.
	 * ----------
	 */
	if (!instead)
		rewritten = lappend(rewritten, parsetree);

1419 1420
	return rewritten;
}
1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439


/*
 * QueryOneRewrite -
 *	  rewrite one query
 */
static List *
QueryRewriteOne(Query *parsetree)
{
	numQueryRewriteInvoked = 0;

	/*
	 * take a deep breath and apply all the rewrite rules - ay
	 */
	return deepRewriteQuery(parsetree);
}


/*
1440
 * BasicQueryRewrite -
1441 1442 1443
 *	  rewrite one query via query rewrite system, possibly returning 0
 *	  or many queries
 */
1444 1445
static List *
BasicQueryRewrite(Query *parsetree)
1446
{
Bruce Momjian's avatar
Bruce Momjian committed
1447 1448 1449 1450
	List	   *querylist;
	List	   *results = NIL;
	List	   *l;
	Query	   *query;
1451 1452 1453 1454 1455 1456 1457 1458 1459

	/*
	 * Step 1
	 *
	 * Apply all non-SELECT rules possibly getting 0 or many queries
	 */
	querylist = QueryRewriteOne(parsetree);

	/*
1460
	 * Step 2
1461 1462 1463
	 *
	 * Apply all the RIR rules on each query
	 */
Bruce Momjian's avatar
Bruce Momjian committed
1464 1465 1466 1467
	foreach(l, querylist)
	{
		query = fireRIRrules((Query *) lfirst(l));

1468
		/*
Bruce Momjian's avatar
Bruce Momjian committed
1469
		 * If the query was marked having aggregates, check if this is
1470
		 * still true after rewriting.	Ditto for sublinks.  Note there
1471
		 * should be no aggs in the qual at this point.
1472 1473
		 */
		if (query->hasAggs)
1474 1475
		{
			query->hasAggs =
1476 1477 1478
				checkExprHasAggs((Node *) (query->targetList)) ||
				checkExprHasAggs((Node *) (query->havingQual));
			if (checkExprHasAggs((Node *) (query->qual)))
1479 1480
				elog(ERROR, "BasicQueryRewrite: failed to remove aggs from qual");
		}
1481
		if (query->hasSubLinks)
1482
			query->hasSubLinks =
1483 1484 1485
				checkExprHasSubLink((Node *) (query->targetList)) ||
				checkExprHasSubLink((Node *) (query->qual)) ||
				checkExprHasSubLink((Node *) (query->havingQual));
1486
		results = lappend(results, query);
1487
	}
1488

1489 1490
	return results;
}
1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501

/*
 * QueryRewrite -
 *	  Primary entry point to the query rewriter.
 *	  Rewrite one query via query rewrite system, possibly returning 0
 *	  or many queries.
 *
 * NOTE: The code in QueryRewrite was formerly in pg_parse_and_plan(), and was
 * moved here so that it would be invoked during EXPLAIN.  The division of
 * labor between this routine and BasicQueryRewrite is not obviously correct
 * ... at least not to me ... tgl 5/99.
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1502
 */
1503 1504 1505 1506 1507 1508
List *
QueryRewrite(Query *parsetree)
{
	List	   *rewritten,
			   *rewritten_item;

Bruce Momjian's avatar
Bruce Momjian committed
1509 1510 1511 1512
	/*
	 * Rewrite Union, Intersect and Except Queries to normal Union Queries
	 * using IN and NOT IN subselects
	 */
1513 1514 1515 1516 1517 1518 1519 1520 1521
	if (parsetree->intersectClause)
		parsetree = Except_Intersect_Rewrite(parsetree);

	/* Rewrite basic queries (retrieve, append, delete, replace) */
	rewritten = BasicQueryRewrite(parsetree);

	/*
	 * Rewrite the UNIONS.
	 */
Bruce Momjian's avatar
Bruce Momjian committed
1522
	foreach(rewritten_item, rewritten)
1523 1524 1525 1526 1527
	{
		Query	   *qry = (Query *) lfirst(rewritten_item);
		List	   *union_result = NIL;
		List	   *union_item;

Bruce Momjian's avatar
Bruce Momjian committed
1528
		foreach(union_item, qry->unionClause)
1529 1530
		{
			union_result = nconc(union_result,
Bruce Momjian's avatar
Bruce Momjian committed
1531
						BasicQueryRewrite((Query *) lfirst(union_item)));
1532 1533 1534 1535 1536 1537 1538 1539 1540 1541
		}
		qry->unionClause = union_result;
	}

	return rewritten;
}

/* This function takes two targetlists as arguments and checks if the
 * targetlists are compatible (i.e. both select for the same number of
 * attributes and the types are compatible */
1542
static void
Bruce Momjian's avatar
Bruce Momjian committed
1543
check_targetlists_are_compatible(List *prev_target, List *current_target)
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1544
{
Bruce Momjian's avatar
Bruce Momjian committed
1545 1546 1547 1548
	List	   *tl,
			   *next_target;
	int			prev_len = 0,
				next_len = 0;
1549

Bruce Momjian's avatar
Bruce Momjian committed
1550 1551
	foreach(tl, prev_target)
		if (!((TargetEntry *) lfirst(tl))->resdom->resjunk)
1552 1553
		prev_len++;

Bruce Momjian's avatar
Bruce Momjian committed
1554 1555
	foreach(next_target, current_target)
		if (!((TargetEntry *) lfirst(next_target))->resdom->resjunk)
1556
		next_len++;
Bruce Momjian's avatar
Bruce Momjian committed
1557 1558 1559 1560 1561

	if (prev_len != next_len)
		elog(ERROR, "Each UNION | EXCEPT | INTERSECT query must have the same number of columns.");

	foreach(next_target, current_target)
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1562
	{
Bruce Momjian's avatar
Bruce Momjian committed
1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574
		Oid			itype;
		Oid			otype;

		otype = ((TargetEntry *) lfirst(prev_target))->resdom->restype;
		itype = ((TargetEntry *) lfirst(next_target))->resdom->restype;

		/* one or both is a NULL column? then don't convert... */
		if (otype == InvalidOid)
		{
			/* propagate a known type forward, if available */
			if (itype != InvalidOid)
				((TargetEntry *) lfirst(prev_target))->resdom->restype = itype;
1575
#ifdef NOT_USED
Bruce Momjian's avatar
Bruce Momjian committed
1576 1577 1578 1579 1580
			else
			{
				((TargetEntry *) lfirst(prev_target))->resdom->restype = UNKNOWNOID;
				((TargetEntry *) lfirst(next_target))->resdom->restype = UNKNOWNOID;
			}
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1581
#endif
Bruce Momjian's avatar
Bruce Momjian committed
1582 1583 1584 1585 1586 1587 1588 1589 1590 1591
		}
		else if (itype == InvalidOid)
		{
		}
		/* they don't match in type? then convert... */
		else if (itype != otype)
		{
			Node	   *expr;

			expr = ((TargetEntry *) lfirst(next_target))->expr;
1592
			expr = CoerceTargetExpr(NULL, expr, itype, otype, -1);
Bruce Momjian's avatar
Bruce Momjian committed
1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610
			if (expr == NULL)
			{
				elog(ERROR, "Unable to transform %s to %s"
					 "\n\tEach UNION | EXCEPT | INTERSECT clause must have compatible target types",
					 typeidTypeName(itype),
					 typeidTypeName(otype));
			}
			((TargetEntry *) lfirst(next_target))->expr = expr;
			((TargetEntry *) lfirst(next_target))->resdom->restype = otype;
		}

		/* both are UNKNOWN? then evaluate as text... */
		else if (itype == UNKNOWNOID)
		{
			((TargetEntry *) lfirst(next_target))->resdom->restype = TEXTOID;
			((TargetEntry *) lfirst(prev_target))->resdom->restype = TEXTOID;
		}
		prev_target = lnext(prev_target);
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1611 1612 1613 1614
	}
}

/* Rewrites UNION INTERSECT and EXCEPT queries to semantiacally equivalent
Bruce Momjian's avatar
Bruce Momjian committed
1615 1616
 * queries that use IN and NOT IN subselects.
 *
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1617 1618 1619
 * The operator tree is attached to 'intersectClause' (see rule
 * 'SelectStmt' in gram.y) of the 'parsetree' given as an
 * argument. First we remember some clauses (the sortClause, the
1620
 * distinctClause etc.)  Then we translate the operator tree to DNF
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1621 1622 1623 1624 1625 1626 1627 1628
 * (disjunctive normal form) by 'cnfify'. (Note that 'cnfify' produces
 * CNF but as we exchanged ANDs with ORs in function A_Expr_to_Expr()
 * earlier we get DNF after exchanging ANDs and ORs again in the
 * result.) Now we create a new query by evaluating the new operator
 * tree which is in DNF now. For every AND we create an entry in the
 * union list and for every OR we create an IN subselect. (NOT IN
 * subselects are created for OR NOT nodes). The first entry of the
 * union list is handed back but before that the remembered clauses
Bruce Momjian's avatar
Bruce Momjian committed
1629
 * (sortClause etc) are attached to the new top Node (Note that the
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1630
 * new top Node can differ from the parsetree given as argument because of
1631 1632
 * the translation to DNF. That's why we have to remember the sortClause
 * and so on!) */
1633
static Query *
Bruce Momjian's avatar
Bruce Momjian committed
1634
Except_Intersect_Rewrite(Query *parsetree)
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1635
{
Bruce Momjian's avatar
Bruce Momjian committed
1636 1637 1638 1639 1640 1641 1642 1643 1644

	SubLink    *n;
	Query	   *result,
			   *intersect_node;
	List	   *elist,
			   *intersect_list = NIL,
			   *intersect,
			   *intersectClause;
	List	   *union_list = NIL,
1645 1646
			   *sortClause,
			   *distinctClause;
Bruce Momjian's avatar
Bruce Momjian committed
1647 1648 1649 1650 1651 1652 1653 1654
	List	   *left_expr,
			   *right_expr,
			   *resnames = NIL;
	char	   *op,
			   *into;
	bool		isBinary,
				isPortal,
				isTemp;
1655 1656
	Node	   *limitOffset,
			   *limitCount;
Bruce Momjian's avatar
Bruce Momjian committed
1657
	CmdType		commandType = CMD_SELECT;
1658
	RangeTblEntry *rtable_insert = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684
	List	   *prev_target = NIL;

	/*
	 * Remember the Resnames of the given parsetree's targetlist (these
	 * are the resnames of the first Select Statement of the query
	 * formulated by the user and he wants the columns named by these
	 * strings. The transformation to DNF can cause another Select
	 * Statment to be the top one which uses other names for its columns.
	 * Therefore we remeber the original names and attach them to the
	 * targetlist of the new topmost Node at the end of this function
	 */
	foreach(elist, parsetree->targetList)
	{
		TargetEntry *tent = (TargetEntry *) lfirst(elist);

		resnames = lappend(resnames, tent->resdom->resname);
	}

	/*
	 * If the Statement is an INSERT INTO ... (SELECT...) statement using
	 * UNIONs, INTERSECTs or EXCEPTs and the transformation to DNF makes
	 * another Node to the top node we have to transform the new top node
	 * to an INSERT node and the original INSERT node to a SELECT node
	 */
	if (parsetree->commandType == CMD_INSERT)
	{
1685

Bruce Momjian's avatar
Bruce Momjian committed
1686 1687 1688 1689
		/*
		 * The result relation ( = the one to insert into) has to be
		 * attached to the rtable list of the new top node
		 */
1690 1691 1692 1693 1694
		rtable_insert = rt_fetch(parsetree->resultRelation, parsetree->rtable);

		parsetree->commandType = CMD_SELECT;
		commandType = CMD_INSERT;
		parsetree->resultRelation = 0;
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1695
	}
Bruce Momjian's avatar
Bruce Momjian committed
1696 1697 1698 1699 1700 1701

	/*
	 * Save some items, to be able to attach them to the resulting top
	 * node at the end of the function
	 */
	sortClause = parsetree->sortClause;
1702
	distinctClause = parsetree->distinctClause;
Bruce Momjian's avatar
Bruce Momjian committed
1703 1704 1705 1706
	into = parsetree->into;
	isBinary = parsetree->isBinary;
	isPortal = parsetree->isPortal;
	isTemp = parsetree->isTemp;
1707 1708
	limitOffset = parsetree->limitOffset;
	limitCount = parsetree->limitCount;
Bruce Momjian's avatar
Bruce Momjian committed
1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763

	/*
	 * The operator tree attached to parsetree->intersectClause is still
	 * 'raw' ( = the leaf nodes are still SelectStmt nodes instead of
	 * Query nodes) So step through the tree and transform the nodes using
	 * parse_analyze().
	 *
	 * The parsetree (given as an argument to Except_Intersect_Rewrite()) has
	 * already been transformed and transforming it again would cause
	 * troubles.  So we give the 'raw' version (of the cooked parsetree)
	 * to the function to prevent an additional transformation. Instead we
	 * hand back the 'cooked' version also given as an argument to
	 * intersect_tree_analyze()
	 */
	intersectClause =
		(List *) intersect_tree_analyze((Node *) parsetree->intersectClause,
								 (Node *) lfirst(parsetree->unionClause),
										(Node *) parsetree);

	/* intersectClause is no longer needed so set it to NIL */
	parsetree->intersectClause = NIL;

	/*
	 * unionClause will be needed later on but the list it delivered is no
	 * longer needed, so set it to NIL
	 */
	parsetree->unionClause = NIL;

	/*
	 * Transform the operator tree to DNF (remember ANDs and ORs have been
	 * exchanged, that's why we get DNF by using cnfify)
	 *
	 * After the call, explicit ANDs are removed and all AND operands are
	 * simply items in the intersectClause list
	 */
	intersectClause = cnfify((Expr *) intersectClause, true);

	/*
	 * For every entry of the intersectClause list we generate one entry
	 * in the union_list
	 */
	foreach(intersect, intersectClause)
	{

		/*
		 * for every OR we create an IN subselect and for every OR NOT we
		 * create a NOT IN subselect, so first extract all the Select
		 * Query nodes from the tree (that contains only OR or OR NOTs any
		 * more because we did a transformation to DNF
		 *
		 * There must be at least one node that is not negated (i.e. just OR
		 * and not OR NOT) and this node will be the first in the list
		 * returned
		 */
		intersect_list = NIL;
1764
		create_intersect_list((Node *) lfirst(intersect), &intersect_list);
Bruce Momjian's avatar
Bruce Momjian committed
1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837

		/*
		 * This one will become the Select Query node, all other nodes are
		 * transformed into subselects under this node!
		 */
		intersect_node = (Query *) lfirst(intersect_list);
		intersect_list = lnext(intersect_list);

		/*
		 * Check if all Select Statements use the same number of
		 * attributes and if all corresponding attributes are of the same
		 * type
		 */
		if (prev_target)
			check_targetlists_are_compatible(prev_target, intersect_node->targetList);
		prev_target = intersect_node->targetList;
		/* End of check for corresponding targetlists */

		/*
		 * Transform all nodes remaining into subselects and add them to
		 * the qualifications of the Select Query node
		 */
		while (intersect_list != NIL)
		{

			n = makeNode(SubLink);

			/* Here we got an OR so transform it to an IN subselect */
			if (IsA(lfirst(intersect_list), Query))
			{

				/*
				 * Check if all Select Statements use the same number of
				 * attributes and if all corresponding attributes are of
				 * the same type
				 */
				check_targetlists_are_compatible(prev_target,
						 ((Query *) lfirst(intersect_list))->targetList);
				/* End of check for corresponding targetlists */

				n->subselect = lfirst(intersect_list);
				op = "=";
				n->subLinkType = ANY_SUBLINK;
				n->useor = false;
			}

			/*
			 * Here we got an OR NOT node so transform it to a NOT IN
			 * subselect
			 */
			else
			{

				/*
				 * Check if all Select Statements use the same number of
				 * attributes and if all corresponding attributes are of
				 * the same type
				 */
				check_targetlists_are_compatible(prev_target,
												 ((Query *) lfirst(((Expr *) lfirst(intersect_list))->args))->targetList);
				/* End of check for corresponding targetlists */

				n->subselect = (Node *) lfirst(((Expr *) lfirst(intersect_list))->args);
				op = "<>";
				n->subLinkType = ALL_SUBLINK;
				n->useor = true;
			}

			/*
			 * Prepare the lefthand side of the Sublinks: All the entries
			 * of the targetlist must be (IN) or must not be (NOT IN) the
			 * subselect
			 */
1838
			n->lefthand = NIL;
Bruce Momjian's avatar
Bruce Momjian committed
1839 1840
			foreach(elist, intersect_node->targetList)
			{
1841
				TargetEntry *tent = (TargetEntry *) lfirst(elist);
Bruce Momjian's avatar
Bruce Momjian committed
1842 1843 1844 1845 1846

				n->lefthand = lappend(n->lefthand, tent->expr);
			}

			/*
1847
			 * Also prepare the list of Opers that must be used for the
1848 1849
			 * comparisons (they depend on the specific datatypes
			 * involved!)
Bruce Momjian's avatar
Bruce Momjian committed
1850 1851 1852
			 */
			left_expr = n->lefthand;
			right_expr = ((Query *) (n->subselect))->targetList;
1853
			n->oper = NIL;
Bruce Momjian's avatar
Bruce Momjian committed
1854 1855 1856 1857

			foreach(elist, left_expr)
			{
				Node	   *lexpr = lfirst(elist);
1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871
				TargetEntry *tent = (TargetEntry *) lfirst(right_expr);
				Operator	optup;
				Form_pg_operator opform;
				Oper	   *newop;

				optup = oper(op,
							 exprType(lexpr),
							 exprType(tent->expr),
							 FALSE);
				opform = (Form_pg_operator) GETSTRUCT(optup);

				if (opform->oprresult != BOOLOID)
					elog(ERROR, "parser: '%s' must return 'bool' to be used with quantified predicate subquery", op);

1872 1873
				newop = makeOper(oprid(optup),	/* opno */
								 InvalidOid,	/* opid */
1874 1875 1876
								 opform->oprresult,
								 0,
								 NULL);
Bruce Momjian's avatar
Bruce Momjian committed
1877

1878
				n->oper = lappend(n->oper, newop);
Bruce Momjian's avatar
Bruce Momjian committed
1879 1880 1881 1882 1883 1884 1885 1886

				right_expr = lnext(right_expr);
			}

			/*
			 * If the Select Query node has aggregates in use add all the
			 * subselects to the HAVING qual else to the WHERE qual
			 */
1887
			if (intersect_node->hasAggs)
Bruce Momjian's avatar
Bruce Momjian committed
1888
				AddHavingQual(intersect_node, (Node *) n);
1889 1890
			else
				AddQual(intersect_node, (Node *) n);
Bruce Momjian's avatar
Bruce Momjian committed
1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905

			/* Now we got sublinks */
			intersect_node->hasSubLinks = true;
			intersect_list = lnext(intersect_list);
		}
		intersect_node->intersectClause = NIL;
		union_list = lappend(union_list, intersect_node);
	}

	/* The first entry to union_list is our new top node */
	result = (Query *) lfirst(union_list);
	/* attach the rest to unionClause */
	result->unionClause = lnext(union_list);
	/* Attach all the items remembered in the beginning of the function */
	result->sortClause = sortClause;
1906
	result->distinctClause = distinctClause;
Bruce Momjian's avatar
Bruce Momjian committed
1907 1908 1909 1910
	result->into = into;
	result->isPortal = isPortal;
	result->isBinary = isBinary;
	result->isTemp = isTemp;
1911 1912
	result->limitOffset = limitOffset;
	result->limitCount = limitCount;
Bruce Momjian's avatar
Bruce Momjian committed
1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935

	/*
	 * The relation to insert into is attached to the range table of the
	 * new top node
	 */
	if (commandType == CMD_INSERT)
	{
		result->rtable = lappend(result->rtable, rtable_insert);
		result->resultRelation = length(result->rtable);
		result->commandType = commandType;
	}

	/*
	 * The resnames of the originally first SelectStatement are attached
	 * to the new first SelectStatement
	 */
	foreach(elist, result->targetList)
	{
		TargetEntry *tent = (TargetEntry *) lfirst(elist);

		tent->resdom->resname = lfirst(resnames);
		resnames = lnext(resnames);
	}
1936

Bruce Momjian's avatar
Bruce Momjian committed
1937
	return result;
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1938 1939 1940 1941 1942 1943 1944
}

/* Create a list of nodes that are either Query nodes of NOT Expr
 * nodes followed by a Query node. The tree given in ptr contains at
 * least one non negated Query node. This node is attached to the
 * beginning of the list */

1945 1946
static void
create_intersect_list(Node *ptr, List **intersect_list)
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1947
{
Bruce Momjian's avatar
Bruce Momjian committed
1948 1949 1950
	List	   *arg;

	if (IsA(ptr, Query))
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1951
	{
Bruce Momjian's avatar
Bruce Momjian committed
1952 1953 1954
		/* The non negated node is attached at the beginning (lcons) */
		*intersect_list = lcons(ptr, *intersect_list);
		return;
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1955
	}
Bruce Momjian's avatar
Bruce Momjian committed
1956 1957

	if (IsA(ptr, Expr))
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1958
	{
Bruce Momjian's avatar
Bruce Momjian committed
1959 1960 1961 1962 1963 1964 1965 1966 1967
		if (((Expr *) ptr)->opType == NOT_EXPR)
		{
			/* negated nodes are appended to the end (lappend) */
			*intersect_list = lappend(*intersect_list, ptr);
			return;
		}
		else
		{
			foreach(arg, ((Expr *) ptr)->args)
1968
				create_intersect_list(lfirst(arg), intersect_list);
Bruce Momjian's avatar
Bruce Momjian committed
1969 1970 1971
			return;
		}
		return;
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1972 1973 1974 1975 1976
	}
}

/* The nodes given in 'tree' are still 'raw' so 'cook' them using parse_analyze().
 * The node given in first_select has already been cooked, so don't transform
Bruce Momjian's avatar
Bruce Momjian committed
1977
 * it again but return a pointer to the previously cooked version given in 'parsetree'
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1978
 * instead. */
1979
static Node *
Bruce Momjian's avatar
Bruce Momjian committed
1980
intersect_tree_analyze(Node *tree, Node *first_select, Node *parsetree)
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
1981
{
Bruce Momjian's avatar
Bruce Momjian committed
1982 1983
	Node	   *result = (Node *) NIL;
	List	   *arg;
1984 1985

	if (IsA(tree, SelectStmt))
Bruce Momjian's avatar
Bruce Momjian committed
1986 1987 1988 1989 1990 1991
	{

		/*
		 * If we get to the tree given in first_select return parsetree
		 * instead of performing parse_analyze()
		 */
1992 1993 1994
		if (tree == first_select)
			result = parsetree;
		else
Bruce Momjian's avatar
Bruce Momjian committed
1995 1996 1997 1998
		{
			/* transform the 'raw' nodes to 'cooked' Query nodes */
			List	   *qtree = parse_analyze(lcons(tree, NIL), NULL);

1999 2000
			result = (Node *) lfirst(qtree);
		}
Bruce Momjian's avatar
Bruce Momjian committed
2001
	}
2002

Bruce Momjian's avatar
Bruce Momjian committed
2003
	if (IsA(tree, Expr))
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
2004
	{
Bruce Momjian's avatar
Bruce Momjian committed
2005 2006 2007 2008
		/* Call recursively for every argument of the node */
		foreach(arg, ((Expr *) tree)->args)
			lfirst(arg) = intersect_tree_analyze(lfirst(arg), first_select, parsetree);
		result = tree;
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
2009
	}
Bruce Momjian's avatar
Bruce Momjian committed
2010
	return result;
Bruce Momjian's avatar
Hi!  
Bruce Momjian committed
2011
}