]> granicus.if.org Git - postgresql/blob - src/backend/rewrite/rewriteHandler.c
From: Jan Wieck <jwieck@debis.com>
[postgresql] / src / backend / rewrite / rewriteHandler.c
1 /*-------------------------------------------------------------------------
2  *
3  * rewriteHandler.c--
4  *
5  * Copyright (c) 1994, Regents of the University of California
6  *
7  *
8  * IDENTIFICATION
9  *        $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.18 1998/08/18 00:48:59 scrappy Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include <string.h>
14 #include "postgres.h"
15 #include "miscadmin.h"
16 #include "utils/palloc.h"
17 #include "utils/elog.h"
18 #include "utils/rel.h"
19 #include "nodes/pg_list.h"
20 #include "nodes/primnodes.h"
21
22 #include "parser/parsetree.h"   /* for parsetree manipulation */
23 #include "parser/parse_relation.h"
24 #include "nodes/parsenodes.h"
25
26 #include "rewrite/rewriteSupport.h"
27 #include "rewrite/rewriteHandler.h"
28 #include "rewrite/rewriteManip.h"
29 #include "rewrite/locks.h"
30
31 #include "commands/creatinh.h"
32 #include "access/heapam.h"
33
34 #include "utils/syscache.h"
35 #include "utils/acl.h"
36 #include "catalog/pg_shadow.h"
37
38 static void
39 ApplyRetrieveRule(Query *parsetree, RewriteRule *rule,
40                                   int rt_index, int relation_level,
41                                   Relation relation, int *modified);
42 static List *
43 fireRules(Query *parsetree, int rt_index, CmdType event,
44                   bool *instead_flag, List *locks, List **qual_products);
45 static void QueryRewriteSubLink(Node *node);
46 static List *QueryRewriteOne(Query *parsetree);
47 static List *deepRewriteQuery(Query *parsetree);
48 static void CheckViewPerms(Relation view, List *rtable);
49 static void RewritePreprocessQuery(Query *parsetree);
50 static Query *RewritePostprocessNonSelect(Query *parsetree);
51
52 /*
53  * gatherRewriteMeta -
54  *        Gather meta information about parsetree, and rule. Fix rule body
55  *        and qualifier so that they can be mixed with the parsetree and
56  *        maintain semantic validity
57  */
58 static RewriteInfo *
59 gatherRewriteMeta(Query *parsetree,
60                                   Query *rule_action,
61                                   Node *rule_qual,
62                                   int rt_index,
63                                   CmdType event,
64                                   bool *instead_flag)
65 {
66         RewriteInfo *info;
67         int                     rt_length;
68         int                     result_reln;
69
70         info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
71         info->rt_index = rt_index;
72         info->event = event;
73         info->instead_flag = *instead_flag;
74         info->rule_action = (Query *) copyObject(rule_action);
75         info->rule_qual = (Node *) copyObject(rule_qual);
76         if (info->rule_action == NULL)
77                 info->nothing = TRUE;
78         else
79         {
80                 info->nothing = FALSE;
81                 info->action = info->rule_action->commandType;
82                 info->current_varno = rt_index;
83                 info->rt = parsetree->rtable;
84                 rt_length = length(info->rt);
85                 info->rt = append(info->rt, info->rule_action->rtable);
86
87                 info->new_varno = PRS2_NEW_VARNO + rt_length;
88                 OffsetVarNodes(info->rule_action->qual, rt_length);
89                 OffsetVarNodes((Node *) info->rule_action->targetList, rt_length);
90                 OffsetVarNodes(info->rule_qual, rt_length);
91                 ChangeVarNodes((Node *) info->rule_action->qual,
92                                            PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
93                 ChangeVarNodes((Node *) info->rule_action->targetList,
94                                            PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
95                 ChangeVarNodes(info->rule_qual,
96                                            PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
97
98                 /*
99                  * bug here about replace CURRENT  -- sort of replace current is
100                  * deprecated now so this code shouldn't really need to be so
101                  * clutzy but.....
102                  */
103                 if (info->action != CMD_SELECT)
104                 {                                               /* i.e update XXXXX */
105                         int                     new_result_reln = 0;
106
107                         result_reln = info->rule_action->resultRelation;
108                         switch (result_reln)
109                         {
110                                 case PRS2_CURRENT_VARNO:
111                                         new_result_reln = rt_index;
112                                         break;
113                                 case PRS2_NEW_VARNO:    /* XXX */
114                                 default:
115                                         new_result_reln = result_reln + rt_length;
116                                         break;
117                         }
118                         info->rule_action->resultRelation = new_result_reln;
119                 }
120         }
121         return info;
122 }
123
124 static List *
125 OptimizeRIRRules(List *locks)
126 {
127         List       *attr_level = NIL,
128                            *i;
129         List       *relation_level = NIL;
130
131         foreach(i, locks)
132         {
133                 RewriteRule *rule_lock = lfirst(i);
134
135                 if (rule_lock->attrno == -1)
136                         relation_level = lappend(relation_level, rule_lock);
137                 else
138                         attr_level = lappend(attr_level, rule_lock);
139         }
140         return nconc(relation_level, attr_level);
141 }
142
143 /*
144  * idea is to fire regular rules first, then qualified instead
145  * rules and unqualified instead rules last. Any lemming is counted for.
146  */
147 static List *
148 orderRules(List *locks)
149 {
150         List    *regular = NIL;
151         List    *instead_rules = NIL;
152         List    *instead_qualified = NIL;
153         List    *i;
154
155         foreach(i, locks)
156         {
157                 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
158
159                 if (rule_lock->isInstead) {
160                         if (rule_lock->qual == NULL)
161                                 instead_rules = lappend(instead_rules, rule_lock);
162                         else
163                                 instead_qualified = lappend(instead_qualified, rule_lock);
164                 } else
165                         regular = lappend(regular, rule_lock);
166         }
167         regular = nconc(regular, instead_qualified);
168         return nconc(regular, instead_rules);
169 }
170
171 static int
172 AllRetrieve(List *actions)
173 {
174         List       *n;
175
176         foreach(n, actions)
177         {
178                 Query      *pt = lfirst(n);
179
180                 /*
181                  * in the old postgres code, we check whether command_type is a
182                  * consp of '('*'.commandType). but we've never supported
183                  * transitive closures. Hence removed    - ay 10/94.
184                  */
185                 if (pt->commandType != CMD_SELECT)
186                         return false;
187         }
188         return true;
189 }
190
191 static List *
192 FireRetrieveRulesAtQuery(Query *parsetree,
193                                                  int rt_index,
194                                                  Relation relation,
195                                                  bool *instead_flag,
196                                                  int rule_flag)
197 {
198         List       *i,
199                            *locks;
200         RuleLock   *rt_entry_locks = NULL;
201         List       *work = NIL;
202
203         if ((rt_entry_locks = relation->rd_rules) == NULL)
204                 return NIL;
205
206         locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);    
207
208         /* find all retrieve instead */
209         foreach(i, locks)
210         {
211                 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
212
213                 if (!rule_lock->isInstead)
214                         continue;
215                 work = lappend(work, rule_lock);
216         }
217         if (work != NIL)
218         {
219                 work = OptimizeRIRRules(locks);
220                 foreach(i, work)
221                 {
222                         RewriteRule *rule_lock = lfirst(i);
223                         int                     relation_level;
224                         int                     modified = FALSE;
225
226                         relation_level = (rule_lock->attrno == -1);
227                         if (rule_lock->actions == NIL)
228                         {
229                                 *instead_flag = TRUE;
230                                 return NIL;
231                         }
232                         if (!rule_flag &&
233                                 length(rule_lock->actions) >= 2 &&
234                                 AllRetrieve(rule_lock->actions))
235                         {
236                                 *instead_flag = TRUE;
237                                 return rule_lock->actions;
238                         }
239                         ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level, relation,
240                                                           &modified);
241                         if (modified)
242                         {
243                                 *instead_flag = TRUE;
244                                 FixResdomTypes(parsetree->targetList);
245                                 return lcons(parsetree, NIL);
246                         }
247                 }
248         }
249         return NIL;
250 }
251
252
253 /* Idea is like this:
254  *
255  * retrieve-instead-retrieve rules have different semantics than update nodes
256  * Separate RIR rules from others.      Pass others to FireRules.
257  * Order RIR rules and process.
258  *
259  * side effect: parsetree's rtable field might be changed
260  */
261 static void
262 ApplyRetrieveRule(Query *parsetree,
263                                   RewriteRule *rule,
264                                   int rt_index,
265                                   int relation_level,
266                                   Relation relation,
267                                   int *modified)
268 {
269         Query      *rule_action = NULL;
270         Node       *rule_qual;
271         List       *rtable,
272                            *rt;
273         int                     nothing,
274                                 rt_length;
275         int                     badsql = FALSE;
276         int                     viewAclOverride = FALSE;
277
278         rule_qual = rule->qual;
279         if (rule->actions)
280         {
281                 if (length(rule->actions) > 1)  /* ??? because we don't handle
282                                                                                  * rules with more than one
283                                                                                  * action? -ay */
284
285                         /*
286                          * WARNING!!! If we sometimes handle rules with more than one
287                          * action, the view acl checks might get broken.
288                          * viewAclOverride should only become true (below) if this is
289                          * a relation_level, instead, select query - Jan
290                          */
291                         return;
292                 rule_action = copyObject(lfirst(rule->actions));
293                 nothing = FALSE;
294
295                 /*
296                  * If this rule is on the relation level, the rule action is a
297                  * select and the rule is instead then it must be a view.
298                  * Permissions for views now follow the owner of the view, not the
299                  * current user.
300                  */
301                 if (relation_level && rule_action->commandType == CMD_SELECT
302                         && rule->isInstead)
303                 {
304                         CheckViewPerms(relation, rule_action->rtable);
305                         viewAclOverride = TRUE;
306                 }
307         }
308         else
309                 nothing = TRUE;
310
311         rtable = copyObject(parsetree->rtable);
312         foreach(rt, rtable)
313         {
314                 RangeTblEntry *rte = lfirst(rt);
315
316                 /*
317                  * this is to prevent add_missing_vars_to_base_rels() from adding
318                  * a bogus entry to the new target list.
319                  */
320                 rte->inFromCl = false;
321         }
322         rt_length = length(rtable);
323
324         if (viewAclOverride)
325         {
326                 List       *rule_rtable,
327                                    *rule_rt;
328                 RangeTblEntry *rte;
329
330                 rule_rtable = copyObject(rule_action->rtable);
331                 foreach(rule_rt, rule_rtable)
332                 {
333                         rte = lfirst(rule_rt);
334
335                         /*
336                          * tell the executor that the ACL check on this range table
337                          * entry is already done
338                          */
339                         rte->skipAcl = true;
340                 }
341
342                 rtable = nconc(rtable, rule_rtable);
343         }
344         else
345                 rtable = nconc(rtable, copyObject(rule_action->rtable));
346         parsetree->rtable = rtable;
347
348         rule_action->rtable = rtable;
349         OffsetVarNodes(rule_action->qual, rt_length);
350         OffsetVarNodes((Node *) rule_action->targetList, rt_length);
351         OffsetVarNodes(rule_qual, rt_length);
352         
353         OffsetVarNodes((Node *) rule_action->groupClause, rt_length);
354         OffsetVarNodes((Node *) rule_action->havingQual, rt_length);
355
356         ChangeVarNodes(rule_action->qual,
357                                    PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
358         ChangeVarNodes((Node *) rule_action->targetList,
359                                    PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
360         ChangeVarNodes(rule_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
361
362         ChangeVarNodes((Node *) rule_action->groupClause,
363                                    PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
364         ChangeVarNodes((Node *) rule_action->havingQual,
365                                    PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
366
367         if (relation_level)
368         {
369           HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
370                          modified);
371         }
372         else
373         {
374           HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
375                                  rt_index, rule->attrno, modified, &badsql);
376         }
377         if (*modified && !badsql) {
378           AddQual(parsetree, rule_action->qual);
379           /* This will only work if the query made to the view defined by the following
380            * groupClause groups by the same attributes or does not use group at all! */
381           if (parsetree->groupClause == NULL)
382             parsetree->groupClause=rule_action->groupClause;
383           AddHavingQual(parsetree, rule_action->havingQual);
384           parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
385           parsetree->hasSubLinks = (rule_action->hasSubLinks ||  parsetree->hasSubLinks);
386         }       
387 }
388
389 static List *
390 ProcessRetrieveQuery(Query *parsetree,
391                                          List *rtable,
392                                          bool *instead_flag,
393                                          bool rule)
394 {
395         List       *rt;
396         List       *product_queries = NIL;
397         int                     rt_index = 0;
398
399
400         foreach(rt, rtable)
401         {
402                 RangeTblEntry *rt_entry = lfirst(rt);
403                 Relation        rt_entry_relation = NULL;
404                 List       *result = NIL;
405
406                 rt_index++;
407                 rt_entry_relation = heap_openr(rt_entry->relname);
408
409
410
411                 if (rt_entry_relation->rd_rules != NULL)
412                 {
413                         result =
414                                 FireRetrieveRulesAtQuery(parsetree,
415                                                                                  rt_index,
416                                                                                  rt_entry_relation,
417                                                                                  instead_flag,
418                                                                                  rule);
419                 }
420                 heap_close(rt_entry_relation);
421                 if (*instead_flag) {
422                         return result;
423                 }
424         }
425         if (rule)
426                 return NIL;
427
428         foreach(rt, rtable)
429         {
430                 RangeTblEntry *rt_entry = lfirst(rt);
431                 Relation        rt_entry_relation = NULL;
432                 RuleLock   *rt_entry_locks = NULL;
433                 List       *result = NIL;
434                 List       *locks = NIL;
435                 List       *dummy_products;
436
437                 rt_index++;
438                 rt_entry_relation = heap_openr(rt_entry->relname);
439                 rt_entry_locks = rt_entry_relation->rd_rules;
440                 heap_close(rt_entry_relation);
441
442
443                 if (rt_entry_locks)
444                 {
445                         locks =
446                                 matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
447                 }
448                 if (locks != NIL)
449                 {
450                         result = fireRules(parsetree, rt_index, CMD_SELECT,
451                                                            instead_flag, locks, &dummy_products);
452                         if (*instead_flag)
453                                 return lappend(NIL, result);
454                         if (result != NIL)
455                                 product_queries = nconc(product_queries, result);
456                 }
457         }
458         return product_queries;
459 }
460
461 static Query *
462 CopyAndAddQual(Query *parsetree,
463                            List *actions,
464                            Node *rule_qual,
465                            int rt_index,
466                            CmdType event)
467 {
468         Query      *new_tree = (Query *) copyObject(parsetree);
469         Node       *new_qual = NULL;
470         Query      *rule_action = NULL;
471
472         if (actions)
473                 rule_action = lfirst(actions);
474         if (rule_qual != NULL)
475                 new_qual = (Node *) copyObject(rule_qual);
476         if (rule_action != NULL)
477         {
478                 List       *rtable;
479                 int                     rt_length;
480
481                 rtable = new_tree->rtable;
482                 rt_length = length(rtable);
483                 rtable = append(rtable, listCopy(rule_action->rtable));
484                 new_tree->rtable = rtable;
485                 OffsetVarNodes(new_qual, rt_length);
486                 ChangeVarNodes(new_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
487         }
488         /* XXX -- where current doesn't work for instead nothing.... yet */
489         AddNotQual(new_tree, new_qual);
490
491         return new_tree;
492 }
493
494
495 /*
496  *      fireRules -
497  *         Iterate through rule locks applying rules.
498  *         All rules create their own parsetrees. Instead rules
499  *         with rule qualification save the original parsetree
500  *         and add their negated qualification to it. Real instead
501  *         rules finally throw away the original parsetree.
502  *         
503  *         remember: reality is for dead birds -- glass
504  *
505  */
506 static List *
507 fireRules(Query *parsetree,
508                   int rt_index,
509                   CmdType event,
510                   bool *instead_flag,
511                   List *locks,
512                   List **qual_products)
513 {
514         RewriteInfo *info;
515         List       *results = NIL;
516         List       *i;
517
518         /* choose rule to fire from list of rules */
519         if (locks == NIL)
520         {
521                 ProcessRetrieveQuery(parsetree,
522                                                          parsetree->rtable,
523                                                          instead_flag, TRUE);
524                 if (*instead_flag)
525                         return lappend(NIL, parsetree);
526                 else
527                         return NIL;
528         }
529
530         locks = orderRules(locks);      /* real instead rules last */
531         foreach(i, locks)
532         {
533                 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
534                 Node       *qual,
535                                    *event_qual;
536                 List       *actions;
537                 List       *r;
538                 bool            orig_instead_flag = *instead_flag;
539
540                 /* multiple rule action time */
541                 *instead_flag = rule_lock->isInstead;
542                 event_qual = rule_lock->qual;
543                 actions = rule_lock->actions;
544                 if (event_qual != NULL && *instead_flag) {
545                         Query           *qual_product;
546                         RewriteInfo     qual_info;
547
548                         /* ----------
549                          * If there are instead rules with qualifications,
550                          * the original query is still performed. But all
551                          * the negated rule qualifications of the instead
552                          * rules are added so it does it's actions only
553                          * in cases where the rule quals of all instead
554                          * rules are false. Think of it as the default
555                          * action in a case. We save this in *qual_products
556                          * so deepRewriteQuery() can add it to the query
557                          * list after we mangled it up enough.
558                          * ----------
559                          */
560                         if (*qual_products == NIL) {
561                                 qual_product = parsetree;
562                         } else {
563                                 qual_product = (Query *)nth(0, *qual_products);
564                         }
565
566                         qual_info.event         = qual_product->commandType;
567                         qual_info.new_varno     = length(qual_product->rtable) + 2;
568                         qual_product = CopyAndAddQual(qual_product, 
569                                         actions, 
570                                         event_qual,
571                                         rt_index,
572                                         event);
573                         
574                         qual_info.rule_action   = qual_product;
575
576                         if (event == CMD_INSERT || event == CMD_UPDATE)
577                                 FixNew(&qual_info, qual_product);
578
579                         *qual_products = lappend(NIL, qual_product);
580                 }
581
582                 foreach(r, actions)
583                 {
584                         Query      *rule_action = lfirst(r);
585                         Node       *rule_qual = copyObject(event_qual);
586
587                         if (rule_action->commandType == CMD_NOTHING)
588                                 continue;
589
590                         /*--------------------------------------------------
591                          * Step 1:
592                          *        Rewrite current.attribute or current to tuple variable
593                          *        this appears to be done in parser?
594                          *--------------------------------------------------
595                          */
596                         info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
597                                                                          rt_index, event, instead_flag);
598
599                         /* handle escapable cases, or those handled by other code */
600                         if (info->nothing)
601                         {
602                                 if (*instead_flag)
603                                         return NIL;
604                                 else
605                                         continue;
606                         }
607
608                         if (info->action == info->event &&
609                                 info->event == CMD_SELECT)
610                                 continue;
611
612                         /*
613                          * Event Qualification forces copying of parsetree and
614                          * splitting into two queries one w/rule_qual, one w/NOT
615                          * rule_qual. Also add user query qual onto rule action
616                          */
617                         qual = parsetree->qual;
618                         AddQual(info->rule_action, qual);
619
620                         if (info->rule_qual != NULL)
621                                 AddQual(info->rule_action, info->rule_qual);
622
623                         /*--------------------------------------------------
624                          * Step 2:
625                          *        Rewrite new.attribute w/ right hand side of target-list
626                          *        entry for appropriate field name in insert/update
627                          *--------------------------------------------------
628                          */
629                         if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE))
630                                 FixNew(info, parsetree);
631
632                         /*--------------------------------------------------
633                          * Step 3:
634                          *        rewriting due to retrieve rules
635                          *--------------------------------------------------
636                          */
637                         info->rule_action->rtable = info->rt;
638                         ProcessRetrieveQuery(info->rule_action, info->rt,
639                                                                  &orig_instead_flag, TRUE);
640
641                         /*--------------------------------------------------
642                          * Step 4
643                          *        Simplify? hey, no algorithm for simplification... let
644                          *        the planner do it.
645                          *--------------------------------------------------
646                          */
647                         results = lappend(results, info->rule_action);
648
649                         pfree(info);
650                 }
651
652                 /* ----------
653                  * If this was an unqualified instead rule,
654                  * throw away an eventually saved 'default' parsetree
655                  * ----------
656                  */
657                 if (event_qual == NULL && *instead_flag) {
658                         *qual_products = NIL;
659                 }
660         }
661         return results;
662 }
663
664 /* ----------
665  * RewritePreprocessQuery -
666  *      adjust details in the parsetree, the rule system
667  *      depends on
668  * ----------
669  */
670 static void
671 RewritePreprocessQuery(Query *parsetree)
672 {
673         /* ----------
674          * if the query has a resultRelation, reassign the
675          * result domain numbers to the attribute numbers in the
676          * target relation. FixNew() depends on it when replacing
677          * *new* references in a rule action by the expressions
678          * from the rewritten query.
679          * ----------
680          */
681         if (parsetree->resultRelation > 0) {
682                 RangeTblEntry   *rte;
683                 Relation        rd;
684                 List            *tl;
685                 TargetEntry     *tle;
686                 int             resdomno;
687         
688                 rte = (RangeTblEntry *)nth(parsetree->resultRelation - 1,
689                                         parsetree->rtable);
690                 rd = heap_openr(rte->relname);
691
692                 foreach (tl, parsetree->targetList) {
693                         tle = (TargetEntry *)lfirst(tl);
694                         resdomno = attnameAttNum(rd, tle->resdom->resname);
695                         tle->resdom->resno = resdomno;
696                 }
697
698                 heap_close(rd);
699         }
700 }
701
702
703 /* ----------
704  * RewritePostprocessNonSelect -
705  *      apply instead select rules on a query fired in by
706  *      the rewrite system
707  * ----------
708  */
709 static Query *
710 RewritePostprocessNonSelect(Query *parsetree)
711 {
712         List            *rt;
713         int             rt_index = 0;
714         Query           *newtree = copyObject(parsetree);
715         
716         foreach(rt, parsetree->rtable)
717         {
718                 RangeTblEntry   *rt_entry = lfirst(rt);
719                 Relation        rt_entry_relation = NULL;
720                 RuleLock        *rt_entry_locks = NULL;
721                 List            *locks = NIL;
722                 List            *instead_locks = NIL;
723                 List            *lock;
724                 RewriteRule     *rule;
725
726                 rt_index++;
727                 rt_entry_relation = heap_openr(rt_entry->relname);
728                 rt_entry_locks = rt_entry_relation->rd_rules;
729
730                 if (rt_entry_locks)
731                 {
732                         int     origcmdtype = newtree->commandType;
733                         newtree->commandType = CMD_SELECT;
734                         locks =
735                                 matchLocks(CMD_SELECT, rt_entry_locks, rt_index, newtree);
736                         newtree->commandType = origcmdtype;
737                 }
738                 if (locks != NIL)
739                 {
740                         foreach (lock, locks) {
741                                 rule = (RewriteRule *)lfirst(lock);
742                                 if (rule->isInstead) {
743                                         instead_locks = nconc(instead_locks, lock);
744                                 }
745                         }
746                 }
747                 if (instead_locks != NIL)
748                 {
749                         foreach (lock, instead_locks) {
750                                 int     relation_level;
751                                 int     modified = 0;
752
753                                 rule = (RewriteRule *)lfirst(lock);
754                                 relation_level = (rule->attrno == -1);
755
756                                 ApplyRetrieveRule(newtree,
757                                         rule,
758                                         rt_index,
759                                         relation_level,
760                                         rt_entry_relation,
761                                         &modified);
762                         }
763                 }
764
765                 heap_close(rt_entry_relation);
766         }
767
768         return newtree;
769 }
770
771 static List *
772 RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
773 {
774         CmdType         event;
775         List       *product_queries = NIL;
776         int                     result_relation = 0;
777
778         Assert(parsetree != NULL);
779
780         event = parsetree->commandType;
781
782         if (event == CMD_UTILITY)
783                 return NIL;
784
785         /*
786          * only for a delete may the targetlist be NULL
787          */
788         if (event != CMD_DELETE)
789                 Assert(parsetree->targetList != NULL);
790
791         result_relation = parsetree->resultRelation;
792
793         if (event != CMD_SELECT)
794         {
795
796                 /*
797                  * the statement is an update, insert or delete
798                  */
799                 RangeTblEntry *rt_entry;
800                 Relation        rt_entry_relation = NULL;
801                 RuleLock   *rt_entry_locks = NULL;
802
803                 rt_entry = rt_fetch(result_relation, parsetree->rtable);
804                 rt_entry_relation = heap_openr(rt_entry->relname);
805                 rt_entry_locks = rt_entry_relation->rd_rules;
806                 heap_close(rt_entry_relation);
807
808                 if (rt_entry_locks != NULL)
809                 {
810                         List       *locks =
811                         matchLocks(event, rt_entry_locks, result_relation, parsetree);
812                         product_queries =
813                                 fireRules(parsetree,
814                                                   result_relation,
815                                                   event,
816                                                   instead_flag,
817                                                   locks,
818                                                   qual_products);
819                 }
820
821                 /* ----------
822                  * deepRewriteQuery does not handle the situation
823                  * where a query fired by a rule uses relations that
824                  * have instead select rules defined (views and the like).
825                  * So we care for them here.
826                  * ----------
827                  */
828                 if (product_queries != NIL) {
829                         List    *pq;
830                         Query   *tmp;
831                         List    *new_products = NIL;
832                 
833                         foreach (pq, product_queries) {
834                                 tmp = (Query *)lfirst(pq);
835                                 tmp = RewritePostprocessNonSelect(tmp);
836                                 new_products = lappend(new_products, tmp);
837                         }
838                         product_queries = new_products;
839                 }
840
841                 return product_queries;
842         }
843         else
844         {
845
846                 /*
847                  * the statement is a select
848                  */
849                 Query      *other;
850
851                 /*
852                  * ApplyRetrieveRule changes the range table XXX Unions are copied
853                  * again.
854                  */
855                 other = copyObject(parsetree);
856
857                 return
858                         ProcessRetrieveQuery(other, parsetree->rtable,
859                                                                  instead_flag, FALSE);
860         }
861 }
862
863 /*
864  * to avoid infinite recursion, we restrict the number of times a query
865  * can be rewritten. Detecting cycles is left for the reader as an excercise.
866  */
867 #ifndef REWRITE_INVOKE_MAX
868 #define REWRITE_INVOKE_MAX              10
869 #endif
870
871 static int      numQueryRewriteInvoked = 0;
872
873 /*
874  * QueryRewrite -
875  *        rewrite one query via QueryRewrite system, possibly returning 0, or many
876  *        queries
877  */
878 List *
879 QueryRewrite(Query *parsetree)
880 {
881         RewritePreprocessQuery(parsetree);
882
883         QueryRewriteSubLink(parsetree->qual);
884         QueryRewriteSubLink(parsetree->havingQual);
885
886         return QueryRewriteOne(parsetree);
887 }
888
889 /*
890  *      QueryRewriteSubLink
891  *
892  *      This rewrites the SubLink subqueries first, doing the lowest ones first.
893  *      We already have code in the main rewrite loops to process correlated
894  *      variables from upper queries that exist in subqueries.
895  */
896 static void
897 QueryRewriteSubLink(Node *node)
898 {
899         if (node == NULL)
900                 return;
901
902         switch (nodeTag(node))
903         {
904                 case T_TargetEntry:
905                         break;
906                 case T_Aggreg:
907                         break;
908                 case T_Expr:
909                         {
910                                 Expr       *expr = (Expr *) node;
911
912                                 QueryRewriteSubLink((Node *) expr->args);
913                         }
914                         break;
915                 case T_Var:
916                         break;
917                 case T_List:
918                         {
919                                 List       *l;
920
921                                 foreach(l, (List *) node)
922                                         QueryRewriteSubLink(lfirst(l));
923                         }
924                         break;
925                 case T_SubLink:
926                         {
927                                 SubLink    *sublink = (SubLink *) node;
928                                 Query      *query = (Query *) sublink->subselect;
929                                 List       *ret;
930
931                                 /*
932                                  * Nest down first.  We do this so if a rewrite adds a
933                                  * SubLink we don't process it as part of this loop.
934                                  */
935                                 QueryRewriteSubLink((Node *) query->qual);
936                                 
937                                 QueryRewriteSubLink((Node *) query->havingQual);
938
939                                 ret = QueryRewriteOne(query);
940                                 if (!ret)
941                                         sublink->subselect = NULL;
942                                 else if (lnext(ret) == NIL)
943                                         sublink->subselect = lfirst(ret);
944                                 else
945                                         elog(ERROR, "Don't know how to process subquery that rewrites to multiple queries.");
946                         }
947                         break;
948                 default:
949                         /* ignore the others */
950                         break;
951         }
952         return;
953 }
954
955 /*
956  * QueryOneRewrite -
957  *        rewrite one query
958  */
959 static List *
960 QueryRewriteOne(Query *parsetree)
961 {
962         numQueryRewriteInvoked = 0;
963
964         /*
965          * take a deep breath and apply all the rewrite rules - ay
966          */
967         return deepRewriteQuery(parsetree);
968 }
969
970 /*
971  * deepRewriteQuery -
972  *        rewrites the query and apply the rules again on the queries rewritten
973  */
974 static List *
975 deepRewriteQuery(Query *parsetree)
976 {
977         List       *n;
978         List       *rewritten = NIL;
979         List       *result = NIL;
980         bool            instead;
981         List       *qual_products = NIL;
982
983
984
985         if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
986         {
987                 elog(ERROR, "query rewritten %d times, may contain cycles",
988                          numQueryRewriteInvoked - 1);
989         }
990
991         instead = FALSE;
992         result = RewriteQuery(parsetree, &instead, &qual_products);
993
994         foreach(n, result)
995         {
996                 Query      *pt = lfirst(n);
997                 List       *newstuff = NIL;
998
999                 newstuff = deepRewriteQuery(pt);
1000                 if (newstuff != NIL)
1001                         rewritten = nconc(rewritten, newstuff);
1002         }
1003
1004         /* ----------
1005          * qual_products are the original query with the negated
1006          * rule qualification of an instead rule
1007          * ----------
1008          */
1009         if (qual_products != NIL)
1010                 rewritten = nconc(rewritten, qual_products);
1011
1012         /* ----------
1013          * The original query is appended last if not instead
1014          * because update and delete rule actions might not do
1015          * anything if they are invoked after the update or
1016          * delete is performed. The command counter increment
1017          * between the query execution makes the deleted (and
1018          * maybe the updated) tuples disappear so the scans
1019          * for them in the rule actions cannot find them.
1020          * ----------
1021          */
1022         if (!instead)
1023                 rewritten = lappend(rewritten, parsetree);
1024
1025         return rewritten;
1026 }
1027
1028
1029 static void
1030 CheckViewPerms(Relation view, List *rtable)
1031 {
1032         HeapTuple       utup;
1033         NameData        uname;
1034         List       *rt;
1035         RangeTblEntry *rte;
1036         int32           aclcheck_res;
1037
1038         /*
1039          * get the usename of the view's owner
1040          */
1041         utup = SearchSysCacheTuple(USESYSID, view->rd_rel->relowner, 0, 0, 0);
1042         if (!HeapTupleIsValid(utup))
1043         {
1044                 elog(ERROR, "cache lookup for userid %d failed",
1045                          view->rd_rel->relowner);
1046         }
1047         StrNCpy(uname.data,
1048                         ((Form_pg_shadow) GETSTRUCT(utup))->usename.data,
1049                         NAMEDATALEN);
1050
1051         /*
1052          * check that we have read access to all the classes in the range
1053          * table of the view
1054          */
1055         foreach(rt, rtable)
1056         {
1057                 rte = (RangeTblEntry *) lfirst(rt);
1058
1059                 aclcheck_res = pg_aclcheck(rte->relname, uname.data, ACL_RD);
1060                 if (aclcheck_res != ACLCHECK_OK)
1061                         elog(ERROR, "%s: %s", rte->relname, aclcheck_error_strings[aclcheck_res]);
1062         }
1063 }