clauses.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,256 行 · 第 1/5 页
C
2,256 行
} /* * Setup error traceback support for ereport(). This is so that we * can finger the function that bad information came from. */ sqlerrcontext.callback = sql_inline_error_callback; sqlerrcontext.arg = funcform; sqlerrcontext.previous = error_context_stack; error_context_stack = &sqlerrcontext; /* * Make a temporary memory context, so that we don't leak all the * stuff that parsing might create. */ mycxt = AllocSetContextCreate(CurrentMemoryContext, "inline_function", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcxt = MemoryContextSwitchTo(mycxt); /* Fetch and parse the function body */ tmp = SysCacheGetAttr(PROCOID, func_tuple, Anum_pg_proc_prosrc, &isNull); if (isNull) elog(ERROR, "null prosrc for function %u", funcid); src = DatumGetCString(DirectFunctionCall1(textout, tmp)); /* * We just do parsing and parse analysis, not rewriting, because * rewriting will not affect table-free-SELECT-only queries, which is * all that we care about. Also, we can punt as soon as we detect * more than one command in the function body. */ raw_parsetree_list = pg_parse_query(src); if (length(raw_parsetree_list) != 1) goto fail; querytree_list = parse_analyze(lfirst(raw_parsetree_list), argtypes, funcform->pronargs); if (length(querytree_list) != 1) goto fail; querytree = (Query *) lfirst(querytree_list); /* * The single command must be a simple "SELECT expression". */ if (!IsA(querytree, Query) || querytree->commandType != CMD_SELECT || querytree->resultRelation != 0 || querytree->into || querytree->hasAggs || querytree->hasSubLinks || querytree->rtable || querytree->jointree->fromlist || querytree->jointree->quals || querytree->groupClause || querytree->havingQual || querytree->distinctClause || querytree->sortClause || querytree->limitOffset || querytree->limitCount || querytree->setOperations || length(querytree->targetList) != 1) goto fail; newexpr = (Node *) ((TargetEntry *) lfirst(querytree->targetList))->expr; /* * If the function has any arguments declared as polymorphic types, * then it wasn't type-checked at definition time; must do so now. * (This will raise an error if wrong, but that's okay since the * function would fail at runtime anyway. Note we do not try this * until we have verified that no rewriting was needed; that's * probably not important, but let's be careful.) */ if (polymorphic) check_sql_fn_retval(result_type, get_typtype(result_type), querytree_list); /* * Additional validity checks on the expression. It mustn't return a * set, and it mustn't be more volatile than the surrounding function * (this is to avoid breaking hacks that involve pretending a function * is immutable when it really ain't). If the surrounding function is * declared strict, then the expression must contain only strict * constructs and must use all of the function parameters (this is * overkill, but an exact analysis is hard). */ if (expression_returns_set(newexpr)) goto fail; if (funcform->provolatile == PROVOLATILE_IMMUTABLE && contain_mutable_functions(newexpr)) goto fail; else if (funcform->provolatile == PROVOLATILE_STABLE && contain_volatile_functions(newexpr)) goto fail; if (funcform->proisstrict && contain_nonstrict_functions(newexpr)) goto fail; /* * We may be able to do it; there are still checks on parameter usage * to make, but those are most easily done in combination with the * actual substitution of the inputs. So start building expression * with inputs substituted. */ usecounts = (int *) palloc0((funcform->pronargs + 1) * sizeof(int)); newexpr = substitute_actual_parameters(newexpr, funcform->pronargs, args, usecounts); /* Now check for parameter usage */ i = 0; foreach(arg, args) { Node *param = lfirst(arg); if (usecounts[i] == 0) { /* Param not used at all: uncool if func is strict */ if (funcform->proisstrict) goto fail; } else if (usecounts[i] != 1) { /* Param used multiple times: uncool if expensive or volatile */ QualCost eval_cost; /* * We define "expensive" as "contains any subplan or more than * 10 operators". Note that the subplan search has to be done * explicitly, since cost_qual_eval() will barf on unplanned * subselects. */ if (contain_subplans(param)) goto fail; cost_qual_eval(&eval_cost, makeList1(param)); if (eval_cost.startup + eval_cost.per_tuple > 10 * cpu_operator_cost) goto fail; /* * Check volatility last since this is more expensive than the * above tests */ if (contain_volatile_functions(param)) goto fail; } i++; } /* * Whew --- we can make the substitution. Copy the modified * expression out of the temporary memory context, and clean up. */ MemoryContextSwitchTo(oldcxt); newexpr = copyObject(newexpr); MemoryContextDelete(mycxt); /* * Recursively try to simplify the modified expression. Here we must * add the current function to the context list of active functions. */ newexpr = eval_const_expressions_mutator(newexpr, lconso(funcid, active_fns)); error_context_stack = sqlerrcontext.previous; return (Expr *) newexpr; /* Here if func is not inlinable: release temp memory and return NULL */fail: MemoryContextSwitchTo(oldcxt); MemoryContextDelete(mycxt); error_context_stack = sqlerrcontext.previous; return NULL;}/* * Replace Param nodes by appropriate actual parameters */static Node *substitute_actual_parameters(Node *expr, int nargs, List *args, int *usecounts){ substitute_actual_parameters_context context; context.nargs = nargs; context.args = args; context.usecounts = usecounts; return substitute_actual_parameters_mutator(expr, &context);}static Node *substitute_actual_parameters_mutator(Node *node, substitute_actual_parameters_context *context){ if (node == NULL) return NULL; if (IsA(node, Param)) { Param *param = (Param *) node; if (param->paramkind != PARAM_NUM) elog(ERROR, "unexpected paramkind: %d", param->paramkind); if (param->paramid <= 0 || param->paramid > context->nargs) elog(ERROR, "invalid paramid: %d", param->paramid); /* Count usage of parameter */ context->usecounts[param->paramid - 1]++; /* Select the appropriate actual arg and replace the Param with it */ /* We don't need to copy at this time (it'll get done later) */ return nth(param->paramid - 1, context->args); } return expression_tree_mutator(node, substitute_actual_parameters_mutator, (void *) context);}/* * error context callback to let us supply a call-stack traceback */static voidsql_inline_error_callback(void *arg){ Form_pg_proc funcform = (Form_pg_proc) arg; errcontext("SQL function \"%s\" during inlining", NameStr(funcform->proname));}/* * evaluate_expr: pre-evaluate a constant expression * * We use the executor's routine ExecEvalExpr() to avoid duplication of * code and ensure we get the same result as the executor would get. */static Expr *evaluate_expr(Expr *expr, Oid result_type){ EState *estate; ExprState *exprstate; MemoryContext oldcontext; Datum const_val; bool const_is_null; int16 resultTypLen; bool resultTypByVal; /* * To use the executor, we need an EState. */ estate = CreateExecutorState(); /* We can use the estate's working context to avoid memory leaks. */ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); /* * Prepare expr for execution. */ exprstate = ExecPrepareExpr(expr, estate); /* * And evaluate it. * * It is OK to use a default econtext because none of the ExecEvalExpr() * code used in this situation will use econtext. That might seem * fortuitous, but it's not so unreasonable --- a constant expression * does not depend on context, by definition, n'est ce pas? */ const_val = ExecEvalExprSwitchContext(exprstate, GetPerTupleExprContext(estate), &const_is_null, NULL); /* Get info needed about result datatype */ get_typlenbyval(result_type, &resultTypLen, &resultTypByVal); /* Get back to outer memory context */ MemoryContextSwitchTo(oldcontext); /* Must copy result out of sub-context used by expression eval */ if (!const_is_null) const_val = datumCopy(const_val, resultTypByVal, resultTypLen); /* Release all the junk we just created */ FreeExecutorState(estate); /* * Make the constant result node. */ return (Expr *) makeConst(result_type, resultTypLen, const_val, const_is_null, resultTypByVal);}/* * Standard expression-tree walking support * * We used to have near-duplicate code in many different routines that * understood how to recurse through an expression node tree. That was * a pain to maintain, and we frequently had bugs due to some particular * routine neglecting to support a particular node type. In most cases, * these routines only actually care about certain node types, and don't * care about other types except insofar as they have to recurse through * non-primitive node types. Therefore, we now provide generic tree-walking * logic to consolidate the redundant "boilerplate" code. There are * two versions: expression_tree_walker() and expression_tree_mutator(). *//*-------------------- * expression_tree_walker() is designed to support routines that traverse * a tree in a read-only fashion (although it will also work for routines * that modify nodes in-place but never add/delete/replace nodes). * A walker routine should look like this: * * bool my_walker (Node *node, my_struct *context) * { * if (node == NULL) * return false; * // check for nodes that special work is required for, eg: * if (IsA(node, Var)) * { * ... do special actions for Var nodes * } * else if (IsA(node, ...)) * { * ... do special actions for other node types * } * // for any node type not specially processed, do: * return expression_tree_walker(node, my_walker, (void *) context); * } * * The "context" argument points to a struct that holds whatever context * information the walker routine needs --- it can be used to return data * gathered by the walker, too. This argument is not touched by * expression_tree_walker, but it is passed down to recursive sub-invocations * of my_walker. The tree walk is started from a setup routine that * fills in the appropriate context struct, calls my_walker with the top-level * node of the tree, and then examines the results. * * The walker routine should return "false" to continue the tree walk, or * "true" to abort the walk and immediately return "true" to the top-level * caller. This can be used to short-circuit the traversal if the walker * has found what it came for. "false" is returned to the top-level caller * iff no invocation of the walker returned "true". * * The node types handled by expression_tree_walker include all those * normally found in target lists and qualifier clauses during the planning * stage. In particular, it handles List nodes since a cnf-ified qual clause * will have List structure at the top level, and it handles TargetEntry nodes * so that a scan of a target list can be handled without additional code. * (But only the "expr" part of a TargetEntry is examined, unless the walker * chooses to process TargetEntry nodes specially.) Also, RangeTblRef, * FromExpr, JoinExpr, and SetOperationStmt nodes are handled, so that query * jointrees and setOperation trees can be processed without additional code. * * expression_tree_walker will handle SubLink nodes by recursing normally into * the "lefthand" arguments (which are expressions belonging to the outer * plan). It will also call the walker on the sub-Query node; however, when * expression_tree_walker itself is called on a Query node, it does nothing * and returns "false". The net effect is that unless the walker does * something special at a Query node, sub-selects will not be visited during * an expression tree walk. This is exactly the behavior wanted in many cases * --- and for those walkers that do want to recurse into sub-selects, special * behavior is typically needed anyway at the entry to a sub-select (such as * incrementing a depth counter). A walker that wants to examine sub-selects * should include code along the lines of: * * if (IsA(node, Query)) * { * adjust context for subquery; * result = query_tree_walker((Query *) node, my_walker, context, * 0); // adjust flags as needed * restore context if needed; * return result; * } * * query_tree_walker is a convenience routine (see below) that calls the * walker on all the expression subtrees of the given Query node. * * expression_tree_walker will handle SubPlan nodes by recursing normally * into the "exprs" and "args" lists (which are expressions belonging to * the outer plan). It will not touch the completed subplan, however. Since * there is no link to the original Query, it is not possible to recurse into * subselects of an already-planned expression tree. This is OK for current * uses, but may need to be revisited in future. *-------------------- */boolexpression_tree_walker(Node *node, bool (*walker) (), void *context){ List *temp; /* * The walker has already visited the current node, and so we need * only recurse into any sub-nodes it has. * * We assume that the walker is not interested in List nodes per se, so * when we expect a List we just recurse directly to self without * bothering to call the walker. */ if (node == NULL) return false; switch (nodeTag(node)) { case T_Var: case T_Const: case T_Param: case T_CoerceToDomainValue: case T_SetToDefault: case T_RangeTblRef: /* primitive node types with no subnodes */ break; case T_Aggref: return walker(((Aggref *) node)->target, context); case T_ArrayRef: { ArrayRef *aref = (ArrayRef *) node; /* recurse directly for upper/lower array index lists */ if (expression_tree_walker((Node *) aref->refupperindexpr, walker, context)) return true; if (expression_tree_walker((Node *) aref->reflowerindexpr, walker, context)) return true; /* walker must see the refexpr and refassgnexpr, however */ if (walker(aref->refexpr, context)) return true; if (walker(aref->refassgnexpr, context)) return true; } break; case T_FuncExp
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?