*
* nodeFunctionscan.cpp
* Support routines for scanning RangeFunctions (functions in rangetable).
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/gausskernel/runtime/executor/nodeFunctionscan.cpp
*
* -------------------------------------------------------------------------
*
* INTERFACE ROUTINES
* ExecFunctionScan scans a function.
* ExecFunctionNext retrieve next tuple in sequential order.
* ExecInitFunctionScan creates and initializes a functionscan node.
* ExecEndFunctionScan releases any storage allocated.
* ExecReScanFunctionScan rescans the function
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include "executor/node/nodeFunctionscan.h"
#include "funcapi.h"
#include "nodes/nodeFuncs.h"
static TupleTableSlot* ExecFunctionScan(PlanState* node);
static TupleTableSlot* FunctionNext(FunctionScanState* node);
* Scan Support
* ----------------------------------------------------------------
*/
* FunctionNext
*
* This is a workhorse for ExecFunctionScan
* ----------------------------------------------------------------
*/
static TupleTableSlot* FunctionNext(FunctionScanState* node)
{
TupleTableSlot* slot = NULL;
EState* estate = NULL;
ScanDirection direction;
Tuplestorestate* tuplestorestate = NULL;
* get information from the estate and scan state
*/
estate = node->ss.ps.state;
direction = estate->es_direction;
tuplestorestate = node->tuplestorestate;
* 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) {
node->tuplestorestate = tuplestorestate = ExecMakeTableFunctionResult(
node->funcexpr, node->ss.ps.ps_ExprContext, node->tupdesc, node->eflags & EXEC_FLAG_BACKWARD, node);
}
* Get the next tuple from tuplestore. Return NULL if no more tuples.
*/
slot = node->ss.ss_ScanTupleSlot;
(void)tuplestore_gettupleslot(tuplestorestate, ScanDirectionIsForward(direction), false, slot);
return slot;
}
* FunctionRecheck -- access method routine to recheck a tuple in EvalPlanQual
*/
static bool FunctionRecheck(FunctionScanState* node, TupleTableSlot* slot)
{
return true;
}
* ExecFunctionScan(node)
*
* Scans the function sequentially and returns the next qualifying
* tuple.
* We call the ExecScan() routine and pass it the appropriate
* access method functions.
* ----------------------------------------------------------------
*/
static TupleTableSlot* ExecFunctionScan(PlanState* state)
{
FunctionScanState* node = castNode(FunctionScanState, state);
return ExecScan(&node->ss, (ExecScanAccessMtd)FunctionNext, (ExecScanRecheckMtd)FunctionRecheck);
}
* ExecInitFunctionScan
* ----------------------------------------------------------------
*/
FunctionScanState* ExecInitFunctionScan(FunctionScan* node, EState* estate, int eflags)
{
FunctionScanState* scanstate = NULL;
Oid funcrettype;
TypeFuncClass functypclass;
TupleDesc tupdesc = NULL;
Assert(!(eflags & EXEC_FLAG_MARK));
* FunctionScan should not have any children.
*/
Assert(outerPlan(node) == NULL);
Assert(innerPlan(node) == NULL);
* create new ScanState for node
*/
scanstate = makeNode(FunctionScanState);
scanstate->ss.ps.plan = (Plan*)node;
scanstate->ss.ps.state = estate;
scanstate->eflags = eflags;
scanstate->ss.ps.ExecProcNode = ExecFunctionScan;
* Miscellaneous initialization
*
* create expression context for node
*/
ExecAssignExprContext(estate, &scanstate->ss.ps);
* tuple table initialization
*/
ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
ExecInitScanTupleSlot(estate, &scanstate->ss);
* initialize child expressions
*/
if (estate->es_is_flt_frame) {
scanstate->ss.ps.qual = (List*)ExecInitQualByFlatten(node->scan.plan.qual, (PlanState*)scanstate);
} else {
scanstate->ss.ps.targetlist = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.targetlist, (PlanState*)scanstate);
scanstate->ss.ps.qual = (List*)ExecInitExprByRecursion((Expr*)node->scan.plan.qual, (PlanState*)scanstate);
}
* Now determine if the function returns a simple or composite type, and
* build an appropriate tupdesc.
*/
functypclass = get_expr_result_type(node->funcexpr, &funcrettype, &tupdesc);
if (functypclass == TYPEFUNC_COMPOSITE) {
Assert(tupdesc);
tupdesc = CreateTupleDescCopy(tupdesc);
if (tupdesc->td_tam_ops != TableAmHeap) {
* and invalidate attcacheoff, since other storage type
* uses different offset values than heap.
*/
int i;
for (i = 0; i < tupdesc->natts; i++) {
tupdesc->attrs[i].attcacheoff = -1;
}
tupdesc->td_tam_ops = TableAmHeap;
}
} else if (functypclass == TYPEFUNC_SCALAR) {
char* attname = strVal(linitial(node->funccolnames));
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc, (AttrNumber)1, attname, funcrettype, -1, 0);
TupleDescInitEntryCollation(tupdesc, (AttrNumber)1, exprCollation(node->funcexpr));
} else if (functypclass == TYPEFUNC_RECORD) {
tupdesc =
BuildDescFromLists(node->funccolnames, node->funccoltypes, node->funccoltypmods, node->funccolcollations);
} else {
ereport(
ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function in FROM has unsupported return type")));
}
* For RECORD results, make sure a typmod has been assigned. (The
* function should do this for itself, but let's cover things in case it
* doesn't.)
*/
BlessTupleDesc(tupdesc);
scanstate->tupdesc = tupdesc;
ExecAssignScanType(&scanstate->ss, tupdesc);
* Other node-specific setup
*/
scanstate->tuplestorestate = NULL;
if (estate->es_is_flt_frame) {
if (IsA((Expr *)node->funcexpr, FuncExpr)) {
scanstate->funcexpr =
(ExprState *)ExecInitTableFunctionResult((Expr *)node->funcexpr, scanstate->ss.ps.ps_ExprContext, &scanstate->ss.ps);
} else {
scanstate->funcexpr = (ExprState *)ExecInitExprByFlatten((Expr *)node->funcexpr, (PlanState *)scanstate);
}
} else {
scanstate->funcexpr = ExecInitExprByRecursion((Expr *)node->funcexpr, (PlanState *)scanstate);
}
scanstate->ss.ps.ps_vec_TupFromTlist = false;
* Initialize result tuple type and projection info.
*/
ExecAssignResultTypeFromTL(
&scanstate->ss.ps,
scanstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor->td_tam_ops);
ExecAssignScanProjectionInfo(&scanstate->ss);
Assert(scanstate->ss.ps.ps_ResultTupleSlot->tts_tupleDescriptor->td_tam_ops);
return scanstate;
}
* ExecEndFunctionScan
*
* frees any storage allocated through C routines.
* ----------------------------------------------------------------
*/
void ExecEndFunctionScan(FunctionScanState* node)
{
* Free the exprcontext
*/
ExecFreeExprContext(&node->ss.ps);
* clean out the tuple table
*/
(void)ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
(void)ExecClearTuple(node->ss.ss_ScanTupleSlot);
* Release tuplestore resources
*/
if (node->tuplestorestate != NULL)
tuplestore_end(node->tuplestorestate);
node->tuplestorestate = NULL;
}
* ExecReScanFunctionScan
*
* Rescans the relation.
* ----------------------------------------------------------------
*/
void ExecReScanFunctionScan(FunctionScanState* node)
{
(void)ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
ExecScanReScan(&node->ss);
* If we haven't materialized yet, just return.
*/
if (!node->tuplestorestate)
return;
* Here we have a choice whether to drop the tuplestore (and recompute the
* function outputs) or just rescan it. We must recompute if the
* expression contains parameters, else we rescan. XXX maybe we should
* recompute if the function is volatile?
*/
if (node->ss.ps.chgParam != NULL) {
tuplestore_end(node->tuplestorestate);
node->tuplestorestate = NULL;
} else
tuplestore_rescan(node->tuplestorestate);
}