diff options
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/execQual.c | 329 | ||||
-rw-r--r-- | src/backend/executor/nodeFunctionscan.c | 171 |
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) |