rewriteHandler.c 15.8 KB
Newer Older
1 2 3 4 5 6 7 8
/*-------------------------------------------------------------------------
 *
 * rewriteHandler.c--
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
9
 *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.5 1997/09/08 02:28:17 momjian Exp $
10 11 12 13 14 15 16 17
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"
#include "miscadmin.h"
#include "utils/palloc.h"
#include "utils/elog.h"
#include "utils/rel.h"
18
#include "nodes/pg_list.h"
19 20
#include "nodes/primnodes.h"

21
#include "parser/parsetree.h"	/* for parsetree manipulation */
22 23
#include "nodes/parsenodes.h"

24
#include "rewrite/rewriteSupport.h"
25 26 27 28 29 30 31
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/locks.h"

#include "commands/creatinh.h"
#include "access/heapam.h"

32 33 34
static void
ApplyRetrieveRule(Query * parsetree, RewriteRule * rule,
				  int rt_index, int relation_level, int *modified);
35
static List *
36 37
fireRules(Query * parsetree, int rt_index, CmdType event,
		  bool * instead_flag, List * locks, List ** qual_products);
38
static List *deepRewriteQuery(Query * parsetree);
39 40 41

/*
 * gatherRewriteMeta -
42 43 44
 *	  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
45 46
 */
static RewriteInfo *
47 48 49 50 51 52
gatherRewriteMeta(Query * parsetree,
				  Query * rule_action,
				  Node * rule_qual,
				  int rt_index,
				  CmdType event,
				  bool * instead_flag)
53
{
54 55 56
	RewriteInfo *info;
	int			rt_length;
	int			result_reln;
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

	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);
		info->rt = append(info->rt, info->rule_action->rtable);

		info->new_varno = PRS2_NEW_VARNO + rt_length;
		OffsetVarNodes(info->rule_action->qual, rt_length);
		OffsetVarNodes((Node *) info->rule_action->targetList, rt_length);
		OffsetVarNodes(info->rule_qual, rt_length);
		ChangeVarNodes((Node *) info->rule_action->qual,
					   PRS2_CURRENT_VARNO + rt_length, rt_index);
		ChangeVarNodes((Node *) info->rule_action->targetList,
					   PRS2_CURRENT_VARNO + rt_length, rt_index);
		ChangeVarNodes(info->rule_qual,
					   PRS2_CURRENT_VARNO + rt_length, rt_index);

		/*
		 * 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 */
93
			int			new_result_reln = 0;
94 95 96 97

			result_reln = info->rule_action->resultRelation;
			switch (result_reln)
			{
98 99 100 101 102 103 104
				case PRS2_CURRENT_VARNO:
					new_result_reln = rt_index;
					break;
				case PRS2_NEW_VARNO:	/* XXX */
				default:
					new_result_reln = result_reln + rt_length;
					break;
105 106 107 108 109
			}
			info->rule_action->resultRelation = new_result_reln;
		}
	}
	return info;
110 111
}

112
static List *
113
OptimizeRIRRules(List * locks)
114
{
115 116 117
	List	   *attr_level = NIL,
			   *i;
	List	   *relation_level = NIL;
118 119 120

	foreach(i, locks)
	{
121
		RewriteRule *rule_lock = lfirst(i);
122 123 124 125 126 127 128

		if (rule_lock->attrno == -1)
			relation_level = lappend(relation_level, rule_lock);
		else
			attr_level = lappend(attr_level, rule_lock);
	}
	return nconc(relation_level, attr_level);
129 130 131 132 133 134
}

/*
 * idea is to put instead rules before regular rules so that
 * excess semantically queasy queries aren't processed
 */
135
static List *
136
orderRules(List * locks)
137
{
138 139 140
	List	   *regular = NIL,
			   *i;
	List	   *instead_rules = NIL;
141 142 143

	foreach(i, locks)
	{
144
		RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
145 146 147 148 149 150 151

		if (rule_lock->isInstead)
			instead_rules = lappend(instead_rules, rule_lock);
		else
			regular = lappend(regular, rule_lock);
	}
	return nconc(regular, instead_rules);
152 153 154
}

static int
155
AllRetrieve(List * actions)
156
{
157
	List	   *n;
158 159 160

	foreach(n, actions)
	{
161
		Query	   *pt = lfirst(n);
162 163 164 165 166 167 168 169 170 171

		/*
		 * in the old postgres code, we check whether command_type is a
		 * consp of '('*'.commandType). but we've never supported
		 * transitive closures. Hence removed	 - ay 10/94.
		 */
		if (pt->commandType != CMD_SELECT)
			return false;
	}
	return true;
172 173
}

