📄 rewritehandler.c
字号:
else continue; } if (info->action == info->event && info->event == CMD_SELECT) continue; /* * 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 */ qual = parsetree->qual; AddQual(info->rule_action, qual); if (info->rule_qual != NULL) AddQual(info->rule_action, info->rule_qual); /*-------------------------------------------------- * Step 2: * Rewrite new.attribute w/ right hand side of target-list * entry for appropriate field name in insert/update *-------------------------------------------------- */ if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE)) FixNew(info, parsetree); /*-------------------------------------------------- * Step 3: * rewriting due to retrieve rules *-------------------------------------------------- */ info->rule_action->rtable = info->rt; /* * ProcessRetrieveQuery(info->rule_action, info->rt, * &orig_instead_flag, TRUE); */ /*-------------------------------------------------- * Step 4 * Simplify? hey, no algorithm for simplification... let * the planner do it. *-------------------------------------------------- */ results = lappend(results, info->rule_action); pfree(info); } /* ---------- * If this was an unqualified instead rule, * throw away an eventually saved 'default' parsetree * ---------- */ if (event_qual == NULL && *instead_flag) *qual_products = NIL; } return results;}static List *RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products){ CmdType event; List *product_queries = NIL; int result_relation = 0; RangeTblEntry *rt_entry; Relation rt_entry_relation = NULL; RuleLock *rt_entry_locks = NULL; Assert(parsetree != NULL); event = parsetree->commandType; /* * SELECT rules are handled later when we have all the queries that * should get executed */ if (event == CMD_SELECT) return NIL; /* * Utilities aren't rewritten at all - why is this here? */ if (event == CMD_UTILITY) return NIL; /* * only for a delete may the targetlist be NULL */ if (event != CMD_DELETE) Assert(parsetree->targetList != NULL); result_relation = parsetree->resultRelation; /* * the statement is an update, insert or delete - fire rules on it. */ rt_entry = rt_fetch(result_relation, parsetree->rtable); rt_entry_relation = heap_openr(rt_entry->relname); rt_entry_locks = rt_entry_relation->rd_rules; heap_close(rt_entry_relation); if (rt_entry_locks != NULL) { List *locks = matchLocks(event, rt_entry_locks, result_relation, parsetree); product_queries = fireRules(parsetree, result_relation, event, instead_flag, locks, qual_products); } return product_queries;}/* * to avoid infinite recursion, we restrict the number of times a query * can be rewritten. Detecting cycles is left for the reader as an excercise. */#ifndef REWRITE_INVOKE_MAX#define REWRITE_INVOKE_MAX 10#endifstatic int numQueryRewriteInvoked = 0;/* * deepRewriteQuery - * rewrites the query and apply the rules again on the queries rewritten */static List *deepRewriteQuery(Query *parsetree){ List *n; List *rewritten = NIL; List *result = NIL; bool instead; List *qual_products = NIL; if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX) { elog(ERROR, "query rewritten %d times, may contain cycles", numQueryRewriteInvoked - 1); } instead = FALSE; result = RewriteQuery(parsetree, &instead, &qual_products); foreach(n, result) { Query *pt = lfirst(n); List *newstuff = NIL; newstuff = deepRewriteQuery(pt); if (newstuff != NIL) rewritten = nconc(rewritten, newstuff); } /* ---------- * qual_products are the original query with the negated * rule qualification of an instead rule * ---------- */ if (qual_products != NIL) rewritten = nconc(rewritten, qual_products); /* ---------- * The original query is appended last if not instead * 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 execution makes the deleted (and * maybe the updated) tuples disappear so the scans * for them in the rule actions cannot find them. * ---------- */ if (!instead) rewritten = lappend(rewritten, parsetree); return rewritten;}/* * QueryOneRewrite - * rewrite one query */static List *QueryRewriteOne(Query *parsetree){ numQueryRewriteInvoked = 0; /* * take a deep breath and apply all the rewrite rules - ay */ return deepRewriteQuery(parsetree);}/* ---------- * RewritePreprocessQuery - * adjust details in the parsetree, the rule system * depends on * ---------- */static voidRewritePreprocessQuery(Query *parsetree){ /* ---------- * if the query has a resultRelation, reassign the * result domain numbers to the attribute numbers in the * target relation. FixNew() depends on it when replacing * *new* references in a rule action by the expressions * from the rewritten query. * resjunk targets are somewhat arbitrarily given a resno of 0; * this is to prevent FixNew() from matching them to var nodes. * ---------- */ if (parsetree->resultRelation > 0) { RangeTblEntry *rte; Relation rd; List *tl; rte = (RangeTblEntry *) nth(parsetree->resultRelation - 1, parsetree->rtable); rd = heap_openr(rte->relname); foreach(tl, parsetree->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(tl); if (! tle->resdom->resjunk) tle->resdom->resno = attnameAttNum(rd, tle->resdom->resname); else tle->resdom->resno = 0; } heap_close(rd); }}/* * BasicQueryRewrite - * rewrite one query via query rewrite system, possibly returning 0 * or many queries */static List *BasicQueryRewrite(Query *parsetree){ List *querylist; List *results = NIL; List *l; Query *query; /* * Step 1 * * There still seems something broken with the resdom numbers so we * reassign them first. */ RewritePreprocessQuery(parsetree); /* * Step 2 * * Apply all non-SELECT rules possibly getting 0 or many queries */ querylist = QueryRewriteOne(parsetree); /* * Step 3 * * Apply all the RIR rules on each query */ foreach(l, querylist) { query = fireRIRrules((Query *) lfirst(l)); /* * If the query was marked having aggregates, check if this is * still true after rewriting. This check must get expanded when * someday aggregates can appear somewhere else than in the * targetlist or the having qual. */ if (query->hasAggs) query->hasAggs = checkQueryHasAggs((Node *) (query->targetList)) | checkQueryHasAggs((Node *) (query->havingQual)); query->hasSubLinks = checkQueryHasSubLink((Node *) (query->qual)) | checkQueryHasSubLink((Node *) (query->havingQual)); results = lappend(results, query); } return results;}/* * QueryRewrite - * Primary entry point to the query rewriter. * Rewrite one query via query rewrite system, possibly returning 0 * or many queries. * * NOTE: The code in QueryRewrite was formerly in pg_parse_and_plan(), and was * moved here so that it would be invoked during EXPLAIN. The division of * labor between this routine and BasicQueryRewrite is not obviously correct * ... at least not to me ... tgl 5/99. */List *QueryRewrite(Query *parsetree){ List *rewritten, *rewritten_item; /***S*I***/ /* * Rewrite Union, Intersect and Except Queries to normal Union Queries * using IN and NOT IN subselects */ if (parsetree->intersectClause) parsetree = Except_Intersect_Rewrite(parsetree); /* Rewrite basic queries (retrieve, append, delete, replace) */ rewritten = BasicQueryRewrite(parsetree); /* * Rewrite the UNIONS. */ foreach(rewritten_item, rewritten) { Query *qry = (Query *) lfirst(rewritten_item); List *union_result = NIL; List *union_item; foreach(union_item, qry->unionClause) { union_result = nconc(union_result, BasicQueryRewrite((Query *) lfirst(union_item))); } qry->unionClause = union_result; } return rewritten;}/***S*I***//* This function takes two targetlists as arguments and checks if the * targetlists are compatible (i.e. both select for the same number of * attributes and the types are compatible */static voidcheck_targetlists_are_compatible(List *prev_target, List *current_target){ List *tl, *next_target; int prev_len = 0, next_len = 0; foreach(tl, prev_target) if (!((TargetEntry *) lfirst(tl))->resdom->resjunk) prev_len++; foreach(next_target, current_target) if (!((TargetEntry *) lfirst(next_target))->resdom->resjunk) next_len++; if (prev_len != next_len) elog(ERROR, "Each UNION | EXCEPT | INTERSECT query must have the same number of columns."); foreach(next_target, current_target) { Oid itype; Oid otype; otype = ((TargetEntry *) lfirst(prev_target))->resdom->restype; itype = ((TargetEntry *) lfirst(next_target))->resdom->restype; /* one or both is a NULL column? then don't convert... */ if (otype == InvalidOid) { /* propagate a known type forward, if available */ if (itype != InvalidOid) ((TargetEntry *) lfirst(prev_target))->resdom->restype = itype;#ifdef NOT_USED else { ((TargetEntry *) lfirst(prev_target))->resdom->restype = UNKNOWNOID; ((TargetEntry *) lfirst(next_target))->resdom->restype = UNKNOWNOID; }#endif } else if (itype == InvalidOid) { } /* they don't match in type? then convert... */ else if (itype != otype) { Node *expr; expr = ((TargetEntry *) lfirst(next_target))->expr; expr = CoerceTargetExpr(NULL, expr, itype, otype); if (expr == NULL) { elog(ERROR, "Unable to transform %s to %s" "\n\tEach UNION | EXCEPT | INTERSECT clause must have compatible target types", typeidTypeName(itype), typeidTypeName(otype)); } ((TargetEntry *) lfirst(next_target))->expr = expr; ((TargetEntry *) lfirst(next_target))->resdom->restype = otype; } /* both are UNKNOWN? then evaluate as text... */ else if (itype == UNKNOWNOID) { ((TargetEntry *) lfirst(next_target))->resdom->restype = TEXTOID; ((TargetEntry *) lfirst(prev_target))->resdom->restype = TEXTOID; } prev_target = lnext(prev_target); }}/***S*I***//* Rewrites UNION INTERSECT and EXCEPT queries to semantiacally equivalent * queries that use IN and NOT IN subselects. * * The operator tree is attached to 'intersectClause' (see rule * 'SelectStmt' in gram.y) of the 'parsetree' given as an * argument. First we remember some clauses (the sortClause, the * unique flag etc.) Then we translate the operator tree to DNF * (disjunctive normal form) by 'cnfify'. (Note that 'cnfify' produces * CNF but as we exchanged ANDs with ORs in function A_Expr_to_Expr() * earlier we get DNF after exchanging ANDs and ORs again in the * result.) Now we create a new query by evaluating the new operator * tree which is in DNF now. For every AND we create an entry in the * union list and for every OR we create an IN subselect. (NOT IN * subselects are created for OR NOT nodes). The first entry of the * union list is handed back but before that the remembered clauses * (sortClause etc) are attached to the new top Node (Note that the * new top Node can differ from the parsetree given as argument because of * the translation to DNF. That's why we have to remember the sortClause or * unique flag!) */static Query *Except_Intersect_Rewrite(Query *parsetree){ SubLink *n; Query *result, *intersect_node; List *elist, *intersect_list = NIL, *intersect, *intersectClause; List *union_list = NIL, *sortClause; List *left_expr, *right_expr, *resnames = NIL; char *op, *uniqueFlag, *into; bool isBinary, isPortal, isTemp; CmdType commandType = CMD_SELECT; List *rtable_insert = NIL; List *prev_target = NIL; /* * Remember the Resnames of the given parsetree's targetlist (these * are the resnames of the first Select Statement of the query * formulated by the user and he wants the columns named by these * strings. The transformation to DNF can cause another Select * Statment to be the top one which uses other names for its columns. * Therefore we remeber the original names and attach them to the * targetlist of the new topmost Node at the end of this function */ foreach(elist, parsetree->targetList) { TargetEntry *tent = (TargetEntry *) lfirst(elist); resnames = lappend(resnames, tent->resdom->resname); } /* * If the Statement is an INSERT INTO ... (SELECT...) statement using * UNIONs, INTERSECTs or EXCEPTs and the transformation to DNF makes * another Node to the top node we have to transform the new top node * to an INSERT node and the original INSERT node to a SELECT node */ if (parsetree->commandType == CMD_INSERT) { parsetree->commandType = CMD_SELECT; commandType = CMD_INSERT; parsetree->resultRelation = 0; /* * The result relation ( = the one to insert into) has to be * attached to the rtable list of the new top node */ rtable_insert = nth(length(parsetree->rtable) - 1, parsetree->rtable); } /* * Save some items, to be able to attach them to the resulting top * node at the end of the function */ sortClause = parsetree->sortClause; uniqueFlag = parsetree->uniqueFlag; into = parsetree->into; isBinary = parsetree->isBinary; isPortal = parsetree->isPortal; isTemp = parsetree->isTemp; /* * The operator tree attached to parsetree->intersectClause is still * 'raw' ( = the leaf nodes are still SelectStmt nodes instead of * Query nodes) So step through the tree and transform the nodes using * parse_analyze(). * * The parsetree (given as an argument to Except_Intersect_Rewrite()) has * already been transformed and transforming it again would cause * troubles. So we give the 'raw' version (of the cooked parsetree) * to the function to prevent an additional transformation. Instead we * hand back the 'cooked' version also given as an argument to * intersect_tree_analyze() */ intersectClause = (List *) intersect_tree_analyze((Node *) parsetree->intersectClause, (Node *) lfirst(parsetree->unionClause), (Node *) parsetree); /* intersectClause is no longer needed so set it to NIL */ parsetree->intersectClause = NIL; /* * unionClause will be needed later on but the list it delivered is no * longer needed, so set it to NIL */ parsetree->unionClause = NIL; /* * Transform the operator tree to DNF (remember ANDs and ORs have been * exchanged, that's why we get DNF by using cnfify) * * After the call, explicit ANDs are removed and all AND operands are * simply items in the intersectClause list */ intersectClause = cnfify((Expr *) intersectClause, true); /* * For every entry of the intersectClause list we generate one entry * in the union_list */ foreach(intersect, intersectClause) { /* * for every OR we create an IN subselect and for every OR NOT we * create a NOT IN subselect, so first extract all the Select * Query nodes from the tree (that contains only OR or OR NOTs any * more because we did a transformation to DNF * * There must be at least one node that is not negated (i.e. just OR * and not OR NOT) and this node will be the first in the list * returned */ intersect_list = NIL; create_intersect_list((Node *) lfirst(intersect), &intersect_list); /* * This one will become the Select Query node, all other nodes are * transformed into subselects under this node! */ intersect_node = (Query *) lfirst(intersect_list); intersect_list = lnext(intersect_list); /* * Check if all Select Statements use the same number of * attributes and if all corresponding attributes are of the same * type */ if (prev_targe
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -