rewriteHandler.c 24.2 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * rewriteHandler.c
4
 *
5
 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
B
Add:  
Bruce Momjian 已提交
6
 * 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.88 2001/01/24 19:43:05 momjian Exp $
11 12 13 14 15
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

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


static RewriteInfo *gatherRewriteMeta(Query *parsetree,
				  Query *rule_action,
				  Node *rule_qual,
				  int rt_index,
				  CmdType event,
39
				  bool instead_flag);
40
static List *adjustJoinTreeList(Query *parsetree, int rt_index, bool *found);
41
static void markQueryForUpdate(Query *qry, bool skipOldNew);
42 43
static List *matchLocks(CmdType event, RuleLock *rulelocks,
						int varno, Query *parsetree);
44
static Query *fireRIRrules(Query *parsetree);
45

46 47 48

/*
 * gatherRewriteMeta -
49 50 51
 *	  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
52 53
 */
static RewriteInfo *
54 55 56
gatherRewriteMeta(Query *parsetree,
				  Query *rule_action,
				  Node *rule_qual,
57 58
				  int rt_index,
				  CmdType event,
59
				  bool instead_flag)
60
{
61
	RewriteInfo *info;
62 63
	Query	   *sub_action;
	Query	  **sub_action_ptr;
64
	int			rt_length;
65 66 67 68

	info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
	info->rt_index = rt_index;
	info->event = event;
69
	info->instead_flag = instead_flag;
70 71 72 73
	info->rule_action = (Query *) copyObject(rule_action);
	info->rule_qual = (Node *) copyObject(rule_qual);
	if (info->rule_action == NULL)
	{
74 75 76 77 78 79 80 81
		info->nothing = TRUE;
		return info;
	}
	info->nothing = FALSE;
	info->action = info->rule_action->commandType;
	info->current_varno = rt_index;
	rt_length = length(parsetree->rtable);
	info->new_varno = PRS2_NEW_VARNO + rt_length;
82

83 84 85 86 87 88 89 90 91
	/*
	 * Adjust rule action and qual to offset its varnos, so that we can
	 * merge its rtable into the main parsetree's rtable.
	 *
	 * If the rule action is an INSERT...SELECT, the OLD/NEW rtable
	 * entries will be in the SELECT part, and we have to modify that
	 * rather than the top-level INSERT (kluge!).
	 */
	sub_action = getInsertSelectQuery(info->rule_action, &sub_action_ptr);
92

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
	OffsetVarNodes((Node *) sub_action, rt_length, 0);
	OffsetVarNodes(info->rule_qual, rt_length, 0);
	/* but references to *OLD* should point at original rt_index */
	ChangeVarNodes((Node *) sub_action,
				   PRS2_OLD_VARNO + rt_length, rt_index, 0);
	ChangeVarNodes(info->rule_qual,
				   PRS2_OLD_VARNO + rt_length, rt_index, 0);

	/*
	 * We want the main parsetree's rtable to end up as the concatenation
	 * of its original contents plus those of all the relevant rule
	 * actions.  Also store same into all the rule_action rtables.
	 * Some of the entries may be unused after we finish rewriting, but
	 * if we tried to clean those out we'd have a much harder job to
	 * adjust RT indexes in the query's Vars.  It's OK to have unused
	 * RT entries, since planner will ignore them.
	 *
	 * NOTE KLUGY HACK: we assume the parsetree rtable had at least one
	 * entry to begin with (OK enough, else where'd the rule come from?).
	 * Because of this, if multiple rules nconc() their rtable additions
	 * onto parsetree->rtable, they'll all see the same rtable because
	 * they all have the same list head pointer.
	 */
	parsetree->rtable = nconc(parsetree->rtable,
							  sub_action->rtable);
	sub_action->rtable = parsetree->rtable;
119

120 121 122 123 124 125 126
	/*
	 * Each rule action's jointree should be the main parsetree's jointree
	 * plus that rule's jointree, but *without* the original rtindex
	 * that we're replacing (if present, which it won't be for INSERT).
	 * Note that if the rule refers to OLD, its jointree will add back
	 * a reference to rt_index.
	 */
127
	if (sub_action->jointree != NULL)
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
	{
		bool	found;
		List   *newjointree = adjustJoinTreeList(parsetree,
												 rt_index,
												 &found);

		sub_action->jointree->fromlist =
			nconc(newjointree, sub_action->jointree->fromlist);
	}

	/*
	 * We copy the qualifications of the parsetree to the action and vice
	 * versa. So force hasSubLinks if one of them has it. If this is not
	 * right, the flag will get cleared later, but we mustn't risk having
	 * it not set when it needs to be.
	 */
	if (parsetree->hasSubLinks)
		sub_action->hasSubLinks = TRUE;
	else if (sub_action->hasSubLinks)
		parsetree->hasSubLinks = TRUE;

	/*
	 * Event Qualification forces copying of parsetree and
	 * splitting into two queries one w/rule_qual, one w/NOT
	 * rule_qual. Also add user query qual onto rule action
	 */
	AddQual(sub_action, info->rule_qual);

	AddQual(sub_action, parsetree->jointree->quals);

	/*
	 * Rewrite new.attribute w/ right hand side of target-list
	 * entry for appropriate field name in insert/update.
	 *
	 * KLUGE ALERT: since ResolveNew returns a mutated copy, we can't just
	 * apply it to sub_action; we have to remember to update the sublink
	 * inside info->rule_action, too.
	 */
	if (info->event == CMD_INSERT || info->event == CMD_UPDATE)
	{
		sub_action = (Query *) ResolveNew((Node *) sub_action,
										  info->new_varno,
										  0,
										  parsetree->targetList,
										  info->event,
										  info->current_varno);
		if (sub_action_ptr)
			*sub_action_ptr = sub_action;
		else
			info->rule_action = sub_action;
178
	}
179

180
	return info;
181 182
}