174
static List *
175 176 177 178 179
FireRetrieveRulesAtQuery(Query * parsetree,
						 int rt_index,
						 Relation relation,
						 bool * instead_flag,
						 int rule_flag)
180
{
181 182 183 184
	List	   *i,
			   *locks;
	RuleLock   *rt_entry_locks = NULL;
	List	   *work = NIL;
185

186
	if ((rt_entry_locks = relation->rd_rules) == NULL)
187
		return NIL;
188 189 190 191 192 193

	locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);

	/* find all retrieve instead */
	foreach(i, locks)
	{
194
		RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
195 196 197 198 199 200 201 202 203 204

		if (!rule_lock->isInstead)
			continue;
		work = lappend(work, rule_lock);
	}
	if (work != NIL)
	{
		work = OptimizeRIRRules(locks);
		foreach(i, work)
		{
205 206 207
			RewriteRule *rule_lock = lfirst(i);
			int			relation_level;
			int			modified = FALSE;
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

			relation_level = (rule_lock->attrno == -1);
			if (rule_lock->actions == NIL)
			{
				*instead_flag = TRUE;
				return NIL;
			}
			if (!rule_flag &&
				length(rule_lock->actions) >= 2 &&
				AllRetrieve(rule_lock->actions))
			{
				*instead_flag = TRUE;
				return rule_lock->actions;
			}
			ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level,
							  &modified);
			if (modified)
			{
				*instead_flag = TRUE;
				FixResdomTypes(parsetree->targetList);
				return lcons(parsetree, NIL);
			}
		}
231
	}
232 233
	return NIL;
}
234 235 236 237 238


/* Idea is like this:
 *
 * retrieve-instead-retrieve rules have different semantics than update nodes
239
 * Separate RIR rules from others.	Pass others to FireRules.
240 241 242 243 244
 * Order RIR rules and process.
 *
 * side effect: parsetree's rtable field might be changed
 */
static void
245 246 247 248 249
ApplyRetrieveRule(Query * parsetree,
				  RewriteRule * rule,
				  int rt_index,
				  int relation_level,
				  int *modified)
250
{
251 252 253 254 255 256 257
	Query	   *rule_action = NULL;
	Node	   *rule_qual;
	List	   *rtable,
			   *rt;
	int			nothing,
				rt_length;
	int			badsql = FALSE;
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276

	rule_qual = rule->qual;
	if (rule->actions)
	{
		if (length(rule->actions) > 1)	/* ??? because we don't handle
										 * rules with more than one
										 * action? -ay */
			return;
		rule_action = copyObject(lfirst(rule->actions));
		nothing = FALSE;
	}
	else
	{
		nothing = TRUE;
	}

	rtable = copyObject(parsetree->rtable);
	foreach(rt, rtable)
	{
277
		RangeTblEntry *rte = lfirst(rt);
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309

		/*
		 * this is to prevent add_missing_vars_to_base_rels() from adding
		 * a bogus entry to the new target list.
		 */
		rte->inFromCl = false;
	}
	rt_length = length(rtable);
	rtable = nconc(rtable, copyObject(rule_action->rtable));
	parsetree->rtable = rtable;

	rule_action->rtable = rtable;
	OffsetVarNodes(rule_action->qual, rt_length);
	OffsetVarNodes((Node *) rule_action->targetList, rt_length);
	OffsetVarNodes(rule_qual, rt_length);
	ChangeVarNodes(rule_action->qual,
				   PRS2_CURRENT_VARNO + rt_length, rt_index);
	ChangeVarNodes((Node *) rule_action->targetList,
				   PRS2_CURRENT_VARNO + rt_length, rt_index);
	ChangeVarNodes(rule_qual, PRS2_CURRENT_VARNO + rt_length, rt_index);
	if (relation_level)
	{
		HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
					   modified);
	}
	else
	{
		HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
							   rt_index, rule->attrno, modified, &badsql);
	}
	if (*modified && !badsql)
		AddQual(parsetree, rule_action->qual);
310 311
}

312
static List *
313 314 315 316
ProcessRetrieveQuery(Query * parsetree,
					 List * rtable,
					 bool * instead_flag,
					 bool rule)
