aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execQual.c329
-rw-r--r--src/backend/executor/nodeFunctionscan.c171
2 files changed, 312 insertions, 188 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
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index d58d312238e..3d2c160fb4f 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.7 2002/08/29 17:14:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.8 2002/08/30 00:28:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -22,7 +22,6 @@
*/
#include "postgres.h"
-#include "miscadmin.h"
#include "access/heapam.h"
#include "catalog/pg_type.h"
#include "executor/execdebug.h"
@@ -32,17 +31,10 @@
#include "parser/parsetree.h"
#include "parser/parse_expr.h"
#include "parser/parse_type.h"
-#include "storage/lmgr.h"
-#include "tcop/pquery.h"
#include "utils/lsyscache.h"
-#include "utils/syscache.h"
-#include "utils/tuplestore.h"
+
static TupleTableSlot *FunctionNext(FunctionScan *node);
-static TupleTableSlot *function_getonetuple(FunctionScanState *scanstate,
- bool *isNull,
- ExprDoneCond *isDone);
-static FunctionMode get_functionmode(Node *expr);
static bool tupledesc_mismatch(TupleDesc tupdesc1, TupleDesc tupdesc2);
/* ----------------------------------------------------------------
@@ -76,53 +68,42 @@ FunctionNext(FunctionScan *node)
tuplestorestate = scanstate->tuplestorestate;
/*
- * If first time through, read all tuples from function and pass them to
- * tuplestore.c. Subsequent calls just fetch tuples from tuplestore.
+ * If first time through, read all tuples from function and put them
+ * in a tuplestore. Subsequent calls just fetch tuples from tuplestore.
*/
if (tuplestorestate == NULL)
{
- /*
- * Initialize tuplestore module.
- */
- tuplestorestate = tuplestore_begin_heap(true, /* randomAccess */
- SortMem);
-
- scanstate->tuplestorestate = (void *) tuplestorestate;
-
- /*
- * Compute all the function tuples and pass to tuplestore.
- */
- for (;;)
- {
- bool isNull;
- ExprDoneCond isDone;
-
- isNull = false;
- isDone = ExprSingleResult;
- slot = function_getonetuple(scanstate, &isNull, &isDone);
- if (TupIsNull(slot))
- break;
-
- tuplestore_puttuple(tuplestorestate, (void *) slot->val);
- ExecClearTuple(slot);
+ ExprContext *econtext = scanstate->csstate.cstate.cs_ExprContext;
+ TupleDesc funcTupdesc;
- if (isDone != ExprMultipleResult)
- break;
- }
+ scanstate->tuplestorestate = tuplestorestate =
+ ExecMakeTableFunctionResult((Expr *) scanstate->funcexpr,
+ econtext,
+ &funcTupdesc);
/*
- * Complete the store.
+ * If function provided a tupdesc, cross-check it. We only really
+ * need to do this for functions returning RECORD, but might as well
+ * do it always.
*/
- tuplestore_donestoring(tuplestorestate);
+ if (funcTupdesc &&
+ tupledesc_mismatch(scanstate->tupdesc, funcTupdesc))
+ elog(ERROR, "Query-specified return tuple and actual function return tuple do not match");
}
/*
* Get the next tuple from tuplestore. Return NULL if no more tuples.
*/
slot = scanstate->csstate.css_ScanTupleSlot;
- heapTuple = tuplestore_getheaptuple(tuplestorestate,
- ScanDirectionIsForward(direction),
- &should_free);
+ if (tuplestorestate)
+ heapTuple = tuplestore_getheaptuple(tuplestorestate,
+ ScanDirectionIsForward(direction),
+ &should_free);
+ else
+ {
+ heapTuple = NULL;
+ should_free = false;
+ }
return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
}
@@ -219,7 +200,6 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
rel = relation_open(funcrelid, AccessShareLock);
tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
relation_close(rel, AccessShareLock);
- scanstate->returnsTuple = true;
}
else if (functyptype == 'b' || functyptype == 'd')
{
@@ -236,7 +216,6 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
-1,
0,
false);
- scanstate->returnsTuple = false;
}
else if (functyptype == 'p' && funcrettype == RECORDOID)
{
@@ -246,13 +225,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
List *coldeflist = rte->coldeflist;
tupdesc = BuildDescForRelation(coldeflist);
- scanstate->returnsTuple = true;
}
else
elog(ERROR, "Unknown kind of return type specified for function");
- scanstate->fn_typeid = funcrettype;
- scanstate->fn_typtype = functyptype;
scanstate->tupdesc = tupdesc;
ExecSetSlotDescriptor(scanstate->csstate.css_ScanTupleSlot,
tupdesc, false);
@@ -263,8 +239,6 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
scanstate->tuplestorestate = NULL;
scanstate->funcexpr = rte->funcexpr;
- scanstate->functionmode = get_functionmode(rte->funcexpr);
-
scanstate->csstate.cstate.cs_TupFromTlist = false;
/*
@@ -322,7 +296,7 @@ ExecEndFunctionScan(FunctionScan *node)
* Release tuplestore resources
*/
if (scanstate->tuplestorestate != NULL)
- tuplestore_end((Tuplestorestate *) scanstate->tuplestorestate);
+ tuplestore_end(scanstate->tuplestorestate);
scanstate->tuplestorestate = NULL;
}
@@ -345,7 +319,7 @@ ExecFunctionMarkPos(FunctionScan *node)
if (!scanstate->tuplestorestate)
return;
- tuplestore_markpos((Tuplestorestate *) scanstate->tuplestorestate);
+ tuplestore_markpos(scanstate->tuplestorestate);
}
/* ----------------------------------------------------------------
@@ -367,7 +341,7 @@ ExecFunctionRestrPos(FunctionScan *node)
if (!scanstate->tuplestorestate)
return;
- tuplestore_restorepos((Tuplestorestate *) scanstate->tuplestorestate);
+ tuplestore_restorepos(scanstate->tuplestorestate);
}
/* ----------------------------------------------------------------
@@ -402,98 +376,13 @@ ExecFunctionReScan(FunctionScan *node, ExprContext *exprCtxt, Plan *parent)
*/
if (node->scan.plan.chgParam != NULL)
{
- tuplestore_end((Tuplestorestate *) scanstate->tuplestorestate);
+ tuplestore_end(scanstate->tuplestorestate);
scanstate->tuplestorestate = NULL;
}
else
- tuplestore_rescan((Tuplestorestate *) scanstate->tuplestorestate);
-}
-
-/*
- * Run the underlying function to get the next tuple
- */
-static TupleTableSlot *
-function_getonetuple(FunctionScanState *scanstate,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- HeapTuple tuple;
- Datum retDatum;
- char nullflag;
- TupleDesc tupdesc = scanstate->tupdesc;
- bool returnsTuple = scanstate->returnsTuple;
- Node *expr = scanstate->funcexpr;
- Oid fn_typeid = scanstate->fn_typeid;
- char fn_typtype = scanstate->fn_typtype;
- ExprContext *econtext = scanstate->csstate.cstate.cs_ExprContext;
- TupleTableSlot *slot = scanstate->csstate.css_ScanTupleSlot;
-
- /*
- * 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);
-
- /*
- * get the next Datum from the function
- */
- retDatum = ExecEvalExprSwitchContext(expr, econtext, isNull, isDone);
-
- /*
- * check to see if we're really done
- */
- if (*isDone == ExprEndResult)
- slot = NULL;
- else
- {
- if (returnsTuple)
- {
- /*
- * Composite data type, i.e. a table's row type
- * function returns pointer to tts??
- */
- slot = (TupleTableSlot *) retDatum;
-
- /*
- * if function return type was RECORD, we need to check to be
- * sure the structure from the query matches the actual return
- * structure
- */
- if (fn_typtype == 'p' && fn_typeid == RECORDOID)
- if (tupledesc_mismatch(tupdesc, slot->ttc_tupleDescriptor))
- elog(ERROR, "Query-specified return tuple and actual function return tuple do not match");
- }
- else
- {
- /*
- * Must be a base data type, i.e. scalar
- * turn it into a tuple
- */
- nullflag = *isNull ? 'n' : ' ';
- tuple = heap_formtuple(tupdesc, &retDatum, &nullflag);
-
- /*
- * save the tuple in the scan tuple slot and return the slot.
- */
- slot = ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- InvalidBuffer, /* buffer associated with
- * this tuple */
- true); /* pfree this tuple */
- }
- }
-
- return slot;
+ tuplestore_rescan(scanstate->tuplestorestate);
}
-static FunctionMode
-get_functionmode(Node *expr)
-{
- /*
- * for the moment, hardwire this
- */
- return PM_REPEATEDCALL;
-}
static bool
tupledesc_mismatch(TupleDesc tupdesc1, TupleDesc tupdesc2)