183
/*
184 185
 * Copy the query's jointree list, and attempt to remove any occurrence
 * of the given rt_index as a top-level join item (we do not look for it
186 187 188
 * within join items; this is OK because we are only expecting to find it
 * as an UPDATE or DELETE target relation, which will be at the top level
 * of the join).  Returns modified jointree list --- original list
189
 * is not changed.  *found is set to indicate if we found the rt_index.
190
 */
191
static List *
192
adjustJoinTreeList(Query *parsetree, int rt_index, bool *found)
193
{
194
	List	   *newjointree = listCopy(parsetree->jointree->fromlist);
195
	List	   *jjt;
196

197 198
	*found = false;
	foreach(jjt, newjointree)
B
Bruce Momjian 已提交
199
	{
200
		RangeTblRef *rtr = lfirst(jjt);
201

202 203 204 205 206 207
		if (IsA(rtr, RangeTblRef) && rtr->rtindex == rt_index)
		{
			newjointree = lremove(rtr, newjointree);
			*found = true;
			break;
		}
208
	}
209
	return newjointree;
210
}
211

212

213
/*
214 215
 * matchLocks -
 *	  match the list of locks and returns the matching rules
216
 */
217 218 219 220 221
static List *
matchLocks(CmdType event,
		   RuleLock *rulelocks,
		   int varno,
		   Query *parsetree)
222
{
223 224 225
	List	   *real_locks = NIL;
	int			nlocks;
	int			i;
226

227 228
	Assert(rulelocks != NULL);	/* we get called iff there is some lock */
	Assert(parsetree != NULL);
229

230
	if (parsetree->commandType != CMD_SELECT)
231
	{
232 233
		if (parsetree->resultRelation != varno)
			return NIL;
234
	}
235

236
	nlocks = rulelocks->numLocks;
237

238
	for (i = 0; i < nlocks; i++)
B
Bruce Momjian 已提交
239
	{
240
		RewriteRule *oneLock = rulelocks->rules[i];
241

242
		if (oneLock->event == event)
243
		{
244 245 246 247 248 249
			if (parsetree->commandType != CMD_SELECT ||
				(oneLock->attrno == -1 ?
				 rangeTableEntry_used((Node *) parsetree, varno, 0) :
				 attribute_used((Node *) parsetree,
								varno, oneLock->attrno, 0)))
				real_locks = lappend(real_locks, oneLock);
250
		}
251
	}
252 253

	return real_locks;
254 255
}