317
{
318 319 320
	List	   *rt;
	List	   *product_queries = NIL;
	int			rt_index = 0;
321 322 323

	foreach(rt, rtable)
	{
324 325 326
		RangeTblEntry *rt_entry = lfirst(rt);
		Relation	rt_entry_relation = NULL;
		List	   *result = NIL;
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342

		rt_index++;
		rt_entry_relation = heap_openr(rt_entry->relname);

		if (rt_entry_relation->rd_rules != NULL)
		{
			result =
				FireRetrieveRulesAtQuery(parsetree,
										 rt_index,
										 rt_entry_relation,
										 instead_flag,
										 rule);
		}
		heap_close(rt_entry_relation);
		if (*instead_flag)
			return result;
343
	}
344 345
	if (rule)
		return NIL;
346

347 348
	foreach(rt, rtable)
	{
349 350 351 352 353 354
		RangeTblEntry *rt_entry = lfirst(rt);
		Relation	rt_entry_relation = NULL;
		RuleLock   *rt_entry_locks = NULL;
		List	   *result = NIL;
		List	   *locks = NIL;
		List	   *dummy_products;
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374

		rt_index++;
		rt_entry_relation = heap_openr(rt_entry->relname);
		rt_entry_locks = rt_entry_relation->rd_rules;
		heap_close(rt_entry_relation);

		if (rt_entry_locks)
		{
			locks =
				matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
		}
		if (locks != NIL)
		{
			result = fireRules(parsetree, rt_index, CMD_SELECT,
							   instead_flag, locks, &dummy_products);
			if (*instead_flag)
				return lappend(NIL, result);
			if (result != NIL)
				product_queries = nconc(product_queries, result);
		}
375
	}
376
	return product_queries;
377 378
}

379
static Query *
380 381 382 383 384
CopyAndAddQual(Query * parsetree,
			   List * actions,
			   Node * rule_qual,
			   int rt_index,
			   CmdType event)
385
{
386 387 388
	Query	   *new_tree = (Query *) copyObject(parsetree);
	Node	   *new_qual = NULL;
	Query	   *rule_action = NULL;
389 390 391 392 393 394 395

	if (actions)
		rule_action = lfirst(actions);
	if (rule_qual != NULL)
		new_qual = (Node *) copyObject(rule_qual);
	if (rule_action != NULL)
	{
396 397
		List	   *rtable;
		int			rt_length;
398 399 400 401 402 403 404 405 406 407 408 409

		rtable = new_tree->rtable;
		rt_length = length(rtable);
		rtable = append(rtable, listCopy(rule_action->rtable));
		new_tree->rtable = rtable;
		OffsetVarNodes(new_qual, rt_length);
		ChangeVarNodes(new_qual, PRS2_CURRENT_VARNO + rt_length, rt_index);
	}
	/* XXX -- where current doesn't work for instead nothing.... yet */
	AddNotQual(new_tree, new_qual);

	return new_tree;
410 411 412 413
}


/*
414 415 416 417 418
 *	fireRules -
 *	   Iterate through rule locks applying rules.  After an instead rule
 *	   rule has been applied, return just new parsetree and let RewriteQuery
 *	   start the process all over again.  The locks are reordered to maintain
 *	   sensible semantics.	remember: reality is for dead birds -- glass
419 420
 *
 */
421
static List *
422 423 424 425 426 427
fireRules(Query * parsetree,
		  int rt_index,
		  CmdType event,
		  bool * instead_flag,
		  List * locks,
		  List ** qual_products)
