aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/optimizer/util/clauses.c537
-rw-r--r--src/backend/utils/cache/lsyscache.c39
-rw-r--r--src/include/optimizer/clauses.h3
-rw-r--r--src/include/utils/lsyscache.h4
4 files changed, 478 insertions, 105 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 14705fadd8e..a0d1b752bc4 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.114 2002/11/30 21:25:04 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.115 2002/12/01 21:05:14 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -19,18 +19,25 @@
#include "postgres.h"
+#include "catalog/pg_language.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
+#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
+#include "parser/analyze.h"
#include "parser/parsetree.h"
+#include "tcop/tcopprot.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
+#include "utils/memutils.h"
#include "utils/syscache.h"
@@ -44,6 +51,13 @@ typedef struct
List *groupClauses;
} check_subplans_for_ungrouped_vars_context;
+typedef struct
+{
+ int nargs;
+ List *args;
+ int *usecounts;
+} substitute_actual_parameters_context;
+
static bool contain_agg_clause_walker(Node *node, void *context);
static bool contain_distinct_agg_clause_walker(Node *node, void *context);
static bool pull_agg_clause_walker(Node *node, List **listptr);
@@ -54,8 +68,17 @@ static bool check_subplans_for_ungrouped_vars_walker(Node *node,
check_subplans_for_ungrouped_vars_context *context);
static bool contain_mutable_functions_walker(Node *node, void *context);
static bool contain_volatile_functions_walker(Node *node, void *context);
-static Node *eval_const_expressions_mutator(Node *node, void *context);
-static Expr *simplify_op_or_func(Expr *expr, List *args);
+static bool contain_nonstrict_functions_walker(Node *node, void *context);
+static Node *eval_const_expressions_mutator(Node *node, List *active_fns);
+static Expr *simplify_op_or_func(Expr *expr, List *args, bool allow_inline,
+ List *active_fns);
+static Expr *evaluate_op_or_func(Expr *expr, List *args, HeapTuple func_tuple);
+static Expr *inline_op_or_func(Expr *expr, List *args, HeapTuple func_tuple,
+ List *active_fns);
+static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
+ int *usecounts);
+static Node *substitute_actual_parameters_mutator(Node *node,
+ substitute_actual_parameters_context *context);
Expr *
@@ -169,7 +192,6 @@ get_rightop(Expr *clause)
* is_funcclause
*
* Returns t iff the clause is a function clause: (func { expr }).
- *
*/
bool
is_funcclause(Node *clause)
@@ -184,7 +206,6 @@ is_funcclause(Node *clause)
*
* Creates a function clause given the FUNC node and the functional
* arguments.
- *
*/
Expr *
make_funcclause(Func *func, List *funcargs)
@@ -206,7 +227,6 @@ make_funcclause(Func *func, List *funcargs)
* or_clause
*
* Returns t iff the clause is an 'or' clause: (OR { expr }).
- *
*/
bool
or_clause(Node *clause)
@@ -220,7 +240,6 @@ or_clause(Node *clause)
* make_orclause
*
* Creates an 'or' clause given a list of its subclauses.
- *
*/
Expr *
make_orclause(List *orclauses)
@@ -242,7 +261,6 @@ make_orclause(List *orclauses)
* not_clause
*
* Returns t iff this is a 'not' clause: (NOT expr).
- *
*/
bool
not_clause(Node *clause)
@@ -256,7 +274,6 @@ not_clause(Node *clause)
* make_notclause
*
* Create a 'not' clause given the expression to be negated.
- *
*/
Expr *
make_notclause(Expr *notclause)
@@ -274,7 +291,6 @@ make_notclause(Expr *notclause)
* get_notclausearg
*
* Retrieve the clause within a 'not' clause
- *
*/
Expr *
get_notclausearg(Expr *notclause)
@@ -291,7 +307,6 @@ get_notclausearg(Expr *notclause)
* and_clause
*
* Returns t iff its argument is an 'and' clause: (AND { expr }).
- *
*/
bool
and_clause(Node *clause)
@@ -849,6 +864,69 @@ contain_volatile_functions_walker(Node *node, void *context)
/*****************************************************************************
+ * Check clauses for nonstrict functions
+ *****************************************************************************/
+
+/*
+ * contain_nonstrict_functions
+ * Recursively search for nonstrict functions within a clause.
+ *
+ * Returns true if any nonstrict construct is found --- ie, anything that
+ * could produce non-NULL output with a NULL input.
+ *
+ * XXX we do not examine sublinks/subplans to see if they contain uses of
+ * nonstrict functions. It's not real clear if that is correct or not...
+ * for the current usage it does not matter, since inline_op_or_func()
+ * rejects cases with sublinks.
+ */
+bool
+contain_nonstrict_functions(Node *clause)
+{
+ return contain_nonstrict_functions_walker(clause, NULL);
+}
+
+static bool
+contain_nonstrict_functions_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Expr))
+ {
+ Expr *expr = (Expr *) node;
+
+ switch (expr->opType)
+ {
+ case OP_EXPR:
+ if (!op_strict(((Oper *) expr->oper)->opno))
+ return true;
+ break;
+ case DISTINCT_EXPR:
+ /* IS DISTINCT FROM is inherently non-strict */
+ return true;
+ case FUNC_EXPR:
+ if (!func_strict(((Func *) expr->oper)->funcid))
+ return true;
+ break;
+ case OR_EXPR:
+ case AND_EXPR:
+ /* OR, AND are inherently non-strict */
+ return true;
+ default:
+ break;
+ }
+ }
+ if (IsA(node, CaseExpr))
+ return true;
+ if (IsA(node, NullTest))
+ return true;
+ if (IsA(node, BooleanTest))
+ return true;
+ return expression_tree_walker(node, contain_nonstrict_functions_walker,
+ context);
+}
+
+
+/*****************************************************************************
* Check for "pseudo-constant" clauses
*****************************************************************************/
@@ -1063,11 +1141,10 @@ NumRelids(Node *clause)
return result;
}
-/*--------------------
+/*
* CommuteClause: commute a binary operator clause
*
* XXX the clause is destructively modified!
- *--------------------
*/
void
CommuteClause(Expr *clause)
@@ -1134,12 +1211,15 @@ CommuteClause(Expr *clause)
Node *
eval_const_expressions(Node *node)
{
- /* no context or special setup needed, so away we go... */
- return eval_const_expressions_mutator(node, NULL);
+ /*
+ * The context for the mutator is a list of SQL functions being
+ * recursively simplified, so we start with an empty list.
+ */
+ return eval_const_expressions_mutator(node, NIL);
}
static Node *
-eval_const_expressions_mutator(Node *node, void *context)
+eval_const_expressions_mutator(Node *node, List *active_fns)
{
if (node == NULL)
return NULL;
@@ -1157,7 +1237,7 @@ eval_const_expressions_mutator(Node *node, void *context)
*/
args = (List *) expression_tree_mutator((Node *) expr->args,
eval_const_expressions_mutator,
- (void *) context);
+ (void *) active_fns);
switch (expr->opType)
{
@@ -1168,7 +1248,8 @@ eval_const_expressions_mutator(Node *node, void *context)
* Code for op/func case is pretty bulky, so split it out
* as a separate function.
*/
- newexpr = simplify_op_or_func(expr, args);
+ newexpr = simplify_op_or_func(expr, args,
+ true, active_fns);
if (newexpr) /* successfully simplified it */
return (Node *) newexpr;
@@ -1212,7 +1293,9 @@ eval_const_expressions_mutator(Node *node, void *context)
return MAKEBOOLCONST(true, false);
/* otherwise try to evaluate the '=' operator */
- newexpr = simplify_op_or_func(expr, args);
+ /* (NOT okay to try to inline it, though!) */
+ newexpr = simplify_op_or_func(expr, args,
+ false, active_fns);
if (newexpr) /* successfully simplified it */
return (Node *) newexpr;
}
@@ -1383,7 +1466,7 @@ eval_const_expressions_mutator(Node *node, void *context)
RelabelType *relabel = (RelabelType *) node;
Node *arg;
- arg = eval_const_expressions_mutator(relabel->arg, context);
+ arg = eval_const_expressions_mutator(relabel->arg, active_fns);
/*
* If we find stacked RelabelTypes (eg, from foo :: int :: oid) we
@@ -1443,7 +1526,7 @@ eval_const_expressions_mutator(Node *node, void *context)
CaseWhen *casewhen = (CaseWhen *)
expression_tree_mutator((Node *) lfirst(arg),
eval_const_expressions_mutator,
- (void *) context);
+ (void *) active_fns);
Assert(IsA(casewhen, CaseWhen));
if (casewhen->expr == NULL ||
@@ -1473,7 +1556,7 @@ eval_const_expressions_mutator(Node *node, void *context)
/* Simplify the default result */
defresult = eval_const_expressions_mutator(caseexpr->defresult,
- context);
+ active_fns);
/*
* If no non-FALSE alternatives, CASE reduces to the default
@@ -1498,78 +1581,39 @@ eval_const_expressions_mutator(Node *node, void *context)
* simplify constant expressions in its subscripts.
*/
return expression_tree_mutator(node, eval_const_expressions_mutator,
- (void *) context);
+ (void *) active_fns);
}
/*
- * Subroutine for eval_const_expressions: try to evaluate an op or func
+ * Subroutine for eval_const_expressions: try to simplify an op or func
+ *
+ * Inputs are the op or func Expr node, and the pre-simplified argument list;
+ * also a list of already-active inline function expansions.
*
- * Inputs are the op or func Expr node, and the pre-simplified argument list.
* Returns a simplified expression if successful, or NULL if cannot
* simplify the op/func.
- *
- * XXX Possible future improvement: if the func is SQL-language, and its
- * definition is simply "SELECT expression", we could parse and substitute
- * the expression here. This would avoid much runtime overhead, and perhaps
- * expose opportunities for constant-folding within the expression even if
- * not all the func's input args are constants. It'd be appropriate to do
- * that here, not in the parser, since we wouldn't want it to happen until
- * after rule substitution/rewriting.
*/
static Expr *
-simplify_op_or_func(Expr *expr, List *args)
+simplify_op_or_func(Expr *expr, List *args, bool allow_inline,
+ List *active_fns)
{
- List *arg;
Oid funcid;
- Oid result_typeid;
HeapTuple func_tuple;
- Form_pg_proc funcform;
- char provolatile;
- bool proisstrict;
- bool proretset;
- int16 resultTypLen;
- bool resultTypByVal;
Expr *newexpr;
- ExprContext *econtext;
- Datum const_val;
- bool has_nonconst_input = false;
- bool has_null_input = false;
- bool const_is_null;
/*
- * Check for constant inputs and especially constant-NULL inputs.
- */
- foreach(arg, args)
- {
- if (IsA(lfirst(arg), Const))
- has_null_input |= ((Const *) lfirst(arg))->constisnull;
- else
- has_nonconst_input = true;
- }
-
- /*
- * If the function is strict and has a constant-NULL input, it will
- * never be called at all, so we can replace the call by a NULL
- * constant even if there are other inputs that aren't constant.
- * Otherwise, we can only simplify if all inputs are constants. We can
- * skip the function lookup if neither case applies.
- */
- if (has_nonconst_input && !has_null_input)
- return NULL;
-
- /*
- * Get the function procedure's OID and look to see whether it is
- * marked immutable.
- *
- * Note we take the result type from the Oper or Func node, not the
- * pg_proc tuple; probably necessary for binary-compatibility cases.
+ * We have two strategies for simplification: either execute the function
+ * to deliver a constant result, or expand in-line the body of the
+ * function definition (which only works for simple SQL-language
+ * functions, but that is a common case). In either case we need access
+ * to the function's pg_proc tuple, so fetch it just once to use in both
+ * attempts.
*/
if (expr->opType == FUNC_EXPR)
{
Func *func = (Func *) expr->oper;
funcid = func->funcid;
- result_typeid = func->funcresulttype;
}
else /* OP_EXPR or DISTINCT_EXPR */
{
@@ -1577,52 +1621,82 @@ simplify_op_or_func(Expr *expr, List *args)
replace_opid(oper); /* OK to scribble on input to this extent */
funcid = oper->opid;
- result_typeid = oper->opresulttype;
}
- /*
- * we could use func_volatile() here, but we need several fields out
- * of the func tuple, so might as well just look it up once.
- */
func_tuple = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcid),
0, 0, 0);
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "Function OID %u does not exist", funcid);
- funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
- provolatile = funcform->provolatile;
- proisstrict = funcform->proisstrict;
- proretset = funcform->proretset;
+
+ newexpr = evaluate_op_or_func(expr, args, func_tuple);
+
+ if (!newexpr && allow_inline)
+ newexpr = inline_op_or_func(expr, args, func_tuple, active_fns);
+
ReleaseSysCache(func_tuple);
- if (provolatile != PROVOLATILE_IMMUTABLE)
- return NULL;
+ return newexpr;
+}
+
+/*
+ * evaluate_op_or_func: try to pre-evaluate an op or func
+ *
+ * We can do this if the function is strict and has any constant-null inputs
+ * (just return a null constant), or if the function is immutable and has all
+ * constant inputs (call it and return the result as a Const node).
+ *
+ * Returns a simplified expression if successful, or NULL if cannot
+ * simplify the op/func.
+ */
+static Expr *
+evaluate_op_or_func(Expr *expr, List *args, HeapTuple func_tuple)
+{
+ Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+ Oid result_typeid = funcform->prorettype;
+ int16 resultTypLen;
+ bool resultTypByVal;
+ bool has_nonconst_input = false;
+ bool has_null_input = false;
+ Expr *newexpr;
+ ExprContext *econtext;
+ Datum const_val;
+ bool const_is_null;
+ List *arg;
/*
- * Also check to make sure it doesn't return a set.
+ * Can't simplify if it returns a set.
*/
- if (proretset)
+ if (funcform->proretset)
return NULL;
/*
- * Now that we know if the function is strict, we can finish the
- * checks for simplifiable inputs that we started above.
+ * Check for constant inputs and especially constant-NULL inputs.
*/
- if (proisstrict && has_null_input)
+ foreach(arg, args)
{
- /*
- * It's strict and has NULL input, so must produce NULL output.
- * Return a NULL constant of the right type.
- */
- return (Expr *) makeNullConst(result_typeid);
+ if (IsA(lfirst(arg), Const))
+ has_null_input |= ((Const *) lfirst(arg))->constisnull;
+ else
+ has_nonconst_input = true;
}
/*
- * Otherwise, can simplify only if all inputs are constants. (For a
- * non-strict function, constant NULL inputs are treated the same as
- * constant non-NULL inputs.)
+ * If the function is strict and has a constant-NULL input, it will
+ * never be called at all, so we can replace the call by a NULL
+ * constant, even if there are other inputs that aren't constant,
+ * and even if the function is not otherwise immutable.
*/
- if (has_nonconst_input)
+ if (funcform->proisstrict && has_null_input)
+ return (Expr *) makeNullConst(result_typeid);
+
+ /*
+ * Otherwise, can simplify only if the function is immutable and
+ * all inputs are constants. (For a non-strict function, constant NULL
+ * inputs are treated the same as constant non-NULL inputs.)
+ */
+ if (funcform->provolatile != PROVOLATILE_IMMUTABLE ||
+ has_nonconst_input)
return NULL;
/*
@@ -1631,9 +1705,9 @@ simplify_op_or_func(Expr *expr, List *args)
* We use the executor's routine ExecEvalExpr() to avoid duplication of
* code and ensure we get the same result as the executor would get.
*
- * Build a new Expr node containing the already-simplified arguments. The
- * only other setup needed here is the replace_opid() that we already
- * did for the OP_EXPR/DISTINCT_EXPR case.
+ * Build a new Expr node containing the already-simplified arguments.
+ * The only other setup needed here is the replace_opid() that
+ * simplify_op_or_func already did for the OP_EXPR/DISTINCT_EXPR case.
*/
newexpr = makeNode(Expr);
newexpr->typeOid = expr->typeOid;
@@ -1670,6 +1744,265 @@ simplify_op_or_func(Expr *expr, List *args)
resultTypByVal);
}
+/*
+ * inline_op_or_func: try to expand inline an op or func
+ *
+ * If the function is a sufficiently simple SQL-language function
+ * (just "SELECT expression"), then we can inline it and avoid the rather
+ * high per-call overhead of SQL functions. Furthermore, this can expose
+ * opportunities for constant-folding within the function expression.
+ *
+ * We have to beware of some special cases however. A directly or
+ * indirectly recursive function would cause us to recurse forever,
+ * so we keep track of which functions we are already expanding and
+ * do not re-expand them. Also, if a parameter is used more than once
+ * in the SQL-function body, we require it not to contain any volatile
+ * functions or sublinks --- volatiles might deliver inconsistent answers,
+ * and subplans might be unreasonably expensive to evaluate multiple times.
+ * We must also beware of changing the volatility or strictness status of
+ * functions by inlining them.
+ *
+ * Returns a simplified expression if successful, or NULL if cannot
+ * simplify the op/func.
+ */
+static Expr *
+inline_op_or_func(Expr *expr, List *args, HeapTuple func_tuple,
+ List *active_fns)
+{
+ Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+ Oid funcid = HeapTupleGetOid(func_tuple);
+ Oid result_typeid = funcform->prorettype;
+ char result_typtype;
+ char *src;
+ Datum tmp;
+ bool isNull;
+ MemoryContext oldcxt;
+ MemoryContext mycxt;
+ StringInfoData stri;
+ List *raw_parsetree_list;
+ List *querytree_list;
+ Query *querytree;
+ Node *newexpr;
+ int *usecounts;
+ List *arg;
+ int i;
+
+ /*
+ * Forget it if the function is not SQL-language or has other
+ * showstopper properties. (The nargs check is just paranoia.)
+ */
+ if (funcform->prolang != SQLlanguageId ||
+ funcform->prosecdef ||
+ funcform->proretset ||
+ funcform->pronargs != length(args))
+ return NULL;
+
+ /* Forget it if return type is tuple or void */
+ result_typtype = get_typtype(result_typeid);
+ if (result_typtype != 'b' &&
+ result_typtype != 'd')
+ return NULL;
+
+ /* Check for recursive function, and give up trying to expand if so */
+ if (intMember(funcid, active_fns))
+ return NULL;
+
+ /* Check permission to call function (fail later, if not) */
+ if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
+ return NULL;
+
+ /*
+ * Make a temporary memory context, so that we don't leak all the
+ * stuff that parsing might create.
+ */
+ mycxt = AllocSetContextCreate(CurrentMemoryContext,
+ "inline_op_or_func",
+ 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, "inline_op_or_func: null prosrc for procedure %u",
+ funcid);
+ src = DatumGetCString(DirectFunctionCall1(textout, tmp));
+
+ /*
+ * We just do parsing and parse analysis, not rewriting, because
+ * rewriting will not affect 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.
+ */
+ initStringInfo(&stri);
+ appendStringInfo(&stri, "%s", src);
+
+ raw_parsetree_list = pg_parse_query(&stri,
+ funcform->proargtypes,
+ funcform->pronargs);
+ if (length(raw_parsetree_list) != 1)
+ goto fail;
+
+ querytree_list = parse_analyze(lfirst(raw_parsetree_list), NULL);
+
+ 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->isPortal ||
+ 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 = ((TargetEntry *) lfirst(querytree->targetList))->expr;
+
+ /*
+ * 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 volatile or expensive */
+ if (contain_volatile_functions(param) ||
+ contain_subplans(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,
+ lconsi(funcid, active_fns));
+
+ return (Expr *) newexpr;
+
+ /* Here if func is not inlinable: release temp memory and return NULL */
+fail:
+ MemoryContextSwitchTo(oldcxt);
+ MemoryContextDelete(mycxt);
+
+ 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, "substitute_actual_parameters_mutator: unexpected paramkind");
+ if (param->paramid <= 0 || param->paramid > context->nargs)
+ elog(ERROR, "substitute_actual_parameters_mutator: unexpected 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);
+}
+
/*
* Standard expression-tree walking support
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index cd7dc8d549f..ec0c45ea78a 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.86 2002/11/25 21:29:42 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.87 2002/12/01 21:05:14 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -416,6 +416,22 @@ op_hashjoinable(Oid opno, Oid ltype, Oid rtype)
}
/*
+ * op_strict
+ *
+ * Get the proisstrict flag for the operator's underlying function.
+ */
+bool
+op_strict(Oid opno)
+{
+ RegProcedure funcid = get_opcode(opno);
+
+ if (funcid == (RegProcedure) InvalidOid)
+ elog(ERROR, "Operator OID %u does not exist", opno);
+
+ return func_strict((Oid) funcid);
+}
+
+/*
* op_volatile
*
* Get the provolatile flag for the operator's underlying function.
@@ -607,6 +623,27 @@ get_func_retset(Oid funcid)
}
/*
+ * func_strict
+ * Given procedure id, return the function's proisstrict flag.
+ */
+bool
+func_strict(Oid funcid)
+{
+ HeapTuple tp;
+ bool result;
+
+ tp = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(funcid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "Function OID %u does not exist", funcid);
+
+ result = ((Form_pg_proc) GETSTRUCT(tp))->proisstrict;
+ ReleaseSysCache(tp);
+ return result;
+}
+
+/*
* func_volatile
* Given procedure id, return the function's provolatile flag.
*/
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 1cf8fbaf839..da0fe4c5102 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: clauses.h,v 1.55 2002/11/06 22:31:24 tgl Exp $
+ * $Id: clauses.h,v 1.56 2002/12/01 21:05:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -51,6 +51,7 @@ extern void check_subplans_for_ungrouped_vars(Query *query);
extern bool contain_mutable_functions(Node *clause);
extern bool contain_volatile_functions(Node *clause);
+extern bool contain_nonstrict_functions(Node *clause);
extern bool is_pseudo_constant_clause(Node *clause);
extern List *pull_constant_clauses(List *quals, List **constantQual);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index ff88d30e73d..a0d3574b246 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: lsyscache.h,v 1.64 2002/09/19 23:40:56 tgl Exp $
+ * $Id: lsyscache.h,v 1.65 2002/12/01 21:05:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -31,6 +31,7 @@ extern bool op_mergejoinable(Oid opno, Oid ltype, Oid rtype,
extern void op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop,
RegProcedure *ltproc, RegProcedure *gtproc);
extern Oid op_hashjoinable(Oid opno, Oid ltype, Oid rtype);
+extern bool op_strict(Oid opno);
extern char op_volatile(Oid opno);
extern Oid get_commutator(Oid opno);
extern Oid get_negator(Oid opno);
@@ -39,6 +40,7 @@ extern RegProcedure get_oprjoin(Oid opno);
extern char *get_func_name(Oid funcid);
extern Oid get_func_rettype(Oid funcid);
extern bool get_func_retset(Oid funcid);
+extern bool func_strict(Oid funcid);
extern char func_volatile(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
extern Oid get_system_catalog_relid(const char *catname);