256

257 258 259 260 261 262 263 264 265 266 267
static Query *
ApplyRetrieveRule(Query *parsetree,
				  RewriteRule *rule,
				  int rt_index,
				  bool relation_level,
				  Relation relation,
				  bool relIsUsed)
{
	Query	   *rule_action;
	RangeTblEntry *rte,
			   *subrte;
268

269 270 271 272 273 274
	if (length(rule->actions) != 1)
		elog(ERROR, "ApplyRetrieveRule: expected just one rule action");
	if (rule->qual != NULL)
		elog(ERROR, "ApplyRetrieveRule: can't handle qualified ON SELECT rule");
	if (! relation_level)
		elog(ERROR, "ApplyRetrieveRule: can't handle per-attribute ON SELECT rule");
275

276
	/*
277 278
	 * Make a modifiable copy of the view query, and recursively expand
	 * any view references inside it.
279
	 */
280
	rule_action = copyObject(lfirst(rule->actions));
281

282
	rule_action = fireRIRrules(rule_action);
283

284
	/*
285 286
	 * VIEWs are really easy --- just plug the view query in as a subselect,
	 * replacing the relation's original RTE.
287
	 */
288
	rte = rt_fetch(rt_index, parsetree->rtable);
289

290 291 292 293
	rte->relname = NULL;
	rte->relid = InvalidOid;
	rte->subquery = rule_action;
	rte->inh = false;			/* must not be set for a subquery */
294

295
	/*
296 297
	 * We move the view's permission check data down to its rangetable.
	 * The checks will actually be done against the *OLD* entry therein.
298
	 */
299 300 301 302
	subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
	Assert(subrte->relid == relation->rd_id);
	subrte->checkForRead = rte->checkForRead;
	subrte->checkForWrite = rte->checkForWrite;
303

304 305
	rte->checkForRead = false;	/* no permission check on subquery itself */
	rte->checkForWrite = false;
306

307
	/*
308
	 * FOR UPDATE of view?
309
	 */
310
	if (intMember(rt_index, parsetree->rowMarks))
311
	{
312
		/*
313 314 315
		 * Remove the view from the list of rels that will actually be
		 * marked FOR UPDATE by the executor.  It will still be access-
		 * checked for write access, though.
316
		 */
317
		parsetree->rowMarks = lremovei(rt_index, parsetree->rowMarks);
B
Bruce Momjian 已提交
318 319

		/*
320
		 * Set up the view's referenced tables as if FOR UPDATE.
321
		 */
322
		markQueryForUpdate(rule_action, true);
323 324
	}

325
	return parsetree;
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
/*
 * Recursively mark all relations used by a view as FOR UPDATE.
 *
 * This may generate an invalid query, eg if some sub-query uses an
 * aggregate.  We leave it to the planner to detect that.
 *
 * NB: this must agree with the parser's transformForUpdate() routine.
 */
static void
markQueryForUpdate(Query *qry, bool skipOldNew)
{
	Index		rti = 0;
	List	   *l;

	foreach(l, qry->rtable)
	{
		RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);

		rti++;

		/* Ignore OLD and NEW entries if we are at top level of view */
		if (skipOldNew &&
			(rti == PRS2_OLD_VARNO || rti == PRS2_NEW_VARNO))
			continue;

		if (rte->subquery)
		{
			/* FOR UPDATE of subquery is propagated to subquery's rels */
			markQueryForUpdate(rte->subquery, false);
		}
		else
		{
			if (!intMember(rti, qry->rowMarks))
				qry->rowMarks = lappendi(qry->rowMarks, rti);
			rte->checkForWrite = true;
		}
	}
}

367

368
/*
369 370 371
 * fireRIRonSubLink -
 *	Apply fireRIRrules() to each SubLink (subselect in expression) found
 *	in the given tree.
372 373
 *
 * NOTE: although this has the form of a walker, we cheat and modify the
374
 * SubLink nodes in-place.	It is caller's responsibility to ensure that
375
 * no unwanted side-effects occur!
376 377 378 379
 *
 * This is unlike most of the other routines that recurse into subselects,
 * because we must take control at the SubLink node in order to replace
 * the SubLink's subselect link with the possibly-rewritten subquery.
380 381
 */
