diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 14705fadd8..a0d1b752bc 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)
@@ -848,6 +863,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,43 +1581,94 @@ 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;
+	Expr	   *newexpr;
+
+	/*
+	 * 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;
+	}
+	else						/* OP_EXPR or DISTINCT_EXPR */
+	{
+		Oper	   *oper = (Oper *) expr->oper;
+
+		replace_opid(oper);		/* OK to scribble on input to this extent */
+		funcid = oper->opid;
+	}
+
+	func_tuple = SearchSysCache(PROCOID,
+								ObjectIdGetDatum(funcid),
+								0, 0, 0);
+	if (!HeapTupleIsValid(func_tuple))
+		elog(ERROR, "Function OID %u does not exist", funcid);
+
+	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);
+
+	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		has_nonconst_input = false;
-	bool		has_null_input = false;
 	bool		const_is_null;
+	List	   *arg;
+
+	/*
+	 * Can't simplify if it returns a set.
+	 */
+	if (funcform->proretset)
+		return NULL;
 
 	/*
 	 * Check for constant inputs and especially constant-NULL inputs.
@@ -1550,79 +1684,19 @@ simplify_op_or_func(Expr *expr, List *args)
 	/*
 	 * 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.
+	 * constant, even if there are other inputs that aren't constant,
+	 * and even if the function is not otherwise immutable.
 	 */
-	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.
-	 */
-	if (expr->opType == FUNC_EXPR)
-	{
-		Func	   *func = (Func *) expr->oper;
-
-		funcid = func->funcid;
-		result_typeid = func->funcresulttype;
-	}
-	else						/* OP_EXPR or DISTINCT_EXPR */
-	{
-		Oper	   *oper = (Oper *) expr->oper;
-
-		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;
-	ReleaseSysCache(func_tuple);
-
-	if (provolatile != PROVOLATILE_IMMUTABLE)
-		return NULL;
-
-	/*
-	 * Also check to make sure it doesn't return a set.
-	 */
-	if (proretset)
-		return NULL;
-
-	/*
-	 * Now that we know if the function is strict, we can finish the
-	 * checks for simplifiable inputs that we started above.
-	 */
-	if (proisstrict && has_null_input)
-	{
-		/*
-		 * It's strict and has NULL input, so must produce NULL output.
-		 * Return a NULL constant of the right type.
-		 */
+	if (funcform->proisstrict && has_null_input)
 		return (Expr *) makeNullConst(result_typeid);
-	}
 
 	/*
-	 * 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.)
+	 * 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 (has_nonconst_input)
+	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 cd7dc8d549..ec0c45ea78 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.
@@ -415,6 +415,22 @@ op_hashjoinable(Oid opno, Oid ltype, Oid rtype)
 	return result;
 }
 
+/*
+ * 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
  *
@@ -606,6 +622,27 @@ get_func_retset(Oid funcid)
 	return result;
 }
 
+/*
+ * 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 1cf8fbaf83..da0fe4c510 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 ff88d30e73..a0d3574b24 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);