1 /*-------------------------------------------------------------------------
5 * Copyright (c) 1994, Regents of the University of California
9 * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.18 1998/08/18 00:48:59 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 "parser/parse_relation.h"
24 #include "nodes/parsenodes.h"
26 #include "rewrite/rewriteSupport.h"
27 #include "rewrite/rewriteHandler.h"
28 #include "rewrite/rewriteManip.h"
29 #include "rewrite/locks.h"
31 #include "commands/creatinh.h"
32 #include "access/heapam.h"
34 #include "utils/syscache.h"
35 #include "utils/acl.h"
36 #include "catalog/pg_shadow.h"
39 ApplyRetrieveRule(Query *parsetree, RewriteRule *rule,
40 int rt_index, int relation_level,
41 Relation relation, int *modified);
43 fireRules(Query *parsetree, int rt_index, CmdType event,
44 bool *instead_flag, List *locks, List **qual_products);
45 static void QueryRewriteSubLink(Node *node);
46 static List *QueryRewriteOne(Query *parsetree);
47 static List *deepRewriteQuery(Query *parsetree);
48 static void CheckViewPerms(Relation view, List *rtable);
49 static void RewritePreprocessQuery(Query *parsetree);
50 static Query *RewritePostprocessNonSelect(Query *parsetree);
54 * Gather meta information about parsetree, and rule. Fix rule body
55 * and qualifier so that they can be mixed with the parsetree and
56 * maintain semantic validity
59 gatherRewriteMeta(Query *parsetree,
70 info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
71 info->rt_index = rt_index;
73 info->instead_flag = *instead_flag;
74 info->rule_action = (Query *) copyObject(rule_action);
75 info->rule_qual = (Node *) copyObject(rule_qual);
76 if (info->rule_action == NULL)
80 info->nothing = FALSE;
81 info->action = info->rule_action->commandType;
82 info->current_varno = rt_index;
83 info->rt = parsetree->rtable;
84 rt_length = length(info->rt);
85 info->rt = append(info->rt, info->rule_action->rtable);
87 info->new_varno = PRS2_NEW_VARNO + rt_length;
88 OffsetVarNodes(info->rule_action->qual, rt_length);
89 OffsetVarNodes((Node *) info->rule_action->targetList, rt_length);
90 OffsetVarNodes(info->rule_qual, rt_length);
91 ChangeVarNodes((Node *) info->rule_action->qual,
92 PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
93 ChangeVarNodes((Node *) info->rule_action->targetList,
94 PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
95 ChangeVarNodes(info->rule_qual,
96 PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
99 * bug here about replace CURRENT -- sort of replace current is
100 * deprecated now so this code shouldn't really need to be so
103 if (info->action != CMD_SELECT)
104 { /* i.e update XXXXX */
105 int new_result_reln = 0;
107 result_reln = info->rule_action->resultRelation;
110 case PRS2_CURRENT_VARNO:
111 new_result_reln = rt_index;
113 case PRS2_NEW_VARNO: /* XXX */
115 new_result_reln = result_reln + rt_length;
118 info->rule_action->resultRelation = new_result_reln;
125 OptimizeRIRRules(List *locks)
127 List *attr_level = NIL,
129 List *relation_level = NIL;
133 RewriteRule *rule_lock = lfirst(i);
135 if (rule_lock->attrno == -1)
136 relation_level = lappend(relation_level, rule_lock);
138 attr_level = lappend(attr_level, rule_lock);
140 return nconc(relation_level, attr_level);
144 * idea is to fire regular rules first, then qualified instead
145 * rules and unqualified instead rules last. Any lemming is counted for.
148 orderRules(List *locks)
151 List *instead_rules = NIL;
152 List *instead_qualified = NIL;
157 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
159 if (rule_lock->isInstead) {
160 if (rule_lock->qual == NULL)
161 instead_rules = lappend(instead_rules, rule_lock);
163 instead_qualified = lappend(instead_qualified, rule_lock);
165 regular = lappend(regular, rule_lock);
167 regular = nconc(regular, instead_qualified);
168 return nconc(regular, instead_rules);
172 AllRetrieve(List *actions)
178 Query *pt = lfirst(n);
181 * in the old postgres code, we check whether command_type is a
182 * consp of '('*'.commandType). but we've never supported
183 * transitive closures. Hence removed - ay 10/94.
185 if (pt->commandType != CMD_SELECT)
192 FireRetrieveRulesAtQuery(Query *parsetree,
200 RuleLock *rt_entry_locks = NULL;
203 if ((rt_entry_locks = relation->rd_rules) == NULL)
206 locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
208 /* find all retrieve instead */
211 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
213 if (!rule_lock->isInstead)
215 work = lappend(work, rule_lock);
219 work = OptimizeRIRRules(locks);
222 RewriteRule *rule_lock = lfirst(i);
224 int modified = FALSE;
226 relation_level = (rule_lock->attrno == -1);
227 if (rule_lock->actions == NIL)
229 *instead_flag = TRUE;
233 length(rule_lock->actions) >= 2 &&
234 AllRetrieve(rule_lock->actions))
236 *instead_flag = TRUE;
237 return rule_lock->actions;
239 ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level, relation,
243 *instead_flag = TRUE;
244 FixResdomTypes(parsetree->targetList);
245 return lcons(parsetree, NIL);
253 /* Idea is like this:
255 * retrieve-instead-retrieve rules have different semantics than update nodes
256 * Separate RIR rules from others. Pass others to FireRules.
257 * Order RIR rules and process.
259 * side effect: parsetree's rtable field might be changed
262 ApplyRetrieveRule(Query *parsetree,
269 Query *rule_action = NULL;
276 int viewAclOverride = FALSE;
278 rule_qual = rule->qual;
281 if (length(rule->actions) > 1) /* ??? because we don't handle
282 * rules with more than one
286 * WARNING!!! If we sometimes handle rules with more than one
287 * action, the view acl checks might get broken.
288 * viewAclOverride should only become true (below) if this is
289 * a relation_level, instead, select query - Jan
292 rule_action = copyObject(lfirst(rule->actions));
296 * If this rule is on the relation level, the rule action is a
297 * select and the rule is instead then it must be a view.
298 * Permissions for views now follow the owner of the view, not the
301 if (relation_level && rule_action->commandType == CMD_SELECT
304 CheckViewPerms(relation, rule_action->rtable);
305 viewAclOverride = TRUE;
311 rtable = copyObject(parsetree->rtable);
314 RangeTblEntry *rte = lfirst(rt);
317 * this is to prevent add_missing_vars_to_base_rels() from adding
318 * a bogus entry to the new target list.
320 rte->inFromCl = false;
322 rt_length = length(rtable);
330 rule_rtable = copyObject(rule_action->rtable);
331 foreach(rule_rt, rule_rtable)
333 rte = lfirst(rule_rt);
336 * tell the executor that the ACL check on this range table
337 * entry is already done
342 rtable = nconc(rtable, rule_rtable);
345 rtable = nconc(rtable, copyObject(rule_action->rtable));
346 parsetree->rtable = rtable;
348 rule_action->rtable = rtable;
349 OffsetVarNodes(rule_action->qual, rt_length);
350 OffsetVarNodes((Node *) rule_action->targetList, rt_length);
351 OffsetVarNodes(rule_qual, rt_length);
353 OffsetVarNodes((Node *) rule_action->groupClause, rt_length);
354 OffsetVarNodes((Node *) rule_action->havingQual, rt_length);
356 ChangeVarNodes(rule_action->qual,
357 PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
358 ChangeVarNodes((Node *) rule_action->targetList,
359 PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
360 ChangeVarNodes(rule_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
362 ChangeVarNodes((Node *) rule_action->groupClause,
363 PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
364 ChangeVarNodes((Node *) rule_action->havingQual,
365 PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
369 HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
374 HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
375 rt_index, rule->attrno, modified, &badsql);
377 if (*modified && !badsql) {
378 AddQual(parsetree, rule_action->qual);
379 /* This will only work if the query made to the view defined by the following
380 * groupClause groups by the same attributes or does not use group at all! */
381 if (parsetree->groupClause == NULL)
382 parsetree->groupClause=rule_action->groupClause;
383 AddHavingQual(parsetree, rule_action->havingQual);
384 parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
385 parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks);
390 ProcessRetrieveQuery(Query *parsetree,
396 List *product_queries = NIL;
402 RangeTblEntry *rt_entry = lfirst(rt);
403 Relation rt_entry_relation = NULL;
407 rt_entry_relation = heap_openr(rt_entry->relname);
411 if (rt_entry_relation->rd_rules != NULL)
414 FireRetrieveRulesAtQuery(parsetree,
420 heap_close(rt_entry_relation);
430 RangeTblEntry *rt_entry = lfirst(rt);
431 Relation rt_entry_relation = NULL;
432 RuleLock *rt_entry_locks = NULL;
435 List *dummy_products;
438 rt_entry_relation = heap_openr(rt_entry->relname);
439 rt_entry_locks = rt_entry_relation->rd_rules;
440 heap_close(rt_entry_relation);
446 matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
450 result = fireRules(parsetree, rt_index, CMD_SELECT,
451 instead_flag, locks, &dummy_products);
453 return lappend(NIL, result);
455 product_queries = nconc(product_queries, result);
458 return product_queries;
462 CopyAndAddQual(Query *parsetree,
468 Query *new_tree = (Query *) copyObject(parsetree);
469 Node *new_qual = NULL;
470 Query *rule_action = NULL;
473 rule_action = lfirst(actions);
474 if (rule_qual != NULL)
475 new_qual = (Node *) copyObject(rule_qual);
476 if (rule_action != NULL)
481 rtable = new_tree->rtable;
482 rt_length = length(rtable);
483 rtable = append(rtable, listCopy(rule_action->rtable));
484 new_tree->rtable = rtable;
485 OffsetVarNodes(new_qual, rt_length);
486 ChangeVarNodes(new_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
488 /* XXX -- where current doesn't work for instead nothing.... yet */
489 AddNotQual(new_tree, new_qual);
497 * Iterate through rule locks applying rules.
498 * All rules create their own parsetrees. Instead rules
499 * with rule qualification save the original parsetree
500 * and add their negated qualification to it. Real instead
501 * rules finally throw away the original parsetree.
503 * remember: reality is for dead birds -- glass
507 fireRules(Query *parsetree,
512 List **qual_products)
518 /* choose rule to fire from list of rules */
521 ProcessRetrieveQuery(parsetree,
525 return lappend(NIL, parsetree);
530 locks = orderRules(locks); /* real instead rules last */
533 RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
538 bool orig_instead_flag = *instead_flag;
540 /* multiple rule action time */
541 *instead_flag = rule_lock->isInstead;
542 event_qual = rule_lock->qual;
543 actions = rule_lock->actions;
544 if (event_qual != NULL && *instead_flag) {
546 RewriteInfo qual_info;
549 * If there are instead rules with qualifications,
550 * the original query is still performed. But all
551 * the negated rule qualifications of the instead
552 * rules are added so it does it's actions only
553 * in cases where the rule quals of all instead
554 * rules are false. Think of it as the default
555 * action in a case. We save this in *qual_products
556 * so deepRewriteQuery() can add it to the query
557 * list after we mangled it up enough.
560 if (*qual_products == NIL) {
561 qual_product = parsetree;
563 qual_product = (Query *)nth(0, *qual_products);
566 qual_info.event = qual_product->commandType;
567 qual_info.new_varno = length(qual_product->rtable) + 2;
568 qual_product = CopyAndAddQual(qual_product,
574 qual_info.rule_action = qual_product;
576 if (event == CMD_INSERT || event == CMD_UPDATE)
577 FixNew(&qual_info, qual_product);
579 *qual_products = lappend(NIL, qual_product);
584 Query *rule_action = lfirst(r);
585 Node *rule_qual = copyObject(event_qual);
587 if (rule_action->commandType == CMD_NOTHING)
590 /*--------------------------------------------------
592 * Rewrite current.attribute or current to tuple variable
593 * this appears to be done in parser?
594 *--------------------------------------------------
596 info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
597 rt_index, event, instead_flag);
599 /* handle escapable cases, or those handled by other code */
608 if (info->action == info->event &&
609 info->event == CMD_SELECT)
613 * Event Qualification forces copying of parsetree and
614 * splitting into two queries one w/rule_qual, one w/NOT
615 * rule_qual. Also add user query qual onto rule action
617 qual = parsetree->qual;
618 AddQual(info->rule_action, qual);
620 if (info->rule_qual != NULL)
621 AddQual(info->rule_action, info->rule_qual);
623 /*--------------------------------------------------
625 * Rewrite new.attribute w/ right hand side of target-list
626 * entry for appropriate field name in insert/update
627 *--------------------------------------------------
629 if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE))
630 FixNew(info, parsetree);
632 /*--------------------------------------------------
634 * rewriting due to retrieve rules
635 *--------------------------------------------------
637 info->rule_action->rtable = info->rt;
638 ProcessRetrieveQuery(info->rule_action, info->rt,
639 &orig_instead_flag, TRUE);
641 /*--------------------------------------------------
643 * Simplify? hey, no algorithm for simplification... let
645 *--------------------------------------------------
647 results = lappend(results, info->rule_action);
653 * If this was an unqualified instead rule,
654 * throw away an eventually saved 'default' parsetree
657 if (event_qual == NULL && *instead_flag) {
658 *qual_products = NIL;
665 * RewritePreprocessQuery -
666 * adjust details in the parsetree, the rule system
671 RewritePreprocessQuery(Query *parsetree)
674 * if the query has a resultRelation, reassign the
675 * result domain numbers to the attribute numbers in the
676 * target relation. FixNew() depends on it when replacing
677 * *new* references in a rule action by the expressions
678 * from the rewritten query.
681 if (parsetree->resultRelation > 0) {
688 rte = (RangeTblEntry *)nth(parsetree->resultRelation - 1,
690 rd = heap_openr(rte->relname);
692 foreach (tl, parsetree->targetList) {
693 tle = (TargetEntry *)lfirst(tl);
694 resdomno = attnameAttNum(rd, tle->resdom->resname);
695 tle->resdom->resno = resdomno;
704 * RewritePostprocessNonSelect -
705 * apply instead select rules on a query fired in by
710 RewritePostprocessNonSelect(Query *parsetree)
714 Query *newtree = copyObject(parsetree);
716 foreach(rt, parsetree->rtable)
718 RangeTblEntry *rt_entry = lfirst(rt);
719 Relation rt_entry_relation = NULL;
720 RuleLock *rt_entry_locks = NULL;
722 List *instead_locks = NIL;
727 rt_entry_relation = heap_openr(rt_entry->relname);
728 rt_entry_locks = rt_entry_relation->rd_rules;
732 int origcmdtype = newtree->commandType;
733 newtree->commandType = CMD_SELECT;
735 matchLocks(CMD_SELECT, rt_entry_locks, rt_index, newtree);
736 newtree->commandType = origcmdtype;
740 foreach (lock, locks) {
741 rule = (RewriteRule *)lfirst(lock);
742 if (rule->isInstead) {
743 instead_locks = nconc(instead_locks, lock);
747 if (instead_locks != NIL)
749 foreach (lock, instead_locks) {
753 rule = (RewriteRule *)lfirst(lock);
754 relation_level = (rule->attrno == -1);
756 ApplyRetrieveRule(newtree,
765 heap_close(rt_entry_relation);
772 RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
775 List *product_queries = NIL;
776 int result_relation = 0;
778 Assert(parsetree != NULL);
780 event = parsetree->commandType;
782 if (event == CMD_UTILITY)
786 * only for a delete may the targetlist be NULL
788 if (event != CMD_DELETE)
789 Assert(parsetree->targetList != NULL);
791 result_relation = parsetree->resultRelation;
793 if (event != CMD_SELECT)
797 * the statement is an update, insert or delete
799 RangeTblEntry *rt_entry;
800 Relation rt_entry_relation = NULL;
801 RuleLock *rt_entry_locks = NULL;
803 rt_entry = rt_fetch(result_relation, parsetree->rtable);
804 rt_entry_relation = heap_openr(rt_entry->relname);
805 rt_entry_locks = rt_entry_relation->rd_rules;
806 heap_close(rt_entry_relation);
808 if (rt_entry_locks != NULL)
811 matchLocks(event, rt_entry_locks, result_relation, parsetree);
822 * deepRewriteQuery does not handle the situation
823 * where a query fired by a rule uses relations that
824 * have instead select rules defined (views and the like).
825 * So we care for them here.
828 if (product_queries != NIL) {
831 List *new_products = NIL;
833 foreach (pq, product_queries) {
834 tmp = (Query *)lfirst(pq);
835 tmp = RewritePostprocessNonSelect(tmp);
836 new_products = lappend(new_products, tmp);
838 product_queries = new_products;
841 return product_queries;
847 * the statement is a select
852 * ApplyRetrieveRule changes the range table XXX Unions are copied
855 other = copyObject(parsetree);
858 ProcessRetrieveQuery(other, parsetree->rtable,
859 instead_flag, FALSE);
864 * to avoid infinite recursion, we restrict the number of times a query
865 * can be rewritten. Detecting cycles is left for the reader as an excercise.
867 #ifndef REWRITE_INVOKE_MAX
868 #define REWRITE_INVOKE_MAX 10
871 static int numQueryRewriteInvoked = 0;
875 * rewrite one query via QueryRewrite system, possibly returning 0, or many
879 QueryRewrite(Query *parsetree)
881 RewritePreprocessQuery(parsetree);
883 QueryRewriteSubLink(parsetree->qual);
884 QueryRewriteSubLink(parsetree->havingQual);
886 return QueryRewriteOne(parsetree);
890 * QueryRewriteSubLink
892 * This rewrites the SubLink subqueries first, doing the lowest ones first.
893 * We already have code in the main rewrite loops to process correlated
894 * variables from upper queries that exist in subqueries.
897 QueryRewriteSubLink(Node *node)
902 switch (nodeTag(node))
910 Expr *expr = (Expr *) node;
912 QueryRewriteSubLink((Node *) expr->args);
921 foreach(l, (List *) node)
922 QueryRewriteSubLink(lfirst(l));
927 SubLink *sublink = (SubLink *) node;
928 Query *query = (Query *) sublink->subselect;
932 * Nest down first. We do this so if a rewrite adds a
933 * SubLink we don't process it as part of this loop.
935 QueryRewriteSubLink((Node *) query->qual);
937 QueryRewriteSubLink((Node *) query->havingQual);
939 ret = QueryRewriteOne(query);
941 sublink->subselect = NULL;
942 else if (lnext(ret) == NIL)
943 sublink->subselect = lfirst(ret);
945 elog(ERROR, "Don't know how to process subquery that rewrites to multiple queries.");
949 /* ignore the others */
960 QueryRewriteOne(Query *parsetree)
962 numQueryRewriteInvoked = 0;
965 * take a deep breath and apply all the rewrite rules - ay
967 return deepRewriteQuery(parsetree);
972 * rewrites the query and apply the rules again on the queries rewritten
975 deepRewriteQuery(Query *parsetree)
978 List *rewritten = NIL;
981 List *qual_products = NIL;
985 if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
987 elog(ERROR, "query rewritten %d times, may contain cycles",
988 numQueryRewriteInvoked - 1);
992 result = RewriteQuery(parsetree, &instead, &qual_products);
996 Query *pt = lfirst(n);
997 List *newstuff = NIL;
999 newstuff = deepRewriteQuery(pt);
1000 if (newstuff != NIL)
1001 rewritten = nconc(rewritten, newstuff);
1005 * qual_products are the original query with the negated
1006 * rule qualification of an instead rule
1009 if (qual_products != NIL)
1010 rewritten = nconc(rewritten, qual_products);
1013 * The original query is appended last if not instead
1014 * because update and delete rule actions might not do
1015 * anything if they are invoked after the update or
1016 * delete is performed. The command counter increment
1017 * between the query execution makes the deleted (and
1018 * maybe the updated) tuples disappear so the scans
1019 * for them in the rule actions cannot find them.
1023 rewritten = lappend(rewritten, parsetree);
1030 CheckViewPerms(Relation view, List *rtable)
1039 * get the usename of the view's owner
1041 utup = SearchSysCacheTuple(USESYSID, view->rd_rel->relowner, 0, 0, 0);
1042 if (!HeapTupleIsValid(utup))
1044 elog(ERROR, "cache lookup for userid %d failed",
1045 view->rd_rel->relowner);
1048 ((Form_pg_shadow) GETSTRUCT(utup))->usename.data,
1052 * check that we have read access to all the classes in the range
1057 rte = (RangeTblEntry *) lfirst(rt);
1059 aclcheck_res = pg_aclcheck(rte->relname, uname.data, ACL_RD);
1060 if (aclcheck_res != ACLCHECK_OK)
1061 elog(ERROR, "%s: %s", rte->relname, aclcheck_error_strings[aclcheck_res]);