diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 90a38f31ee2ee50f3d8ebbbe9974e393707085db..cb31c025df24878c8ae6388e36719f47fe384f31 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.111 2002/11/30 21:25:04 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.112 2002/12/01 20:27:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,6 +40,7 @@
 #include "executor/functions.h"
 #include "executor/nodeSubplan.h"
 #include "miscadmin.h"
+#include "parser/parse_expr.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fcache.h"
@@ -820,80 +821,109 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
  * object.	(If function returns an empty set, we just return NULL instead.)
  */
 Tuplestorestate *
-ExecMakeTableFunctionResult(Expr *funcexpr,
+ExecMakeTableFunctionResult(Node *funcexpr,
 							ExprContext *econtext,
 							TupleDesc expectedDesc,
 							TupleDesc *returnDesc)
 {
 	Tuplestorestate *tupstore = NULL;
 	TupleDesc	tupdesc = NULL;
-	Func	   *func;
-	List	   *argList;
-	FunctionCachePtr fcache;
+	Oid			funcrettype;
 	FunctionCallInfoData fcinfo;
 	ReturnSetInfo rsinfo;
-	ExprDoneCond argDone;
 	MemoryContext callerContext;
 	MemoryContext oldcontext;
 	TupleTableSlot *slot;
+	bool		direct_function_call;
 	bool		first_time = true;
 	bool		returnsTuple = false;
 
-	/* Extract data from function-call expression node */
-	if (!funcexpr || !IsA(funcexpr, Expr) ||funcexpr->opType != FUNC_EXPR)
-		elog(ERROR, "ExecMakeTableFunctionResult: expression is not a function call");
-	func = (Func *) funcexpr->oper;
-	argList = funcexpr->args;
-
 	/*
-	 * get the fcache from the Func node. If it is NULL, then initialize
-	 * it
+	 * Normally the passed expression tree will be a FUNC_EXPR, since the
+	 * grammar only allows a function call at the top level of a table
+	 * function reference.  However, if the function doesn't return set then
+	 * the planner might have replaced the function call via constant-folding
+	 * or inlining.  So if we see any other kind of expression node, execute
+	 * it via the general ExecEvalExpr() code; the only difference is that
+	 * we don't get a chance to pass a special ReturnSetInfo to any functions
+	 * buried in the expression.
 	 */
-	fcache = func->func_fcache;
-	if (fcache == NULL)
+	if (funcexpr &&
+		IsA(funcexpr, Expr) &&
+		((Expr *) funcexpr)->opType == FUNC_EXPR)
 	{
-		fcache = init_fcache(func->funcid, length(argList),
-							 econtext->ecxt_per_query_memory);
-		func->func_fcache = fcache;
-	}
+		Func	   *func;
+		List	   *argList;
+		FunctionCachePtr fcache;
+		ExprDoneCond argDone;
 
-	/*
-	 * Evaluate the function's argument list.
-	 *
-	 * Note: ideally, we'd do this in the per-tuple context, but then the
-	 * argument values would disappear when we reset the context in the
-	 * inner loop.	So do it in caller context.  Perhaps we should make a
-	 * separate context just to hold the evaluated arguments?
-	 */
-	MemSet(&fcinfo, 0, sizeof(fcinfo));
-	fcinfo.flinfo = &(fcache->func);
-	argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
-	/* We don't allow sets in the arguments of the table function */
-	if (argDone != ExprSingleResult)
-		elog(ERROR, "Set-valued function called in context that cannot accept a set");
+		/*
+		 * This path is similar to ExecMakeFunctionResult.
+		 */
+		direct_function_call = true;
 
-	/*
-	 * If function is strict, and there are any NULL arguments, skip
-	 * calling the function and return NULL (actually an empty set).
-	 */
-	if (fcache->func.fn_strict)
-	{
-		int			i;
+		funcrettype = ((Expr *) funcexpr)->typeOid;
+		func = (Func *) ((Expr *) funcexpr)->oper;
+		argList = ((Expr *) funcexpr)->args;
 
-		for (i = 0; i < fcinfo.nargs; i++)
+		/*
+		 * get the fcache from the Func node. If it is NULL, then initialize
+		 * it
+		 */
+		fcache = func->func_fcache;
+		if (fcache == NULL)
 		{
-			if (fcinfo.argnull[i])
+			fcache = init_fcache(func->funcid, length(argList),
+								 econtext->ecxt_per_query_memory);
+			func->func_fcache = fcache;
+		}
+
+		/*
+		 * Evaluate the function's argument list.
+		 *
+		 * Note: ideally, we'd do this in the per-tuple context, but then the
+		 * argument values would disappear when we reset the context in the
+		 * inner loop.	So do it in caller context.  Perhaps we should make a
+		 * separate context just to hold the evaluated arguments?
+		 */
+		MemSet(&fcinfo, 0, sizeof(fcinfo));
+		fcinfo.flinfo = &(fcache->func);
+		argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
+		/* We don't allow sets in the arguments of the table function */
+		if (argDone != ExprSingleResult)
+			elog(ERROR, "Set-valued function called in context that cannot accept a set");
+
+		/*
+		 * If function is strict, and there are any NULL arguments, skip
+		 * calling the function and return NULL (actually an empty set).
+		 */
+		if (fcache->func.fn_strict)
+		{
+			int			i;
+
+			for (i = 0; i < fcinfo.nargs; i++)
 			{
-				*returnDesc = NULL;
-				return NULL;
+				if (fcinfo.argnull[i])
+				{
+					*returnDesc = NULL;
+					return NULL;
+				}
 			}
 		}
 	}
+	else
+	{
+		/* Treat funcexpr as a generic expression */
+		direct_function_call = false;
+		funcrettype = exprType(funcexpr);
+	}
 
 	/*
 	 * Prepare a resultinfo node for communication.  We always do this
 	 * even if not expecting a set result, so that we can pass
-	 * expectedDesc.
+	 * expectedDesc.  In the generic-expression case, the expression
+	 * doesn't actually get to see the resultinfo, but set it up anyway
+	 * because we use some of the fields as our own state variables.
 	 */
 	fcinfo.resultinfo = (Node *) &rsinfo;
 	rsinfo.type = T_ReturnSetInfo;
@@ -906,12 +936,13 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
 	rsinfo.setDesc = NULL;
 
 	/*
-	 * Switch to short-lived context for calling the function.
+	 * Switch to short-lived context for calling the function or expression.
 	 */
 	callerContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
 
 	/*
-	 * Loop to handle the ValuePerCall protocol.
+	 * Loop to handle the ValuePerCall protocol (which is also the same
+	 * behavior needed in the generic ExecEvalExpr path).
 	 */
 	for (;;)
 	{
@@ -920,15 +951,23 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
 
 		/*
 		 * reset per-tuple memory context before each call of the
-		 * function. This cleans up any local memory the function may leak
-		 * when called.
+		 * function or expression. This cleans up any local memory the
+		 * function may leak when called.
 		 */
 		ResetExprContext(econtext);
 
-		/* Call the function one time */
-		fcinfo.isnull = false;
-		rsinfo.isDone = ExprSingleResult;
-		result = FunctionCallInvoke(&fcinfo);
+		/* Call the function or expression one time */
+		if (direct_function_call)
+		{
+			fcinfo.isnull = false;
+			rsinfo.isDone = ExprSingleResult;
+			result = FunctionCallInvoke(&fcinfo);
+		}
+		else
+		{
+			result = ExecEvalExpr(funcexpr, econtext,
+								  &fcinfo.isnull, &rsinfo.isDone);
+		}
 
 		/* Which protocol does function want to use? */
 		if (rsinfo.returnMode == SFRM_ValuePerCall)
@@ -949,8 +988,6 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
 			 */
 			if (first_time)
 			{
-				Oid			funcrettype = funcexpr->typeOid;
-
 				oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
 				if (funcrettype == RECORDOID ||
 					get_typtype(funcrettype) == 'c')
@@ -960,7 +997,9 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
 					 * TupleTableSlot; use its descriptor
 					 */
 					slot = (TupleTableSlot *) DatumGetPointer(result);
-					if (fcinfo.isnull || !slot || !IsA(slot, TupleTableSlot) ||
+					if (fcinfo.isnull ||
+						!slot ||
+						!IsA(slot, TupleTableSlot) ||
 						!slot->ttc_tupleDescriptor)
 						elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
 					tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
@@ -993,7 +1032,9 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
 			if (returnsTuple)
 			{
 				slot = (TupleTableSlot *) DatumGetPointer(result);
-				if (fcinfo.isnull || !slot || !IsA(slot, TupleTableSlot) ||
+				if (fcinfo.isnull ||
+					!slot ||
+					!IsA(slot, TupleTableSlot) ||
 					TupIsNull(slot))
 					elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
 				tuple = slot->val;
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index cf8d74a06f2b93125b3af732478e395206350ced..97da70b2b7860fd3f0d890529275ef7cf4f7fb6e 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.12 2002/09/04 20:31:18 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.13 2002/12/01 20:27:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -78,7 +78,7 @@ FunctionNext(FunctionScan *node)
 		TupleDesc	funcTupdesc;
 
 		scanstate->tuplestorestate = tuplestorestate =
-			ExecMakeTableFunctionResult((Expr *) scanstate->funcexpr,
+			ExecMakeTableFunctionResult(scanstate->funcexpr,
 										econtext,
 										scanstate->tupdesc,
 										&funcTupdesc);
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 312c56fa85c6717b28291259fdb10d45cbeefb2e..33b83bce6fe29caff2966ee7e80f5f8b7d381967 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.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: executor.h,v 1.79 2002/11/30 05:21:03 tgl Exp $
+ * $Id: executor.h,v 1.80 2002/12/01 20:27:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -81,7 +81,7 @@ extern Datum ExecMakeFunctionResult(FunctionCachePtr fcache,
 					   ExprContext *econtext,
 					   bool *isNull,
 					   ExprDoneCond *isDone);
-extern Tuplestorestate *ExecMakeTableFunctionResult(Expr *funcexpr,
+extern Tuplestorestate *ExecMakeTableFunctionResult(Node *funcexpr,
 							ExprContext *econtext,
 							TupleDesc expectedDesc,
 							TupleDesc *returnDesc);