📄 rewritehandler.c
字号:
/*------------------------------------------------------------------------- * * rewriteHandler.c * Primary module of query rewriter. * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.158.2.2 2005/11/23 17:21:22 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/pg_operator.h"#include "catalog/pg_type.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/prep.h"#include "optimizer/var.h"#include "parser/analyze.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "parser/parse_oper.h"#include "parser/parse_type.h"#include "parser/parsetree.h"#include "rewrite/rewriteHandler.h"#include "rewrite/rewriteManip.h"#include "utils/builtins.h"#include "utils/lsyscache.h"/* We use a list of these to detect recursion in RewriteQuery */typedef struct rewrite_event{ Oid relation; /* OID of relation having rules */ CmdType event; /* type of rule being fired */} rewrite_event;static bool acquireLocksOnSubLinks(Node *node, void *context);static Query *rewriteRuleAction(Query *parsetree, Query *rule_action, Node *rule_qual, int rt_index, CmdType event);static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);static void rewriteTargetList(Query *parsetree, Relation target_relation);static TargetEntry *process_matched_tle(TargetEntry *src_tle, TargetEntry *prior_tle, const char *attrName);static Node *get_assignment_input(Node *node);static void markQueryForLocking(Query *qry, bool forUpdate, bool noWait, bool skipOldNew);static List *matchLocks(CmdType event, RuleLock *rulelocks, int varno, Query *parsetree);static Query *fireRIRrules(Query *parsetree, List *activeRIRs);/* * AcquireRewriteLocks - * Acquire suitable locks on all the relations mentioned in the Query. * These locks will ensure that the relation schemas don't change under us * while we are rewriting and planning the query. * * A secondary purpose of this routine is to fix up JOIN RTE references to * dropped columns (see details below). Because the RTEs are modified in * place, it is generally appropriate for the caller of this routine to have * first done a copyObject() to make a writable copy of the querytree in the * current memory context. * * This processing can, and for efficiency's sake should, be skipped when the * querytree has just been built by the parser: parse analysis already got * all the same locks we'd get here, and the parser will have omitted dropped * columns from JOINs to begin with. But we must do this whenever we are * dealing with a querytree produced earlier than the current command. * * About JOINs and dropped columns: although the parser never includes an * already-dropped column in a JOIN RTE's alias var list, it is possible for * such a list in a stored rule to include references to dropped columns. * (If the column is not explicitly referenced anywhere else in the query, * the dependency mechanism won't consider it used by the rule and so won't * prevent the column drop.) To support get_rte_attribute_is_dropped(), * we replace join alias vars that reference dropped columns with NULL Const * nodes. * * (In PostgreSQL 8.0, we did not do this processing but instead had * get_rte_attribute_is_dropped() recurse to detect dropped columns in joins. * That approach had horrible performance unfortunately; in particular * construction of a nested join was O(N^2) in the nesting depth.) */voidAcquireRewriteLocks(Query *parsetree){ ListCell *l; int rt_index; /* * First, process RTEs of the current query level. */ rt_index = 0; foreach(l, parsetree->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); Relation rel; LOCKMODE lockmode; List *newaliasvars; Index curinputvarno; RangeTblEntry *curinputrte; ListCell *ll; ++rt_index; switch (rte->rtekind) { case RTE_RELATION: /* * Grab the appropriate lock type for the relation, and do not * release it until end of transaction. This protects the * rewriter and planner against schema changes mid-query. * * If the relation is the query's result relation, then we * need RowExclusiveLock. Otherwise, check to see if the * relation is accessed FOR UPDATE/SHARE or not. We can't * just grab AccessShareLock because then the executor would * be trying to upgrade the lock, leading to possible * deadlocks. */ if (rt_index == parsetree->resultRelation) lockmode = RowExclusiveLock; else if (list_member_int(parsetree->rowMarks, rt_index)) lockmode = RowShareLock; else lockmode = AccessShareLock; rel = heap_open(rte->relid, lockmode); heap_close(rel, NoLock); break; case RTE_JOIN: /* * Scan the join's alias var list to see if any columns have * been dropped, and if so replace those Vars with NULL * Consts. * * Since a join has only two inputs, we can expect to see * multiple references to the same input RTE; optimize away * multiple fetches. */ newaliasvars = NIL; curinputvarno = 0; curinputrte = NULL; foreach(ll, rte->joinaliasvars) { Var *aliasvar = (Var *) lfirst(ll); /* * If the list item isn't a simple Var, then it must * represent a merged column, ie a USING column, and so it * couldn't possibly be dropped, since it's referenced in * the join clause. (Conceivably it could also be a NULL * constant already? But that's OK too.) */ if (IsA(aliasvar, Var)) { /* * The elements of an alias list have to refer to * earlier RTEs of the same rtable, because that's the * order the planner builds things in. So we already * processed the referenced RTE, and so it's safe to * use get_rte_attribute_is_dropped on it. (This might * not hold after rewriting or planning, but it's OK * to assume here.) */ Assert(aliasvar->varlevelsup == 0); if (aliasvar->varno != curinputvarno) { curinputvarno = aliasvar->varno; if (curinputvarno >= rt_index) elog(ERROR, "unexpected varno %d in JOIN RTE %d", curinputvarno, rt_index); curinputrte = rt_fetch(curinputvarno, parsetree->rtable); } if (get_rte_attribute_is_dropped(curinputrte, aliasvar->varattno)) { /* * can't use vartype here, since that might be a * now-dropped type OID, but it doesn't really * matter what type the Const claims to be. */ aliasvar = (Var *) makeNullConst(INT4OID); } } newaliasvars = lappend(newaliasvars, aliasvar); } rte->joinaliasvars = newaliasvars; break; case RTE_SUBQUERY: /* * The subquery RTE itself is all right, but we have to * recurse to process the represented subquery. */ AcquireRewriteLocks(rte->subquery); break; default: /* ignore other types of RTEs */ break; } } /* * Recurse into sublink subqueries, too. But we already did the ones in * the rtable. */ if (parsetree->hasSubLinks) query_tree_walker(parsetree, acquireLocksOnSubLinks, NULL, QTW_IGNORE_RT_SUBQUERIES);}/* * Walker to find sublink subqueries for AcquireRewriteLocks */static boolacquireLocksOnSubLinks(Node *node, void *context){ if (node == NULL) return false; if (IsA(node, SubLink)) { SubLink *sub = (SubLink *) node; /* Do what we came for */ AcquireRewriteLocks((Query *) sub->subselect); /* Fall through to process lefthand args of SubLink */ } /* * Do NOT recurse into Query nodes, because AcquireRewriteLocks already * processed subselects of subselects for us. */ return expression_tree_walker(node, acquireLocksOnSubLinks, context);}/* * rewriteRuleAction - * Rewrite the rule action with appropriate qualifiers (taken from * the triggering query). */static Query *rewriteRuleAction(Query *parsetree, Query *rule_action, Node *rule_qual, int rt_index, CmdType event){ int current_varno, new_varno; int rt_length; Query *sub_action; Query **sub_action_ptr; /* * Make modifiable copies of rule action and qual (what we're passed are * the stored versions in the relcache; don't touch 'em!). */ rule_action = (Query *) copyObject(rule_action); rule_qual = (Node *) copyObject(rule_qual); /* * Acquire necessary locks and fix any deleted JOIN RTE entries. */ AcquireRewriteLocks(rule_action); (void) acquireLocksOnSubLinks(rule_qual, NULL); current_varno = rt_index; rt_length = list_length(parsetree->rtable); new_varno = PRS2_NEW_VARNO + rt_length; /* * Adjust rule action and qual to offset its varnos, so that we can merge * its rtable with the main parsetree's rtable. * * If the rule action is an INSERT...SELECT, the OLD/NEW rtable entries * will be in the SELECT part, and we have to modify that rather than the * top-level INSERT (kluge!). */ sub_action = getInsertSelectQuery(rule_action, &sub_action_ptr); OffsetVarNodes((Node *) sub_action, rt_length, 0); OffsetVarNodes(rule_qual, rt_length, 0); /* but references to *OLD* should point at original rt_index */ ChangeVarNodes((Node *) sub_action, PRS2_OLD_VARNO + rt_length, rt_index, 0); ChangeVarNodes(rule_qual, PRS2_OLD_VARNO + rt_length, rt_index, 0); /* * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, * but we leave them all in place for two reasons: * * We'd have a much harder job to adjust the query's varnos if we * selectively removed RT entries. * * If the rule is INSTEAD, then the original query won't be executed at * all, and so its rtable must be preserved so that the executor will do * the correct permissions checks on it. * * RT entries that are not referenced in the completed jointree will be * ignored by the planner, so they do not affect query semantics. But any * permissions checks specified in them will be applied during executor * startup (see ExecCheckRTEPerms()). This allows us to check that the * caller has, say, insert-permission on a view, when the view is not * semantically referenced at all in the resulting query. * * When a rule is not INSTEAD, the permissions checks done on its copied * RT entries will be redundant with those done during execution of the * original query, but we don't bother to treat that case differently. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with * the main rtable. Hence do a deep copy here. */ sub_action->rtable = list_concat((List *) copyObject(parsetree->rtable), sub_action->rtable); /* * Each rule action's jointree should be the main parsetree's jointree * plus that rule's jointree, but usually *without* the original rtindex * that we're replacing (if present, which it won't be for INSERT). Note * that if the rule action refers to OLD, its jointree will add a * reference to rt_index. If the rule action doesn't refer to OLD, but * either the rule_qual or the user query quals do, then we need to keep * the original rtindex in the jointree to provide data for the quals. We * don't want the original rtindex to be joined twice, however, so avoid * keeping it if the rule action mentions it. * * As above, the action's jointree must not share substructure with the * main parsetree's. */ if (sub_action->commandType != CMD_UTILITY) { bool keeporig; List *newjointree; Assert(sub_action->jointree != NULL); keeporig = (!rangeTableEntry_used((Node *) sub_action->jointree, rt_index, 0)) && (rangeTableEntry_used(rule_qual, rt_index, 0) || rangeTableEntry_used(parsetree->jointree->quals, rt_index, 0)); newjointree = adjustJoinTreeList(parsetree, !keeporig, rt_index); if (newjointree != NIL) { /* * If sub_action is a setop, manipulating its jointree will do no * good at all, because the jointree is dummy. (Perhaps someday * we could push the joining and quals down to the member * statements of the setop?) */ if (sub_action->setOperations != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented"))); sub_action->jointree->fromlist = list_concat(newjointree, sub_action->jointree->fromlist); /* * There could have been some SubLinks in newjointree, in which * case we'd better mark the sub_action correctly. */ if (parsetree->hasSubLinks && !sub_action->hasSubLinks) sub_action->hasSubLinks = checkExprHasSubLink((Node *) newjointree); } } /* * Event Qualification forces copying of parsetree and splitting into two * queries one w/rule_qual, one w/NOT rule_qual. Also add user query qual * onto rule action */ AddQual(sub_action, rule_qual); AddQual(sub_action, parsetree->jointree->quals); /* * Rewrite new.attribute w/ right hand side of target-list entry for * appropriate field name in insert/update. * * KLUGE ALERT: since ResolveNew returns a mutated copy, we can't just * apply it to sub_action; we have to remember to update the sublink * inside rule_action, too. */ if ((event == CMD_INSERT || event == CMD_UPDATE) &&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -