rewritehandler.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,349 行 · 第 1/3 页
C
1,349 行
((ArrayRef *) prior_tle->expr)->refassgnexpr == NULL || ((ArrayRef *) src_tle->expr)->refrestype != ((ArrayRef *) prior_tle->expr)->refrestype) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple assignments to same column \"%s\"", attrName))); /* * Prior TLE could be a nest of ArrayRefs if we do this more than * once. */ priorbottom = (Node *) ((ArrayRef *) prior_tle->expr)->refexpr; while (priorbottom != NULL && IsA(priorbottom, ArrayRef) && ((ArrayRef *) priorbottom)->refassgnexpr != NULL) priorbottom = (Node *) ((ArrayRef *) priorbottom)->refexpr; if (!equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple assignments to same column \"%s\"", attrName))); /* * Looks OK to nest 'em. */ newexpr = makeNode(ArrayRef); memcpy(newexpr, src_tle->expr, sizeof(ArrayRef)); newexpr->refexpr = prior_tle->expr; return makeTargetEntry(resdom, (Expr *) newexpr);}/* * Make an expression tree for the default value for a column. * * If there is no default, return a NULL instead. */Node *build_column_default(Relation rel, int attrno){ TupleDesc rd_att = rel->rd_att; Form_pg_attribute att_tup = rd_att->attrs[attrno - 1]; Oid atttype = att_tup->atttypid; int32 atttypmod = att_tup->atttypmod; Node *expr = NULL; Oid exprtype; /* * Scan to see if relation has a default for this column. */ if (rd_att->constr && rd_att->constr->num_defval > 0) { AttrDefault *defval = rd_att->constr->defval; int ndef = rd_att->constr->num_defval; while (--ndef >= 0) { if (attrno == defval[ndef].adnum) { /* * Found it, convert string representation to node tree. */ expr = stringToNode(defval[ndef].adbin); break; } } } if (expr == NULL) { /* * No per-column default, so look for a default for the type * itself. */ if (att_tup->attisset) { /* * Set attributes are represented as OIDs no matter what the * set element type is, and the element type's default is * irrelevant too. */ } else 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;}static Query *ApplyRetrieveRule(Query *parsetree, RewriteRule *rule, int rt_index, bool relation_level, Relation relation, bool relIsUsed, List *activeRIRs){ Query *rule_action; RangeTblEntry *rte, *subrte; if (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 recursively expand * any view references inside it. */ rule_action = copyObject(lfirst(rule->actions)); 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->checkForRead = rte->checkForRead; subrte->checkForWrite = rte->checkForWrite; subrte->checkAsUser = rte->checkAsUser; rte->checkForRead = false; /* no permission check on subquery itself */ rte->checkForWrite = false; rte->checkAsUser = InvalidOid; /* * FOR UPDATE of view? */ if (intMember(rt_index, parsetree->rowMarks)) { /* * Remove the view from the list of rels that will actually be * marked FOR UPDATE by the executor. It will still be access- * checked for write access, though. */ parsetree->rowMarks = lremovei(rt_index, parsetree->rowMarks); /* * Set up the view's referenced tables as if FOR UPDATE. */ markQueryForUpdate(rule_action, true); } return parsetree;}/* * Recursively mark all relations used by a view as FOR UPDATE. * * 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 transformForUpdate() routine. */static voidmarkQueryForUpdate(Query *qry, bool skipOldNew){ Index rti = 0; List *l; 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) { if (!intMember(rti, qry->rowMarks)) qry->rowMarks = lappendi(qry->rowMarks, rti); rte->checkForWrite = true; } else if (rte->rtekind == RTE_SUBQUERY) { /* FOR UPDATE of subquery is propagated to subquery's rels */ markQueryForUpdate(rte->subquery, 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 < length(parsetree->rtable)) { RangeTblEntry *rte; Relation rel; List *locks; RuleLock *rules; RewriteRule *rule; LOCKMODE lockmode; bool relIsUsed; 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. */ relIsUsed = rangeTableEntry_used((Node *) parsetree, rt_index, 0); if (!relIsUsed && rt_index != parsetree->resultRelation) continue; /* * This may well be the first access to the relation during the * current statement (it will be, if this Query was extracted from * a rule or somehow got here other than via the parser). * Therefore, 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 * RewriteQuery() already got the right lock on it, so we need no * additional lock. Otherwise, check to see if the relation is * accessed FOR UPDATE or not. */ if (rt_index == parsetree->resultRelation) lockmode = NoLock; else if (intMember(rt_index, parsetree->rowMarks)) lockmode = RowShareLock; else lockmode = AccessShareLock; rel = heap_open(rte->relid, lockmode); /* * 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) { List *newActiveRIRs; List *l; if (oidMember(RelationGetRelid(rel), activeRIRs)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("infinite recursion detected in rules for relation \"%s\"", RelationGetRelationName(rel)))); newActiveRIRs = lconso(RelationGetRelid(rel), activeRIRs); foreach(l, locks) { rule = lfirst(l); parsetree = ApplyRetrieveRule(parsetree, rule, rt_index, rule->attrno == -1, rel, relIsUsed, newActiveRIRs); } } heap_close(rel, NoLock);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?