📄 rewritehandler.c
字号:
{ /* Don't scribble on the passed qual (it's in the relcache!) */ Node *new_qual = (Node *) copyObject(rule_qual); /* * In case there are subqueries in the qual, acquire necessary locks and * fix any deleted JOIN RTE entries. (This is somewhat redundant with * rewriteRuleAction, but not entirely ... consider restructuring so that * we only need to process the qual this way once.) */ (void) acquireLocksOnSubLinks(new_qual, NULL); /* Fix references to OLD */ ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0); /* Fix references to NEW */ if (event == CMD_INSERT || event == CMD_UPDATE) new_qual = ResolveNew(new_qual, PRS2_NEW_VARNO, 0, rt_fetch(rt_index, parsetree->rtable), parsetree->targetList, event, rt_index); /* And attach the fixed qual */ AddInvertedQual(parsetree, new_qual); return parsetree;}/* * fireRules - * Iterate through rule locks applying rules. * * Input arguments: * parsetree - original query * rt_index - RT index of result relation in original query * event - type of rule event * locks - list of rules to fire * Output arguments: * *instead_flag - set TRUE if any unqualified INSTEAD rule is found * (must be initialized to FALSE) * *qual_product - filled with modified original query if any qualified * INSTEAD rule is found (must be initialized to NULL) * Return value: * list of rule actions adjusted for use with this query * * Qualified INSTEAD rules generate their action with the qualification * condition added. They also generate a modified version of the original * query with the negated qualification added, so that it will run only for * rows that the qualified action doesn't act on. (If there are multiple * qualified INSTEAD rules, we AND all the negated quals onto a single * modified original query.) We won't execute the original, unmodified * query if we find either qualified or unqualified INSTEAD rules. If * we find both, the modified original query is discarded too. */static List *fireRules(Query *parsetree, int rt_index, CmdType event, List *locks, bool *instead_flag, Query **qual_product){ List *results = NIL; ListCell *l; foreach(l, locks) { RewriteRule *rule_lock = (RewriteRule *) lfirst(l); Node *event_qual = rule_lock->qual; List *actions = rule_lock->actions; QuerySource qsrc; ListCell *r; /* Determine correct QuerySource value for actions */ if (rule_lock->isInstead) { if (event_qual != NULL) qsrc = QSRC_QUAL_INSTEAD_RULE; else { qsrc = QSRC_INSTEAD_RULE; *instead_flag = true; /* report unqualified INSTEAD */ } } else qsrc = QSRC_NON_INSTEAD_RULE; if (qsrc == QSRC_QUAL_INSTEAD_RULE) { /* * If there are INSTEAD rules with qualifications, the original * query is still performed. But all the negated rule * qualifications of the INSTEAD rules are added so it does its * actions only in cases where the rule quals of all INSTEAD rules * are false. Think of it as the default action in a case. We save * this in *qual_product so RewriteQuery() can add it to the query * list after we mangled it up enough. * * If we have already found an unqualified INSTEAD rule, then * *qual_product won't be used, so don't bother building it. */ if (!*instead_flag) { if (*qual_product == NULL) *qual_product = copyObject(parsetree); *qual_product = CopyAndAddInvertedQual(*qual_product, event_qual, rt_index, event); } } /* Now process the rule's actions and add them to the result list */ foreach(r, actions) { Query *rule_action = lfirst(r); if (rule_action->commandType == CMD_NOTHING) continue; rule_action = rewriteRuleAction(parsetree, rule_action, event_qual, rt_index, event); rule_action->querySource = qsrc; rule_action->canSetTag = false; /* might change later */ results = lappend(results, rule_action); } } return results;}/* * RewriteQuery - * rewrites the query and apply the rules again on the queries rewritten * * rewrite_events is a list of open query-rewrite actions, so we can detect * infinite recursion. */static List *RewriteQuery(Query *parsetree, List *rewrite_events){ CmdType event = parsetree->commandType; bool instead = false; Query *qual_product = NULL; List *rewritten = NIL; /* * If the statement is an update, insert or delete - fire rules on it. * * SELECT rules are handled later when we have all the queries that should * get executed. Also, utilities aren't rewritten at all (do we still * need that check?) */ if (event != CMD_SELECT && event != CMD_UTILITY) { int result_relation; RangeTblEntry *rt_entry; Relation rt_entry_relation; List *locks; result_relation = parsetree->resultRelation; Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); /* * We can use NoLock here since either the parser or * AcquireRewriteLocks should have locked the rel already. */ rt_entry_relation = heap_open(rt_entry->relid, NoLock); /* * If it's an INSERT or UPDATE, rewrite the targetlist into standard * form. This will be needed by the planner anyway, and doing it now * ensures that any references to NEW.field will behave sanely. */ if (event == CMD_INSERT || event == CMD_UPDATE) rewriteTargetList(parsetree, rt_entry_relation); /* * Collect and apply the appropriate rules. */ locks = matchLocks(event, rt_entry_relation->rd_rules, result_relation, parsetree); if (locks != NIL) { List *product_queries; product_queries = fireRules(parsetree, result_relation, event, locks, &instead, &qual_product); /* * If we got any product queries, recursively rewrite them --- but * first check for recursion! */ if (product_queries != NIL) { ListCell *n; rewrite_event *rev; foreach(n, rewrite_events) { rev = (rewrite_event *) lfirst(n); if (rev->relation == RelationGetRelid(rt_entry_relation) && rev->event == event) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("infinite recursion detected in rules for relation \"%s\"", RelationGetRelationName(rt_entry_relation)))); } rev = (rewrite_event *) palloc(sizeof(rewrite_event)); rev->relation = RelationGetRelid(rt_entry_relation); rev->event = event; rewrite_events = lcons(rev, rewrite_events); foreach(n, product_queries) { Query *pt = (Query *) lfirst(n); List *newstuff; newstuff = RewriteQuery(pt, rewrite_events); rewritten = list_concat(rewritten, newstuff); } rewrite_events = list_delete_first(rewrite_events); } } heap_close(rt_entry_relation, NoLock); } /* * For INSERTs, the original query is done first; for UPDATE/DELETE, it is * done last. This is needed because update and delete rule actions might * not do anything if they are invoked after the update or delete is * performed. The command counter increment between the query executions * makes the deleted (and maybe the updated) tuples disappear so the scans * for them in the rule actions cannot find them. * * If we found any unqualified INSTEAD, the original query is not done at * all, in any form. Otherwise, we add the modified form if qualified * INSTEADs were found, else the unmodified form. */ if (!instead) { if (parsetree->commandType == CMD_INSERT) { if (qual_product != NULL) rewritten = lcons(qual_product, rewritten); else rewritten = lcons(parsetree, rewritten); } else { if (qual_product != NULL) rewritten = lappend(rewritten, qual_product); else rewritten = lappend(rewritten, parsetree); } } return rewritten;}/* * QueryRewrite - * Primary entry point to the query rewriter. * Rewrite one query via query rewrite system, possibly returning 0 * or many queries. * * NOTE: the parsetree must either have come straight from the parser, * or have been scanned by AcquireRewriteLocks to acquire suitable locks. */List *QueryRewrite(Query *parsetree){ List *querylist; List *results = NIL; ListCell *l; CmdType origCmdType; bool foundOriginalQuery; Query *lastInstead; /* * Step 1 * * Apply all non-SELECT rules possibly getting 0 or many queries */ querylist = RewriteQuery(parsetree, NIL); /* * Step 2 * * Apply all the RIR rules on each query */ foreach(l, querylist) { Query *query = (Query *) lfirst(l); query = fireRIRrules(query, NIL); /* * If the query target was rewritten as a view, complain. */ if (query->resultRelation) { RangeTblEntry *rte = rt_fetch(query->resultRelation, query->rtable); if (rte->rtekind == RTE_SUBQUERY) { switch (query->commandType) { case CMD_INSERT: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot insert into a view"), errhint("You need an unconditional ON INSERT DO INSTEAD rule."))); break; case CMD_UPDATE: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot update a view"), errhint("You need an unconditional ON UPDATE DO INSTEAD rule."))); break; case CMD_DELETE: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot delete from a view"), errhint("You need an unconditional ON DELETE DO INSTEAD rule."))); break; default: elog(ERROR, "unrecognized commandType: %d", (int) query->commandType); break; } } } results = lappend(results, query); } /* * Step 3 * * Determine which, if any, of the resulting queries is supposed to set * the command-result tag; and update the canSetTag fields accordingly. * * If the original query is still in the list, it sets the command tag. * Otherwise, the last INSTEAD query of the same kind as the original is * allowed to set the tag. (Note these rules can leave us with no query * setting the tag. The tcop code has to cope with this by setting up a * default tag based on the original un-rewritten query.) * * The Asserts verify that at most one query in the result list is marked * canSetTag. If we aren't checking asserts, we can fall out of the loop * as soon as we find the original query. */ origCmdType = parsetree->commandType; foundOriginalQuery = false; lastInstead = NULL; foreach(l, results) { Query *query = (Query *) lfirst(l); if (query->querySource == QSRC_ORIGINAL) { Assert(query->canSetTag); Assert(!foundOriginalQuery); foundOriginalQuery = true;#ifndef USE_ASSERT_CHECKING break;#endif } else { Assert(!query->canSetTag); if (query->commandType == origCmdType && (query->querySource == QSRC_INSTEAD_RULE || query->querySource == QSRC_QUAL_INSTEAD_RULE)) lastInstead = query; } } if (!foundOriginalQuery && lastInstead != NULL) lastInstead->canSetTag = true; return results;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -