1 /*-------------------------------------------------------------------------
5 * Copyright (c) 1994, Regents of the University of California
9 * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.15 1998/03/30 16:36:43 momjian Exp $
11 *-------------------------------------------------------------------------
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"
22 #include "parser/parsetree.h" /* for parsetree manipulation */
23 #include "nodes/parsenodes.h"
25 #include "rewrite/rewriteSupport.h"
26 #include "rewrite/rewriteHandler.h"
27 #include "rewrite/rewriteManip.h"
28 #include "rewrite/locks.h"
30 #include "commands/creatinh.h"
31 #include "access/heapam.h"
33 #include "utils/syscache.h"
34 #include "utils/acl.h"
35 #include "catalog/pg_shadow.h"
38 ApplyRetrieveRule(Query *parsetree, RewriteRule *rule,
39 int rt_index, int relation_level,
40 Relation relation, int *modified);
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);
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
56 gatherRewriteMeta(Query *parsetree,
67 info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
68 info->rt_index = rt_index;
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)
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);
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);
96 * bug here about replace CURRENT -- sort of replace current is
97 * deprecated now so this code shouldn't really need to be so
100 if (info->action != CMD_SELECT)
101 { /* i.e update XXXXX */
102 int new_result_reln = 0;
104 result_reln = info->rule_action->resultRelation;
107 case PRS2_CURRENT_VARNO:
108 new_result_reln = rt_index;
110 case PRS2_NEW_VARNO: /* XXX */
112 new_result_reln = result_reln + rt_length;
115 info->rule_action->resultRelation = new_result_reln;
122 OptimizeRIRRules(List *locks)
124 List *attr_level = NIL,
126 List *relation_level = NIL;
130 RewriteRule *rule_lock = lfirst(i);
132 if (rule_lock->attrno == -1)
133 relation_level = lappend(relation_level, rule_lock);
135 attr_level = lappend(attr_level, rule_lock);
137 return nconc(relation_level, attr_level);
141 * idea is to put instead rules before regular rules so that
142 * excess semantically queasy queries aren't processed
145 orderRules(List *locks)
149 List *instead_rules = NIL;
153 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
155 if (rule_lock->isInstead)
156 instead_rules = lappend(instead_rules, rule_lock);
158 regular = lappend(regular, rule_lock);
160 return nconc(regular, instead_rules);
164 AllRetrieve(List *actions)
170 Query *pt = lfirst(n);
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.
177 if (pt->commandType != CMD_SELECT)
184 FireRetrieveRulesAtQuery(Query *parsetree,
192 RuleLock *rt_entry_locks = NULL;
195 if ((rt_entry_locks = relation->rd_rules) == NULL)
198 locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
200 /* find all retrieve instead */
203 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
205 if (!rule_lock->isInstead)
207 work = lappend(work, rule_lock);
211 work = OptimizeRIRRules(locks);
214 RewriteRule *rule_lock = lfirst(i);
216 int modified = FALSE;
218 relation_level = (rule_lock->attrno == -1);
219 if (rule_lock->actions == NIL)
221 *instead_flag = TRUE;
225 length(rule_lock->actions) >= 2 &&
226 AllRetrieve(rule_lock->actions))
228 *instead_flag = TRUE;
229 return rule_lock->actions;
231 ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level, relation,
235 *instead_flag = TRUE;
236 FixResdomTypes(parsetree->targetList);
238 return lcons(parsetree, NIL);
246 /* Idea is like this:
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.
252 * side effect: parsetree's rtable field might be changed
255 ApplyRetrieveRule(Query *parsetree,
262 Query *rule_action = NULL;
269 int viewAclOverride = FALSE;
271 rule_qual = rule->qual;
274 if (length(rule->actions) > 1) /* ??? because we don't handle
275 * rules with more than one
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
285 rule_action = copyObject(lfirst(rule->actions));
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
294 if (relation_level && rule_action->commandType == CMD_SELECT
297 CheckViewPerms(relation, rule_action->rtable);
298 viewAclOverride = TRUE;
306 rtable = copyObject(parsetree->rtable);
309 RangeTblEntry *rte = lfirst(rt);
312 * this is to prevent add_missing_vars_to_base_rels() from adding
313 * a bogus entry to the new target list.
315 rte->inFromCl = false;
317 rt_length = length(rtable);
325 rule_rtable = copyObject(rule_action->rtable);
326 foreach(rule_rt, rule_rtable)
328 rte = lfirst(rule_rt);
331 * tell the executor that the ACL check on this range table
332 * entry is already done
337 rtable = nconc(rtable, rule_rtable);
341 rtable = nconc(rtable, copyObject(rule_action->rtable));
343 parsetree->rtable = rtable;
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);
356 HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
361 HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
362 rt_index, rule->attrno, modified, &badsql);
364 if (*modified && !badsql)
365 AddQual(parsetree, rule_action->qual);
369 ProcessRetrieveQuery(Query *parsetree,
375 List *product_queries = NIL;
381 RangeTblEntry *rt_entry = lfirst(rt);
382 Relation rt_entry_relation = NULL;
386 rt_entry_relation = heap_openr(rt_entry->relname);
390 if (rt_entry_relation->rd_rules != NULL)
393 FireRetrieveRulesAtQuery(parsetree,
399 heap_close(rt_entry_relation);
408 RangeTblEntry *rt_entry = lfirst(rt);
409 Relation rt_entry_relation = NULL;
410 RuleLock *rt_entry_locks = NULL;
413 List *dummy_products;
416 rt_entry_relation = heap_openr(rt_entry->relname);
417 rt_entry_locks = rt_entry_relation->rd_rules;
418 heap_close(rt_entry_relation);
424 matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
428 result = fireRules(parsetree, rt_index, CMD_SELECT,
429 instead_flag, locks, &dummy_products);
431 return lappend(NIL, result);
433 product_queries = nconc(product_queries, result);
436 return product_queries;
440 CopyAndAddQual(Query *parsetree,
446 Query *new_tree = (Query *) copyObject(parsetree);
447 Node *new_qual = NULL;
448 Query *rule_action = NULL;
451 rule_action = lfirst(actions);
452 if (rule_qual != NULL)
453 new_qual = (Node *) copyObject(rule_qual);
454 if (rule_action != NULL)
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);
466 /* XXX -- where current doesn't work for instead nothing.... yet */
467 AddNotQual(new_tree, new_qual);
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
482 fireRules(Query *parsetree,
487 List **qual_products)
493 /* choose rule to fire from list of rules */
496 ProcessRetrieveQuery(parsetree,
500 return lappend(NIL, parsetree);
505 locks = orderRules(locks); /* instead rules first */
508 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
513 bool orig_instead_flag = *instead_flag;
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)
521 lappend(*qual_products,
522 CopyAndAddQual(parsetree, actions, event_qual,
526 Query *rule_action = lfirst(r);
527 Node *rule_qual = copyObject(event_qual);
529 /*--------------------------------------------------
531 * Rewrite current.attribute or current to tuple variable
532 * this appears to be done in parser?
533 *--------------------------------------------------
535 info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
536 rt_index, event, instead_flag);
538 /* handle escapable cases, or those handled by other code */
547 if (info->action == info->event &&
548 info->event == CMD_SELECT)
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
556 qual = parsetree->qual;
557 AddQual(info->rule_action, qual);
559 if (info->rule_qual != NULL)
560 AddQual(info->rule_action, info->rule_qual);
562 /*--------------------------------------------------
564 * Rewrite new.attribute w/ right hand side of target-list
565 * entry for appropriate field name in insert/update
566 *--------------------------------------------------
568 if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE))
570 FixNew(info, parsetree);
573 /*--------------------------------------------------
575 * rewriting due to retrieve rules
576 *--------------------------------------------------
578 info->rule_action->rtable = info->rt;
579 ProcessRetrieveQuery(info->rule_action, info->rt,
580 &orig_instead_flag, TRUE);
582 /*--------------------------------------------------
584 * Simplify? hey, no algorithm for simplification... let
586 *--------------------------------------------------
588 results = lappend(results, info->rule_action);
599 RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
602 List *product_queries = NIL;
603 int result_relation = 0;
605 Assert(parsetree != NULL);
607 event = parsetree->commandType;
609 if (event == CMD_UTILITY)
613 * only for a delete may the targetlist be NULL
615 if (event != CMD_DELETE)
617 Assert(parsetree->targetList != NULL);
620 result_relation = parsetree->resultRelation;
622 if (event != CMD_SELECT)
626 * the statement is an update, insert or delete
628 RangeTblEntry *rt_entry;
629 Relation rt_entry_relation = NULL;
630 RuleLock *rt_entry_locks = NULL;
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);
637 if (rt_entry_locks != NULL)
640 matchLocks(event, rt_entry_locks, result_relation, parsetree);
650 return product_queries;
656 * the statement is a select
661 * ApplyRetrieveRule changes the range table XXX Unions are copied
664 other = copyObject(parsetree);
667 ProcessRetrieveQuery(other, parsetree->rtable,
668 instead_flag, FALSE);
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.
676 #ifndef REWRITE_INVOKE_MAX
677 #define REWRITE_INVOKE_MAX 10
680 static int numQueryRewriteInvoked = 0;
684 * rewrite one query via QueryRewrite system, possibly returning 0, or many
688 QueryRewrite(Query *parsetree)
690 QueryRewriteSubLink(parsetree->qual);
691 return QueryRewriteOne(parsetree);
695 * QueryRewriteSubLink
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.
702 QueryRewriteSubLink(Node *node)
707 switch (nodeTag(node))
715 Expr *expr = (Expr *) node;
717 QueryRewriteSubLink((Node *) expr->args);
726 foreach(l, (List *) node)
727 QueryRewriteSubLink(lfirst(l));
732 SubLink *sublink = (SubLink *) node;
733 Query *query = (Query *) sublink->subselect;
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.
740 QueryRewriteSubLink((Node *) query->qual);
742 ret = QueryRewriteOne(query);
744 sublink->subselect = NULL;
745 else if (lnext(ret) == NIL)
746 sublink->subselect = lfirst(ret);
748 elog(ERROR, "Don't know how to process subquery that rewrites to multiple queries.");
752 /* ignore the others */
763 QueryRewriteOne(Query *parsetree)
765 numQueryRewriteInvoked = 0;
768 * take a deep breath and apply all the rewrite rules - ay
770 return deepRewriteQuery(parsetree);
775 * rewrites the query and apply the rules again on the queries rewritten
778 deepRewriteQuery(Query *parsetree)
781 List *rewritten = NIL;
784 List *qual_products = NIL;
788 if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
790 elog(ERROR, "query rewritten %d times, may contain cycles",
791 numQueryRewriteInvoked - 1);
795 result = RewriteQuery(parsetree, &instead, &qual_products);
797 rewritten = lcons(parsetree, NIL);
801 Query *pt = lfirst(n);
802 List *newstuff = NIL;
804 newstuff = deepRewriteQuery(pt);
806 rewritten = nconc(rewritten, newstuff);
808 if (qual_products != NIL)
809 rewritten = nconc(rewritten, qual_products);
816 CheckViewPerms(Relation view, List *rtable)
825 * get the usename of the view's owner
827 utup = SearchSysCacheTuple(USESYSID, view->rd_rel->relowner, 0, 0, 0);
828 if (!HeapTupleIsValid(utup))
830 elog(ERROR, "cache lookup for userid %d failed",
831 view->rd_rel->relowner);
834 ((Form_pg_shadow) GETSTRUCT(utup))->usename.data,
838 * check that we have read access to all the classes in the range
843 rte = (RangeTblEntry *) lfirst(rt);
845 aclcheck_res = pg_aclcheck(rte->relname, uname.data, ACL_RD);
846 if (aclcheck_res != ACLCHECK_OK)
848 elog(ERROR, "%s: %s", rte->relname, aclcheck_error_strings[aclcheck_res]);