aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execQual.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/execQual.c')
-rw-r--r--src/backend/executor/execQual.c329
1 files changed, 282 insertions, 47 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index e481ea11596..b422adc2061 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.101 2002/08/26 17:53:57 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.102 2002/08/30 00:28:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -35,12 +35,15 @@
#include "postgres.h"
#include "access/heapam.h"
+#include "catalog/pg_type.h"
#include "executor/execdebug.h"
#include "executor/functions.h"
#include "executor/nodeSubplan.h"
+#include "miscadmin.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fcache.h"
+#include "utils/lsyscache.h"
/* static function decls */
@@ -646,9 +649,6 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
* ExecMakeFunctionResult
*
* Evaluate the arguments to a function and then the function itself.
- *
- * NOTE: econtext is used only for evaluating the argument expressions;
- * it is not passed to the function itself.
*/
Datum
ExecMakeFunctionResult(FunctionCachePtr fcache,
@@ -707,6 +707,11 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
fcinfo.resultinfo = (Node *) &rsinfo;
rsinfo.type = T_ReturnSetInfo;
rsinfo.econtext = econtext;
+ rsinfo.allowedModes = (int) SFRM_ValuePerCall;
+ rsinfo.returnMode = SFRM_ValuePerCall;
+ /* isDone is filled below */
+ rsinfo.setResult = NULL;
+ rsinfo.setDesc = NULL;
}
/*
@@ -837,10 +842,240 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
}
+/*
+ * ExecMakeTableFunctionResult
+ *
+ * Evaluate a table function, producing a materialized result in a Tuplestore
+ * object. (If function returns an empty set, we just return NULL instead.)
+ */
+Tuplestorestate *
+ExecMakeTableFunctionResult(Expr *funcexpr,
+ ExprContext *econtext,
+ TupleDesc *returnDesc)
+{
+ Tuplestorestate *tupstore = NULL;
+ TupleDesc tupdesc = NULL;
+ Func *func;
+ List *argList;
+ FunctionCachePtr fcache;
+ FunctionCallInfoData fcinfo;
+ ReturnSetInfo rsinfo; /* for functions returning sets */
+ ExprDoneCond argDone;
+ MemoryContext callerContext;
+ MemoryContext oldcontext;
+ TupleTableSlot *slot;
+ 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
+ */
+ fcache = func->func_fcache;
+ if (fcache == NULL)
+ {
+ 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++)
+ {
+ if (fcinfo.argnull[i])
+ {
+ *returnDesc = NULL;
+ return NULL;
+ }
+ }
+ }
+
+ /*
+ * If function returns set, prepare a resultinfo node for
+ * communication
+ */
+ if (fcache->func.fn_retset)
+ {
+ fcinfo.resultinfo = (Node *) &rsinfo;
+ rsinfo.type = T_ReturnSetInfo;
+ rsinfo.econtext = econtext;
+ rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
+ }
+ /* we set these fields always since we examine them below */
+ rsinfo.returnMode = SFRM_ValuePerCall;
+ /* isDone is filled below */
+ rsinfo.setResult = NULL;
+ rsinfo.setDesc = NULL;
+
+ /*
+ * Switch to short-lived context for calling the function.
+ */
+ callerContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+ /*
+ * Loop to handle the ValuePerCall protocol.
+ */
+ for (;;)
+ {
+ Datum result;
+ HeapTuple tuple;
+
+ /*
+ * reset per-tuple memory context before each call of the function.
+ * 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);
+
+ /* Which protocol does function want to use? */
+ if (rsinfo.returnMode == SFRM_ValuePerCall)
+ {
+ /*
+ * Check for end of result set.
+ *
+ * Note: if function returns an empty set, we don't build a
+ * tupdesc or tuplestore (since we can't get a tupdesc in the
+ * function-returning-tuple case)
+ */
+ if (rsinfo.isDone == ExprEndResult)
+ break;
+ /*
+ * If first time through, build tupdesc and tuplestore for result
+ */
+ if (first_time)
+ {
+ Oid funcrettype = funcexpr->typeOid;
+
+ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+ if (funcrettype == RECORDOID ||
+ get_typtype(funcrettype) == 'c')
+ {
+ /*
+ * Composite type, so function should have returned a
+ * TupleTableSlot; use its descriptor
+ */
+ slot = (TupleTableSlot *) DatumGetPointer(result);
+ if (fcinfo.isnull || !slot || !IsA(slot, TupleTableSlot) ||
+ !slot->ttc_tupleDescriptor)
+ elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
+ tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
+ returnsTuple = true;
+ }
+ else
+ {
+ /*
+ * Scalar type, so make a single-column descriptor
+ */
+ tupdesc = CreateTemplateTupleDesc(1, WITHOUTOID);
+ TupleDescInitEntry(tupdesc,
+ (AttrNumber) 1,
+ "column",
+ funcrettype,
+ -1,
+ 0,
+ false);
+ }
+ tupstore = tuplestore_begin_heap(true, /* randomAccess */
+ SortMem);
+ MemoryContextSwitchTo(oldcontext);
+ rsinfo.setResult = tupstore;
+ rsinfo.setDesc = tupdesc;
+ }
+
+ /*
+ * Store current resultset item.
+ */
+ if (returnsTuple)
+ {
+ slot = (TupleTableSlot *) DatumGetPointer(result);
+ if (fcinfo.isnull || !slot || !IsA(slot, TupleTableSlot) ||
+ TupIsNull(slot))
+ elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
+ tuple = slot->val;
+ }
+ else
+ {
+ char nullflag;
+
+ nullflag = fcinfo.isnull ? 'n' : ' ';
+ tuple = heap_formtuple(tupdesc, &result, &nullflag);
+ }
+
+ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+ tuplestore_puttuple(tupstore, tuple);
+ MemoryContextSwitchTo(oldcontext);
+
+ /*
+ * Are we done?
+ */
+ if (rsinfo.isDone != ExprMultipleResult)
+ break;
+ }
+ else if (rsinfo.returnMode == SFRM_Materialize)
+ {
+ /* check we're on the same page as the function author */
+ if (!first_time || rsinfo.isDone != ExprSingleResult)
+ elog(ERROR, "ExecMakeTableFunctionResult: Materialize-mode protocol not followed");
+ /* Done evaluating the set result */
+ break;
+ }
+ else
+ elog(ERROR, "ExecMakeTableFunctionResult: unknown returnMode %d",
+ (int) rsinfo.returnMode);
+
+ first_time = false;
+ }
+
+ /* If we have a locally-created tupstore, close it up */
+ if (tupstore)
+ {
+ MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+ tuplestore_donestoring(tupstore);
+ }
+
+ MemoryContextSwitchTo(callerContext);
+
+ /* The returned pointers are those in rsinfo */
+ *returnDesc = rsinfo.setDesc;
+ return rsinfo.setResult;
+}
+
+
/* ----------------------------------------------------------------
* ExecEvalOper
- * ExecEvalDistinct
* ExecEvalFunc
+ * ExecEvalDistinct
*
* Evaluate the functional result of a list of arguments by calling the
* function manager.
@@ -887,6 +1122,48 @@ ExecEvalOper(Expr *opClause,
}
/* ----------------------------------------------------------------
+ * ExecEvalFunc
+ * ----------------------------------------------------------------
+ */
+
+static Datum
+ExecEvalFunc(Expr *funcClause,
+ ExprContext *econtext,
+ bool *isNull,
+ ExprDoneCond *isDone)
+{
+ Func *func;
+ List *argList;
+ FunctionCachePtr fcache;
+
+ /*
+ * we extract the oid of the function associated with the func node
+ * and then pass the work onto ExecMakeFunctionResult which evaluates
+ * the arguments and returns the result of calling the function on the
+ * evaluated arguments.
+ *
+ * this is nearly identical to the ExecEvalOper code.
+ */
+ func = (Func *) funcClause->oper;
+ argList = funcClause->args;
+
+ /*
+ * get the fcache from the Func node. If it is NULL, then initialize
+ * it
+ */
+ fcache = func->func_fcache;
+ if (fcache == NULL)
+ {
+ fcache = init_fcache(func->funcid, length(argList),
+ econtext->ecxt_per_query_memory);
+ func->func_fcache = fcache;
+ }
+
+ return ExecMakeFunctionResult(fcache, argList, econtext,
+ isNull, isDone);
+}
+
+/* ----------------------------------------------------------------
* ExecEvalDistinct
*
* IS DISTINCT FROM must evaluate arguments to determine whether
@@ -961,48 +1238,6 @@ ExecEvalDistinct(Expr *opClause,
}
/* ----------------------------------------------------------------
- * ExecEvalFunc
- * ----------------------------------------------------------------
- */
-
-static Datum
-ExecEvalFunc(Expr *funcClause,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- Func *func;
- List *argList;
- FunctionCachePtr fcache;
-
- /*
- * we extract the oid of the function associated with the func node
- * and then pass the work onto ExecMakeFunctionResult which evaluates
- * the arguments and returns the result of calling the function on the
- * evaluated arguments.
- *
- * this is nearly identical to the ExecEvalOper code.
- */
- func = (Func *) funcClause->oper;
- argList = funcClause->args;
-
- /*
- * get the fcache from the Func node. If it is NULL, then initialize
- * it
- */
- fcache = func->func_fcache;
- if (fcache == NULL)
- {
- fcache = init_fcache(func->funcid, length(argList),
- econtext->ecxt_per_query_memory);
- func->func_fcache = fcache;
- }
-
- return ExecMakeFunctionResult(fcache, argList, econtext,
- isNull, isDone);
-}
-
-/* ----------------------------------------------------------------
* ExecEvalNot
* ExecEvalOr
* ExecEvalAnd