1 /*-------------------------------------------------------------------------
5 * Copyright (c) 1994, Regents of the University of California
9 * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.12 1998/02/21 06:31:57 scrappy 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_user.h"
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);
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
54 gatherRewriteMeta(Query *parsetree,
65 info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
66 info->rt_index = rt_index;
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)
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);
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);
94 * bug here about replace CURRENT -- sort of replace current is
95 * deprecated now so this code shouldn't really need to be so
98 if (info->action != CMD_SELECT)
99 { /* i.e update XXXXX */
100 int new_result_reln = 0;
102 result_reln = info->rule_action->resultRelation;
105 case PRS2_CURRENT_VARNO:
106 new_result_reln = rt_index;
108 case PRS2_NEW_VARNO: /* XXX */
110 new_result_reln = result_reln + rt_length;
113 info->rule_action->resultRelation = new_result_reln;
120 OptimizeRIRRules(List *locks)
122 List *attr_level = NIL,
124 List *relation_level = NIL;
128 RewriteRule *rule_lock = lfirst(i);
130 if (rule_lock->attrno == -1)
131 relation_level = lappend(relation_level, rule_lock);
133 attr_level = lappend(attr_level, rule_lock);
135 return nconc(relation_level, attr_level);
139 * idea is to put instead rules before regular rules so that
140 * excess semantically queasy queries aren't processed
143 orderRules(List *locks)
147 List *instead_rules = NIL;
151 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
153 if (rule_lock->isInstead)
154 instead_rules = lappend(instead_rules, rule_lock);
156 regular = lappend(regular, rule_lock);
158 return nconc(regular, instead_rules);
162 AllRetrieve(List *actions)
168 Query *pt = lfirst(n);
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.
175 if (pt->commandType != CMD_SELECT)
182 FireRetrieveRulesAtQuery(Query *parsetree,
190 RuleLock *rt_entry_locks = NULL;
193 if ((rt_entry_locks = relation->rd_rules) == NULL)
196 locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
198 /* find all retrieve instead */
201 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
203 if (!rule_lock->isInstead)
205 work = lappend(work, rule_lock);
209 work = OptimizeRIRRules(locks);
212 RewriteRule *rule_lock = lfirst(i);
214 int modified = FALSE;
216 relation_level = (rule_lock->attrno == -1);
217 if (rule_lock->actions == NIL)
219 *instead_flag = TRUE;
223 length(rule_lock->actions) >= 2 &&
224 AllRetrieve(rule_lock->actions))
226 *instead_flag = TRUE;
227 return rule_lock->actions;
229 ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level, relation,
233 *instead_flag = TRUE;
234 FixResdomTypes(parsetree->targetList);
236 return lcons(parsetree, NIL);
244 /* Idea is like this:
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.
250 * side effect: parsetree's rtable field might be changed
253 ApplyRetrieveRule(Query *parsetree,
260 Query *rule_action = NULL;
267 int viewAclOverride = FALSE;
269 rule_qual = rule->qual;
272 if (length(rule->actions) > 1) /* ??? because we don't handle
273 * rules with more than one
277 * If we sometimes handle
278 * rules with more than one
279 * action, the view acl checks
281 * viewAclOverride should only
282 * become true (below) if this
283 * is a relation_level, instead,
287 rule_action = copyObject(lfirst(rule->actions));
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.
296 if (relation_level && rule_action->commandType == CMD_SELECT
299 CheckViewPerms(relation, rule_action->rtable);
300 viewAclOverride = TRUE;
308 rtable = copyObject(parsetree->rtable);
311 RangeTblEntry *rte = lfirst(rt);
314 * this is to prevent add_missing_vars_to_base_rels() from adding
315 * a bogus entry to the new target list.
317 rte->inFromCl = false;
319 rt_length = length(rtable);
323 List *rule_rtable, *rule_rt;
326 rule_rtable = copyObject(rule_action->rtable);
327 foreach(rule_rt, rule_rtable)
329 rte = lfirst(rule_rt);
332 * tell the executor that the ACL check on this
333 * range table entry is already done
338 rtable = nconc(rtable, rule_rtable);
342 rtable = nconc(rtable, copyObject(rule_action->rtable));
344 parsetree->rtable = rtable;
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);
357 HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
362 HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
363 rt_index, rule->attrno, modified, &badsql);
365 if (*modified && !badsql)
366 AddQual(parsetree, rule_action->qual);
370 ProcessRetrieveQuery(Query *parsetree,
376 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);
388 if (rt_entry_relation->rd_rules != NULL)
391 FireRetrieveRulesAtQuery(parsetree,
397 heap_close(rt_entry_relation);
406 RangeTblEntry *rt_entry = lfirst(rt);
407 Relation rt_entry_relation = NULL;
408 RuleLock *rt_entry_locks = NULL;
411 List *dummy_products;
414 rt_entry_relation = heap_openr(rt_entry->relname);
415 rt_entry_locks = rt_entry_relation->rd_rules;
416 heap_close(rt_entry_relation);
421 matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
425 result = fireRules(parsetree, rt_index, CMD_SELECT,
426 instead_flag, locks, &dummy_products);
428 return lappend(NIL, result);
430 product_queries = nconc(product_queries, result);
433 return product_queries;
437 CopyAndAddQual(Query *parsetree,
443 Query *new_tree = (Query *) copyObject(parsetree);
444 Node *new_qual = NULL;
445 Query *rule_action = NULL;
448 rule_action = lfirst(actions);
449 if (rule_qual != NULL)
450 new_qual = (Node *) copyObject(rule_qual);
451 if (rule_action != NULL)
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);
463 /* XXX -- where current doesn't work for instead nothing.... yet */
464 AddNotQual(new_tree, new_qual);
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
479 fireRules(Query *parsetree,
484 List **qual_products)
490 /* choose rule to fire from list of rules */
493 ProcessRetrieveQuery(parsetree,
497 return lappend(NIL, parsetree);
502 locks = orderRules(locks); /* instead rules first */
505 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
510 bool orig_instead_flag = *instead_flag;
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)
518 lappend(*qual_products,
519 CopyAndAddQual(parsetree, actions, event_qual,
523 Query *rule_action = lfirst(r);
524 Node *rule_qual = copyObject(event_qual);
526 /*--------------------------------------------------
528 * Rewrite current.attribute or current to tuple variable
529 * this appears to be done in parser?
530 *--------------------------------------------------
532 info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
533 rt_index, event, instead_flag);
535 /* handle escapable cases, or those handled by other code */
544 if (info->action == info->event &&
545 info->event == CMD_SELECT)
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
553 qual = parsetree->qual;
554 AddQual(info->rule_action, qual);
556 if (info->rule_qual != NULL)
557 AddQual(info->rule_action, info->rule_qual);
559 /*--------------------------------------------------
561 * Rewrite new.attribute w/ right hand side of target-list
562 * entry for appropriate field name in insert/update
563 *--------------------------------------------------
565 if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE))
567 FixNew(info, parsetree);
570 /*--------------------------------------------------
572 * rewriting due to retrieve rules
573 *--------------------------------------------------
575 info->rule_action->rtable = info->rt;
576 ProcessRetrieveQuery(info->rule_action, info->rt,
577 &orig_instead_flag, TRUE);
579 /*--------------------------------------------------
581 * Simplify? hey, no algorithm for simplification... let
583 *--------------------------------------------------
585 results = lappend(results, info->rule_action);
596 RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
599 List *product_queries = NIL;
600 int result_relation = 0;
602 Assert(parsetree != NULL);
604 event = parsetree->commandType;
606 if (event == CMD_UTILITY)
610 * only for a delete may the targetlist be NULL
612 if (event != CMD_DELETE)
614 Assert(parsetree->targetList != NULL);
617 result_relation = parsetree->resultRelation;
619 if (event != CMD_SELECT)
623 * the statement is an update, insert or delete
625 RangeTblEntry *rt_entry;
626 Relation rt_entry_relation = NULL;
627 RuleLock *rt_entry_locks = NULL;
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);
634 if (rt_entry_locks != NULL)
637 matchLocks(event, rt_entry_locks, result_relation, parsetree);
647 return product_queries;
653 * the statement is a select
658 * ApplyRetrieveRule changes the range table
659 * XXX Unions are copied again.
661 other = copyObject(parsetree);
664 ProcessRetrieveQuery(other, parsetree->rtable,
665 instead_flag, FALSE);
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.
673 #ifndef REWRITE_INVOKE_MAX
674 #define REWRITE_INVOKE_MAX 10
677 static int numQueryRewriteInvoked = 0;
681 * rewrite one query via QueryRewrite system, possibly returning 0, or many
685 QueryRewrite(Query *parsetree)
688 QueryRewriteSubLink(parsetree->qual);
689 return QueryRewriteOne(parsetree);
693 * QueryRewriteSubLink
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.
700 QueryRewriteSubLink(Node *node)
705 switch (nodeTag(node))
713 Expr *expr = (Expr *) node;
715 QueryRewriteSubLink((Node *)expr->args);
724 foreach(l, (List *) node)
725 QueryRewriteSubLink(lfirst(l));
730 SubLink *sublink = (SubLink *) node;
731 Query *query = (Query *)sublink->subselect;
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.
738 QueryRewriteSubLink((Node *)query->qual);
740 ret = QueryRewriteOne(query);
742 sublink->subselect = NULL;
743 else if (lnext(ret) == NIL)
744 sublink->subselect = lfirst(ret);
746 elog(ERROR,"Don't know how to process subquery that rewrites to multiple queries.");
750 /* ignore the others */
761 QueryRewriteOne(Query *parsetree)
763 numQueryRewriteInvoked = 0;
766 * take a deep breath and apply all the rewrite rules - ay
768 return deepRewriteQuery(parsetree);
773 * rewrites the query and apply the rules again on the queries rewritten
776 deepRewriteQuery(Query *parsetree)
779 List *rewritten = NIL;
782 List *qual_products = NIL;
784 if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
786 elog(ERROR, "query rewritten %d times, may contain cycles",
787 numQueryRewriteInvoked - 1);
791 result = RewriteQuery(parsetree, &instead, &qual_products);
793 rewritten = lcons(parsetree, NIL);
797 Query *pt = lfirst(n);
798 List *newstuff = NIL;
800 newstuff = deepRewriteQuery(pt);
802 rewritten = nconc(rewritten, newstuff);
804 if (qual_products != NIL)
805 rewritten = nconc(rewritten, qual_products);
812 CheckViewPerms(Relation view, List *rtable)
821 * get the usename of the view's owner
823 utup = SearchSysCacheTuple(USESYSID, view->rd_rel->relowner, 0, 0, 0);
824 if (!HeapTupleIsValid(utup))
826 elog(ERROR, "cache lookup for userid %d failed",
827 view->rd_rel->relowner);
830 ((Form_pg_user) GETSTRUCT(utup))->usename.data,
834 * check that we have read access to all the
835 * classes in the range table of the view
839 rte = (RangeTblEntry *)lfirst(rt);
841 aclcheck_res = pg_aclcheck(rte->relname, uname.data, ACL_RD);
842 if (aclcheck_res != ACLCHECK_OK)
844 elog(ERROR, "%s: %s", rte->relname, aclcheck_error_strings[aclcheck_res]);