88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.111 2002/11/30 21:25:04 tgl Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.112 2002/12/01 20:27:32 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
4040#include "executor/functions.h"
4141#include "executor/nodeSubplan.h"
4242#include "miscadmin.h"
43+ #include "parser/parse_expr.h"
4344#include "utils/array.h"
4445#include "utils/builtins.h"
4546#include "utils/fcache.h"
@@ -820,80 +821,109 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
820821 * object.(If function returns an empty set, we just return NULL instead.)
821822 */
822823Tuplestorestate *
823- ExecMakeTableFunctionResult (Expr * funcexpr ,
824+ ExecMakeTableFunctionResult (Node * funcexpr ,
824825ExprContext * econtext ,
825826TupleDesc expectedDesc ,
826827TupleDesc * returnDesc )
827828{
828829Tuplestorestate * tupstore = NULL ;
829830TupleDesc tupdesc = NULL ;
830- Func * func ;
831- List * argList ;
832- FunctionCachePtr fcache ;
831+ Oid funcrettype ;
833832FunctionCallInfoData fcinfo ;
834833ReturnSetInfo rsinfo ;
835- ExprDoneCond argDone ;
836834MemoryContext callerContext ;
837835MemoryContext oldcontext ;
838836TupleTableSlot * slot ;
837+ bool direct_function_call ;
839838bool first_time = true;
840839bool returnsTuple = false;
841840
842- /* Extract data from function-call expression node */
843- if (!funcexpr || !IsA (funcexpr ,Expr )|| funcexpr -> opType != FUNC_EXPR )
844- elog (ERROR ,"ExecMakeTableFunctionResult: expression is not a function call" );
845- func = (Func * )funcexpr -> oper ;
846- argList = funcexpr -> args ;
847-
848841/*
849- * get the fcache from the Func node. If it is NULL, then initialize
850- * it
842+ * Normally the passed expression tree will be a FUNC_EXPR, since the
843+ * grammar only allows a function call at the top level of a table
844+ * function reference. However, if the function doesn't return set then
845+ * the planner might have replaced the function call via constant-folding
846+ * or inlining. So if we see any other kind of expression node, execute
847+ * it via the general ExecEvalExpr() code; the only difference is that
848+ * we don't get a chance to pass a special ReturnSetInfo to any functions
849+ * buried in the expression.
851850 */
852- fcache = func -> func_fcache ;
853- if (fcache == NULL )
851+ if (funcexpr &&
852+ IsA (funcexpr ,Expr )&&
853+ ((Expr * )funcexpr )-> opType == FUNC_EXPR )
854854{
855- fcache = init_fcache ( func -> funcid , length ( argList ),
856- econtext -> ecxt_per_query_memory ) ;
857- func -> func_fcache = fcache ;
858- }
855+ Func * func ;
856+ List * argList ;
857+ FunctionCachePtr fcache ;
858+ ExprDoneCond argDone ;
859859
860- /*
861- * Evaluate the function's argument list.
862- *
863- * Note: ideally, we'd do this in the per-tuple context, but then the
864- * argument values would disappear when we reset the context in the
865- * inner loop.So do it in caller context. Perhaps we should make a
866- * separate context just to hold the evaluated arguments?
867- */
868- MemSet (& fcinfo ,0 ,sizeof (fcinfo ));
869- fcinfo .flinfo = & (fcache -> func );
870- argDone = ExecEvalFuncArgs (& fcinfo ,argList ,econtext );
871- /* We don't allow sets in the arguments of the table function */
872- if (argDone != ExprSingleResult )
873- elog (ERROR ,"Set-valued function called in context that cannot accept a set" );
860+ /*
861+ * This path is similar to ExecMakeFunctionResult.
862+ */
863+ direct_function_call = true;
874864
875- /*
876- * If function is strict, and there are any NULL arguments, skip
877- * calling the function and return NULL (actually an empty set).
878- */
879- if (fcache -> func .fn_strict )
880- {
881- int i ;
865+ funcrettype = ((Expr * )funcexpr )-> typeOid ;
866+ func = (Func * ) ((Expr * )funcexpr )-> oper ;
867+ argList = ((Expr * )funcexpr )-> args ;
882868
883- for (i = 0 ;i < fcinfo .nargs ;i ++ )
869+ /*
870+ * get the fcache from the Func node. If it is NULL, then initialize
871+ * it
872+ */
873+ fcache = func -> func_fcache ;
874+ if (fcache == NULL )
884875{
885- if (fcinfo .argnull [i ])
876+ fcache = init_fcache (func -> funcid ,length (argList ),
877+ econtext -> ecxt_per_query_memory );
878+ func -> func_fcache = fcache ;
879+ }
880+
881+ /*
882+ * Evaluate the function's argument list.
883+ *
884+ * Note: ideally, we'd do this in the per-tuple context, but then the
885+ * argument values would disappear when we reset the context in the
886+ * inner loop.So do it in caller context. Perhaps we should make a
887+ * separate context just to hold the evaluated arguments?
888+ */
889+ MemSet (& fcinfo ,0 ,sizeof (fcinfo ));
890+ fcinfo .flinfo = & (fcache -> func );
891+ argDone = ExecEvalFuncArgs (& fcinfo ,argList ,econtext );
892+ /* We don't allow sets in the arguments of the table function */
893+ if (argDone != ExprSingleResult )
894+ elog (ERROR ,"Set-valued function called in context that cannot accept a set" );
895+
896+ /*
897+ * If function is strict, and there are any NULL arguments, skip
898+ * calling the function and return NULL (actually an empty set).
899+ */
900+ if (fcache -> func .fn_strict )
901+ {
902+ int i ;
903+
904+ for (i = 0 ;i < fcinfo .nargs ;i ++ )
886905{
887- * returnDesc = NULL ;
888- return NULL ;
906+ if (fcinfo .argnull [i ])
907+ {
908+ * returnDesc = NULL ;
909+ return NULL ;
910+ }
889911}
890912}
891913}
914+ else
915+ {
916+ /* Treat funcexpr as a generic expression */
917+ direct_function_call = false;
918+ funcrettype = exprType (funcexpr );
919+ }
892920
893921/*
894922 * Prepare a resultinfo node for communication. We always do this
895923 * even if not expecting a set result, so that we can pass
896- * expectedDesc.
924+ * expectedDesc. In the generic-expression case, the expression
925+ * doesn't actually get to see the resultinfo, but set it up anyway
926+ * because we use some of the fields as our own state variables.
897927 */
898928fcinfo .resultinfo = (Node * )& rsinfo ;
899929rsinfo .type = T_ReturnSetInfo ;
@@ -906,12 +936,13 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
906936rsinfo .setDesc = NULL ;
907937
908938/*
909- * Switch to short-lived context for calling the function.
939+ * Switch to short-lived context for calling the function or expression .
910940 */
911941callerContext = MemoryContextSwitchTo (econtext -> ecxt_per_tuple_memory );
912942
913943/*
914- * Loop to handle the ValuePerCall protocol.
944+ * Loop to handle the ValuePerCall protocol (which is also the same
945+ * behavior needed in the generic ExecEvalExpr path).
915946 */
916947for (;;)
917948{
@@ -920,15 +951,23 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
920951
921952/*
922953 * reset per-tuple memory context before each call of the
923- * function. This cleans up any local memory the function may leak
924- * when called.
954+ * function or expression . This cleans up any local memory the
955+ *function may leak when called.
925956 */
926957ResetExprContext (econtext );
927958
928- /* Call the function one time */
929- fcinfo .isnull = false;
930- rsinfo .isDone = ExprSingleResult ;
931- result = FunctionCallInvoke (& fcinfo );
959+ /* Call the function or expression one time */
960+ if (direct_function_call )
961+ {
962+ fcinfo .isnull = false;
963+ rsinfo .isDone = ExprSingleResult ;
964+ result = FunctionCallInvoke (& fcinfo );
965+ }
966+ else
967+ {
968+ result = ExecEvalExpr (funcexpr ,econtext ,
969+ & fcinfo .isnull ,& rsinfo .isDone );
970+ }
932971
933972/* Which protocol does function want to use? */
934973if (rsinfo .returnMode == SFRM_ValuePerCall )
@@ -949,8 +988,6 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
949988 */
950989if (first_time )
951990{
952- Oid funcrettype = funcexpr -> typeOid ;
953-
954991oldcontext = MemoryContextSwitchTo (econtext -> ecxt_per_query_memory );
955992if (funcrettype == RECORDOID ||
956993get_typtype (funcrettype )== 'c' )
@@ -960,7 +997,9 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
960997 * TupleTableSlot; use its descriptor
961998 */
962999slot = (TupleTableSlot * )DatumGetPointer (result );
963- if (fcinfo .isnull || !slot || !IsA (slot ,TupleTableSlot )||
1000+ if (fcinfo .isnull ||
1001+ !slot ||
1002+ !IsA (slot ,TupleTableSlot )||
9641003!slot -> ttc_tupleDescriptor )
9651004elog (ERROR ,"ExecMakeTableFunctionResult: Invalid result from function returning tuple" );
9661005tupdesc = CreateTupleDescCopy (slot -> ttc_tupleDescriptor );
@@ -993,7 +1032,9 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
9931032if (returnsTuple )
9941033{
9951034slot = (TupleTableSlot * )DatumGetPointer (result );
996- if (fcinfo .isnull || !slot || !IsA (slot ,TupleTableSlot )||
1035+ if (fcinfo .isnull ||
1036+ !slot ||
1037+ !IsA (slot ,TupleTableSlot )||
9971038TupIsNull (slot ))
9981039elog (ERROR ,"ExecMakeTableFunctionResult: Invalid result from function returning tuple" );
9991040tuple = slot -> val ;