diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index f660a57da237d7e59883f6cee31f1e0e417f119e..1bd0ef2cd13a1e373f416aaec2a6005fed06291d 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.129 2009/01/01 17:23:40 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.130 2009/01/09 15:46:10 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -50,7 +50,7 @@
 #include "executor/executor.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
-#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/var.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
@@ -2390,8 +2390,8 @@ GetDomainConstraints(Oid typeOid)
 
 			check_expr = (Expr *) stringToNode(TextDatumGetCString(val));
 
-			/* ExecInitExpr assumes we already fixed opfuncids */
-			fix_opfuncids((Node *) check_expr);
+			/* ExecInitExpr assumes we've planned the expression */
+			check_expr = expression_planner(check_expr);
 
 			r = makeNode(DomainConstraintState);
 			r->constrainttype = DOM_CONSTRAINT_CHECK;
diff --git a/src/backend/executor/README b/src/backend/executor/README
index 7644cc2cc42fda7f2d2e8225634fbd02b3299dbc..467d6272d1e334300002fd4c8507321f38066696 100644
--- a/src/backend/executor/README
+++ b/src/backend/executor/README
@@ -1,4 +1,4 @@
-$PostgreSQL: pgsql/src/backend/executor/README,v 1.7 2008/03/21 13:23:28 momjian Exp $
+$PostgreSQL: pgsql/src/backend/executor/README,v 1.8 2009/01/09 15:46:10 tgl Exp $
 
 The Postgres Executor
 =====================
@@ -124,7 +124,8 @@ be hidden inside function calls).  This case has a flow of control like
 		creates per-tuple context
 
 	ExecPrepareExpr
-		switch to per-query context to run ExecInitExpr
+		temporarily switch to per-query context
+		run the expression through expression_planner
 		ExecInitExpr
 
 	Repeatedly do:
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index ed53a62e383a4f2ae4910a9c404d0a3f35710172..f74a5da6b281126d5a3dc9fa44af3b198b719fc2 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.240 2009/01/01 17:23:41 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.241 2009/01/09 15:46:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,7 +45,7 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
-#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "pgstat.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
@@ -4794,10 +4794,11 @@ ExecInitExpr(Expr *node, PlanState *parent)
  * Plan tree context.
  *
  * This differs from ExecInitExpr in that we don't assume the caller is
- * already running in the EState's per-query context.  Also, we apply
- * fix_opfuncids() to the passed expression tree to be sure it is ready
- * to run.	(In ordinary Plan trees the planner will have fixed opfuncids,
- * but callers outside the executor will not have done this.)
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
  */
 ExprState *
 ExecPrepareExpr(Expr *node, EState *estate)
@@ -4805,10 +4806,10 @@ ExecPrepareExpr(Expr *node, EState *estate)
 	ExprState  *result;
 	MemoryContext oldcontext;
 
-	fix_opfuncids((Node *) node);
-
 	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
 
+	node = expression_planner(node);
+
 	result = ExecInitExpr(node, NULL);
 
 	MemoryContextSwitchTo(oldcontext);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 841d85f7397da28f478290fdaae80de563a89c53..34747d0f97119303ceef7b3ed565f41a2576886a 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.250 2009/01/01 17:23:44 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.251 2009/01/09 15:46:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2576,3 +2576,39 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
 			elog(ERROR, "failed to deconstruct sort operators into partitioning/ordering operators");
 	}
 }
+
+
+/*
+ * expression_planner
+ *		Perform planner's transformations on a standalone expression.
+ *
+ * Various utility commands need to evaluate expressions that are not part
+ * of a plannable query.  They can do so using the executor's regular
+ * expression-execution machinery, but first the expression has to be fed
+ * through here to transform it from parser output to something executable.
+ *
+ * Currently, we disallow sublinks in standalone expressions, so there's no
+ * real "planning" involved here.  (That might not always be true though.)
+ * What we must do is run eval_const_expressions to ensure that any function
+ * default arguments get inserted.  The fact that constant subexpressions
+ * get simplified is a side-effect that is useful when the expression will
+ * get evaluated more than once.  Also, we must fix operator function IDs.
+ *
+ * Note: this must not make any damaging changes to the passed-in expression
+ * tree.  (It would actually be okay to apply fix_opfuncids to it, but since
+ * we first do an expression_tree_mutator-based walk, what is returned will
+ * be a new node tree.)
+ */
+Expr *
+expression_planner(Expr *expr)
+{
+	Node	   *result;
+
+	/* Insert default arguments and simplify constant subexprs */
+	result = eval_const_expressions(NULL, (Node *) expr);
+
+	/* Fill in opfuncid values if missing */
+	fix_opfuncids(result);
+
+	return (Expr *) result;
+}
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a0205d7b8c60f0aaf4bb30c6c0ef82c832da52f1..65c9b61458460954b0b6a0bfbc68349fa7a5e0cb 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.274 2009/01/06 01:23:21 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.275 2009/01/09 15:46:10 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -2018,6 +2018,9 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
  *
  * NOTE: the planner assumes that this will always flatten nested AND and
  * OR clauses into N-argument form.  See comments in prepqual.c.
+ *
+ * NOTE: another critical effect is that any function calls that require
+ * default arguments will be expanded.
  *--------------------
  */
 Node *
@@ -3854,10 +3857,14 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod)
 	/* We can use the estate's working context to avoid memory leaks. */
 	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
 
+	/* Make sure any opfuncids are filled in. */
+	fix_opfuncids((Node *) expr);
+
 	/*
-	 * Prepare expr for execution.
+	 * Prepare expr for execution.  (Note: we can't use ExecPrepareExpr
+	 * because it'd result in recursively invoking eval_const_expressions.)
 	 */
-	exprstate = ExecPrepareExpr(expr, estate);
+	exprstate = ExecInitExpr(expr, NULL);
 
 	/*
 	 * And evaluate it.
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
index 4bbcb2fe229cbeeaccb2ac4da4faa75cea9f8eaa..678f560978da3cb8a8abe57876cfcd798051a544 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/predtest.c,v 1.23 2009/01/01 17:23:45 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/predtest.c,v 1.24 2009/01/09 15:46:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,7 @@
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
+#include "optimizer/planmain.h"
 #include "optimizer/predtest.h"
 #include "utils/array.h"
 #include "utils/inval.h"
@@ -1405,8 +1406,11 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
 							  (Expr *) pred_const,
 							  (Expr *) clause_const);
 
+	/* Fill in opfuncids */
+	fix_opfuncids((Node *) test_expr);
+
 	/* Prepare it for execution */
-	test_exprstate = ExecPrepareExpr(test_expr, estate);
+	test_exprstate = ExecInitExpr(test_expr, NULL);
 
 	/* And execute it. */
 	test_result = ExecEvalExprSwitchContext(test_exprstate,
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index a00bc4b1cf7017d1d8b2e8c0c62841e64e4dc641..ec43663b292e321b0a63647a2f0f8ada83dbbf3a 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.46 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.47 2009/01/09 15:46:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,9 +29,12 @@ extern PlannedStmt *planner(Query *parse, int cursorOptions,
 		ParamListInfo boundParams);
 extern PlannedStmt *standard_planner(Query *parse, int cursorOptions,
 				 ParamListInfo boundParams);
+
 extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse,
 				 PlannerInfo *parent_root,
 				 bool hasRecursion, double tuple_fraction,
 				 PlannerInfo **subroot);
 
+extern Expr *expression_planner(Expr *expr);
+
 #endif   /* PLANNER_H */