static bool
382
fireRIRonSubLink(Node *node, void *context)
383 384
{
	if (node == NULL)
385 386
		return false;
	if (IsA(node, SubLink))
B
Bruce Momjian 已提交
387
	{
388 389 390
		SubLink    *sub = (SubLink *) node;

		/* Do what we came for */
391 392
		sub->subselect = (Node *) fireRIRrules((Query *) (sub->subselect));
		/* Fall through to process lefthand args of SubLink */
393
	}
394 395
	/*
	 * Do NOT recurse into Query nodes, because fireRIRrules already
396
	 * processed subselects of subselects for us.
397
	 */
398
	return expression_tree_walker(node, fireRIRonSubLink,
399
								  (void *) context);
400 401 402 403 404 405 406 407 408 409
}


/*
 * fireRIRrules -
 *	Apply all RIR rules on each rangetable entry in a query
 */
static Query *
fireRIRrules(Query *parsetree)
{
B
Bruce Momjian 已提交
410
	int			rt_index;
411

412 413 414
	/*
	 * don't try to convert this into a foreach loop, because rtable list
	 * can get changed each time through...
415
	 */
416
	rt_index = 0;
B
Bruce Momjian 已提交
417 418
	while (rt_index < length(parsetree->rtable))
	{
419 420 421 422 423 424 425 426 427 428
		RangeTblEntry *rte;
		Relation	rel;
		List	   *locks;
		RuleLock   *rules;
		RewriteRule *rule;
		LOCKMODE	lockmode;
		bool		relIsUsed;
		int			i;
		List	   *l;

429 430
		++rt_index;

431
		rte = rt_fetch(rt_index, parsetree->rtable);
432

433 434 435 436 437 438 439 440 441 442 443
		/*
		 * A subquery RTE can't have associated rules, so there's nothing
		 * to do to this level of the query, but we must recurse into the
		 * subquery to expand any rule references in it.
		 */
		if (rte->subquery)
		{
			rte->subquery = fireRIRrules(rte->subquery);
			continue;
		}

444
		/*
445 446 447
		 * 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
448 449
		 * part of the join set (a source table), or is referenced by any
		 * Var nodes, or is the result table.
450
		 */
451 452 453
		relIsUsed = rangeTableEntry_used((Node *) parsetree, rt_index, 0);

		if (!relIsUsed && rt_index != parsetree->resultRelation)
454
			continue;
B
Bruce Momjian 已提交
455

456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
		/*
		 * This may well be the first access to the relation during
		 * the current statement (it will be, if this Query was extracted
		 * from a rule or somehow got here other than via the parser).
		 * Therefore, grab the appropriate lock type for the relation,
		 * and do not release it until end of transaction.  This protects
		 * the rewriter and planner against schema changes mid-query.
		 *
		 * If the relation is the query's result relation, then RewriteQuery()
		 * already got the right lock on it, so we need no additional lock.
		 * Otherwise, check to see if the relation is accessed FOR UPDATE
		 * or not.
		 */
		if (rt_index == parsetree->resultRelation)
			lockmode = NoLock;
		else if (intMember(rt_index, parsetree->rowMarks))
			lockmode = RowShareLock;
		else
			lockmode = AccessShareLock;

		rel = heap_openr(rte->relname, lockmode);

478 479
		rules = rel->rd_rules;
		if (rules == NULL)
B
Bruce Momjian 已提交
480
		{
481
			heap_close(rel, NoLock);
482 483 484 485 486 487
			continue;
		}

		/*
		 * Collect the RIR rules that we must apply
		 */
488
		locks = NIL;
B
Bruce Momjian 已提交
489 490
		for (i = 0; i < rules->numLocks; i++)
		{
491 492 493
			rule = rules->rules[i];
			if (rule->event != CMD_SELECT)
				continue;
B
Bruce Momjian 已提交
494

495 496 497
			if (rule->attrno > 0)
			{
				/* per-attr rule; do we need it? */
498
				if (!attribute_used((Node *) parsetree, rt_index,
499
									rule->attrno, 0))
500 501
					continue;
			}
502 503 504 505 506 507 508

			locks = lappend(locks, rule);
		}

		/*
		 * Now apply them
		 */
B
Bruce Momjian 已提交
509 510
		foreach(l, locks)
		{
511 512
			rule = lfirst(l);

513
			parsetree = ApplyRetrieveRule(parsetree,
514
										  rule,
515
										  rt_index,
516
										  rule->attrno == -1,
517
										  rel,
518
										  relIsUsed);
519 520
		}

521
		heap_close(rel, NoLock);
522 523
	}

524 525 526 527
	/*
	 * Recurse into sublink subqueries, too.
	 */
	if (parsetree->hasSubLinks)
528 529
		query_tree_walker(parsetree, fireRIRonSubLink, NULL,
						  false /* already handled the ones in rtable */);
530

531 532 533 534 535 536 537 538 539 540 541 542 543 544
	/*
	 * If the query was marked having aggregates, check if this is
	 * still true after rewriting.	Ditto for sublinks.  Note there
	 * should be no aggs in the qual at this point.  (Does this code
	 * still do anything useful?  The view-becomes-subselect-in-FROM
	 * approach doesn't look like it could remove aggs or sublinks...)
	 */
	if (parsetree->hasAggs)
	{
		parsetree->hasAggs = checkExprHasAggs((Node *) parsetree);
		if (parsetree->hasAggs)
			if (checkExprHasAggs((Node *) parsetree->jointree))
				elog(ERROR, "fireRIRrules: failed to remove aggs from qual");
	}
545
	if (parsetree->hasSubLinks)
546 547 548
	{
		parsetree->hasSubLinks = checkExprHasSubLink((Node *) parsetree);
	}
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
	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);
