]> granicus.if.org Git - postgresql/blob - src/backend/rewrite/rewriteHandler.c
I started adding the Having Clause and it works quite fine for
[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.15 1998/03/30 16:36:43 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         {
303                 nothing = TRUE;
304         }
305
306         rtable = copyObject(parsetree->rtable);
307         foreach(rt, rtable)
308         {
309                 RangeTblEntry *rte = lfirst(rt);
310
311                 /*
312                  * this is to prevent add_missing_vars_to_base_rels() from adding
313                  * a bogus entry to the new target list.
314                  */
315                 rte->inFromCl = false;
316         }
317         rt_length = length(rtable);
318
319         if (viewAclOverride)
320         {
321                 List       *rule_rtable,
322                                    *rule_rt;
323                 RangeTblEntry *rte;
324
325                 rule_rtable = copyObject(rule_action->rtable);
326                 foreach(rule_rt, rule_rtable)
327                 {
328                         rte = lfirst(rule_rt);
329
330                         /*
331                          * tell the executor that the ACL check on this range table
332                          * entry is already done
333                          */
334                         rte->skipAcl = true;
335                 }
336
337                 rtable = nconc(rtable, rule_rtable);
338         }
339         else
340         {
341                 rtable = nconc(rtable, copyObject(rule_action->rtable));
342         }
343         parsetree->rtable = rtable;
344
345         rule_action->rtable = rtable;
346         OffsetVarNodes(rule_action->qual, rt_length);
347         OffsetVarNodes((Node *) rule_action->targetList, rt_length);
348         OffsetVarNodes(rule_qual, rt_length);
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         if (relation_level)
355         {
356                 HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
357                                            modified);
358         }
359         else
360         {
361                 HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
362                                                            rt_index, rule->attrno, modified, &badsql);
363         }
364         if (*modified && !badsql)
365                 AddQual(parsetree, rule_action->qual);
366 }
367
368 static List *
369 ProcessRetrieveQuery(Query *parsetree,
370                                          List *rtable,
371                                          bool *instead_flag,
372                                          bool rule)
373 {
374         List       *rt;
375         List       *product_queries = NIL;
376         int                     rt_index = 0;
377
378
379         foreach(rt, rtable)
380         {
381                 RangeTblEntry *rt_entry = lfirst(rt);
382                 Relation        rt_entry_relation = NULL;
383                 List       *result = NIL;
384
385                 rt_index++;
386                 rt_entry_relation = heap_openr(rt_entry->relname);
387
388
389
390                 if (rt_entry_relation->rd_rules != NULL)
391                 {
392                         result =
393                                 FireRetrieveRulesAtQuery(parsetree,
394                                                                                  rt_index,
395                                                                                  rt_entry_relation,
396                                                                                  instead_flag,
397                                                                                  rule);
398                 }
399                 heap_close(rt_entry_relation);
400                 if (*instead_flag)
401                         return result;
402         }
403         if (rule)
404                 return NIL;
405
406         foreach(rt, rtable)
407         {
408                 RangeTblEntry *rt_entry = lfirst(rt);
409                 Relation        rt_entry_relation = NULL;
410                 RuleLock   *rt_entry_locks = NULL;
411                 List       *result = NIL;
412                 List       *locks = NIL;
413                 List       *dummy_products;
414
415                 rt_index++;
416                 rt_entry_relation = heap_openr(rt_entry->relname);
417                 rt_entry_locks = rt_entry_relation->rd_rules;
418                 heap_close(rt_entry_relation);
419
420
421                 if (rt_entry_locks)
422                 {
423                         locks =
424                                 matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
425                 }
426                 if (locks != NIL)
427                 {
428                         result = fireRules(parsetree, rt_index, CMD_SELECT,
429                                                            instead_flag, locks, &dummy_products);
430                         if (*instead_flag)
431                                 return lappend(NIL, result);
432                         if (result != NIL)
433                                 product_queries = nconc(product_queries, result);
434                 }
435         }
436         return product_queries;
437 }
438
439 static Query *
440 CopyAndAddQual(Query *parsetree,
441                            List *actions,
442                            Node *rule_qual,
443                            int rt_index,
444                            CmdType event)
445 {
446         Query      *new_tree = (Query *) copyObject(parsetree);
447         Node       *new_qual = NULL;
448         Query      *rule_action = NULL;
449
450         if (actions)
451                 rule_action = lfirst(actions);
452         if (rule_qual != NULL)
453                 new_qual = (Node *) copyObject(rule_qual);
454         if (rule_action != NULL)
455         {
456                 List       *rtable;
457                 int                     rt_length;
458
459                 rtable = new_tree->rtable;
460                 rt_length = length(rtable);
461                 rtable = append(rtable, listCopy(rule_action->rtable));
462                 new_tree->rtable = rtable;
463                 OffsetVarNodes(new_qual, rt_length);
464                 ChangeVarNodes(new_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
465         }
466         /* XXX -- where current doesn't work for instead nothing.... yet */
467         AddNotQual(new_tree, new_qual);
468
469         return new_tree;
470 }
471
472
473 /*
474  *      fireRules -
475  *         Iterate through rule locks applying rules.  After an instead rule
476  *         rule has been applied, return just new parsetree and let RewriteQuery
477  *         start the process all over again.  The locks are reordered to maintain
478  *         sensible semantics.  remember: reality is for dead birds -- glass
479  *
480  */
481 static List *
482 fireRules(Query *parsetree,
483                   int rt_index,
484                   CmdType event,
485                   bool *instead_flag,
486                   List *locks,
487                   List **qual_products)
488 {
489         RewriteInfo *info;
490         List       *results = NIL;
491         List       *i;
492
493         /* choose rule to fire from list of rules */
494         if (locks == NIL)
495         {
496                 ProcessRetrieveQuery(parsetree,
497                                                          parsetree->rtable,
498                                                          instead_flag, TRUE);
499                 if (*instead_flag)
500                         return lappend(NIL, parsetree);
501                 else
502                         return NIL;
503         }
504
505         locks = orderRules(locks);      /* instead rules first */
506         foreach(i, locks)
507         {
508                 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
509                 Node       *qual,
510                                    *event_qual;
511                 List       *actions;
512                 List       *r;
513                 bool            orig_instead_flag = *instead_flag;
514
515                 /* multiple rule action time */
516                 *instead_flag = rule_lock->isInstead;
517                 event_qual = rule_lock->qual;
518                 actions = rule_lock->actions;
519                 if (event_qual != NULL && *instead_flag)
520                         *qual_products =
521                                 lappend(*qual_products,
522                                                 CopyAndAddQual(parsetree, actions, event_qual,
523                                                                            rt_index, event));
524                 foreach(r, actions)
525                 {
526                         Query      *rule_action = lfirst(r);
527                         Node       *rule_qual = copyObject(event_qual);
528
529                         /*--------------------------------------------------
530                          * Step 1:
531                          *        Rewrite current.attribute or current to tuple variable
532                          *        this appears to be done in parser?
533                          *--------------------------------------------------
534                          */
535                         info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
536                                                                          rt_index, event, instead_flag);
537
538                         /* handle escapable cases, or those handled by other code */
539                         if (info->nothing)
540                         {
541                                 if (*instead_flag)
542                                         return NIL;
543                                 else
544                                         continue;
545                         }
546
547                         if (info->action == info->event &&
548                                 info->event == CMD_SELECT)
549                                 continue;
550
551                         /*
552                          * Event Qualification forces copying of parsetree --- XXX and
553                          * splitting into two queries one w/rule_qual, one w/NOT
554                          * rule_qual. Also add user query qual onto rule action
555                          */
556                         qual = parsetree->qual;
557                         AddQual(info->rule_action, qual);
558
559                         if (info->rule_qual != NULL)
560                                 AddQual(info->rule_action, info->rule_qual);
561
562                         /*--------------------------------------------------
563                          * Step 2:
564                          *        Rewrite new.attribute w/ right hand side of target-list
565                          *        entry for appropriate field name in insert/update
566                          *--------------------------------------------------
567                          */
568                         if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE))
569                         {
570                                 FixNew(info, parsetree);
571                         }
572
573                         /*--------------------------------------------------
574                          * Step 3:
575                          *        rewriting due to retrieve rules
576                          *--------------------------------------------------
577                          */
578                         info->rule_action->rtable = info->rt;
579                         ProcessRetrieveQuery(info->rule_action, info->rt,
580                                                                  &orig_instead_flag, TRUE);
581
582                         /*--------------------------------------------------
583                          * Step 4
584                          *        Simplify? hey, no algorithm for simplification... let
585                          *        the planner do it.
586                          *--------------------------------------------------
587                          */
588                         results = lappend(results, info->rule_action);
589
590                         pfree(info);
591                 }
592                 if (*instead_flag)
593                         break;
594         }
595         return results;
596 }
597
598 static List *
599 RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
600 {
601         CmdType         event;
602         List       *product_queries = NIL;
603         int                     result_relation = 0;
604
605         Assert(parsetree != NULL);
606
607         event = parsetree->commandType;
608
609         if (event == CMD_UTILITY)
610                 return NIL;
611
612         /*
613          * only for a delete may the targetlist be NULL
614          */
615         if (event != CMD_DELETE)
616         {
617                 Assert(parsetree->targetList != NULL);
618         }
619
620         result_relation = parsetree->resultRelation;
621
622         if (event != CMD_SELECT)
623         {
624
625                 /*
626                  * the statement is an update, insert or delete
627                  */
628                 RangeTblEntry *rt_entry;
629                 Relation        rt_entry_relation = NULL;
630                 RuleLock   *rt_entry_locks = NULL;
631
632                 rt_entry = rt_fetch(result_relation, parsetree->rtable);
633                 rt_entry_relation = heap_openr(rt_entry->relname);
634                 rt_entry_locks = rt_entry_relation->rd_rules;
635                 heap_close(rt_entry_relation);
636
637                 if (rt_entry_locks != NULL)
638                 {
639                         List       *locks =
640                         matchLocks(event, rt_entry_locks, result_relation, parsetree);
641
642                         product_queries =
643                                 fireRules(parsetree,
644                                                   result_relation,
645                                                   event,
646                                                   instead_flag,
647                                                   locks,
648                                                   qual_products);
649                 }
650                 return product_queries;
651         }
652         else
653         {
654
655                 /*
656                  * the statement is a select
657                  */
658                 Query      *other;
659
660                 /*
661                  * ApplyRetrieveRule changes the range table XXX Unions are copied
662                  * again.
663                  */
664                 other = copyObject(parsetree);
665
666                 return
667                         ProcessRetrieveQuery(other, parsetree->rtable,
668                                                                  instead_flag, FALSE);
669         }
670 }
671
672 /*
673  * to avoid infinite recursion, we restrict the number of times a query
674  * can be rewritten. Detecting cycles is left for the reader as an excercise.
675  */
676 #ifndef REWRITE_INVOKE_MAX
677 #define REWRITE_INVOKE_MAX              10
678 #endif
679
680 static int      numQueryRewriteInvoked = 0;
681
682 /*
683  * QueryRewrite -
684  *        rewrite one query via QueryRewrite system, possibly returning 0, or many
685  *        queries
686  */
687 List *
688 QueryRewrite(Query *parsetree)
689 {
690         QueryRewriteSubLink(parsetree->qual);
691         return QueryRewriteOne(parsetree);
692 }
693
694 /*
695  *      QueryRewriteSubLink
696  *
697  *      This rewrites the SubLink subqueries first, doing the lowest ones first.
698  *      We already have code in the main rewrite loops to process correlated
699  *      variables from upper queries that exist in subqueries.
700  */
701 static void
702 QueryRewriteSubLink(Node *node)
703 {
704         if (node == NULL)
705                 return;
706
707         switch (nodeTag(node))
708         {
709                 case T_TargetEntry:
710                         break;
711                 case T_Aggreg:
712                         break;
713                 case T_Expr:
714                         {
715                                 Expr       *expr = (Expr *) node;
716
717                                 QueryRewriteSubLink((Node *) expr->args);
718                         }
719                         break;
720                 case T_Var:
721                         break;
722                 case T_List:
723                         {
724                                 List       *l;
725
726                                 foreach(l, (List *) node)
727                                         QueryRewriteSubLink(lfirst(l));
728                         }
729                         break;
730                 case T_SubLink:
731                         {
732                                 SubLink    *sublink = (SubLink *) node;
733                                 Query      *query = (Query *) sublink->subselect;
734                                 List       *ret;
735
736                                 /*
737                                  * Nest down first.  We do this so if a rewrite adds a
738                                  * SubLink we don't process it as part of this loop.
739                                  */
740                                 QueryRewriteSubLink((Node *) query->qual);
741
742                                 ret = QueryRewriteOne(query);
743                                 if (!ret)
744                                         sublink->subselect = NULL;
745                                 else if (lnext(ret) == NIL)
746                                         sublink->subselect = lfirst(ret);
747                                 else
748                                         elog(ERROR, "Don't know how to process subquery that rewrites to multiple queries.");
749                         }
750                         break;
751                 default:
752                         /* ignore the others */
753                         break;
754         }
755         return;
756 }
757
758 /*
759  * QueryOneRewrite -
760  *        rewrite one query
761  */
762 static List *
763 QueryRewriteOne(Query *parsetree)
764 {
765         numQueryRewriteInvoked = 0;
766
767         /*
768          * take a deep breath and apply all the rewrite rules - ay
769          */
770         return deepRewriteQuery(parsetree);
771 }
772
773 /*
774  * deepRewriteQuery -
775  *        rewrites the query and apply the rules again on the queries rewritten
776  */
777 static List *
778 deepRewriteQuery(Query *parsetree)
779 {
780         List       *n;
781         List       *rewritten = NIL;
782         List       *result = NIL;
783         bool            instead;
784         List       *qual_products = NIL;
785
786
787
788         if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
789         {
790                 elog(ERROR, "query rewritten %d times, may contain cycles",
791                          numQueryRewriteInvoked - 1);
792         }
793
794         instead = FALSE;
795         result = RewriteQuery(parsetree, &instead, &qual_products);
796         if (!instead)
797                 rewritten = lcons(parsetree, NIL);
798
799         foreach(n, result)
800         {
801                 Query      *pt = lfirst(n);
802                 List       *newstuff = NIL;
803
804                 newstuff = deepRewriteQuery(pt);
805                 if (newstuff != NIL)
806                         rewritten = nconc(rewritten, newstuff);
807         }
808         if (qual_products != NIL)
809                 rewritten = nconc(rewritten, qual_products);
810
811         return rewritten;
812 }
813
814
815 static void
816 CheckViewPerms(Relation view, List *rtable)
817 {
818         HeapTuple       utup;
819         NameData        uname;
820         List       *rt;
821         RangeTblEntry *rte;
822         int32           aclcheck_res;
823
824         /*
825          * get the usename of the view's owner
826          */
827         utup = SearchSysCacheTuple(USESYSID, view->rd_rel->relowner, 0, 0, 0);
828         if (!HeapTupleIsValid(utup))
829         {
830                 elog(ERROR, "cache lookup for userid %d failed",
831                          view->rd_rel->relowner);
832         }
833         StrNCpy(uname.data,
834                         ((Form_pg_shadow) GETSTRUCT(utup))->usename.data,
835                         NAMEDATALEN);
836
837         /*
838          * check that we have read access to all the classes in the range
839          * table of the view
840          */
841         foreach(rt, rtable)
842         {
843                 rte = (RangeTblEntry *) lfirst(rt);
844
845                 aclcheck_res = pg_aclcheck(rte->relname, uname.data, ACL_RD);
846                 if (aclcheck_res != ACLCHECK_OK)
847                 {
848                         elog(ERROR, "%s: %s", rte->relname, aclcheck_error_strings[aclcheck_res]);
849                 }
850         }
851 }