2002-05-12 22:10:05 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* nodeFunctionscan.c
|
|
|
|
* Support routines for scanning RangeFunctions (functions in rangetable).
|
|
|
|
*
|
2002-06-20 22:29:54 +02:00
|
|
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
2002-05-12 22:10:05 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2002-06-20 22:29:54 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.2 2002/06/20 20:29:28 momjian Exp $
|
2002-05-12 22:10:05 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* 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 "miscadmin.h"
|
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "executor/execdebug.h"
|
|
|
|
#include "executor/execdefs.h"
|
|
|
|
#include "executor/execdesc.h"
|
|
|
|
#include "executor/nodeFunctionscan.h"
|
|
|
|
#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(TupleTableSlot *slot,
|
|
|
|
Node *expr,
|
|
|
|
ExprContext *econtext,
|
|
|
|
TupleDesc tupdesc,
|
|
|
|
bool returnsTuple,
|
|
|
|
bool *isNull,
|
|
|
|
ExprDoneCond *isDone);
|
|
|
|
static FunctionMode get_functionmode(Node *expr);
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* Scan Support
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* FunctionNext
|
|
|
|
*
|
|
|
|
* This is a workhorse for ExecFunctionScan
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
static TupleTableSlot *
|
|
|
|
FunctionNext(FunctionScan *node)
|
|
|
|
{
|
|
|
|
TupleTableSlot *slot;
|
|
|
|
Node *expr;
|
|
|
|
ExprContext *econtext;
|
|
|
|
TupleDesc tupdesc;
|
|
|
|
EState *estate;
|
|
|
|
ScanDirection direction;
|
|
|
|
Tuplestorestate *tuplestorestate;
|
|
|
|
FunctionScanState *scanstate;
|
|
|
|
bool should_free;
|
|
|
|
HeapTuple heapTuple;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get information from the estate and scan state
|
|
|
|
*/
|
|
|
|
scanstate = (FunctionScanState *) node->scan.scanstate;
|
|
|
|
estate = node->scan.plan.state;
|
|
|
|
direction = estate->es_direction;
|
|
|
|
econtext = scanstate->csstate.cstate.cs_ExprContext;
|
|
|
|
|
|
|
|
tuplestorestate = scanstate->tuplestorestate;
|
|
|
|
tupdesc = scanstate->tupdesc;
|
|
|
|
expr = scanstate->funcexpr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If first time through, read all tuples from function and pass them to
|
|
|
|
* tuplestore.c. 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->csstate.css_ScanTupleSlot,
|
|
|
|
expr, econtext, tupdesc,
|
|
|
|
scanstate->returnsTuple,
|
|
|
|
&isNull, &isDone);
|
|
|
|
if (TupIsNull(slot))
|
|
|
|
break;
|
|
|
|
|
|
|
|
tuplestore_puttuple(tuplestorestate, (void *) slot->val);
|
|
|
|
ExecClearTuple(slot);
|
|
|
|
|
|
|
|
if (isDone != ExprMultipleResult)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Complete the store.
|
|
|
|
*/
|
|
|
|
tuplestore_donestoring(tuplestorestate);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* ExecFunctionScan(node)
|
|
|
|
*
|
|
|
|
* Scans the Function sequentially and returns the next qualifying
|
|
|
|
* tuple.
|
|
|
|
* It calls the ExecScan() routine and passes it the access method
|
|
|
|
* which retrieve tuples sequentially.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
TupleTableSlot *
|
|
|
|
ExecFunctionScan(FunctionScan *node)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* use FunctionNext as access method
|
|
|
|
*/
|
|
|
|
return ExecScan(&node->scan, (ExecScanAccessMtd) FunctionNext);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* ExecInitFunctionScan
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
|
|
|
|
{
|
|
|
|
FunctionScanState *scanstate;
|
|
|
|
RangeTblEntry *rte;
|
|
|
|
Oid funcrettype;
|
|
|
|
Oid funcrelid;
|
|
|
|
TupleDesc tupdesc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FunctionScan should not have any children.
|
|
|
|
*/
|
|
|
|
Assert(outerPlan((Plan *) node) == NULL);
|
|
|
|
Assert(innerPlan((Plan *) node) == NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* assign the node's execution state
|
|
|
|
*/
|
|
|
|
node->scan.plan.state = estate;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* create new ScanState for node
|
|
|
|
*/
|
|
|
|
scanstate = makeNode(FunctionScanState);
|
|
|
|
node->scan.scanstate = &scanstate->csstate;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Miscellaneous initialization
|
|
|
|
*
|
|
|
|
* create expression context for node
|
|
|
|
*/
|
|
|
|
ExecAssignExprContext(estate, &scanstate->csstate.cstate);
|
|
|
|
|
|
|
|
#define FUNCTIONSCAN_NSLOTS 2
|
|
|
|
|
|
|
|
/*
|
|
|
|
* tuple table initialization
|
|
|
|
*/
|
|
|
|
ExecInitResultTupleSlot(estate, &scanstate->csstate.cstate);
|
|
|
|
ExecInitScanTupleSlot(estate, &scanstate->csstate);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get info about function
|
|
|
|
*/
|
|
|
|
rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
|
|
|
|
Assert(rte->rtekind == RTE_FUNCTION);
|
|
|
|
funcrettype = exprType(rte->funcexpr);
|
|
|
|
funcrelid = typeidTypeRelid(funcrettype);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build a suitable tupledesc representing the output rows
|
|
|
|
*/
|
|
|
|
if (OidIsValid(funcrelid))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Composite data type, i.e. a table's row type
|
|
|
|
* Same as ordinary relation RTE
|
|
|
|
*/
|
|
|
|
Relation rel;
|
|
|
|
|
|
|
|
rel = relation_open(funcrelid, AccessShareLock);
|
|
|
|
tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
|
|
|
|
relation_close(rel, AccessShareLock);
|
|
|
|
scanstate->returnsTuple = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Must be a base data type, i.e. scalar
|
|
|
|
*/
|
|
|
|
char *attname = strVal(lfirst(rte->eref->colnames));
|
|
|
|
|
|
|
|
tupdesc = CreateTemplateTupleDesc(1);
|
|
|
|
TupleDescInitEntry(tupdesc,
|
|
|
|
(AttrNumber) 1,
|
|
|
|
attname,
|
|
|
|
funcrettype,
|
|
|
|
-1,
|
|
|
|
0,
|
|
|
|
false);
|
|
|
|
scanstate->returnsTuple = false;
|
|
|
|
}
|
|
|
|
scanstate->tupdesc = tupdesc;
|
|
|
|
ExecSetSlotDescriptor(scanstate->csstate.css_ScanTupleSlot,
|
|
|
|
tupdesc, false);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Other node-specific setup
|
|
|
|
*/
|
|
|
|
scanstate->tuplestorestate = NULL;
|
|
|
|
scanstate->funcexpr = rte->funcexpr;
|
|
|
|
|
|
|
|
scanstate->functionmode = get_functionmode(rte->funcexpr);
|
|
|
|
|
|
|
|
scanstate->csstate.cstate.cs_TupFromTlist = false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialize tuple type
|
|
|
|
*/
|
|
|
|
ExecAssignResultTypeFromTL((Plan *) node, &scanstate->csstate.cstate);
|
|
|
|
ExecAssignProjectionInfo((Plan *) node, &scanstate->csstate.cstate);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ExecCountSlotsFunctionScan(FunctionScan *node)
|
|
|
|
{
|
|
|
|
return ExecCountSlotsNode(outerPlan(node)) +
|
|
|
|
ExecCountSlotsNode(innerPlan(node)) +
|
|
|
|
FUNCTIONSCAN_NSLOTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* ExecEndFunctionScan
|
|
|
|
*
|
|
|
|
* frees any storage allocated through C routines.
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ExecEndFunctionScan(FunctionScan *node)
|
|
|
|
{
|
|
|
|
FunctionScanState *scanstate;
|
|
|
|
EState *estate;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get information from node
|
|
|
|
*/
|
|
|
|
scanstate = (FunctionScanState *) node->scan.scanstate;
|
|
|
|
estate = node->scan.plan.state;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free the projection info and the scan attribute info
|
|
|
|
*
|
|
|
|
* Note: we don't ExecFreeResultType(scanstate) because the rule manager
|
|
|
|
* depends on the tupType returned by ExecMain(). So for now, this is
|
|
|
|
* freed at end-transaction time. -cim 6/2/91
|
|
|
|
*/
|
|
|
|
ExecFreeProjectionInfo(&scanstate->csstate.cstate);
|
|
|
|
ExecFreeExprContext(&scanstate->csstate.cstate);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* clean out the tuple table
|
|
|
|
*/
|
|
|
|
ExecClearTuple(scanstate->csstate.cstate.cs_ResultTupleSlot);
|
|
|
|
ExecClearTuple(scanstate->csstate.css_ScanTupleSlot);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Release tuplestore resources
|
|
|
|
*/
|
|
|
|
if (scanstate->tuplestorestate != NULL)
|
|
|
|
tuplestore_end((Tuplestorestate *) scanstate->tuplestorestate);
|
|
|
|
scanstate->tuplestorestate = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* ExecFunctionMarkPos
|
|
|
|
*
|
|
|
|
* Calls tuplestore to save the current position in the stored file.
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ExecFunctionMarkPos(FunctionScan *node)
|
|
|
|
{
|
|
|
|
FunctionScanState *scanstate;
|
|
|
|
|
|
|
|
scanstate = (FunctionScanState *) node->scan.scanstate;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if we haven't materialized yet, just return.
|
|
|
|
*/
|
|
|
|
if (!scanstate->tuplestorestate)
|
|
|
|
return;
|
|
|
|
|
|
|
|
tuplestore_markpos((Tuplestorestate *) scanstate->tuplestorestate);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* ExecFunctionRestrPos
|
|
|
|
*
|
|
|
|
* Calls tuplestore to restore the last saved file position.
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ExecFunctionRestrPos(FunctionScan *node)
|
|
|
|
{
|
|
|
|
FunctionScanState *scanstate;
|
|
|
|
|
|
|
|
scanstate = (FunctionScanState *) node->scan.scanstate;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if we haven't materialized yet, just return.
|
|
|
|
*/
|
|
|
|
if (!scanstate->tuplestorestate)
|
|
|
|
return;
|
|
|
|
|
|
|
|
tuplestore_restorepos((Tuplestorestate *) scanstate->tuplestorestate);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* ExecFunctionReScan
|
|
|
|
*
|
|
|
|
* Rescans the relation.
|
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ExecFunctionReScan(FunctionScan *node, ExprContext *exprCtxt, Plan *parent)
|
|
|
|
{
|
|
|
|
FunctionScanState *scanstate;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get information from node
|
|
|
|
*/
|
|
|
|
scanstate = (FunctionScanState *) node->scan.scanstate;
|
|
|
|
|
|
|
|
ExecClearTuple(scanstate->csstate.cstate.cs_ResultTupleSlot);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we haven't materialized yet, just return.
|
|
|
|
*/
|
|
|
|
if (!scanstate->tuplestorestate)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here we have a choice whether to drop the tuplestore (and recompute
|
|
|
|
* the function outputs) or just rescan it. This should depend on
|
|
|
|
* whether the function expression contains parameters and/or is
|
|
|
|
* marked volatile. FIXME soon.
|
|
|
|
*/
|
|
|
|
if (node->scan.plan.chgParam != NULL)
|
|
|
|
{
|
|
|
|
tuplestore_end((Tuplestorestate *) scanstate->tuplestorestate);
|
|
|
|
scanstate->tuplestorestate = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
tuplestore_rescan((Tuplestorestate *) scanstate->tuplestorestate);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Run the underlying function to get the next tuple
|
|
|
|
*/
|
|
|
|
static TupleTableSlot *
|
|
|
|
function_getonetuple(TupleTableSlot *slot,
|
|
|
|
Node *expr,
|
|
|
|
ExprContext *econtext,
|
|
|
|
TupleDesc tupdesc,
|
|
|
|
bool returnsTuple,
|
|
|
|
bool *isNull,
|
|
|
|
ExprDoneCond *isDone)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Datum retDatum;
|
|
|
|
char nullflag;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
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 pointer */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return slot;
|
|
|
|
}
|
|
|
|
|
|
|
|
static FunctionMode
|
|
|
|
get_functionmode(Node *expr)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* for the moment, hardwire this
|
|
|
|
*/
|
|
|
|
return PM_REPEATEDCALL;
|
|
|
|
}
|