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 + -
显示快捷键?