1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-30 21:42:05 +03:00

First pass at set-returning-functions in FROM, by Joe Conway with

some kibitzing from Tom Lane.  Not everything works yet, and there's
no documentation or regression test, but let's commit this so Joe
doesn't need to cope with tracking changes in so many files ...
This commit is contained in:
Tom Lane
2002-05-12 20:10:05 +00:00
parent 71009354c8
commit f9e4f611a1
48 changed files with 1813 additions and 329 deletions

View File

@ -0,0 +1,469 @@
/*-------------------------------------------------------------------------
*
* nodeFunctionscan.c
* Support routines for scanning RangeFunctions (functions in rangetable).
*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.1 2002/05/12 20:10:02 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 "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;
}