]> granicus.if.org Git - postgresql/blob - src/backend/rewrite/rewriteHandler.c
Repair bug reported by Huxton, 1/24/01. We need to include a rule's
[postgresql] / src / backend / rewrite / rewriteHandler.c
1 /*-------------------------------------------------------------------------
2  *
3  * rewriteHandler.c
4  *
5  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
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.89 2001/01/27 04:40:59 tgl Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
16 #include "access/heapam.h"
17 #include "catalog/pg_operator.h"
18 #include "catalog/pg_type.h"
19 #include "miscadmin.h"
20 #include "nodes/makefuncs.h"
21 #include "optimizer/clauses.h"
22 #include "optimizer/prep.h"
23 #include "optimizer/var.h"
24 #include "parser/analyze.h"
25 #include "parser/parse_expr.h"
26 #include "parser/parse_oper.h"
27 #include "parser/parse_target.h"
28 #include "parser/parsetree.h"
29 #include "parser/parse_type.h"
30 #include "rewrite/rewriteManip.h"
31 #include "utils/lsyscache.h"
32
33
34 static RewriteInfo *gatherRewriteMeta(Query *parsetree,
35                                   Query *rule_action,
36                                   Node *rule_qual,
37                                   int rt_index,
38                                   CmdType event,
39                                   bool instead_flag);
40 static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
41 static void markQueryForUpdate(Query *qry, bool skipOldNew);
42 static List *matchLocks(CmdType event, RuleLock *rulelocks,
43                                                 int varno, Query *parsetree);
44 static Query *fireRIRrules(Query *parsetree);
45
46
47 /*
48  * gatherRewriteMeta -
49  *        Gather meta information about parsetree, and rule. Fix rule body
50  *        and qualifier so that they can be mixed with the parsetree and
51  *        maintain semantic validity
52  */
53 static RewriteInfo *
54 gatherRewriteMeta(Query *parsetree,
55                                   Query *rule_action,
56                                   Node *rule_qual,
57                                   int rt_index,
58                                   CmdType event,
59                                   bool instead_flag)
60 {
61         RewriteInfo *info;
62         Query      *sub_action;
63         Query     **sub_action_ptr;
64         int                     rt_length;
65
66         info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
67         info->rt_index = rt_index;
68         info->event = event;
69         info->instead_flag = instead_flag;
70         info->rule_action = (Query *) copyObject(rule_action);
71         info->rule_qual = (Node *) copyObject(rule_qual);
72         if (info->rule_action == NULL)
73         {
74                 info->nothing = TRUE;
75                 return info;
76         }
77         info->nothing = FALSE;
78         info->action = info->rule_action->commandType;
79         info->current_varno = rt_index;
80         rt_length = length(parsetree->rtable);
81         info->new_varno = PRS2_NEW_VARNO + rt_length;
82
83         /*
84          * Adjust rule action and qual to offset its varnos, so that we can
85          * merge its rtable into the main parsetree's rtable.
86          *
87          * If the rule action is an INSERT...SELECT, the OLD/NEW rtable
88          * entries will be in the SELECT part, and we have to modify that
89          * rather than the top-level INSERT (kluge!).
90          */
91         sub_action = getInsertSelectQuery(info->rule_action, &sub_action_ptr);
92
93         OffsetVarNodes((Node *) sub_action, rt_length, 0);
94         OffsetVarNodes(info->rule_qual, rt_length, 0);
95         /* but references to *OLD* should point at original rt_index */
96         ChangeVarNodes((Node *) sub_action,
97                                    PRS2_OLD_VARNO + rt_length, rt_index, 0);
98         ChangeVarNodes(info->rule_qual,
99                                    PRS2_OLD_VARNO + rt_length, rt_index, 0);
100
101         /*
102          * We want the main parsetree's rtable to end up as the concatenation
103          * of its original contents plus those of all the relevant rule
104          * actions.  Also store same into all the rule_action rtables.
105          * Some of the entries may be unused after we finish rewriting, but
106          * if we tried to clean those out we'd have a much harder job to
107          * adjust RT indexes in the query's Vars.  It's OK to have unused
108          * RT entries, since planner will ignore them.
109          *
110          * NOTE KLUGY HACK: we assume the parsetree rtable had at least one
111          * entry to begin with (OK enough, else where'd the rule come from?).
112          * Because of this, if multiple rules nconc() their rtable additions
113          * onto parsetree->rtable, they'll all see the same rtable because
114          * they all have the same list head pointer.
115          */
116         parsetree->rtable = nconc(parsetree->rtable,
117                                                           sub_action->rtable);
118         sub_action->rtable = parsetree->rtable;
119
120         /*
121          * Each rule action's jointree should be the main parsetree's jointree
122          * plus that rule's jointree, but usually *without* the original rtindex
123          * that we're replacing (if present, which it won't be for INSERT).
124          * Note that if the rule action refers to OLD, its jointree will add
125          * a reference to rt_index.  If the rule action doesn't refer to OLD,
126          * but either the rule_qual or the user query quals do, then we need to
127          * keep the original rtindex in the jointree to provide data for the
128          * quals.  We don't want the original rtindex to be joined twice,
129          * however, so avoid keeping it if the rule action mentions it.
130          */
131         if (sub_action->jointree != NULL)
132         {
133                 bool    keeporig;
134                 List   *newjointree;
135
136                 keeporig = (! rangeTableEntry_used((Node *) sub_action->jointree,
137                                                                                    rt_index, 0)) &&
138                         (rangeTableEntry_used(info->rule_qual, rt_index, 0) ||
139                          rangeTableEntry_used(parsetree->jointree->quals, rt_index, 0));
140                 newjointree = adjustJoinTreeList(parsetree, !keeporig, rt_index);
141                 sub_action->jointree->fromlist =
142                         nconc(newjointree, sub_action->jointree->fromlist);
143         }
144
145         /*
146          * We copy the qualifications of the parsetree to the action and vice
147          * versa. So force hasSubLinks if one of them has it. If this is not
148          * right, the flag will get cleared later, but we mustn't risk having
149          * it not set when it needs to be.
150          */
151         if (parsetree->hasSubLinks)
152                 sub_action->hasSubLinks = TRUE;
153         else if (sub_action->hasSubLinks)
154                 parsetree->hasSubLinks = TRUE;
155
156         /*
157          * Event Qualification forces copying of parsetree and
158          * splitting into two queries one w/rule_qual, one w/NOT
159          * rule_qual. Also add user query qual onto rule action
160          */
161         AddQual(sub_action, info->rule_qual);
162
163         AddQual(sub_action, parsetree->jointree->quals);
164
165         /*
166          * Rewrite new.attribute w/ right hand side of target-list
167          * entry for appropriate field name in insert/update.
168          *
169          * KLUGE ALERT: since ResolveNew returns a mutated copy, we can't just
170          * apply it to sub_action; we have to remember to update the sublink
171          * inside info->rule_action, too.
172          */
173         if (info->event == CMD_INSERT || info->event == CMD_UPDATE)
174         {
175                 sub_action = (Query *) ResolveNew((Node *) sub_action,
176                                                                                   info->new_varno,
177                                                                                   0,
178                                                                                   parsetree->targetList,
179                                                                                   info->event,
180                                                                                   info->current_varno);
181                 if (sub_action_ptr)
182                         *sub_action_ptr = sub_action;
183                 else
184                         info->rule_action = sub_action;
185         }
186
187         return info;
188 }
189
190 /*
191  * Copy the query's jointree list, and optionally attempt to remove any
192  * occurrence of the given rt_index as a top-level join item (we do not look
193  * for it within join items; this is OK because we are only expecting to find
194  * it as an UPDATE or DELETE target relation, which will be at the top level
195  * of the join).  Returns modified jointree list --- original list is not
196  * changed.
197  */
198 static List *
199 adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
200 {
201         List       *newjointree = listCopy(parsetree->jointree->fromlist);
202         List       *jjt;
203
204         if (removert)
205         {
206                 foreach(jjt, newjointree)
207                 {
208                         RangeTblRef *rtr = lfirst(jjt);
209
210                         if (IsA(rtr, RangeTblRef) && rtr->rtindex == rt_index)
211                         {
212                                 newjointree = lremove(rtr, newjointree);
213                                 break;
214                         }
215                 }
216         }
217         return newjointree;
218 }
219
220
221 /*
222  * matchLocks -
223  *        match the list of locks and returns the matching rules
224  */
225 static List *
226 matchLocks(CmdType event,
227                    RuleLock *rulelocks,
228                    int varno,
229                    Query *parsetree)
230 {
231         List       *real_locks = NIL;
232         int                     nlocks;
233         int                     i;
234
235         Assert(rulelocks != NULL);      /* we get called iff there is some lock */
236         Assert(parsetree != NULL);
237
238         if (parsetree->commandType != CMD_SELECT)
239         {
240                 if (parsetree->resultRelation != varno)
241                         return NIL;
242         }
243
244         nlocks = rulelocks->numLocks;
245
246         for (i = 0; i < nlocks; i++)
247         {
248                 RewriteRule *oneLock = rulelocks->rules[i];
249
250                 if (oneLock->event == event)
251                 {
252                         if (parsetree->commandType != CMD_SELECT ||
253                                 (oneLock->attrno == -1 ?
254                                  rangeTableEntry_used((Node *) parsetree, varno, 0) :
255                                  attribute_used((Node *) parsetree,
256                                                                 varno, oneLock->attrno, 0)))
257                                 real_locks = lappend(real_locks, oneLock);
258                 }
259         }
260
261         return real_locks;
262 }
263
264
265 static Query *
266 ApplyRetrieveRule(Query *parsetree,
267                                   RewriteRule *rule,
268                                   int rt_index,
269                                   bool relation_level,
270                                   Relation relation,
271                                   bool relIsUsed)
272 {
273         Query      *rule_action;
274         RangeTblEntry *rte,
275                            *subrte;
276
277         if (length(rule->actions) != 1)
278                 elog(ERROR, "ApplyRetrieveRule: expected just one rule action");
279         if (rule->qual != NULL)
280                 elog(ERROR, "ApplyRetrieveRule: can't handle qualified ON SELECT rule");
281         if (! relation_level)
282                 elog(ERROR, "ApplyRetrieveRule: can't handle per-attribute ON SELECT rule");
283
284         /*
285          * Make a modifiable copy of the view query, and recursively expand
286          * any view references inside it.
287          */
288         rule_action = copyObject(lfirst(rule->actions));
289
290         rule_action = fireRIRrules(rule_action);
291
292         /*
293          * VIEWs are really easy --- just plug the view query in as a subselect,
294          * replacing the relation's original RTE.
295          */
296         rte = rt_fetch(rt_index, parsetree->rtable);
297
298         rte->relname = NULL;
299         rte->relid = InvalidOid;
300         rte->subquery = rule_action;
301         rte->inh = false;                       /* must not be set for a subquery */
302
303         /*
304          * We move the view's permission check data down to its rangetable.
305          * The checks will actually be done against the *OLD* entry therein.
306          */
307         subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
308         Assert(subrte->relid == relation->rd_id);
309         subrte->checkForRead = rte->checkForRead;
310         subrte->checkForWrite = rte->checkForWrite;
311
312         rte->checkForRead = false;      /* no permission check on subquery itself */
313         rte->checkForWrite = false;
314
315         /*
316          * FOR UPDATE of view?
317          */
318         if (intMember(rt_index, parsetree->rowMarks))
319         {
320                 /*
321                  * Remove the view from the list of rels that will actually be
322                  * marked FOR UPDATE by the executor.  It will still be access-
323                  * checked for write access, though.
324                  */
325                 parsetree->rowMarks = lremovei(rt_index, parsetree->rowMarks);
326
327                 /*
328                  * Set up the view's referenced tables as if FOR UPDATE.
329                  */
330                 markQueryForUpdate(rule_action, true);
331         }
332
333         return parsetree;
334 }
335
336 /*
337  * Recursively mark all relations used by a view as FOR UPDATE.
338  *
339  * This may generate an invalid query, eg if some sub-query uses an
340  * aggregate.  We leave it to the planner to detect that.
341  *
342  * NB: this must agree with the parser's transformForUpdate() routine.
343  */
344 static void
345 markQueryForUpdate(Query *qry, bool skipOldNew)
346 {
347         Index           rti = 0;
348         List       *l;
349
350         foreach(l, qry->rtable)
351         {
352                 RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
353
354                 rti++;
355
356                 /* Ignore OLD and NEW entries if we are at top level of view */
357                 if (skipOldNew &&
358                         (rti == PRS2_OLD_VARNO || rti == PRS2_NEW_VARNO))
359                         continue;
360
361                 if (rte->subquery)
362                 {
363                         /* FOR UPDATE of subquery is propagated to subquery's rels */
364                         markQueryForUpdate(rte->subquery, false);
365                 }
366                 else
367                 {
368                         if (!intMember(rti, qry->rowMarks))
369                                 qry->rowMarks = lappendi(qry->rowMarks, rti);
370                         rte->checkForWrite = true;
371                 }
372         }
373 }
374
375
376 /*
377  * fireRIRonSubLink -
378  *      Apply fireRIRrules() to each SubLink (subselect in expression) found
379  *      in the given tree.
380  *
381  * NOTE: although this has the form of a walker, we cheat and modify the
382  * SubLink nodes in-place.      It is caller's responsibility to ensure that
383  * no unwanted side-effects occur!
384  *
385  * This is unlike most of the other routines that recurse into subselects,
386  * because we must take control at the SubLink node in order to replace
387  * the SubLink's subselect link with the possibly-rewritten subquery.
388  */
389 static bool
390 fireRIRonSubLink(Node *node, void *context)
391 {
392         if (node == NULL)
393                 return false;
394         if (IsA(node, SubLink))
395         {
396                 SubLink    *sub = (SubLink *) node;
397
398                 /* Do what we came for */
399                 sub->subselect = (Node *) fireRIRrules((Query *) (sub->subselect));
400                 /* Fall through to process lefthand args of SubLink */
401         }
402         /*
403          * Do NOT recurse into Query nodes, because fireRIRrules already
404          * processed subselects of subselects for us.
405          */
406         return expression_tree_walker(node, fireRIRonSubLink,
407                                                                   (void *) context);
408 }
409
410
411 /*
412  * fireRIRrules -
413  *      Apply all RIR rules on each rangetable entry in a query
414  */
415 static Query *
416 fireRIRrules(Query *parsetree)
417 {
418         int                     rt_index;
419
420         /*
421          * don't try to convert this into a foreach loop, because rtable list
422          * can get changed each time through...
423          */
424         rt_index = 0;
425         while (rt_index < length(parsetree->rtable))
426         {
427                 RangeTblEntry *rte;
428                 Relation        rel;
429                 List       *locks;
430                 RuleLock   *rules;
431                 RewriteRule *rule;
432                 LOCKMODE        lockmode;
433                 bool            relIsUsed;
434                 int                     i;
435                 List       *l;
436
437                 ++rt_index;
438
439                 rte = rt_fetch(rt_index, parsetree->rtable);
440
441                 /*
442                  * A subquery RTE can't have associated rules, so there's nothing
443                  * to do to this level of the query, but we must recurse into the
444                  * subquery to expand any rule references in it.
445                  */
446                 if (rte->subquery)
447                 {
448                         rte->subquery = fireRIRrules(rte->subquery);
449                         continue;
450                 }
451
452                 /*
453                  * If the table is not referenced in the query, then we ignore it.
454                  * This prevents infinite expansion loop due to new rtable entries
455                  * inserted by expansion of a rule. A table is referenced if it is
456                  * part of the join set (a source table), or is referenced by any
457                  * Var nodes, or is the result table.
458                  */
459                 relIsUsed = rangeTableEntry_used((Node *) parsetree, rt_index, 0);
460
461                 if (!relIsUsed && rt_index != parsetree->resultRelation)
462                         continue;
463
464                 /*
465                  * This may well be the first access to the relation during
466                  * the current statement (it will be, if this Query was extracted
467                  * from a rule or somehow got here other than via the parser).
468                  * Therefore, grab the appropriate lock type for the relation,
469                  * and do not release it until end of transaction.  This protects
470                  * the rewriter and planner against schema changes mid-query.
471                  *
472                  * If the relation is the query's result relation, then RewriteQuery()
473                  * already got the right lock on it, so we need no additional lock.
474                  * Otherwise, check to see if the relation is accessed FOR UPDATE
475                  * or not.
476                  */
477                 if (rt_index == parsetree->resultRelation)
478                         lockmode = NoLock;
479                 else if (intMember(rt_index, parsetree->rowMarks))
480                         lockmode = RowShareLock;
481                 else
482                         lockmode = AccessShareLock;
483
484                 rel = heap_openr(rte->relname, lockmode);
485
486                 rules = rel->rd_rules;
487                 if (rules == NULL)
488                 {
489                         heap_close(rel, NoLock);
490                         continue;
491                 }
492
493                 /*
494                  * Collect the RIR rules that we must apply
495                  */
496                 locks = NIL;
497                 for (i = 0; i < rules->numLocks; i++)
498                 {
499                         rule = rules->rules[i];
500                         if (rule->event != CMD_SELECT)
501                                 continue;
502
503                         if (rule->attrno > 0)
504                         {
505                                 /* per-attr rule; do we need it? */
506                                 if (!attribute_used((Node *) parsetree, rt_index,
507                                                                         rule->attrno, 0))
508                                         continue;
509                         }
510
511                         locks = lappend(locks, rule);
512                 }
513
514                 /*
515                  * Now apply them
516                  */
517                 foreach(l, locks)
518                 {
519                         rule = lfirst(l);
520
521                         parsetree = ApplyRetrieveRule(parsetree,
522                                                                                   rule,
523                                                                                   rt_index,
524                                                                                   rule->attrno == -1,
525                                                                                   rel,
526                                                                                   relIsUsed);
527                 }
528
529                 heap_close(rel, NoLock);
530         }
531
532         /*
533          * Recurse into sublink subqueries, too.
534          */
535         if (parsetree->hasSubLinks)
536                 query_tree_walker(parsetree, fireRIRonSubLink, NULL,
537                                                   false /* already handled the ones in rtable */);
538
539         /*
540          * If the query was marked having aggregates, check if this is
541          * still true after rewriting.  Ditto for sublinks.  Note there
542          * should be no aggs in the qual at this point.  (Does this code
543          * still do anything useful?  The view-becomes-subselect-in-FROM
544          * approach doesn't look like it could remove aggs or sublinks...)
545          */
546         if (parsetree->hasAggs)
547         {
548                 parsetree->hasAggs = checkExprHasAggs((Node *) parsetree);
549                 if (parsetree->hasAggs)
550                         if (checkExprHasAggs((Node *) parsetree->jointree))
551                                 elog(ERROR, "fireRIRrules: failed to remove aggs from qual");
552         }
553         if (parsetree->hasSubLinks)
554         {
555                 parsetree->hasSubLinks = checkExprHasSubLink((Node *) parsetree);
556         }
557
558         return parsetree;
559 }
560
561
562 /*
563  * idea is to fire regular rules first, then qualified instead
564  * rules and unqualified instead rules last. Any lemming is counted for.
565  */
566 static List *
567 orderRules(List *locks)
568 {
569         List       *regular = NIL;
570         List       *instead_rules = NIL;
571         List       *instead_qualified = NIL;
572         List       *i;
573
574         foreach(i, locks)
575         {
576                 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
577
578                 if (rule_lock->isInstead)
579                 {
580                         if (rule_lock->qual == NULL)
581                                 instead_rules = lappend(instead_rules, rule_lock);
582                         else
583                                 instead_qualified = lappend(instead_qualified, rule_lock);
584                 }
585                 else
586                         regular = lappend(regular, rule_lock);
587         }
588         return nconc(nconc(regular, instead_qualified), instead_rules);
589 }
590
591
592 /*
593  * Modify the given query by adding 'AND NOT rule_qual' to its qualification.
594  * This is used to generate suitable "else clauses" for conditional INSTEAD
595  * rules.
596  *
597  * The rule_qual may contain references to OLD or NEW.  OLD references are
598  * replaced by references to the specified rt_index (the relation that the
599  * rule applies to).  NEW references are only possible for INSERT and UPDATE
600  * queries on the relation itself, and so they should be replaced by copies
601  * of the related entries in the query's own targetlist.
602  */
603 static Query *
604 CopyAndAddQual(Query *parsetree,
605                            Node *rule_qual,
606                            int rt_index,
607                            CmdType event)
608 {
609         Query      *new_tree = (Query *) copyObject(parsetree);
610         Node       *new_qual = (Node *) copyObject(rule_qual);
611
612         /* Fix references to OLD */
613         ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);
614         /* Fix references to NEW */
615         if (event == CMD_INSERT || event == CMD_UPDATE)
616                 new_qual = ResolveNew(new_qual,
617                                                           PRS2_NEW_VARNO,
618                                                           0,
619                                                           parsetree->targetList,
620                                                           event,
621                                                           rt_index);
622         /* And attach the fixed qual */
623         AddNotQual(new_tree, new_qual);
624
625         return new_tree;
626 }
627
628
629
630 /*
631  *      fireRules -
632  *         Iterate through rule locks applying rules.
633  *         All rules create their own parsetrees. Instead rules
634  *         with rule qualification save the original parsetree
635  *         and add their negated qualification to it. Real instead
636  *         rules finally throw away the original parsetree.
637  *
638  *         remember: reality is for dead birds -- glass
639  *
640  */
641 static List *
642 fireRules(Query *parsetree,
643                   int rt_index,
644                   CmdType event,
645                   bool *instead_flag,
646                   List *locks,
647                   List **qual_products)
648 {
649         List       *results = NIL;
650         List       *i;
651
652         /* choose rule to fire from list of rules */
653         if (locks == NIL)
654                 return NIL;
655
656         locks = orderRules(locks);      /* real instead rules last */
657
658         foreach(i, locks)
659         {
660                 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
661                 Node       *event_qual;
662                 List       *actions;
663                 List       *r;
664
665                 /* multiple rule action time */
666                 *instead_flag = rule_lock->isInstead;
667                 event_qual = rule_lock->qual;
668                 actions = rule_lock->actions;
669
670                 if (event_qual != NULL && *instead_flag)
671                 {
672                         Query      *qual_product;
673
674                         /* ----------
675                          * If there are instead rules with qualifications,
676                          * the original query is still performed. But all
677                          * the negated rule qualifications of the instead
678                          * rules are added so it does its actions only
679                          * in cases where the rule quals of all instead
680                          * rules are false. Think of it as the default
681                          * action in a case. We save this in *qual_products
682                          * so deepRewriteQuery() can add it to the query
683                          * list after we mangled it up enough.
684                          * ----------
685                          */
686                         if (*qual_products == NIL)
687                                 qual_product = parsetree;
688                         else
689                                 qual_product = (Query *) lfirst(*qual_products);
690
691                         qual_product = CopyAndAddQual(qual_product,
692                                                                                   event_qual,
693                                                                                   rt_index,
694                                                                                   event);
695
696                         *qual_products = makeList1(qual_product);
697                 }
698
699                 foreach(r, actions)
700                 {
701                         Query      *rule_action = lfirst(r);
702                         RewriteInfo *info;
703
704                         if (rule_action->commandType == CMD_NOTHING)
705                                 continue;
706
707                         info = gatherRewriteMeta(parsetree, rule_action, event_qual,
708                                                                          rt_index, event, *instead_flag);
709
710                         /* handle escapable cases, or those handled by other code */
711                         if (info->nothing)
712                         {
713                                 if (*instead_flag)
714                                         return NIL;
715                                 else
716                                         continue;
717                         }
718
719                         results = lappend(results, info->rule_action);
720
721                         pfree(info);
722                 }
723
724                 /* ----------
725                  * If this was an unqualified instead rule,
726                  * throw away an eventually saved 'default' parsetree
727                  * ----------
728                  */
729                 if (event_qual == NULL && *instead_flag)
730                         *qual_products = NIL;
731         }
732         return results;
733 }
734
735
736
737 static List *
738 RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
739 {
740         CmdType         event;
741         List       *product_queries = NIL;
742         int                     result_relation;
743         RangeTblEntry *rt_entry;
744         Relation        rt_entry_relation;
745         RuleLock   *rt_entry_locks;
746
747         Assert(parsetree != NULL);
748
749         event = parsetree->commandType;
750
751         /*
752          * SELECT rules are handled later when we have all the queries that
753          * should get executed
754          */
755         if (event == CMD_SELECT)
756                 return NIL;
757
758         /*
759          * Utilities aren't rewritten at all - why is this here?
760          */
761         if (event == CMD_UTILITY)
762                 return NIL;
763
764         /*
765          * the statement is an update, insert or delete - fire rules on it.
766          */
767         result_relation = parsetree->resultRelation;
768         Assert(result_relation != 0);
769         rt_entry = rt_fetch(result_relation, parsetree->rtable);
770
771         /*
772          * This may well be the first access to the result relation during
773          * the current statement (it will be, if this Query was extracted
774          * from a rule or somehow got here other than via the parser).
775          * Therefore, grab the appropriate lock type for a result relation,
776          * and do not release it until end of transaction.  This protects the
777          * rewriter and planner against schema changes mid-query.
778          */
779         rt_entry_relation = heap_openr(rt_entry->relname, RowExclusiveLock);
780
781         rt_entry_locks = rt_entry_relation->rd_rules;
782
783         if (rt_entry_locks != NULL)
784         {
785                 List       *locks = matchLocks(event, rt_entry_locks,
786                                                                            result_relation, parsetree);
787
788                 product_queries = fireRules(parsetree,
789                                                                         result_relation,
790                                                                         event,
791                                                                         instead_flag,
792                                                                         locks,
793                                                                         qual_products);
794         }
795
796         heap_close(rt_entry_relation, NoLock); /* keep lock! */
797
798         return product_queries;
799 }
800
801
802 /*
803  * to avoid infinite recursion, we restrict the number of times a query
804  * can be rewritten. Detecting cycles is left for the reader as an exercise.
805  */
806 #ifndef REWRITE_INVOKE_MAX
807 #define REWRITE_INVOKE_MAX              10
808 #endif
809
810 static int      numQueryRewriteInvoked = 0;
811
812 /*
813  * deepRewriteQuery -
814  *        rewrites the query and apply the rules again on the queries rewritten
815  */
816 static List *
817 deepRewriteQuery(Query *parsetree)
818 {
819         List       *n;
820         List       *rewritten = NIL;
821         List       *result;
822         bool            instead;
823         List       *qual_products = NIL;
824
825         if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
826         {
827                 elog(ERROR, "query rewritten %d times, may contain cycles",
828                          numQueryRewriteInvoked - 1);
829         }
830
831         instead = FALSE;
832         result = RewriteQuery(parsetree, &instead, &qual_products);
833
834         foreach(n, result)
835         {
836                 Query      *pt = lfirst(n);
837                 List       *newstuff;
838
839                 newstuff = deepRewriteQuery(pt);
840                 if (newstuff != NIL)
841                         rewritten = nconc(rewritten, newstuff);
842         }
843
844         /* ----------
845          * qual_products are the original query with the negated
846          * rule qualification of an instead rule
847          * ----------
848          */
849         if (qual_products != NIL)
850                 rewritten = nconc(rewritten, qual_products);
851
852         /* ----------
853          * The original query is appended last (if no "instead" rule)
854          * because update and delete rule actions might not do
855          * anything if they are invoked after the update or
856          * delete is performed. The command counter increment
857          * between the query execution makes the deleted (and
858          * maybe the updated) tuples disappear so the scans
859          * for them in the rule actions cannot find them.
860          * ----------
861          */
862         if (!instead)
863                 rewritten = lappend(rewritten, parsetree);
864
865         return rewritten;
866 }
867
868
869 /*
870  * QueryRewriteOne -
871  *        rewrite one query
872  */
873 static List *
874 QueryRewriteOne(Query *parsetree)
875 {
876         numQueryRewriteInvoked = 0;
877
878         /*
879          * take a deep breath and apply all the rewrite rules - ay
880          */
881         return deepRewriteQuery(parsetree);
882 }
883
884
885 /*
886  * QueryRewrite -
887  *        Primary entry point to the query rewriter.
888  *        Rewrite one query via query rewrite system, possibly returning 0
889  *        or many queries.
890  *
891  * NOTE: The code in QueryRewrite was formerly in pg_parse_and_plan(), and was
892  * moved here so that it would be invoked during EXPLAIN.
893  */
894 List *
895 QueryRewrite(Query *parsetree)
896 {
897         List       *querylist;
898         List       *results = NIL;
899         List       *l;
900
901         /*
902          * Step 1
903          *
904          * Apply all non-SELECT rules possibly getting 0 or many queries
905          */
906         querylist = QueryRewriteOne(parsetree);
907
908         /*
909          * Step 2
910          *
911          * Apply all the RIR rules on each query
912          */
913         foreach(l, querylist)
914         {
915                 Query   *query = (Query *) lfirst(l);
916
917                 query = fireRIRrules(query);
918
919                 /*
920                  * If the query target was rewritten as a view, complain.
921                  */
922                 if (query->resultRelation)
923                 {
924                         RangeTblEntry *rte = rt_fetch(query->resultRelation,
925                                                                                   query->rtable);
926
927                         if (rte->subquery)
928                         {
929                                 switch (query->commandType)
930                                 {
931                                         case CMD_INSERT:
932                                                 elog(ERROR, "Cannot insert into a view without an appropriate rule");
933                                                 break;
934                                         case CMD_UPDATE:
935                                                 elog(ERROR, "Cannot update a view without an appropriate rule");
936                                                 break;
937                                         case CMD_DELETE:
938                                                 elog(ERROR, "Cannot delete from a view without an appropriate rule");
939                                                 break;
940                                         default:
941                                                 elog(ERROR, "QueryRewrite: unexpected commandType %d",
942                                                          (int) query->commandType);
943                                                 break;
944                                 }
945                         }
946                 }
947
948                 results = lappend(results, query);
949         }
950
951         return results;
952 }