576
		}
577 578
		else
			regular = lappend(regular, rule_lock);
579
	}
580
	return nconc(nconc(regular, instead_qualified), instead_rules);
581 582
}

583

584 585 586 587 588 589 590 591 592 593 594
/*
 * Modify the given query by adding 'AND NOT rule_qual' to its qualification.
 * This is used to generate suitable "else clauses" for conditional INSTEAD
 * rules.
 *
 * The rule_qual may contain references to OLD or NEW.  OLD references are
 * replaced by references to the specified rt_index (the relation that the
 * rule applies to).  NEW references are only possible for INSERT and UPDATE
 * queries on the relation itself, and so they should be replaced by copies
 * of the related entries in the query's own targetlist.
 */
595
static Query *
596 597
CopyAndAddQual(Query *parsetree,
			   Node *rule_qual,
598 599
			   int rt_index,
			   CmdType event)
600
{
601
	Query	   *new_tree = (Query *) copyObject(parsetree);
602 603 604 605 606 607 608 609 610 611 612 613 614
	Node	   *new_qual = (Node *) copyObject(rule_qual);

	/* Fix references to OLD */
	ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);
	/* Fix references to NEW */
	if (event == CMD_INSERT || event == CMD_UPDATE)
		new_qual = ResolveNew(new_qual,
							  PRS2_NEW_VARNO,
							  0,
							  parsetree->targetList,
							  event,
							  rt_index);
	/* And attach the fixed qual */
615 616 617
	AddNotQual(new_tree, new_qual);

	return new_tree;
618 619 620
}


621

622
/*
623
 *	fireRules -
M
 
Marc G. Fournier 已提交
624 625 626 627 628
 *	   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.
629
 *
M
 
Marc G. Fournier 已提交
630
 *	   remember: reality is for dead birds -- glass
631 632
 *
 */
633
static List *
634
fireRules(Query *parsetree,
635 636
		  int rt_index,
		  CmdType event,
637 638 639
		  bool *instead_flag,
		  List *locks,
		  List **qual_products)
