mirror of
https://github.com/postgres/postgres.git
synced 2026-02-09 22:04:24 -05:00
mode while callers hold pointers to in-memory tuples. I reported this for the case of nodeWindowAgg's primary scan tuple, but inspection of the code shows that all of the calls in nodeWindowAgg and nodeCtescan are at risk. For the moment, fix it with a rather brute-force approach of copying whenever one of the at-risk callers requests a tuple. Later we might think of some sort of reference-count approach to reduce tuple copying.
290 lines
7.4 KiB
C
290 lines
7.4 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* nodeFunctionscan.c
|
|
* Support routines for scanning RangeFunctions (functions in rangetable).
|
|
*
|
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.51 2009/03/27 18:30:21 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
/*
|
|
* 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.
|
|
* ExecFunctionReScan rescans the function
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "executor/nodeFunctionscan.h"
|
|
#include "funcapi.h"
|
|
#include "utils/builtins.h"
|
|
|
|
|
|
static TupleTableSlot *FunctionNext(FunctionScanState *node);
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Scan Support
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
/* ----------------------------------------------------------------
|
|
* FunctionNext
|
|
*
|
|
* This is a workhorse for ExecFunctionScan
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
static TupleTableSlot *
|
|
FunctionNext(FunctionScanState *node)
|
|
{
|
|
TupleTableSlot *slot;
|
|
EState *estate;
|
|
ScanDirection direction;
|
|
Tuplestorestate *tuplestorestate;
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecFunctionScan(node)
|
|
*
|
|
* Scans the function sequentially and returns the next qualifying
|
|
* tuple.
|
|
* It calls the ExecScan() routine and passes it the access method
|
|
* which retrieves tuples sequentially.
|
|
*
|
|
*/
|
|
|
|
TupleTableSlot *
|
|
ExecFunctionScan(FunctionScanState *node)
|
|
{
|
|
/*
|
|
* use FunctionNext as access method
|
|
*/
|
|
return ExecScan(&node->ss, (ExecScanAccessMtd) FunctionNext);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecInitFunctionScan
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
FunctionScanState *
|
|
ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
|
|
{
|
|
FunctionScanState *scanstate;
|
|
Oid funcrettype;
|
|
TypeFuncClass functypclass;
|
|
TupleDesc tupdesc = NULL;
|
|
|
|
/* check for unsupported flags */
|
|
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;
|
|
|
|
/*
|
|
* Miscellaneous initialization
|
|
*
|
|
* create expression context for node
|
|
*/
|
|
ExecAssignExprContext(estate, &scanstate->ss.ps);
|
|
|
|
#define FUNCTIONSCAN_NSLOTS 2
|
|
|
|
/*
|
|
* tuple table initialization
|
|
*/
|
|
ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
|
|
ExecInitScanTupleSlot(estate, &scanstate->ss);
|
|
|
|
/*
|
|
* initialize child expressions
|
|
*/
|
|
scanstate->ss.ps.targetlist = (List *)
|
|
ExecInitExpr((Expr *) node->scan.plan.targetlist,
|
|
(PlanState *) scanstate);
|
|
scanstate->ss.ps.qual = (List *)
|
|
ExecInitExpr((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)
|
|
{
|
|
/* Composite data type, e.g. a table's row type */
|
|
Assert(tupdesc);
|
|
/* Must copy it out of typcache for safety */
|
|
tupdesc = CreateTupleDescCopy(tupdesc);
|
|
}
|
|
else if (functypclass == TYPEFUNC_SCALAR)
|
|
{
|
|
/* Base data type, i.e. scalar */
|
|
char *attname = strVal(linitial(node->funccolnames));
|
|
|
|
tupdesc = CreateTemplateTupleDesc(1, false);
|
|
TupleDescInitEntry(tupdesc,
|
|
(AttrNumber) 1,
|
|
attname,
|
|
funcrettype,
|
|
-1,
|
|
0);
|
|
}
|
|
else if (functypclass == TYPEFUNC_RECORD)
|
|
{
|
|
tupdesc = BuildDescFromLists(node->funccolnames,
|
|
node->funccoltypes,
|
|
node->funccoltypmods);
|
|
}
|
|
else
|
|
{
|
|
/* crummy error message, but parser should have caught this */
|
|
elog(ERROR, "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;
|
|
scanstate->funcexpr = ExecInitExpr((Expr *) node->funcexpr,
|
|
(PlanState *) scanstate);
|
|
|
|
scanstate->ss.ps.ps_TupFromTlist = false;
|
|
|
|
/*
|
|
* Initialize result tuple type and projection info.
|
|
*/
|
|
ExecAssignResultTypeFromTL(&scanstate->ss.ps);
|
|
ExecAssignScanProjectionInfo(&scanstate->ss);
|
|
|
|
return scanstate;
|
|
}
|
|
|
|
int
|
|
ExecCountSlotsFunctionScan(FunctionScan *node)
|
|
{
|
|
return ExecCountSlotsNode(outerPlan(node)) +
|
|
ExecCountSlotsNode(innerPlan(node)) +
|
|
FUNCTIONSCAN_NSLOTS;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecEndFunctionScan
|
|
*
|
|
* frees any storage allocated through C routines.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecEndFunctionScan(FunctionScanState *node)
|
|
{
|
|
/*
|
|
* Free the exprcontext
|
|
*/
|
|
ExecFreeExprContext(&node->ss.ps);
|
|
|
|
/*
|
|
* clean out the tuple table
|
|
*/
|
|
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
|
|
ExecClearTuple(node->ss.ss_ScanTupleSlot);
|
|
|
|
/*
|
|
* Release tuplestore resources
|
|
*/
|
|
if (node->tuplestorestate != NULL)
|
|
tuplestore_end(node->tuplestorestate);
|
|
node->tuplestorestate = NULL;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecFunctionReScan
|
|
*
|
|
* Rescans the relation.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecFunctionReScan(FunctionScanState *node, ExprContext *exprCtxt)
|
|
{
|
|
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
|
|
node->ss.ps.ps_TupFromTlist = false;
|
|
|
|
/*
|
|
* 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);
|
|
}
|