428
{
429 430 431
	RewriteInfo *info;
	List	   *results = NIL;
	List	   *i;
432 433 434 435 436 437 438

	/* choose rule to fire from list of rules */
	if (locks == NIL)
	{
		ProcessRetrieveQuery(parsetree,
							 parsetree->rtable,
							 instead_flag, TRUE);
439
		if (*instead_flag)
440
			return lappend(NIL, parsetree);
441
		else
442
			return NIL;
443
	}
444 445 446 447

	locks = orderRules(locks);	/* instead rules first */
	foreach(i, locks)
	{
448 449 450 451 452 453
		RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
		Node	   *qual,
				   *event_qual;
		List	   *actions;
		List	   *r;
		bool		orig_instead_flag = *instead_flag;
454 455 456 457 458 459 460 461 462 463 464 465

		/* multiple rule action time */
		*instead_flag = rule_lock->isInstead;
		event_qual = rule_lock->qual;
		actions = rule_lock->actions;
		if (event_qual != NULL && *instead_flag)
			*qual_products =
				lappend(*qual_products,
						CopyAndAddQual(parsetree, actions, event_qual,
									   rt_index, event));
		foreach(r, actions)
		{
466 467
			Query	   *rule_action = lfirst(r);
			Node	   *rule_qual = copyObject(event_qual);
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535

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

			/*
			 * Event Qualification forces copying of parsetree --- XXX and
			 * 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;
			ProcessRetrieveQuery(info->rule_action, info->rt,
								 &orig_instead_flag, TRUE);

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

			pfree(info);
		}
		if (*instead_flag)
			break;
	}
	return results;
536 537
}

538
static List *
539
RewriteQuery(Query * parsetree, bool * instead_flag, List ** qual_products)
540
{
541 542 543
	CmdType		event;
	List	   *product_queries = NIL;
	int			result_relation = 0;
544

545 546 547 548 549 550
	Assert(parsetree != NULL);

	event = parsetree->commandType;

	if (event == CMD_UTILITY)
		return NIL;
551 552

	/*
553
	 * only for a delete may the targetlist be NULL
554
	 */
555 556 557 558 559 560 561 562 563 564 565 566 567
	if (event != CMD_DELETE)
	{
		Assert(parsetree->targetList != NULL);
	}

	result_relation = parsetree->resultRelation;

	if (event != CMD_SELECT)
	{

		/*
		 * the statement is an update, insert or delete
		 */
568 569 570
		RangeTblEntry *rt_entry;
		Relation	rt_entry_relation = NULL;
		RuleLock   *rt_entry_locks = NULL;
571 572 573 574 575 576 577 578

		rt_entry = rt_fetch(result_relation, parsetree->rtable);
		rt_entry_relation = heap_openr(rt_entry->relname);
		rt_entry_locks = rt_entry_relation->rd_rules;
		heap_close(rt_entry_relation);

		if (rt_entry_locks != NULL)
		{
579
			List	   *locks =
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
			matchLocks(event, rt_entry_locks, result_relation, parsetree);

			product_queries =
				fireRules(parsetree,
						  result_relation,
						  event,
						  instead_flag,
						  locks,
						  qual_products);
		}
		return product_queries;
	}
	else
	{

		/*
		 * the statement is a select
		 */
598
		Query	   *other;
599 600 601 602 603 604

		other = copyObject(parsetree);	/* ApplyRetrieveRule changes the
										 * range table */
		return
			ProcessRetrieveQuery(other, parsetree->rtable,
								 instead_flag, FALSE);
605 606 607 608 609 610 611 612
	}
}

/*
 * 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
613
#define REWRITE_INVOKE_MAX		10
614 615
#endif

616
static int	numQueryRewriteInvoked = 0;
617 618 619

/*
 * QueryRewrite -
620 621 622
 *	  rewrite one query via QueryRewrite system, possibly returning 0, or many
 *	  queries
 */
623
List	   *
624
QueryRewrite(Query * parsetree)
625
{
626
	numQueryRewriteInvoked = 0;
627

628 629 630 631
	/*
	 * take a deep breath and apply all the rewrite rules - ay
	 */
	return deepRewriteQuery(parsetree);
632 633 634 635
}

/*
 * deepRewriteQuery -
636
 *	  rewrites the query and apply the rules again on the queries rewritten
637
 */
638
static List *
639
deepRewriteQuery(Query * parsetree)
640
{
641 642 643 644 645
	List	   *n;
	List	   *rewritten = NIL;
	List	   *result = NIL;
	bool		instead;
	List	   *qual_products = NIL;
646 647 648 649 650 651 652 653 654 655 656

	if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
	{
		elog(WARN, "query rewritten %d times, may contain cycles",
			 numQueryRewriteInvoked - 1);
	}

	instead = FALSE;
	result = RewriteQuery(parsetree, &instead, &qual_products);
	if (!instead)
		rewritten = lcons(parsetree, NIL);
657

658 659
	foreach(n, result)
	{
660 661
		Query	   *pt = lfirst(n);
		List	   *newstuff = NIL;
662 663 664 665 666 667 668 669 670 671

		newstuff = deepRewriteQuery(pt);
		if (newstuff != NIL)
			rewritten = nconc(rewritten, newstuff);
	}
	if (qual_products != NIL)
		rewritten = nconc(rewritten, qual_products);

	return rewritten;
}