640
{
641 642
	List	   *results = NIL;
	List	   *i;
643 644 645

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

M
 
Marc G. Fournier 已提交
648
	locks = orderRules(locks);	/* real instead rules last */
649

650 651
	foreach(i, locks)
	{
652
		RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
653
		Node	   *event_qual;
654 655
		List	   *actions;
		List	   *r;
656 657 658 659 660

		/* multiple rule action time */
		*instead_flag = rule_lock->isInstead;
		event_qual = rule_lock->qual;
		actions = rule_lock->actions;
661

662 663 664
		if (event_qual != NULL && *instead_flag)
		{
			Query	   *qual_product;
M
 
Marc G. Fournier 已提交
665 666 667 668 669

			/* ----------
			 * If there are instead rules with qualifications,
			 * the original query is still performed. But all
			 * the negated rule qualifications of the instead
B
Bruce Momjian 已提交
670
			 * rules are added so it does its actions only
M
 
Marc G. Fournier 已提交
671 672 673 674 675 676 677
			 * 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.
			 * ----------
			 */
678
			if (*qual_products == NIL)
M
 
Marc G. Fournier 已提交
679
				qual_product = parsetree;
680
			else
681
				qual_product = (Query *) lfirst(*qual_products);
M
 
Marc G. Fournier 已提交
682

683 684 685 686 687
			qual_product = CopyAndAddQual(qual_product,
										  event_qual,
										  rt_index,
										  event);

688
			*qual_products = makeList1(qual_product);
M
 
Marc G. Fournier 已提交
689 690
		}

691 692
		foreach(r, actions)
		{
693
			Query	   *rule_action = lfirst(r);
694
			RewriteInfo *info;
695

M
 
Marc G. Fournier 已提交
696 697 698
			if (rule_action->commandType == CMD_NOTHING)
				continue;

699
			info = gatherRewriteMeta(parsetree, rule_action, event_qual,
700
									 rt_index, event, *instead_flag);
701 702 703 704 705 706 707 708 709 710 711 712 713 714

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

			results = lappend(results, info->rule_action);

			pfree(info);
		}
M
 
Marc G. Fournier 已提交
715 716 717 718 719 720

		/* ----------
		 * If this was an unqualified instead rule,
		 * throw away an eventually saved 'default' parsetree
		 * ----------
		 */
721
		if (event_qual == NULL && *instead_flag)
M
 
Marc G. Fournier 已提交
722
			*qual_products = NIL;
723 724
	}
	return results;
725 726
}

M
 
Marc G. Fournier 已提交
727 728


729
static List *
730
RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
731
{
732
	CmdType		event;
B
Bruce Momjian 已提交
733
	List	   *product_queries = NIL;
734
	int			result_relation;
B
Bruce Momjian 已提交
735
	RangeTblEntry *rt_entry;
736 737
	Relation	rt_entry_relation;
	RuleLock   *rt_entry_locks;
738

739 740 741 742
	Assert(parsetree != NULL);

	event = parsetree->commandType;

743
	/*
B
Bruce Momjian 已提交
744 745
	 * SELECT rules are handled later when we have all the queries that
	 * should get executed
746 747 748 749 750 751 752
	 */
	if (event == CMD_SELECT)
		return NIL;

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

756
	/*
B
Bruce Momjian 已提交
757
	 * the statement is an update, insert or delete - fire rules on it.
758
	 */
759
	result_relation = parsetree->resultRelation;
760
	Assert(result_relation != 0);
761
	rt_entry = rt_fetch(result_relation, parsetree->rtable);
762 763 764 765 766 767 768 769 770 771 772

	/*
	 * This may well be the first access to the result relation during
	 * the current statement (it will be, if this Query was extracted
	 * from a rule or somehow got here other than via the parser).
	 * Therefore, grab the appropriate lock type for a result relation,
	 * and do not release it until end of transaction.  This protects the
	 * rewriter and planner against schema changes mid-query.
	 */
	rt_entry_relation = heap_openr(rt_entry->relname, RowExclusiveLock);

773
	rt_entry_locks = rt_entry_relation->rd_rules;
M
 
Marc G. Fournier 已提交
774

775
	if (rt_entry_locks != NULL)
776
	{
777 778
		List	   *locks = matchLocks(event, rt_entry_locks,
									   result_relation, parsetree);
779

780
		product_queries = fireRules(parsetree,
B
Bruce Momjian 已提交
781 782 783 784 785
									result_relation,
									event,
									instead_flag,
									locks,
									qual_products);
786
	}
787

788
	heap_close(rt_entry_relation, NoLock); /* keep lock! */
789

790
	return product_queries;
791 792
}

