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