aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeFunctionscan.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2002-05-12 20:10:05 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2002-05-12 20:10:05 +0000
commitf9e4f611a18f64fd9106a72ec9af9e2220075780 (patch)
treebfbc1d3d9fb5a008d8fe3405dce3366659c7e7cc /src/backend/executor/nodeFunctionscan.c
parent71009354c848964e657e540e24dac6b4c9a81570 (diff)
downloadpostgresql-f9e4f611a18f64fd9106a72ec9af9e2220075780.tar.gz
postgresql-f9e4f611a18f64fd9106a72ec9af9e2220075780.zip
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 ...
Diffstat (limited to 'src/backend/executor/nodeFunctionscan.c')
-rw-r--r--src/backend/executor/nodeFunctionscan.c469
1 files changed, 469 insertions, 0 deletions
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
new file mode 100644
index 00000000000..7b6d466a812
--- /dev/null
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -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;
+}