📄 rewritehandler.c
字号:
} if (expr == NULL) { /* * No per-column default, so look for a default for the type itself. */ expr = get_typdefault(atttype); } if (expr == NULL) return NULL; /* No default anywhere */ /* * Make sure the value is coerced to the target column type; this will * generally be true already, but there seem to be some corner cases * involving domain defaults where it might not be true. This should match * the parser's processing of non-defaulted expressions --- see * updateTargetListEntry(). */ exprtype = exprType(expr); expr = coerce_to_target_type(NULL, /* no UNKNOWN params here */ expr, exprtype, atttype, atttypmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); if (expr == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" is of type %s" " but default expression is of type %s", NameStr(att_tup->attname), format_type_be(atttype), format_type_be(exprtype)), errhint("You will need to rewrite or cast the expression."))); return expr;}/* * matchLocks - * match the list of locks and returns the matching rules */static List *matchLocks(CmdType event, RuleLock *rulelocks, int varno, Query *parsetree){ List *matching_locks = NIL; int nlocks; int i; if (rulelocks == NULL) return NIL; if (parsetree->commandType != CMD_SELECT) { if (parsetree->resultRelation != varno) return NIL; } nlocks = rulelocks->numLocks; for (i = 0; i < nlocks; i++) { RewriteRule *oneLock = rulelocks->rules[i]; if (oneLock->event == event) { if (parsetree->commandType != CMD_SELECT || (oneLock->attrno == -1 ? rangeTableEntry_used((Node *) parsetree, varno, 0) : attribute_used((Node *) parsetree, varno, oneLock->attrno, 0))) matching_locks = lappend(matching_locks, oneLock); } } return matching_locks;}/* * ApplyRetrieveRule - expand an ON SELECT rule */static Query *ApplyRetrieveRule(Query *parsetree, RewriteRule *rule, int rt_index, bool relation_level, Relation relation, List *activeRIRs){ Query *rule_action; RangeTblEntry *rte, *subrte; if (list_length(rule->actions) != 1) elog(ERROR, "expected just one rule action"); if (rule->qual != NULL) elog(ERROR, "cannot handle qualified ON SELECT rule"); if (!relation_level) elog(ERROR, "cannot handle per-attribute ON SELECT rule"); /* * Make a modifiable copy of the view query, and acquire needed locks on * the relations it mentions. */ rule_action = copyObject(linitial(rule->actions)); AcquireRewriteLocks(rule_action); /* * Recursively expand any view references inside the view. */ rule_action = fireRIRrules(rule_action, activeRIRs); /* * VIEWs are really easy --- just plug the view query in as a subselect, * replacing the relation's original RTE. */ rte = rt_fetch(rt_index, parsetree->rtable); rte->rtekind = RTE_SUBQUERY; rte->relid = InvalidOid; rte->subquery = rule_action; rte->inh = false; /* must not be set for a subquery */ /* * We move the view's permission check data down to its rangetable. The * checks will actually be done against the *OLD* entry therein. */ subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); Assert(subrte->relid == relation->rd_id); subrte->requiredPerms = rte->requiredPerms; subrte->checkAsUser = rte->checkAsUser; rte->requiredPerms = 0; /* no permission check on subquery itself */ rte->checkAsUser = InvalidOid; /* * FOR UPDATE/SHARE of view? */ if (list_member_int(parsetree->rowMarks, rt_index)) { /* * Remove the view from the list of rels that will actually be marked * FOR UPDATE/SHARE by the executor. It will still be access- checked * for write access, though. */ parsetree->rowMarks = list_delete_int(parsetree->rowMarks, rt_index); /* * Set up the view's referenced tables as if FOR UPDATE/SHARE. */ markQueryForLocking(rule_action, parsetree->forUpdate, parsetree->rowNoWait, true); } return parsetree;}/* * Recursively mark all relations used by a view as FOR UPDATE/SHARE. * * This may generate an invalid query, eg if some sub-query uses an * aggregate. We leave it to the planner to detect that. * * NB: this must agree with the parser's transformLocking() routine. */static voidmarkQueryForLocking(Query *qry, bool forUpdate, bool noWait, bool skipOldNew){ Index rti = 0; ListCell *l; if (qry->rowMarks) { if (forUpdate != qry->forUpdate) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot use both FOR UPDATE and FOR SHARE in one query"))); if (noWait != qry->rowNoWait) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot use both wait and NOWAIT in one query"))); } qry->forUpdate = forUpdate; qry->rowNoWait = noWait; foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); rti++; /* Ignore OLD and NEW entries if we are at top level of view */ if (skipOldNew && (rti == PRS2_OLD_VARNO || rti == PRS2_NEW_VARNO)) continue; if (rte->rtekind == RTE_RELATION) { qry->rowMarks = list_append_unique_int(qry->rowMarks, rti); rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { /* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */ markQueryForLocking(rte->subquery, forUpdate, noWait, false); } }}/* * fireRIRonSubLink - * Apply fireRIRrules() to each SubLink (subselect in expression) found * in the given tree. * * NOTE: although this has the form of a walker, we cheat and modify the * SubLink nodes in-place. It is caller's responsibility to ensure that * no unwanted side-effects occur! * * This is unlike most of the other routines that recurse into subselects, * because we must take control at the SubLink node in order to replace * the SubLink's subselect link with the possibly-rewritten subquery. */static boolfireRIRonSubLink(Node *node, List *activeRIRs){ if (node == NULL) return false; if (IsA(node, SubLink)) { SubLink *sub = (SubLink *) node; /* Do what we came for */ sub->subselect = (Node *) fireRIRrules((Query *) sub->subselect, activeRIRs); /* Fall through to process lefthand args of SubLink */ } /* * Do NOT recurse into Query nodes, because fireRIRrules already processed * subselects of subselects for us. */ return expression_tree_walker(node, fireRIRonSubLink, (void *) activeRIRs);}/* * fireRIRrules - * Apply all RIR rules on each rangetable entry in a query */static Query *fireRIRrules(Query *parsetree, List *activeRIRs){ int rt_index; /* * don't try to convert this into a foreach loop, because rtable list can * get changed each time through... */ rt_index = 0; while (rt_index < list_length(parsetree->rtable)) { RangeTblEntry *rte; Relation rel; List *locks; RuleLock *rules; RewriteRule *rule; int i; ++rt_index; rte = rt_fetch(rt_index, parsetree->rtable); /* * A subquery RTE can't have associated rules, so there's nothing to * do to this level of the query, but we must recurse into the * subquery to expand any rule references in it. */ if (rte->rtekind == RTE_SUBQUERY) { rte->subquery = fireRIRrules(rte->subquery, activeRIRs); continue; } /* * Joins and other non-relation RTEs can be ignored completely. */ if (rte->rtekind != RTE_RELATION) continue; /* * If the table is not referenced in the query, then we ignore it. * This prevents infinite expansion loop due to new rtable entries * inserted by expansion of a rule. A table is referenced if it is * part of the join set (a source table), or is referenced by any Var * nodes, or is the result table. */ if (rt_index != parsetree->resultRelation && !rangeTableEntry_used((Node *) parsetree, rt_index, 0)) continue; /* * We can use NoLock here since either the parser or * AcquireRewriteLocks should have locked the rel already. */ rel = heap_open(rte->relid, NoLock); /* * Collect the RIR rules that we must apply */ rules = rel->rd_rules; if (rules == NULL) { heap_close(rel, NoLock); continue; } locks = NIL; for (i = 0; i < rules->numLocks; i++) { rule = rules->rules[i]; if (rule->event != CMD_SELECT) continue; if (rule->attrno > 0) { /* per-attr rule; do we need it? */ if (!attribute_used((Node *) parsetree, rt_index, rule->attrno, 0)) continue; } locks = lappend(locks, rule); } /* * If we found any, apply them --- but first check for recursion! */ if (locks != NIL) { ListCell *l; if (list_member_oid(activeRIRs, RelationGetRelid(rel))) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("infinite recursion detected in rules for relation \"%s\"", RelationGetRelationName(rel)))); activeRIRs = lcons_oid(RelationGetRelid(rel), activeRIRs); foreach(l, locks) { rule = lfirst(l); parsetree = ApplyRetrieveRule(parsetree, rule, rt_index, rule->attrno == -1, rel, activeRIRs); } activeRIRs = list_delete_first(activeRIRs); } heap_close(rel, NoLock); } /* * Recurse into sublink subqueries, too. But we already did the ones in * the rtable. */ if (parsetree->hasSubLinks) query_tree_walker(parsetree, fireRIRonSubLink, (void *) activeRIRs, QTW_IGNORE_RT_SUBQUERIES); return parsetree;}/* * Modify the given query by adding 'AND rule_qual IS NOT TRUE' to its * qualification. This is used to generate suitable "else clauses" for * conditional INSTEAD rules. (Unfortunately we must use "x IS NOT TRUE", * not just "NOT x" which the planner is much smarter about, else we will * do the wrong thing when the qual evaluates to NULL.) * * The rule_qual may contain references to OLD or NEW. OLD references are * replaced by references to the specified rt_index (the relation that the * rule applies to). NEW references are only possible for INSERT and UPDATE * queries on the relation itself, and so they should be replaced by copies * of the related entries in the query's own targetlist. */static Query *CopyAndAddInvertedQual(Query *parsetree, Node *rule_qual, int rt_index, CmdType event)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -