aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/Makefile8
-rw-r--r--src/backend/executor/execAmi.c15
-rw-r--r--src/backend/executor/execMain.c14
-rw-r--r--src/backend/executor/execProcnode.c27
-rw-r--r--src/backend/executor/execQual.c3
-rw-r--r--src/backend/executor/execUtils.c102
-rw-r--r--src/backend/executor/functions.c51
-rw-r--r--src/backend/executor/nodeFunctionscan.c469
-rw-r--r--src/backend/executor/nodeSubqueryscan.c4
9 files changed, 673 insertions, 20 deletions
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 363ea342e9f..0a66e1be03e 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -4,7 +4,7 @@
# Makefile for executor
#
# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.17 2001/09/18 01:59:06 tgl Exp $
+# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.18 2002/05/12 20:10:02 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -16,9 +16,9 @@ OBJS = execAmi.o execFlatten.o execJunk.o execMain.o \
execProcnode.o execQual.o execScan.o execTuples.o \
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o nodeHash.o \
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
- nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSetOp.o nodeSort.o \
- nodeUnique.o nodeLimit.o nodeGroup.o nodeSubplan.o \
- nodeSubqueryscan.o nodeTidscan.o spi.o
+ nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \
+ nodeSetOp.o nodeSort.o nodeUnique.o nodeLimit.o nodeGroup.o \
+ nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o spi.o
all: SUBSYS.o
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 119c89b1c27..d2fe9da9ada 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: execAmi.c,v 1.62 2002/03/02 21:39:24 momjian Exp $
+ * $Id: execAmi.c,v 1.63 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -35,6 +35,7 @@
#include "executor/nodeSort.h"
#include "executor/nodeSubplan.h"
#include "executor/nodeSubqueryscan.h"
+#include "executor/nodeFunctionscan.h"
#include "executor/nodeUnique.h"
@@ -100,6 +101,10 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
ExecSubqueryReScan((SubqueryScan *) node, exprCtxt, parent);
break;
+ case T_FunctionScan:
+ ExecFunctionReScan((FunctionScan *) node, exprCtxt, parent);
+ break;
+
case T_Material:
ExecMaterialReScan((Material *) node, exprCtxt, parent);
break;
@@ -187,6 +192,10 @@ ExecMarkPos(Plan *node)
ExecIndexMarkPos((IndexScan *) node);
break;
+ case T_FunctionScan:
+ ExecFunctionMarkPos((FunctionScan *) node);
+ break;
+
case T_Material:
ExecMaterialMarkPos((Material *) node);
break;
@@ -229,6 +238,10 @@ ExecRestrPos(Plan *node)
ExecIndexRestrPos((IndexScan *) node);
break;
+ case T_FunctionScan:
+ ExecFunctionRestrPos((FunctionScan *) node);
+ break;
+
case T_Material:
ExecMaterialRestrPos((Material *) node);
break;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 68fcb325a63..a2c43bc0359 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -27,7 +27,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.160 2002/04/27 21:24:34 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.161 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -311,7 +311,7 @@ ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
/* Recursively check the subquery */
rte = rt_fetch(scan->scan.scanrelid, rangeTable);
- Assert(rte->subquery != NULL);
+ Assert(rte->rtekind == RTE_SUBQUERY);
ExecCheckQueryPerms(operation, rte->subquery, scan->subplan);
break;
}
@@ -362,10 +362,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
Oid userid;
AclResult aclcheck_result;
- /*
- * If it's a subquery RTE, ignore it --- it will be checked when
- * ExecCheckPlanPerms finds the SubqueryScan node for it.
- */
+ /*
+ * Only plain-relation RTEs need to be checked here. Subquery RTEs
+ * will be checked when ExecCheckPlanPerms finds the SubqueryScan node,
+ * and function RTEs are checked by init_fcache when the function is
+ * prepared for execution. Join and special RTEs need no checks.
+ */
if (rte->rtekind != RTE_RELATION)
return;
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 5cbd2ea5622..c0f005f1e67 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.28 2001/10/25 05:49:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.29 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -96,6 +96,7 @@
#include "executor/nodeSort.h"
#include "executor/nodeSubplan.h"
#include "executor/nodeSubqueryscan.h"
+#include "executor/nodeFunctionscan.h"
#include "executor/nodeUnique.h"
#include "miscadmin.h"
#include "tcop/tcopprot.h"
@@ -168,6 +169,11 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
parent);
break;
+ case T_FunctionScan:
+ result = ExecInitFunctionScan((FunctionScan *) node, estate,
+ parent);
+ break;
+
/*
* join nodes
*/
@@ -297,6 +303,10 @@ ExecProcNode(Plan *node, Plan *parent)
result = ExecSubqueryScan((SubqueryScan *) node);
break;
+ case T_FunctionScan:
+ result = ExecFunctionScan((FunctionScan *) node);
+ break;
+
/*
* join nodes
*/
@@ -392,6 +402,9 @@ ExecCountSlotsNode(Plan *node)
case T_SubqueryScan:
return ExecCountSlotsSubqueryScan((SubqueryScan *) node);
+ case T_FunctionScan:
+ return ExecCountSlotsFunctionScan((FunctionScan *) node);
+
/*
* join nodes
*/
@@ -503,6 +516,10 @@ ExecEndNode(Plan *node, Plan *parent)
ExecEndSubqueryScan((SubqueryScan *) node);
break;
+ case T_FunctionScan:
+ ExecEndFunctionScan((FunctionScan *) node);
+ break;
+
/*
* join nodes
*/
@@ -640,6 +657,14 @@ ExecGetTupType(Plan *node)
}
break;
+ case T_FunctionScan:
+ {
+ CommonScanState *scanstate = ((FunctionScan *) node)->scan.scanstate;
+
+ slot = scanstate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
case T_Material:
{
MaterialState *matstate = ((Material *) node)->matstate;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 5cc1bbaa718..bebb664f299 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.91 2002/04/27 03:45:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.92 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -700,6 +700,7 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
{
fcinfo.resultinfo = (Node *) &rsinfo;
rsinfo.type = T_ReturnSetInfo;
+ rsinfo.econtext = econtext;
}
/*
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 9b03401e444..a6b5048326b 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.80 2002/04/12 20:38:26 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.81 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,6 +20,9 @@
* ExecCloseIndices | referenced by InitPlan, EndPlan,
* ExecInsertIndexTuples / ExecAppend, ExecReplace
*
+ * RegisterExprContextCallback Register function shutdown callback
+ * UnregisterExprContextCallback Deregister function shutdown callback
+ *
* NOTES
* This file has traditionally been the place to stick misc.
* executor support stuff that doesn't really go anyplace else.
@@ -58,6 +61,9 @@ extern int NIndexTupleProcessed; /* have to be defined in the
* access method level so that the
* cinterface.a will link ok. */
+
+static void ShutdownExprContext(ExprContext *econtext);
+
/* ----------------------------------------------------------------
* statistic functions
* ----------------------------------------------------------------
@@ -120,8 +126,6 @@ DisplayTupleCount(FILE *statfp)
/* ----------------------------------------------------------------
* miscellaneous node-init support functions
- *
- * ExecAssignExprContext - assigns the node's expression context
* ----------------------------------------------------------------
*/
@@ -160,6 +164,7 @@ ExecAssignExprContext(EState *estate, CommonState *commonstate)
econtext->ecxt_param_list_info = estate->es_param_list_info;
econtext->ecxt_aggvalues = NULL;
econtext->ecxt_aggnulls = NULL;
+ econtext->ecxt_callbacks = NULL;
commonstate->cs_ExprContext = econtext;
}
@@ -204,6 +209,7 @@ MakeExprContext(TupleTableSlot *slot,
econtext->ecxt_param_list_info = NULL;
econtext->ecxt_aggvalues = NULL;
econtext->ecxt_aggnulls = NULL;
+ econtext->ecxt_callbacks = NULL;
return econtext;
}
@@ -216,6 +222,9 @@ MakeExprContext(TupleTableSlot *slot,
void
FreeExprContext(ExprContext *econtext)
{
+ /* Call any registered callbacks */
+ ShutdownExprContext(econtext);
+ /* And clean up the memory used */
MemoryContextDelete(econtext->ecxt_per_tuple_memory);
pfree(econtext);
}
@@ -370,6 +379,11 @@ ExecFreeExprContext(CommonState *commonstate)
return;
/*
+ * clean up any registered callbacks
+ */
+ ShutdownExprContext(econtext);
+
+ /*
* clean up memory used.
*/
MemoryContextDelete(econtext->ecxt_per_tuple_memory);
@@ -689,3 +703,85 @@ SetChangedParamList(Plan *node, List *newchg)
node->chgParam = lappendi(node->chgParam, paramId);
}
}
+
+/*
+ * Register a shutdown callback in an ExprContext.
+ *
+ * Shutdown callbacks will be called (in reverse order of registration)
+ * when the ExprContext is deleted or rescanned. This provides a hook
+ * for functions called in the context to do any cleanup needed --- it's
+ * particularly useful for functions returning sets. Note that the
+ * callback will *not* be called in the event that execution is aborted
+ * by an error.
+ */
+void
+RegisterExprContextCallback(ExprContext *econtext,
+ ExprContextCallbackFunction function,
+ Datum arg)
+{
+ ExprContext_CB *ecxt_callback;
+
+ /* Save the info in appropriate memory context */
+ ecxt_callback = (ExprContext_CB *)
+ MemoryContextAlloc(econtext->ecxt_per_query_memory,
+ sizeof(ExprContext_CB));
+
+ ecxt_callback->function = function;
+ ecxt_callback->arg = arg;
+
+ /* link to front of list for appropriate execution order */
+ ecxt_callback->next = econtext->ecxt_callbacks;
+ econtext->ecxt_callbacks = ecxt_callback;
+}
+
+/*
+ * Deregister a shutdown callback in an ExprContext.
+ *
+ * Any list entries matching the function and arg will be removed.
+ * This can be used if it's no longer necessary to call the callback.
+ */
+void
+UnregisterExprContextCallback(ExprContext *econtext,
+ ExprContextCallbackFunction function,
+ Datum arg)
+{
+ ExprContext_CB **prev_callback;
+ ExprContext_CB *ecxt_callback;
+
+ prev_callback = &econtext->ecxt_callbacks;
+
+ while ((ecxt_callback = *prev_callback) != NULL)
+ {
+ if (ecxt_callback->function == function && ecxt_callback->arg == arg)
+ {
+ *prev_callback = ecxt_callback->next;
+ pfree(ecxt_callback);
+ }
+ else
+ {
+ prev_callback = &ecxt_callback->next;
+ }
+ }
+}
+
+/*
+ * Call all the shutdown callbacks registered in an ExprContext.
+ *
+ * The callback list is emptied (important in case this is only a rescan
+ * reset, and not deletion of the ExprContext).
+ */
+static void
+ShutdownExprContext(ExprContext *econtext)
+{
+ ExprContext_CB *ecxt_callback;
+
+ /*
+ * Call each callback function in reverse registration order.
+ */
+ while ((ecxt_callback = econtext->ecxt_callbacks) != NULL)
+ {
+ econtext->ecxt_callbacks = ecxt_callback->next;
+ (*ecxt_callback->function) (ecxt_callback->arg);
+ pfree(ecxt_callback);
+ }
+}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 885d93a2aff..938f7e17f93 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.49 2002/02/27 19:34:51 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.50 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,7 +28,7 @@
/*
- * We have an execution_state record for each query in the function.
+ * We have an execution_state record for each query in a function.
*/
typedef enum
{
@@ -56,6 +56,7 @@ typedef struct
int typlen; /* length of the return type */
bool typbyval; /* true if return type is pass by value */
bool returnsTuple; /* true if return type is a tuple */
+ bool shutdown_reg; /* true if registered shutdown callback */
TupleTableSlot *funcSlot; /* if one result we need to copy it before
* we end execution of the function and
@@ -79,6 +80,7 @@ static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo);
static Datum postquel_execute(execution_state *es,
FunctionCallInfo fcinfo,
SQLFunctionCachePtr fcache);
+static void ShutdownSQLFunction(Datum arg);
static execution_state *
@@ -546,6 +548,15 @@ fmgr_sql(PG_FUNCTION_ARGS)
elog(ERROR, "Set-valued function called in context that cannot accept a set");
fcinfo->isnull = true;
result = (Datum) 0;
+
+ /* Deregister shutdown callback, if we made one */
+ if (fcache->shutdown_reg)
+ {
+ UnregisterExprContextCallback(rsi->econtext,
+ ShutdownSQLFunction,
+ PointerGetDatum(fcache));
+ fcache->shutdown_reg = false;
+ }
}
MemoryContextSwitchTo(oldcontext);
@@ -570,9 +581,45 @@ fmgr_sql(PG_FUNCTION_ARGS)
rsi->isDone = ExprMultipleResult;
else
elog(ERROR, "Set-valued function called in context that cannot accept a set");
+
+ /*
+ * Ensure we will get shut down cleanly if the exprcontext is
+ * not run to completion.
+ */
+ if (!fcache->shutdown_reg)
+ {
+ RegisterExprContextCallback(rsi->econtext,
+ ShutdownSQLFunction,
+ PointerGetDatum(fcache));
+ fcache->shutdown_reg = true;
+ }
}
MemoryContextSwitchTo(oldcontext);
return result;
}
+
+/*
+ * callback function in case a function-returning-set needs to be shut down
+ * before it has been run to completion
+ */
+static void
+ShutdownSQLFunction(Datum arg)
+{
+ SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) DatumGetPointer(arg);
+ execution_state *es = fcache->func_state;
+
+ while (es != NULL)
+ {
+ /* Shut down anything still running */
+ if (es->status == F_EXEC_RUN)
+ postquel_end(es);
+ /* Reset states to START in case we're called again */
+ es->status = F_EXEC_START;
+ es = es->next;
+ }
+
+ /* execUtils will deregister the callback... */
+ fcache->shutdown_reg = false;
+}
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;
+}
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index edd4640ba41..12f659847c6 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.11 2001/10/25 05:49:29 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.12 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -146,7 +146,7 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent)
* This should agree with ExecInitSubPlan
*/
rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
- Assert(rte->subquery != NULL);
+ Assert(rte->rtekind == RTE_SUBQUERY);
sp_estate = CreateExecutorState();
subquerystate->sss_SubEState = sp_estate;