793

794 795
/*
 * to avoid infinite recursion, we restrict the number of times a query
796
 * can be rewritten. Detecting cycles is left for the reader as an exercise.
797 798
 */
#ifndef REWRITE_INVOKE_MAX
799
#define REWRITE_INVOKE_MAX		10
800 801
#endif

802
static int	numQueryRewriteInvoked = 0;
803 804 805

/*
 * deepRewriteQuery -
806
 *	  rewrites the query and apply the rules again on the queries rewritten
807
 */
808
static List *
809
deepRewriteQuery(Query *parsetree)
810
{
811 812
	List	   *n;
	List	   *rewritten = NIL;
813
	List	   *result;
814 815
	bool		instead;
	List	   *qual_products = NIL;
816 817 818

	if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
	{
819
		elog(ERROR, "query rewritten %d times, may contain cycles",
820 821 822 823 824
			 numQueryRewriteInvoked - 1);
	}

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

826 827
	foreach(n, result)
	{
828
		Query	   *pt = lfirst(n);
829
		List	   *newstuff;
830 831 832 833 834

		newstuff = deepRewriteQuery(pt);
		if (newstuff != NIL)
			rewritten = nconc(rewritten, newstuff);
	}
M
 
Marc G. Fournier 已提交
835 836 837 838 839 840

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

M
 
Marc G. Fournier 已提交
844
	/* ----------
845
	 * The original query is appended last (if no "instead" rule)
M
 
Marc G. Fournier 已提交
846 847 848 849 850 851 852 853 854 855 856
	 * 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);

857 858
	return rewritten;
}
859 860 861


/*
862
 * QueryRewriteOne -
863 864 865 866 867 868 869 870 871 872 873 874 875 876 877
 *	  rewrite one query
 */
static List *
QueryRewriteOne(Query *parsetree)
{
	numQueryRewriteInvoked = 0;

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


/*
878 879 880 881 882 883 884
 * 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.
885
 */
886 887
List *
QueryRewrite(Query *parsetree)
888
{
B
Bruce Momjian 已提交
889 890 891
	List	   *querylist;
	List	   *results = NIL;
	List	   *l;
892 893 894 895 896 897 898 899 900

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

	/*
901
	 * Step 2
902 903 904
	 *
	 * Apply all the RIR rules on each query
	 */
B
Bruce Momjian 已提交
905 906
	foreach(l, querylist)
	{
907
		Query   *query = (Query *) lfirst(l);
B
Bruce Momjian 已提交
908

909
		query = fireRIRrules(query);
910

B
Bruce Momjian 已提交
911
		/*
912
		 * If the query target was rewritten as a view, complain.
B
Bruce Momjian 已提交
913
		 */
914
		if (query->resultRelation)
B
Bruce Momjian 已提交
915
		{
916 917
			RangeTblEntry *rte = rt_fetch(query->resultRelation,
										  query->rtable);
B
Bruce Momjian 已提交
918

919
			if (rte->subquery)
B
Bruce Momjian 已提交
920
			{
921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936
				switch (query->commandType)
				{
					case CMD_INSERT:
						elog(ERROR, "Cannot insert into a view without an appropriate rule");
						break;
					case CMD_UPDATE:
						elog(ERROR, "Cannot update a view without an appropriate rule");
						break;
					case CMD_DELETE:
						elog(ERROR, "Cannot delete from a view without an appropriate rule");
						break;
					default:
						elog(ERROR, "QueryRewrite: unexpected commandType %d",
							 (int) query->commandType);
						break;
				}
B
Bruce Momjian 已提交
937 938 939
			}
		}

940
		results = lappend(results, query);
B
Bruce Momjian 已提交
941
	}
942

943
	return results;
B
Hi!  
Bruce Momjian 已提交
944
}