diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2002-05-12 20:10:05 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2002-05-12 20:10:05 +0000 |
commit | f9e4f611a18f64fd9106a72ec9af9e2220075780 (patch) | |
tree | bfbc1d3d9fb5a008d8fe3405dce3366659c7e7cc /src/backend/executor | |
parent | 71009354c848964e657e540e24dac6b4c9a81570 (diff) | |
download | postgresql-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')
-rw-r--r-- | src/backend/executor/Makefile | 8 | ||||
-rw-r--r-- | src/backend/executor/execAmi.c | 15 | ||||
-rw-r--r-- | src/backend/executor/execMain.c | 14 | ||||
-rw-r--r-- | src/backend/executor/execProcnode.c | 27 | ||||
-rw-r--r-- | src/backend/executor/execQual.c | 3 | ||||
-rw-r--r-- | src/backend/executor/execUtils.c | 102 | ||||
-rw-r--r-- | src/backend/executor/functions.c | 51 | ||||
-rw-r--r-- | src/backend/executor/nodeFunctionscan.c | 469 | ||||
-rw-r--r-- | src/backend/executor/nodeSubqueryscan.c | 4 |
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; |