]> granicus.if.org Git - postgresql/blob - src/backend/rewrite/rewriteHandler.c
1) Queries using the having clause on base tables should work well
[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.17 1998/07/19 05:49:24 momjian 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 "nodes/parsenodes.h"
24
25 #include "rewrite/rewriteSupport.h"
26 #include "rewrite/rewriteHandler.h"
27 #include "rewrite/rewriteManip.h"
28 #include "rewrite/locks.h"
29
30 #include "commands/creatinh.h"
31 #include "access/heapam.h"
32
33 #include "utils/syscache.h"
34 #include "utils/acl.h"
35 #include "catalog/pg_shadow.h"
36
37 static void
38 ApplyRetrieveRule(Query *parsetree, RewriteRule *rule,
39                                   int rt_index, int relation_level,
40                                   Relation relation, int *modified);
41 static List *
42 fireRules(Query *parsetree, int rt_index, CmdType event,
43                   bool *instead_flag, List *locks, List **qual_products);
44 static void QueryRewriteSubLink(Node *node);
45 static List *QueryRewriteOne(Query *parsetree);
46 static List *deepRewriteQuery(Query *parsetree);
47 static void CheckViewPerms(Relation view, List *rtable);
48
49 /*
50  * gatherRewriteMeta -
51  *        Gather meta information about parsetree, and rule. Fix rule body
52  *        and qualifier so that they can be mixed with the parsetree and
53  *        maintain semantic validity
54  */
55 static RewriteInfo *
56 gatherRewriteMeta(Query *parsetree,
57                                   Query *rule_action,
58                                   Node *rule_qual,
59                                   int rt_index,
60                                   CmdType event,
61                                   bool *instead_flag)
62 {
63         RewriteInfo *info;
64         int                     rt_length;
65         int                     result_reln;
66
67         info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
68         info->rt_index = rt_index;
69         info->event = event;
70         info->instead_flag = *instead_flag;
71         info->rule_action = (Query *) copyObject(rule_action);
72         info->rule_qual = (Node *) copyObject(rule_qual);
73         if (info->rule_action == NULL)
74                 info->nothing = TRUE;
75         else
76         {
77                 info->nothing = FALSE;
78                 info->action = info->rule_action->commandType;
79                 info->current_varno = rt_index;
80                 info->rt = parsetree->rtable;
81                 rt_length = length(info->rt);
82                 info->rt = append(info->rt, info->rule_action->rtable);
83
84                 info->new_varno = PRS2_NEW_VARNO + rt_length;
85                 OffsetVarNodes(info->rule_action->qual, rt_length);
86                 OffsetVarNodes((Node *) info->rule_action->targetList, rt_length);
87                 OffsetVarNodes(info->rule_qual, rt_length);
88                 ChangeVarNodes((Node *) info->rule_action->qual,
89                                            PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
90                 ChangeVarNodes((Node *) info->rule_action->targetList,
91                                            PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
92                 ChangeVarNodes(info->rule_qual,
93                                            PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
94
95                 /*
96                  * bug here about replace CURRENT  -- sort of replace current is
97                  * deprecated now so this code shouldn't really need to be so
98                  * clutzy but.....
99                  */
100                 if (info->action != CMD_SELECT)
101                 {                                               /* i.e update XXXXX */
102                         int                     new_result_reln = 0;
103
104                         result_reln = info->rule_action->resultRelation;
105                         switch (result_reln)
106                         {
107                                 case PRS2_CURRENT_VARNO:
108                                         new_result_reln = rt_index;
109                                         break;
110                                 case PRS2_NEW_VARNO:    /* XXX */
111                                 default:
112                                         new_result_reln = result_reln + rt_length;
113                                         break;
114                         }
115                         info->rule_action->resultRelation = new_result_reln;
116                 }
117         }
118         return info;
119 }
120
121 static List *
122 OptimizeRIRRules(List *locks)
123 {
124         List       *attr_level = NIL,
125                            *i;
126         List       *relation_level = NIL;
127
128         foreach(i, locks)
129         {
130                 RewriteRule *rule_lock = lfirst(i);
131
132                 if (rule_lock->attrno == -1)
133                         relation_level = lappend(relation_level, rule_lock);
134                 else
135                         attr_level = lappend(attr_level, rule_lock);
136         }
137         return nconc(relation_level, attr_level);
138 }
139
140 /*
141  * idea is to put instead rules before regular rules so that
142  * excess semantically queasy queries aren't processed
143  */
144 static List *
145 orderRules(List *locks)
146 {
147         List       *regular = NIL,
148                            *i;
149         List       *instead_rules = NIL;
150
151         foreach(i, locks)
152         {
153                 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
154
155                 if (rule_lock->isInstead)
156                         instead_rules = lappend(instead_rules, rule_lock);
157                 else
158                         regular = lappend(regular, rule_lock);
159         }
160         return nconc(regular, instead_rules);
161 }
162
163 static int
164 AllRetrieve(List *actions)
165 {
166         List       *n;
167
168         foreach(n, actions)
169         {
170                 Query      *pt = lfirst(n);
171
172                 /*
173                  * in the old postgres code, we check whether command_type is a
174                  * consp of '('*'.commandType). but we've never supported
175                  * transitive closures. Hence removed    - ay 10/94.
176                  */
177                 if (pt->commandType != CMD_SELECT)
178                         return false;
179         }
180         return true;
181 }
182
183 static List *
184 FireRetrieveRulesAtQuery(Query *parsetree,
185                                                  int rt_index,
186                                                  Relation relation,
187                                                  bool *instead_flag,
188                                                  int rule_flag)
189 {
190         List       *i,
191                            *locks;
192         RuleLock   *rt_entry_locks = NULL;
193         List       *work = NIL;
194
195         if ((rt_entry_locks = relation->rd_rules) == NULL)
196                 return NIL;
197
198         locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);    
199
200         /* find all retrieve instead */
201         foreach(i, locks)
202         {
203                 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
204
205                 if (!rule_lock->isInstead)
206                         continue;
207                 work = lappend(work, rule_lock);
208         }
209         if (work != NIL)
210         {
211                 work = OptimizeRIRRules(locks);
212                 foreach(i, work)
213                 {
214                         RewriteRule *rule_lock = lfirst(i);
215                         int                     relation_level;
216                         int                     modified = FALSE;
217
218                         relation_level = (rule_lock->attrno == -1);
219                         if (rule_lock->actions == NIL)
220                         {
221                                 *instead_flag = TRUE;
222                                 return NIL;
223                         }
224                         if (!rule_flag &&
225                                 length(rule_lock->actions) >= 2 &&
226                                 AllRetrieve(rule_lock->actions))
227                         {
228                                 *instead_flag = TRUE;
229                                 return rule_lock->actions;
230                         }
231                         ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level, relation,
232                                                           &modified);
233                         if (modified)
234                         {
235                                 *instead_flag = TRUE;
236                                 FixResdomTypes(parsetree->targetList);
237
238                                 return lcons(parsetree, NIL);
239                         }
240                 }
241         }
242         return NIL;
243 }
244
245
246 /* Idea is like this:
247  *
248  * retrieve-instead-retrieve rules have different semantics than update nodes
249  * Separate RIR rules from others.      Pass others to FireRules.
250  * Order RIR rules and process.
251  *
252  * side effect: parsetree's rtable field might be changed
253  */
254 static void
255 ApplyRetrieveRule(Query *parsetree,
256                                   RewriteRule *rule,
257                                   int rt_index,
258                                   int relation_level,
259                                   Relation relation,
260                                   int *modified)
261 {
262         Query      *rule_action = NULL;
263         Node       *rule_qual;
264         List       *rtable,
265                            *rt;
266         int                     nothing,
267                                 rt_length;
268         int                     badsql = FALSE;
269         int                     viewAclOverride = FALSE;
270
271         rule_qual = rule->qual;
272         if (rule->actions)
273         {
274                 if (length(rule->actions) > 1)  /* ??? because we don't handle
275                                                                                  * rules with more than one
276                                                                                  * action? -ay */
277
278                         /*
279                          * WARNING!!! If we sometimes handle rules with more than one
280                          * action, the view acl checks might get broken.
281                          * viewAclOverride should only become true (below) if this is
282                          * a relation_level, instead, select query - Jan
283                          */
284                         return;
285                 rule_action = copyObject(lfirst(rule->actions));
286                 nothing = FALSE;
287
288                 /*
289                  * If this rule is on the relation level, the rule action is a
290                  * select and the rule is instead then it must be a view.
291                  * Permissions for views now follow the owner of the view, not the
292                  * current user.
293                  */
294                 if (relation_level && rule_action->commandType == CMD_SELECT
295                         && rule->isInstead)
296                 {
297                         CheckViewPerms(relation, rule_action->rtable);
298                         viewAclOverride = TRUE;
299                 }
300         }
301         else
302                 nothing = TRUE;
303
304         rtable = copyObject(parsetree->rtable);
305         foreach(rt, rtable)
306         {
307                 RangeTblEntry *rte = lfirst(rt);
308
309                 /*
310                  * this is to prevent add_missing_vars_to_base_rels() from adding
311                  * a bogus entry to the new target list.
312                  */
313                 rte->inFromCl = false;
314         }
315         rt_length = length(rtable);
316
317         if (viewAclOverride)
318         {
319                 List       *rule_rtable,
320                                    *rule_rt;
321                 RangeTblEntry *rte;
322
323                 rule_rtable = copyObject(rule_action->rtable);
324                 foreach(rule_rt, rule_rtable)
325                 {
326                         rte = lfirst(rule_rt);
327
328                         /*
329                          * tell the executor that the ACL check on this range table
330                          * entry is already done
331                          */
332                         rte->skipAcl = true;
333                 }
334
335                 rtable = nconc(rtable, rule_rtable);
336         }
337         else
338                 rtable = nconc(rtable, copyObject(rule_action->rtable));
339         parsetree->rtable = rtable;
340
341         rule_action->rtable = rtable;
342         OffsetVarNodes(rule_action->qual, rt_length);
343         OffsetVarNodes((Node *) rule_action->targetList, rt_length);
344         OffsetVarNodes(rule_qual, rt_length);
345         
346         OffsetVarNodes((Node *) rule_action->groupClause, rt_length);
347         OffsetVarNodes((Node *) rule_action->havingQual, rt_length);
348
349         ChangeVarNodes(rule_action->qual,
350                                    PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
351         ChangeVarNodes((Node *) rule_action->targetList,
352                                    PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
353         ChangeVarNodes(rule_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
354
355         ChangeVarNodes((Node *) rule_action->groupClause,
356                                    PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
357         ChangeVarNodes((Node *) rule_action->havingQual,
358                                    PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
359
360         if (relation_level)
361         {
362           HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
363                          modified);
364         }
365         else
366         {
367           HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
368                                  rt_index, rule->attrno, modified, &badsql);
369         }
370         if (*modified && !badsql) {
371           AddQual(parsetree, rule_action->qual);
372           /* This will only work if the query made to the view defined by the following
373            * groupClause groups by the same attributes or does not use group at all! */
374           if (parsetree->groupClause == NULL)
375             parsetree->groupClause=rule_action->groupClause;
376           AddHavingQual(parsetree, rule_action->havingQual);
377           parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
378           parsetree->hasSubLinks = (rule_action->hasSubLinks ||  parsetree->hasSubLinks);
379         }       
380 }
381
382 static List *
383 ProcessRetrieveQuery(Query *parsetree,
384                                          List *rtable,
385                                          bool *instead_flag,
386                                          bool rule)
387 {
388         List       *rt;
389         List       *product_queries = NIL;
390         int                     rt_index = 0;
391
392
393         foreach(rt, rtable)
394         {
395                 RangeTblEntry *rt_entry = lfirst(rt);
396                 Relation        rt_entry_relation = NULL;
397                 List       *result = NIL;
398
399                 rt_index++;
400                 rt_entry_relation = heap_openr(rt_entry->relname);
401
402
403
404                 if (rt_entry_relation->rd_rules != NULL)
405                 {
406                         result =
407                                 FireRetrieveRulesAtQuery(parsetree,
408                                                                                  rt_index,
409                                                                                  rt_entry_relation,
410                                                                                  instead_flag,
411                                                                                  rule);
412                 }
413                 heap_close(rt_entry_relation);
414                 if (*instead_flag)
415                         return result;
416         }
417         if (rule)
418                 return NIL;
419
420         foreach(rt, rtable)
421         {
422                 RangeTblEntry *rt_entry = lfirst(rt);
423                 Relation        rt_entry_relation = NULL;
424                 RuleLock   *rt_entry_locks = NULL;
425                 List       *result = NIL;
426                 List       *locks = NIL;
427                 List       *dummy_products;
428
429                 rt_index++;
430                 rt_entry_relation = heap_openr(rt_entry->relname);
431                 rt_entry_locks = rt_entry_relation->rd_rules;
432                 heap_close(rt_entry_relation);
433
434
435                 if (rt_entry_locks)
436                 {
437                         locks =
438                                 matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
439                 }
440                 if (locks != NIL)
441                 {
442                         result = fireRules(parsetree, rt_index, CMD_SELECT,
443                                                            instead_flag, locks, &dummy_products);
444                         if (*instead_flag)
445                                 return lappend(NIL, result);
446                         if (result != NIL)
447                                 product_queries = nconc(product_queries, result);
448                 }
449         }
450         return product_queries;
451 }
452
453 static Query *
454 CopyAndAddQual(Query *parsetree,
455                            List *actions,
456                            Node *rule_qual,
457                            int rt_index,
458                            CmdType event)
459 {
460         Query      *new_tree = (Query *) copyObject(parsetree);
461         Node       *new_qual = NULL;
462         Query      *rule_action = NULL;
463
464         if (actions)
465                 rule_action = lfirst(actions);
466         if (rule_qual != NULL)
467                 new_qual = (Node *) copyObject(rule_qual);
468         if (rule_action != NULL)
469         {
470                 List       *rtable;
471                 int                     rt_length;
472
473                 rtable = new_tree->rtable;
474                 rt_length = length(rtable);
475                 rtable = append(rtable, listCopy(rule_action->rtable));
476                 new_tree->rtable = rtable;
477                 OffsetVarNodes(new_qual, rt_length);
478                 ChangeVarNodes(new_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
479         }
480         /* XXX -- where current doesn't work for instead nothing.... yet */
481         AddNotQual(new_tree, new_qual);
482
483         return new_tree;
484 }
485
486
487 /*
488  *      fireRules -
489  *         Iterate through rule locks applying rules.  After an instead rule
490  *         rule has been applied, return just new parsetree and let RewriteQuery
491  *         start the process all over again.  The locks are reordered to maintain
492  *         sensible semantics.  remember: reality is for dead birds -- glass
493  *
494  */
495 static List *
496 fireRules(Query *parsetree,
497                   int rt_index,
498                   CmdType event,
499                   bool *instead_flag,
500                   List *locks,
501                   List **qual_products)
502 {
503         RewriteInfo *info;
504         List       *results = NIL;
505         List       *i;
506
507         /* choose rule to fire from list of rules */
508         if (locks == NIL)
509         {
510                 ProcessRetrieveQuery(parsetree,
511                                                          parsetree->rtable,
512                                                          instead_flag, TRUE);
513                 if (*instead_flag)
514                         return lappend(NIL, parsetree);
515                 else
516                         return NIL;
517         }
518
519         locks = orderRules(locks);      /* instead rules first */
520         foreach(i, locks)
521         {
522                 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
523                 Node       *qual,
524                                    *event_qual;
525                 List       *actions;
526                 List       *r;
527                 bool            orig_instead_flag = *instead_flag;
528
529                 /* multiple rule action time */
530                 *instead_flag = rule_lock->isInstead;
531                 event_qual = rule_lock->qual;
532                 actions = rule_lock->actions;
533                 if (event_qual != NULL && *instead_flag)
534                         *qual_products =
535                                 lappend(*qual_products,
536                                                 CopyAndAddQual(parsetree, actions, event_qual,
537                                                                            rt_index, event));
538                 foreach(r, actions)
539                 {
540                         Query      *rule_action = lfirst(r);
541                         Node       *rule_qual = copyObject(event_qual);
542
543                         /*--------------------------------------------------
544                          * Step 1:
545                          *        Rewrite current.attribute or current to tuple variable
546                          *        this appears to be done in parser?
547                          *--------------------------------------------------
548                          */
549                         info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
550                                                                          rt_index, event, instead_flag);
551
552                         /* handle escapable cases, or those handled by other code */
553                         if (info->nothing)
554                         {
555                                 if (*instead_flag)
556                                         return NIL;
557                                 else
558                                         continue;
559                         }
560
561                         if (info->action == info->event &&
562                                 info->event == CMD_SELECT)
563                                 continue;
564
565                         /*
566                          * Event Qualification forces copying of parsetree --- XXX and
567                          * splitting into two queries one w/rule_qual, one w/NOT
568                          * rule_qual. Also add user query qual onto rule action
569                          */
570                         qual = parsetree->qual;
571                         AddQual(info->rule_action, qual);
572
573                         if (info->rule_qual != NULL)
574                                 AddQual(info->rule_action, info->rule_qual);
575
576                         /*--------------------------------------------------
577                          * Step 2:
578                          *        Rewrite new.attribute w/ right hand side of target-list
579                          *        entry for appropriate field name in insert/update
580                          *--------------------------------------------------
581                          */
582                         if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE))
583                                 FixNew(info, parsetree);
584
585                         /*--------------------------------------------------
586                          * Step 3:
587                          *        rewriting due to retrieve rules
588                          *--------------------------------------------------
589                          */
590                         info->rule_action->rtable = info->rt;
591                         ProcessRetrieveQuery(info->rule_action, info->rt,
592                                                                  &orig_instead_flag, TRUE);
593
594                         /*--------------------------------------------------
595                          * Step 4
596                          *        Simplify? hey, no algorithm for simplification... let
597                          *        the planner do it.
598                          *--------------------------------------------------
599                          */
600                         results = lappend(results, info->rule_action);
601
602                         pfree(info);
603                 }
604                 if (*instead_flag)
605                         break;
606         }
607         return results;
608 }
609
610 static List *
611 RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
612 {
613         CmdType         event;
614         List       *product_queries = NIL;
615         int                     result_relation = 0;
616
617         Assert(parsetree != NULL);
618
619         event = parsetree->commandType;
620
621         if (event == CMD_UTILITY)
622                 return NIL;
623
624         /*
625          * only for a delete may the targetlist be NULL
626          */
627         if (event != CMD_DELETE)
628                 Assert(parsetree->targetList != NULL);
629
630         result_relation = parsetree->resultRelation;
631
632         if (event != CMD_SELECT)
633         {
634
635                 /*
636                  * the statement is an update, insert or delete
637                  */
638                 RangeTblEntry *rt_entry;
639                 Relation        rt_entry_relation = NULL;
640                 RuleLock   *rt_entry_locks = NULL;
641
642                 rt_entry = rt_fetch(result_relation, parsetree->rtable);
643                 rt_entry_relation = heap_openr(rt_entry->relname);
644                 rt_entry_locks = rt_entry_relation->rd_rules;
645                 heap_close(rt_entry_relation);
646
647                 if (rt_entry_locks != NULL)
648                 {
649                         List       *locks =
650                         matchLocks(event, rt_entry_locks, result_relation, parsetree);
651
652                         product_queries =
653                                 fireRules(parsetree,
654                                                   result_relation,
655                                                   event,
656                                                   instead_flag,
657                                                   locks,
658                                                   qual_products);
659                 }
660                 return product_queries;
661         }
662         else
663         {
664
665                 /*
666                  * the statement is a select
667                  */
668                 Query      *other;
669
670                 /*
671                  * ApplyRetrieveRule changes the range table XXX Unions are copied
672                  * again.
673                  */
674                 other = copyObject(parsetree);
675
676                 return
677                         ProcessRetrieveQuery(other, parsetree->rtable,
678                                                                  instead_flag, FALSE);
679         }
680 }
681
682 /*
683  * to avoid infinite recursion, we restrict the number of times a query
684  * can be rewritten. Detecting cycles is left for the reader as an excercise.
685  */
686 #ifndef REWRITE_INVOKE_MAX
687 #define REWRITE_INVOKE_MAX              10
688 #endif
689
690 static int      numQueryRewriteInvoked = 0;
691
692 /*
693  * QueryRewrite -
694  *        rewrite one query via QueryRewrite system, possibly returning 0, or many
695  *        queries
696  */
697 List *
698 QueryRewrite(Query *parsetree)
699 {
700         QueryRewriteSubLink(parsetree->qual);
701         QueryRewriteSubLink(parsetree->havingQual);
702
703         return QueryRewriteOne(parsetree);
704 }
705
706 /*
707  *      QueryRewriteSubLink
708  *
709  *      This rewrites the SubLink subqueries first, doing the lowest ones first.
710  *      We already have code in the main rewrite loops to process correlated
711  *      variables from upper queries that exist in subqueries.
712  */
713 static void
714 QueryRewriteSubLink(Node *node)
715 {
716         if (node == NULL)
717                 return;
718
719         switch (nodeTag(node))
720         {
721                 case T_TargetEntry:
722                         break;
723                 case T_Aggreg:
724                         break;
725                 case T_Expr:
726                         {
727                                 Expr       *expr = (Expr *) node;
728
729                                 QueryRewriteSubLink((Node *) expr->args);
730                         }
731                         break;
732                 case T_Var:
733                         break;
734                 case T_List:
735                         {
736                                 List       *l;
737
738                                 foreach(l, (List *) node)
739                                         QueryRewriteSubLink(lfirst(l));
740                         }
741                         break;
742                 case T_SubLink:
743                         {
744                                 SubLink    *sublink = (SubLink *) node;
745                                 Query      *query = (Query *) sublink->subselect;
746                                 List       *ret;
747
748                                 /*
749                                  * Nest down first.  We do this so if a rewrite adds a
750                                  * SubLink we don't process it as part of this loop.
751                                  */
752                                 QueryRewriteSubLink((Node *) query->qual);
753                                 
754                                 QueryRewriteSubLink((Node *) query->havingQual);
755
756                                 ret = QueryRewriteOne(query);
757                                 if (!ret)
758                                         sublink->subselect = NULL;
759                                 else if (lnext(ret) == NIL)
760                                         sublink->subselect = lfirst(ret);
761                                 else
762                                         elog(ERROR, "Don't know how to process subquery that rewrites to multiple queries.");
763                         }
764                         break;
765                 default:
766                         /* ignore the others */
767                         break;
768         }
769         return;
770 }
771
772 /*
773  * QueryOneRewrite -
774  *        rewrite one query
775  */
776 static List *
777 QueryRewriteOne(Query *parsetree)
778 {
779         numQueryRewriteInvoked = 0;
780
781         /*
782          * take a deep breath and apply all the rewrite rules - ay
783          */
784         return deepRewriteQuery(parsetree);
785 }
786
787 /*
788  * deepRewriteQuery -
789  *        rewrites the query and apply the rules again on the queries rewritten
790  */
791 static List *
792 deepRewriteQuery(Query *parsetree)
793 {
794         List       *n;
795         List       *rewritten = NIL;
796         List       *result = NIL;
797         bool            instead;
798         List       *qual_products = NIL;
799
800
801
802         if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
803         {
804                 elog(ERROR, "query rewritten %d times, may contain cycles",
805                          numQueryRewriteInvoked - 1);
806         }
807
808         instead = FALSE;
809         result = RewriteQuery(parsetree, &instead, &qual_products);
810         if (!instead)
811                 rewritten = lcons(parsetree, NIL);
812
813         foreach(n, result)
814         {
815                 Query      *pt = lfirst(n);
816                 List       *newstuff = NIL;
817
818                 newstuff = deepRewriteQuery(pt);
819                 if (newstuff != NIL)
820                         rewritten = nconc(rewritten, newstuff);
821         }
822         if (qual_products != NIL)
823                 rewritten = nconc(rewritten, qual_products);
824
825         return rewritten;
826 }
827
828
829 static void
830 CheckViewPerms(Relation view, List *rtable)
831 {
832         HeapTuple       utup;
833         NameData        uname;
834         List       *rt;
835         RangeTblEntry *rte;
836         int32           aclcheck_res;
837
838         /*
839          * get the usename of the view's owner
840          */
841         utup = SearchSysCacheTuple(USESYSID, view->rd_rel->relowner, 0, 0, 0);
842         if (!HeapTupleIsValid(utup))
843         {
844                 elog(ERROR, "cache lookup for userid %d failed",
845                          view->rd_rel->relowner);
846         }
847         StrNCpy(uname.data,
848                         ((Form_pg_shadow) GETSTRUCT(utup))->usename.data,
849                         NAMEDATALEN);
850
851         /*
852          * check that we have read access to all the classes in the range
853          * table of the view
854          */
855         foreach(rt, rtable)
856         {
857                 rte = (RangeTblEntry *) lfirst(rt);
858
859                 aclcheck_res = pg_aclcheck(rte->relname, uname.data, ACL_RD);
860                 if (aclcheck_res != ACLCHECK_OK)
861                         elog(ERROR, "%s: %s", rte->relname, aclcheck_error_strings[aclcheck_res]);
862         }
863 }