mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
PL/pgSQL functions can return sets. Neil Conway's patch, modified so
that the functionality is available to anyone via ReturnSetInfo, rather than hard-wiring it to PL/pgSQL.
This commit is contained in:
@ -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.
|
||||
@ -886,6 +1121,48 @@ ExecEvalOper(Expr *opClause,
|
||||
isNull, isDone);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* 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
|
||||
*
|
||||
@ -960,48 +1237,6 @@ ExecEvalDistinct(Expr *opClause,
|
||||
return BoolGetDatum(result);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* 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
|
||||
|
@ -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);
|
||||
ExprContext *econtext = scanstate->csstate.cstate.cs_ExprContext;
|
||||
TupleDesc funcTupdesc;
|
||||
|
||||
scanstate->tuplestorestate = (void *) tuplestorestate;
|
||||
scanstate->tuplestorestate = tuplestorestate =
|
||||
ExecMakeTableFunctionResult((Expr *) scanstate->funcexpr,
|
||||
econtext,
|
||||
&funcTupdesc);
|
||||
|
||||
/*
|
||||
* Compute all the function tuples and pass to tuplestore.
|
||||
* 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.
|
||||
*/
|
||||
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);
|
||||
|
||||
if (isDone != ExprMultipleResult)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Complete the store.
|
||||
*/
|
||||
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);
|
||||
tuplestore_rescan(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;
|
||||
}
|
||||
|
||||
static FunctionMode
|
||||
get_functionmode(Node *expr)
|
||||
{
|
||||
/*
|
||||
* for the moment, hardwire this
|
||||
*/
|
||||
return PM_REPEATEDCALL;
|
||||
}
|
||||
|
||||
static bool
|
||||
tupledesc_mismatch(TupleDesc tupdesc1, TupleDesc tupdesc2)
|
||||
|
Reference in New Issue
Block a user