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

@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.18 2002/05/05 00:03:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.19 2002/05/12 20:10:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -492,7 +492,8 @@ FuncnameGetCandidates(List *names, int nargs)
elog(ERROR, "Cross-database references are not implemented");
break;
default:
elog(ERROR, "Improper qualified name (too many dotted names)");
elog(ERROR, "Improper qualified name (too many dotted names): %s",
NameListToString(names));
break;
}
@ -746,7 +747,8 @@ OpernameGetCandidates(List *names, char oprkind)
elog(ERROR, "Cross-database references are not implemented");
break;
default:
elog(ERROR, "Improper qualified name (too many dotted names)");
elog(ERROR, "Improper qualified name (too many dotted names): %s",
NameListToString(names));
break;
}
@ -1199,7 +1201,8 @@ QualifiedNameGetCreationNamespace(List *names, char **objname_p)
elog(ERROR, "Cross-database references are not implemented");
break;
default:
elog(ERROR, "Improper qualified name (too many dotted names)");
elog(ERROR, "Improper qualified name (too many dotted names): %s",
NameListToString(names));
break;
}

View File

@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.76 2002/05/03 15:56:45 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.77 2002/05/12 20:10:02 tgl Exp $
*
*/
@ -262,6 +262,9 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
case T_SubqueryScan:
pname = "Subquery Scan";
break;
case T_FunctionScan:
pname = "Function Scan";
break;
case T_Material:
pname = "Materialize";
break;
@ -336,7 +339,7 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
char *relname;
/* Assume it's on a real relation */
Assert(rte->relid);
Assert(rte->rtekind == RTE_RELATION);
/* We only show the rel name, not schema name */
relname = get_rel_name(rte->relid);
@ -358,6 +361,33 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
quote_identifier(rte->eref->aliasname));
}
break;
case T_FunctionScan:
if (((Scan *) plan)->scanrelid > 0)
{
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
es->rtable);
Expr *expr;
Func *funcnode;
Oid funcid;
char *proname;
/* Assert it's on a RangeFunction */
Assert(rte->rtekind == RTE_FUNCTION);
expr = (Expr *) rte->funcexpr;
funcnode = (Func *) expr->oper;
funcid = funcnode->funcid;
/* We only show the func name, not schema name */
proname = get_func_name(funcid);
appendStringInfo(str, " on %s",
quote_identifier(proname));
if (strcmp(rte->eref->aliasname, proname) != 0)
appendStringInfo(str, " %s",
quote_identifier(rte->eref->aliasname));
}
break;
default:
break;
}
@ -397,6 +427,7 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
break;
case T_SeqScan:
case T_TidScan:
case T_FunctionScan:
show_scan_qual(plan->qual, false,
"Filter",
((Scan *) plan)->scanrelid,
@ -545,7 +576,7 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
es->rtable);
List *saved_rtable = es->rtable;
Assert(rte->subquery != NULL);
Assert(rte->rtekind == RTE_SUBQUERY);
es->rtable = rte->subquery->rtable;
for (i = 0; i < indent; i++)
@ -623,11 +654,7 @@ show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
/* Generate deparse context */
Assert(scanrelid > 0 && scanrelid <= length(es->rtable));
rte = rt_fetch(scanrelid, es->rtable);
/* Assume it's on a real relation */
Assert(rte->relid);
scancontext = deparse_context_for_relation(rte->eref->aliasname,
rte->relid);
scancontext = deparse_context_for_rte(rte);
/*
* If we have an outer plan that is referenced by the qual, add it to

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.72 2002/04/27 03:45:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.73 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -452,7 +452,8 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
elog(ERROR, "Cross-database references are not implemented");
break;
default:
elog(ERROR, "Improper opclass name (too many dotted names)");
elog(ERROR, "Improper opclass name (too many dotted names): %s",
NameListToString(attribute->opclass));
break;
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
/*

View File

@ -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);
}
@ -369,6 +378,11 @@ ExecFreeExprContext(CommonState *commonstate)
if (econtext == NULL)
return;
/*
* clean up any registered callbacks
*/
ShutdownExprContext(econtext);
/*
* clean up memory used.
*/
@ -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);
}
}

View File

@ -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;
}

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;
}

View File

@ -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;

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.182 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.183 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -310,6 +310,23 @@ _copySubqueryScan(SubqueryScan *from)
return newnode;
}
/* ----------------
* _copyFunctionScan
* ----------------
*/
static FunctionScan *
_copyFunctionScan(FunctionScan *from)
{
FunctionScan *newnode = makeNode(FunctionScan);
/*
* copy node superclass fields
*/
CopyPlanFields((Plan *) from, (Plan *) newnode);
CopyScanFields((Scan *) from, (Scan *) newnode);
return newnode;
}
/* ----------------
* CopyJoinFields
@ -1083,7 +1100,7 @@ _copyRelOptInfo(RelOptInfo *from)
Node_Copy(from, newnode, cheapest_total_path);
newnode->pruneable = from->pruneable;
newnode->issubquery = from->issubquery;
newnode->rtekind = from->rtekind;
Node_Copy(from, newnode, indexlist);
newnode->pages = from->pages;
newnode->tuples = from->tuples;
@ -1473,6 +1490,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
newnode->rtekind = from->rtekind;
newnode->relid = from->relid;
Node_Copy(from, newnode, subquery);
Node_Copy(from, newnode, funcexpr);
newnode->jointype = from->jointype;
Node_Copy(from, newnode, joinaliasvars);
Node_Copy(from, newnode, alias);
@ -1690,6 +1708,17 @@ _copyRangeSubselect(RangeSubselect *from)
return newnode;
}
static RangeFunction *
_copyRangeFunction(RangeFunction *from)
{
RangeFunction *newnode = makeNode(RangeFunction);
Node_Copy(from, newnode, funccallnode);
Node_Copy(from, newnode, alias);
return newnode;
}
static TypeCast *
_copyTypeCast(TypeCast *from)
{
@ -2621,6 +2650,9 @@ copyObject(void *from)
case T_SubqueryScan:
retval = _copySubqueryScan(from);
break;
case T_FunctionScan:
retval = _copyFunctionScan(from);
break;
case T_Join:
retval = _copyJoin(from);
break;
@ -3001,6 +3033,9 @@ copyObject(void *from)
case T_RangeSubselect:
retval = _copyRangeSubselect(from);
break;
case T_RangeFunction:
retval = _copyRangeFunction(from);
break;
case T_TypeName:
retval = _copyTypeName(from);
break;

View File

@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.130 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.131 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1573,6 +1573,17 @@ _equalRangeSubselect(RangeSubselect *a, RangeSubselect *b)
return true;
}
static bool
_equalRangeFunction(RangeFunction *a, RangeFunction *b)
{
if (!equal(a->funccallnode, b->funccallnode))
return false;
if (!equal(a->alias, b->alias))
return false;
return true;
}
static bool
_equalTypeName(TypeName *a, TypeName *b)
{
@ -1678,6 +1689,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
return false;
if (!equal(a->subquery, b->subquery))
return false;
if (!equal(a->funcexpr, b->funcexpr))
return false;
if (a->jointype != b->jointype)
return false;
if (!equal(a->joinaliasvars, b->joinaliasvars))
@ -2166,6 +2179,9 @@ equal(void *a, void *b)
case T_RangeSubselect:
retval = _equalRangeSubselect(a, b);
break;
case T_RangeFunction:
retval = _equalRangeFunction(a, b);
break;
case T_TypeName:
retval = _equalTypeName(a, b);
break;

View File

@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.157 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.158 2002/05/12 20:10:03 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@ -551,6 +551,18 @@ _outSubqueryScan(StringInfo str, SubqueryScan *node)
_outNode(str, node->subplan);
}
/*
* FunctionScan is a subclass of Scan
*/
static void
_outFunctionScan(StringInfo str, FunctionScan *node)
{
appendStringInfo(str, " FUNCTIONSCAN ");
_outPlanInfo(str, (Plan *) node);
appendStringInfo(str, " :scanrelid %u ", node->scan.scanrelid);
}
/*
* Material is a subclass of Plan
*/
@ -980,6 +992,10 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
appendStringInfo(str, ":subquery ");
_outNode(str, node->subquery);
break;
case RTE_FUNCTION:
appendStringInfo(str, ":funcexpr ");
_outNode(str, node->funcexpr);
break;
case RTE_JOIN:
appendStringInfo(str, ":jointype %d :joinaliasvars ",
(int) node->jointype);
@ -1598,6 +1614,9 @@ _outNode(StringInfo str, void *obj)
case T_SubqueryScan:
_outSubqueryScan(str, obj);
break;
case T_FunctionScan:
_outFunctionScan(str, obj);
break;
case T_Material:
_outMaterial(str, obj);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.54 2002/03/24 04:31:07 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.55 2002/05/12 20:10:03 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -254,12 +254,33 @@ print_rt(List *rtable)
{
RangeTblEntry *rte = lfirst(l);
if (rte->rtekind == RTE_RELATION)
printf("%d\t%s\t%u",
i, rte->eref->aliasname, rte->relid);
else
printf("%d\t%s\t[subquery]",
i, rte->eref->aliasname);
switch (rte->rtekind)
{
case RTE_RELATION:
printf("%d\t%s\t%u",
i, rte->eref->aliasname, rte->relid);
break;
case RTE_SUBQUERY:
printf("%d\t%s\t[subquery]",
i, rte->eref->aliasname);
break;
case RTE_FUNCTION:
printf("%d\t%s\t[rangefunction]",
i, rte->eref->aliasname);
break;
case RTE_JOIN:
printf("%d\t%s\t[join]",
i, rte->eref->aliasname);
break;
case RTE_SPECIAL:
printf("%d\t%s\t[special]",
i, rte->eref->aliasname);
break;
default:
printf("%d\t%s\t[unknown rtekind]",
i, rte->eref->aliasname);
}
printf("\t%s\t%s\n",
(rte->inh ? "inh" : ""),
(rte->inFromCl ? "inFromCl" : ""));
@ -459,6 +480,8 @@ plannode_type(Plan *p)
return "TIDSCAN";
case T_SubqueryScan:
return "SUBQUERYSCAN";
case T_FunctionScan:
return "FUNCTIONSCAN";
case T_Join:
return "JOIN";
case T_NestLoop:
@ -489,12 +512,8 @@ plannode_type(Plan *p)
}
/*
prints the ascii description of the plan nodes
does this recursively by doing a depth-first traversal of the
plan tree. for SeqScan and IndexScan, the name of the table is also
printed out
*/
* Recursively prints a simple text description of the plan tree
*/
void
print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
{
@ -523,6 +542,13 @@ print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
}
else if (IsA(p, FunctionScan))
{
RangeTblEntry *rte;
rte = rt_fetch(((FunctionScan *) p)->scan.scanrelid, parsetree->rtable);
StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
}
else
extraInfo[0] = '\0';
if (extraInfo[0] != '\0')

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.120 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.121 2002/05/12 20:10:03 tgl Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
@ -651,6 +651,24 @@ _readSubqueryScan(void)
return local_node;
}
/* ----------------
* _readFunctionScan
*
* FunctionScan is a subclass of Scan
* ----------------
*/
static FunctionScan *
_readFunctionScan(void)
{
FunctionScan *local_node;
local_node = makeNode(FunctionScan);
_getScan((Scan *) local_node);
return local_node;
}
/* ----------------
* _readSort
*
@ -1514,6 +1532,11 @@ _readRangeTblEntry(void)
local_node->subquery = nodeRead(true); /* now read it */
break;
case RTE_FUNCTION:
token = pg_strtok(&length); /* eat :funcexpr */
local_node->funcexpr = nodeRead(true); /* now read it */
break;
case RTE_JOIN:
token = pg_strtok(&length); /* eat :jointype */
token = pg_strtok(&length); /* get jointype */
@ -2031,6 +2054,8 @@ parsePlanString(void)
return_value = _readTidScan();
else if (length == 12 && strncmp(token, "SUBQUERYSCAN", length) == 0)
return_value = _readSubqueryScan();
else if (length == 12 && strncmp(token, "FUNCTIONSCAN", length) == 0)
return_value = _readFunctionScan();
else if (length == 4 && strncmp(token, "SORT", length) == 0)
return_value = _readSort();
else if (length == 6 && strncmp(token, "AGGREG", length) == 0)

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.83 2001/12/10 22:54:12 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.84 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -42,6 +42,8 @@ static void set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
List *inheritlist);
static void set_subquery_pathlist(Query *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
static void set_function_pathlist(Query *root, RelOptInfo *rel,
RangeTblEntry *rte);
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
List *initial_rels);
@ -98,11 +100,16 @@ set_base_rel_pathlists(Query *root)
rti = lfirsti(rel->relids);
rte = rt_fetch(rti, root->rtable);
if (rel->issubquery)
if (rel->rtekind == RTE_SUBQUERY)
{
/* Subquery --- generate a separate plan for it */
set_subquery_pathlist(root, rel, rti, rte);
}
else if (rel->rtekind == RTE_FUNCTION)
{
/* RangeFunction --- generate a separate plan for it */
set_function_pathlist(root, rel, rte);
}
else if ((inheritlist = expand_inherted_rtentry(root, rti, true))
!= NIL)
{
@ -385,6 +392,23 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
set_cheapest(rel);
}
/*
* set_function_pathlist
* Build the (single) access path for a function RTE
*/
static void
set_function_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
{
/* Mark rel with estimated output rows, width, etc */
set_function_size_estimates(root, rel);
/* Generate appropriate path */
add_path(rel, create_functionscan_path(root, rel));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
/*
* make_fromexpr_rel
* Build access paths for a FromExpr jointree node.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.49 2002/03/06 06:09:50 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.50 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -381,7 +381,7 @@ clause_selectivity(Query *root,
{
RangeTblEntry *rte = rt_fetch(var->varno, root->rtable);
if (rte->subquery)
if (rte->rtekind == RTE_SUBQUERY)
{
/*
* XXX not smart about subquery references... any way to

View File

@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.83 2002/03/12 00:51:42 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.84 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -111,7 +111,7 @@ cost_seqscan(Path *path, Query *root,
/* Should only be applied to base relations */
Assert(length(baserel->relids) == 1);
Assert(!baserel->issubquery);
Assert(baserel->rtekind == RTE_RELATION);
if (!enable_seqscan)
startup_cost += disable_cost;
@ -224,9 +224,10 @@ cost_index(Path *path, Query *root,
b;
/* Should only be applied to base relations */
Assert(IsA(baserel, RelOptInfo) &&IsA(index, IndexOptInfo));
Assert(IsA(baserel, RelOptInfo) &&
IsA(index, IndexOptInfo));
Assert(length(baserel->relids) == 1);
Assert(!baserel->issubquery);
Assert(baserel->rtekind == RTE_RELATION);
if (!enable_indexscan && !is_injoin)
startup_cost += disable_cost;
@ -372,6 +373,10 @@ cost_tidscan(Path *path, Query *root,
Cost cpu_per_tuple;
int ntuples = length(tideval);
/* Should only be applied to base relations */
Assert(length(baserel->relids) == 1);
Assert(baserel->rtekind == RTE_RELATION);
if (!enable_tidscan)
startup_cost += disable_cost;
@ -386,6 +391,36 @@ cost_tidscan(Path *path, Query *root,
path->total_cost = startup_cost + run_cost;
}
/*
* cost_functionscan
* Determines and returns the cost of scanning a function RTE.
*/
void
cost_functionscan(Path *path, Query *root, RelOptInfo *baserel)
{
Cost startup_cost = 0;
Cost run_cost = 0;
Cost cpu_per_tuple;
/* Should only be applied to base relations that are functions */
Assert(length(baserel->relids) == 1);
Assert(baserel->rtekind == RTE_FUNCTION);
/*
* For now, estimate function's cost at one operator eval per function
* call. Someday we should revive the function cost estimate columns in
* pg_proc...
*/
cpu_per_tuple = cpu_operator_cost;
/* Add scanning CPU costs */
cpu_per_tuple += cpu_tuple_cost + baserel->baserestrictcost;
run_cost += cpu_per_tuple * baserel->tuples;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
/*
* cost_sort
* Determines and returns the cost of sorting a relation.
@ -1298,6 +1333,54 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
rel->width = outer_rel->width + inner_rel->width;
}
/*
* set_function_size_estimates
* Set the size estimates for a base relation that is a function call.
*
* The rel's targetlist and restrictinfo list must have been constructed
* already.
*
* We set the following fields of the rel node:
* rows: the estimated number of output tuples (after applying
* restriction clauses).
* width: the estimated average output tuple width in bytes.
* baserestrictcost: estimated cost of evaluating baserestrictinfo clauses.
*/
void
set_function_size_estimates(Query *root, RelOptInfo *rel)
{
/* Should only be applied to base relations that are functions */
Assert(length(rel->relids) == 1);
Assert(rel->rtekind == RTE_FUNCTION);
/*
* Estimate number of rows the function itself will return.
*
* XXX no idea how to do this yet; but should at least check whether
* function returns set or not...
*/
rel->tuples = 1000;
/* Now estimate number of output rows */
rel->rows = rel->tuples *
restrictlist_selectivity(root,
rel->baserestrictinfo,
lfirsti(rel->relids));
/*
* Force estimate to be at least one row, to make explain output look
* better and to avoid possible divide-by-zero when interpolating
* cost.
*/
if (rel->rows < 1.0)
rel->rows = 1.0;
rel->baserestrictcost = cost_qual_eval(rel->baserestrictinfo);
set_rel_width(root, rel);
}
/*
* set_rel_width
* Set the estimated output width of the relation.

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.113 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.114 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -43,6 +43,8 @@ static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist,
List *scan_clauses);
static SubqueryScan *create_subqueryscan_plan(Path *best_path,
List *tlist, List *scan_clauses);
static FunctionScan *create_functionscan_plan(Path *best_path,
List *tlist, List *scan_clauses);
static NestLoop *create_nestloop_plan(Query *root,
NestPath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
@ -77,6 +79,8 @@ static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
ScanDirection indexscandir);
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
List *tideval);
static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
Index scanrelid);
static NestLoop *make_nestloop(List *tlist,
List *joinclauses, List *otherclauses,
Plan *lefttree, Plan *righttree,
@ -119,6 +123,7 @@ create_plan(Query *root, Path *best_path)
case T_SeqScan:
case T_TidScan:
case T_SubqueryScan:
case T_FunctionScan:
plan = (Plan *) create_scan_plan(root, best_path);
break;
case T_HashJoin:
@ -200,6 +205,12 @@ create_scan_plan(Query *root, Path *best_path)
scan_clauses);
break;
case T_FunctionScan:
plan = (Scan *) create_functionscan_plan(best_path,
tlist,
scan_clauses);
break;
default:
elog(ERROR, "create_scan_plan: unknown node type: %d",
best_path->pathtype);
@ -353,7 +364,7 @@ create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
/* there should be exactly one base rel involved... */
Assert(length(best_path->parent->relids) == 1);
Assert(!best_path->parent->issubquery);
Assert(best_path->parent->rtekind == RTE_RELATION);
scan_relid = (Index) lfirsti(best_path->parent->relids);
@ -397,7 +408,7 @@ create_indexscan_plan(Query *root,
/* there should be exactly one base rel involved... */
Assert(length(best_path->path.parent->relids) == 1);
Assert(!best_path->path.parent->issubquery);
Assert(best_path->path.parent->rtekind == RTE_RELATION);
baserelid = lfirsti(best_path->path.parent->relids);
@ -515,7 +526,7 @@ create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
/* there should be exactly one base rel involved... */
Assert(length(best_path->path.parent->relids) == 1);
Assert(!best_path->path.parent->issubquery);
Assert(best_path->path.parent->rtekind == RTE_RELATION);
scan_relid = (Index) lfirsti(best_path->path.parent->relids);
@ -546,7 +557,7 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
/* there should be exactly one base rel involved... */
Assert(length(best_path->parent->relids) == 1);
/* and it must be a subquery */
Assert(best_path->parent->issubquery);
Assert(best_path->parent->rtekind == RTE_SUBQUERY);
scan_relid = (Index) lfirsti(best_path->parent->relids);
@ -558,6 +569,31 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
return scan_plan;
}
/*
* create_functionscan_plan
* Returns a functionscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
static FunctionScan *
create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses)
{
FunctionScan *scan_plan;
Index scan_relid;
/* there should be exactly one base rel involved... */
Assert(length(best_path->parent->relids) == 1);
/* and it must be a function */
Assert(best_path->parent->rtekind == RTE_FUNCTION);
scan_relid = (Index) lfirsti(best_path->parent->relids);
scan_plan = make_functionscan(tlist, scan_clauses, scan_relid);
copy_path_costsize(&scan_plan->scan.plan, best_path);
return scan_plan;
}
/*****************************************************************************
*
* JOIN METHODS
@ -791,6 +827,7 @@ create_mergejoin_plan(Query *root,
{
case T_SeqScan:
case T_IndexScan:
case T_FunctionScan:
case T_Material:
case T_Sort:
/* OK, these inner plans support mark/restore */
@ -1305,6 +1342,26 @@ make_subqueryscan(List *qptlist,
return node;
}
static FunctionScan *
make_functionscan(List *qptlist,
List *qpqual,
Index scanrelid)
{
FunctionScan *node = makeNode(FunctionScan);
Plan *plan = &node->scan.plan;
/* cost should be inserted by caller */
plan->state = (EState *) NULL;
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
node->scan.scanstate = (CommonScanState *) NULL;
return node;
}
Append *
make_append(List *appendplans, bool isTarget, List *tlist)
{

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.75 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.76 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -120,6 +120,10 @@ set_plan_references(Query *root, Plan *plan)
/* Recurse into subplan too */
set_plan_references(root, ((SubqueryScan *) plan)->subplan);
break;
case T_FunctionScan:
fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual);
break;
case T_NestLoop:
set_join_references(root, (Join *) plan);
fix_expr_references(plan, (Node *) plan->targetlist);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.51 2002/04/16 23:08:11 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.52 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -352,13 +352,13 @@ make_subplan(SubLink *slink)
}
break;
case T_Material:
case T_FunctionScan:
case T_Sort:
/*
* Don't add another Material node if there's one
* already, nor if the top node is a Sort, since Sort
* materializes its output anyway. (I doubt either
* case can happen in practice for a subplan, but...)
* already, nor if the top node is any other type that
* materializes its output anyway.
*/
use_material = false;
break;
@ -686,6 +686,7 @@ SS_finalize_plan(Plan *plan)
case T_SetOp:
case T_Limit:
case T_Group:
case T_FunctionScan:
break;
default:

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.97 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.98 2002/05/12 20:10:03 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -1922,6 +1922,7 @@ query_tree_walker(Query *query,
{
case RTE_RELATION:
case RTE_SPECIAL:
case RTE_FUNCTION:
/* nothing to do */
break;
case RTE_SUBQUERY:
@ -2309,6 +2310,7 @@ query_tree_mutator(Query *query,
{
case RTE_RELATION:
case RTE_SPECIAL:
case RTE_FUNCTION:
/* nothing to do, don't bother to make a copy */
break;
case RTE_SUBQUERY:

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.76 2001/10/25 05:49:34 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.77 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -454,6 +454,25 @@ create_subqueryscan_path(RelOptInfo *rel)
return pathnode;
}
/*
* create_functionscan_path
* Creates a path corresponding to a sequential scan of a function,
* returning the pathnode.
*/
Path *
create_functionscan_path(Query *root, RelOptInfo *rel)
{
Path *pathnode = makeNode(Path);
pathnode->pathtype = T_FunctionScan;
pathnode->parent = rel;
pathnode->pathkeys = NIL; /* for now, assume unordered result */
cost_functionscan(pathnode, root, rel);
return pathnode;
}
/*
* create_nestloop_path
* Creates a pathnode corresponding to a nestloop join between two

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.36 2002/03/12 00:51:51 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.37 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -140,7 +140,7 @@ make_base_rel(Query *root, int relid)
rel->cheapest_startup_path = NULL;
rel->cheapest_total_path = NULL;
rel->pruneable = true;
rel->issubquery = false;
rel->rtekind = rte->rtekind;
rel->indexlist = NIL;
rel->pages = 0;
rel->tuples = 0;
@ -168,8 +168,8 @@ make_base_rel(Query *root, int relid)
break;
}
case RTE_SUBQUERY:
/* Subquery --- mark it as such for later processing */
rel->issubquery = true;
case RTE_FUNCTION:
/* Subquery or function --- nothing to do here */
break;
case RTE_JOIN:
/* Join --- must be an otherrel */
@ -351,7 +351,7 @@ build_join_rel(Query *root,
joinrel->cheapest_startup_path = NULL;
joinrel->cheapest_total_path = NULL;
joinrel->pruneable = true;
joinrel->issubquery = false;
joinrel->rtekind = RTE_JOIN;
joinrel->indexlist = NIL;
joinrel->pages = 0;
joinrel->tuples = 0;

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.233 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.234 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -2679,7 +2679,7 @@ transformForUpdate(Query *qry, List *forUpdate)
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
++i;
if (rte->subquery)
if (rte->rtekind == RTE_SUBQUERY)
{
/* FOR UPDATE of subquery is propagated to subquery's rels */
transformForUpdate(rte->subquery, makeList1(NULL));
@ -2707,7 +2707,7 @@ transformForUpdate(Query *qry, List *forUpdate)
++i;
if (strcmp(rte->eref->aliasname, relname) == 0)
{
if (rte->subquery)
if (rte->rtekind == RTE_SUBQUERY)
{
/* propagate to subquery */
transformForUpdate(rte->subquery, makeList1(NULL));

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.313 2002/05/06 19:47:30 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.314 2002/05/12 20:10:04 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -52,6 +52,7 @@
#include "access/htup.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/params.h"
@ -250,7 +251,7 @@ static void doNegateFloat(Value *v);
%type <defelt> def_elem
%type <node> def_arg, columnElem, where_clause, insert_column_item,
a_expr, b_expr, c_expr, AexprConst,
in_expr, having_clause
in_expr, having_clause, func_table
%type <list> row_descriptor, row_list, in_expr_nodes
%type <node> row_expr
%type <node> case_expr, case_arg, when_clause, case_default
@ -4074,6 +4075,19 @@ table_ref: relation_expr
$1->alias = $2;
$$ = (Node *) $1;
}
| func_table
{
RangeFunction *n = makeNode(RangeFunction);
n->funccallnode = $1;
$$ = (Node *) n;
}
| func_table alias_clause
{
RangeFunction *n = makeNode(RangeFunction);
n->funccallnode = $1;
n->alias = $2;
$$ = (Node *) n;
}
| select_with_parens
{
/*
@ -4109,6 +4123,7 @@ table_ref: relation_expr
}
;
/*
* It may seem silly to separate joined_table from table_ref, but there is
* method in SQL92's madness: if you don't do it this way you get reduce-
@ -4280,6 +4295,28 @@ relation_expr: qualified_name
}
;
func_table: func_name '(' ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = NIL;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
| func_name '(' expr_list ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
$$ = (Node *)n;
}
;
where_clause: WHERE a_expr { $$ = $2; }
| /*EMPTY*/ { $$ = NULL; /* no qualifiers */ }
;
@ -5845,26 +5882,33 @@ qualified_name_list: qualified_name
{ $$ = lappend($1, $3); }
;
qualified_name: ColId
qualified_name: relation_name
{
$$ = makeNode(RangeVar);
$$->catalogname = NULL;
$$->schemaname = NULL;
$$->relname = $1;
}
| ColId '.' ColId
| dotted_name
{
$$ = makeNode(RangeVar);
$$->catalogname = NULL;
$$->schemaname = $1;
$$->relname = $3;
}
| ColId '.' ColId '.' ColId
{
$$ = makeNode(RangeVar);
$$->catalogname = $1;
$$->schemaname = $3;
$$->relname = $5;
switch (length($1))
{
case 2:
$$->catalogname = NULL;
$$->schemaname = strVal(lfirst($1));
$$->relname = strVal(lsecond($1));
break;
case 3:
$$->catalogname = strVal(lfirst($1));
$$->schemaname = strVal(lsecond($1));
$$->relname = strVal(lfirst(lnext(lnext($1))));
break;
default:
elog(ERROR, "Improper qualified name (too many dotted names): %s",
NameListToString($1));
break;
}
}
;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.90 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.91 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -17,6 +17,7 @@
#include "access/heapam.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/analyze.h"
@ -49,6 +50,8 @@ static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j,
static RangeTblRef *transformTableEntry(ParseState *pstate, RangeVar *r);
static RangeTblRef *transformRangeSubselect(ParseState *pstate,
RangeSubselect *r);
static RangeTblRef *transformRangeFunction(ParseState *pstate,
RangeFunction *r);
static Node *transformFromClauseItem(ParseState *pstate, Node *n,
List **containedRels);
static Node *buildMergedJoinVar(JoinType jointype,
@ -82,9 +85,9 @@ transformFromClause(ParseState *pstate, List *frmList)
/*
* The grammar will have produced a list of RangeVars,
* RangeSubselects, and/or JoinExprs. Transform each one (possibly
* adding entries to the rtable), check for duplicate refnames, and
* then add it to the joinlist and namespace.
* RangeSubselects, RangeFunctions, and/or JoinExprs. Transform each one
* (possibly adding entries to the rtable), check for duplicate refnames,
* and then add it to the joinlist and namespace.
*/
foreach(fl, frmList)
{
@ -453,6 +456,71 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
}
/*
* transformRangeFunction --- transform a function call appearing in FROM
*/
static RangeTblRef *
transformRangeFunction(ParseState *pstate, RangeFunction *r)
{
Node *funcexpr;
char *funcname;
RangeTblEntry *rte;
RangeTblRef *rtr;
/*
* Transform the raw FuncCall node
*/
funcexpr = transformExpr(pstate, r->funccallnode);
Assert(IsA(r->funccallnode, FuncCall));
funcname = strVal(llast(((FuncCall *) r->funccallnode)->funcname));
/*
* Disallow aggregate functions and subselects in the expression.
* (Aggregates clearly make no sense; perhaps later we could support
* subselects, though.)
*/
if (contain_agg_clause(funcexpr))
elog(ERROR, "cannot use aggregate function in FROM function expression");
if (contain_subplans(funcexpr))
elog(ERROR, "cannot use subselect in FROM function expression");
/*
* Remove any Iter nodes added by parse_func.c. We oughta get rid of
* Iter completely ...
*/
while (funcexpr && IsA(funcexpr, Iter))
funcexpr = ((Iter *) funcexpr)->iterexpr;
/*
* Insist we now have a bare function call (explain.c is the only place
* that depends on this, I think). If this fails, it's probably because
* transformExpr interpreted the function notation as a type coercion.
*/
if (!funcexpr ||
!IsA(funcexpr, Expr) ||
((Expr *) funcexpr)->opType != FUNC_EXPR)
elog(ERROR, "Coercion function not allowed in FROM clause");
/*
* OK, build an RTE for the function.
*/
rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
r->alias, true);
/*
* We create a RangeTblRef, but we do not add it to the joinlist or
* namespace; our caller must do that if appropriate.
*/
rtr = makeNode(RangeTblRef);
/* assume new rte is at end */
rtr->rtindex = length(pstate->p_rtable);
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
return rtr;
}
/*
* transformFromClauseItem -
* Transform a FROM-clause item, adding any required entries to the
@ -486,6 +554,15 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
*containedRels = makeListi1(rtr->rtindex);
return (Node *) rtr;
}
else if (IsA(n, RangeFunction))
{
/* function is like a plain relation */
RangeTblRef *rtr;
rtr = transformRangeFunction(pstate, (RangeFunction *) n);
*containedRels = makeListi1(rtr->rtindex);
return (Node *) rtr;
}
else if (IsA(n, JoinExpr))
{
/* A newfangled join expression */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.127 2002/05/03 20:15:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.128 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -181,27 +181,32 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* sizeof(Pointer) to signal that the runtime representation
* will be a pointer not an Oid.
*/
if (rte->rtekind != RTE_RELATION)
switch (rte->rtekind)
{
/*
* RTE is a join or subselect; must fail for lack of a
* named tuple type
*/
if (is_column)
elog(ERROR, "No such attribute %s.%s",
refname, strVal(lfirst(funcname)));
else
{
elog(ERROR, "Cannot pass result of sub-select or join %s to a function",
refname);
}
case RTE_RELATION:
toid = get_rel_type_id(rte->relid);
if (!OidIsValid(toid))
elog(ERROR, "Cannot find type OID for relation %u",
rte->relid);
break;
case RTE_FUNCTION:
toid = exprType(rte->funcexpr);
break;
default:
/*
* RTE is a join or subselect; must fail for lack of a
* named tuple type
*/
if (is_column)
elog(ERROR, "No such attribute %s.%s",
refname, strVal(lfirst(funcname)));
else
elog(ERROR, "Cannot pass result of sub-select or join %s to a function",
refname);
toid = InvalidOid; /* keep compiler quiet */
break;
}
toid = get_rel_type_id(rte->relid);
if (!OidIsValid(toid))
elog(ERROR, "Cannot find type OID for relation %u",
rte->relid);
/* replace RangeVar in the arg list */
lfirst(i) = makeVar(vnum,
InvalidAttrNumber,

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.68 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.69 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -672,6 +672,117 @@ addRangeTableEntryForSubquery(ParseState *pstate,
return rte;
}
/*
* Add an entry for a function to the pstate's range table (p_rtable).
*
* This is just like addRangeTableEntry() except that it makes a function RTE.
*/
RangeTblEntry *
addRangeTableEntryForFunction(ParseState *pstate,
char *funcname,
Node *funcexpr,
Alias *alias,
bool inFromCl)
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
Oid funcrettype = exprType(funcexpr);
Oid funcrelid;
Alias *eref;
int numaliases;
int varattno;
rte->rtekind = RTE_FUNCTION;
rte->relid = InvalidOid;
rte->subquery = NULL;
rte->funcexpr = funcexpr;
rte->alias = alias;
eref = alias ? (Alias *) copyObject(alias) : makeAlias(funcname, NIL);
rte->eref = eref;
numaliases = length(eref->colnames);
/*
* Now determine if the function returns a simple or composite type,
* and check/add column aliases.
*/
funcrelid = typeidTypeRelid(funcrettype);
if (OidIsValid(funcrelid))
{
/*
* Composite data type, i.e. a table's row type
*
* Get the rel's relcache entry. This access ensures that we have an
* up-to-date relcache entry for the rel.
*/
Relation rel;
int maxattrs;
rel = heap_open(funcrelid, AccessShareLock);
/*
* Since the rel is open anyway, let's check that the number of column
* aliases is reasonable.
*/
maxattrs = RelationGetNumberOfAttributes(rel);
if (maxattrs < numaliases)
elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
RelationGetRelationName(rel), maxattrs, numaliases);
/* fill in alias columns using actual column names */
for (varattno = numaliases; varattno < maxattrs; varattno++)
{
char *attrname;
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
eref->colnames = lappend(eref->colnames, makeString(attrname));
}
/*
* Drop the rel refcount, but keep the access lock till end of
* transaction so that the table can't be deleted or have its schema
* modified underneath us.
*/
heap_close(rel, NoLock);
}
else
{
/*
* Must be a base data type, i.e. scalar.
* Just add one alias column named for the function.
*/
if (numaliases > 1)
elog(ERROR, "Too many column aliases specified for function %s",
funcname);
if (numaliases == 0)
eref->colnames = makeList1(makeString(funcname));
}
/*----------
* Flags:
* - this RTE should be expanded to include descendant tables,
* - this RTE is in the FROM clause,
* - this RTE should be checked for read/write access rights.
*----------
*/
rte->inh = false; /* never true for functions */
rte->inFromCl = inFromCl;
rte->checkForRead = true;
rte->checkForWrite = false;
rte->checkAsUser = InvalidOid;
/*
* Add completed RTE to pstate's range table list, but not to join
* list nor namespace --- caller must do that if appropriate.
*/
if (pstate != NULL)
pstate->p_rtable = lappend(pstate->p_rtable, rte);
return rte;
}
/*
* Add an entry for a join to the pstate's range table (p_rtable).
*
@ -834,124 +945,201 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
/* Need the RT index of the entry for creating Vars */
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
if (rte->rtekind == RTE_RELATION)
switch (rte->rtekind)
{
/* Ordinary relation RTE */
Relation rel;
int maxattrs;
int numaliases;
rel = heap_open(rte->relid, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
numaliases = length(rte->eref->colnames);
for (varattno = 0; varattno < maxattrs; varattno++)
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
if (colnames)
case RTE_RELATION:
{
char *label;
/* Ordinary relation RTE */
Relation rel;
int maxattrs;
int numaliases;
if (varattno < numaliases)
label = strVal(nth(varattno, rte->eref->colnames));
rel = heap_open(rte->relid, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
numaliases = length(rte->eref->colnames);
for (varattno = 0; varattno < maxattrs; varattno++)
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
if (colnames)
{
char *label;
if (varattno < numaliases)
label = strVal(nth(varattno, rte->eref->colnames));
else
label = NameStr(attr->attname);
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, attr->attnum,
attr->atttypid, attr->atttypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
heap_close(rel, AccessShareLock);
}
break;
case RTE_SUBQUERY:
{
/* Subquery RTE */
List *aliasp = rte->eref->colnames;
List *tlistitem;
varattno = 0;
foreach(tlistitem, rte->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
if (te->resdom->resjunk)
continue;
varattno++;
Assert(varattno == te->resdom->resno);
if (colnames)
{
/* Assume there is one alias per target item */
char *label = strVal(lfirst(aliasp));
*colnames = lappend(*colnames, makeString(pstrdup(label)));
aliasp = lnext(aliasp);
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, varattno,
te->resdom->restype,
te->resdom->restypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
}
break;
case RTE_FUNCTION:
{
/* Function RTE */
Oid funcrettype = exprType(rte->funcexpr);
Oid funcrelid = typeidTypeRelid(funcrettype);
if (OidIsValid(funcrelid))
{
/*
* Composite data type, i.e. a table's row type
* Same as ordinary relation RTE
*/
Relation rel;
int maxattrs;
int numaliases;
rel = heap_open(funcrelid, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
numaliases = length(rte->eref->colnames);
for (varattno = 0; varattno < maxattrs; varattno++)
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
if (colnames)
{
char *label;
if (varattno < numaliases)
label = strVal(nth(varattno, rte->eref->colnames));
else
label = NameStr(attr->attname);
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, attr->attnum,
attr->atttypid, attr->atttypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
heap_close(rel, AccessShareLock);
}
else
label = NameStr(attr->attname);
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
{
/*
* Must be a base data type, i.e. scalar
*/
if (colnames)
*colnames = lappend(*colnames,
lfirst(rte->eref->colnames));
if (colvars)
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, 1,
funcrettype, -1,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
}
break;
case RTE_JOIN:
{
Var *varnode;
/* Join RTE */
List *aliasp = rte->eref->colnames;
List *aliasvars = rte->joinaliasvars;
varnode = makeVar(rtindex, attr->attnum,
attr->atttypid, attr->atttypmod,
sublevels_up);
varattno = 0;
while (aliasp)
{
Assert(aliasvars);
varattno++;
*colvars = lappend(*colvars, varnode);
if (colnames)
{
char *label = strVal(lfirst(aliasp));
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
if (colvars)
{
Node *aliasvar = (Node *) lfirst(aliasvars);
Var *varnode;
varnode = makeVar(rtindex, varattno,
exprType(aliasvar),
exprTypmod(aliasvar),
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
aliasp = lnext(aliasp);
aliasvars = lnext(aliasvars);
}
Assert(aliasvars == NIL);
}
}
heap_close(rel, AccessShareLock);
break;
default:
elog(ERROR, "expandRTE: unsupported RTE kind %d",
(int) rte->rtekind);
}
else if (rte->rtekind == RTE_SUBQUERY)
{
/* Subquery RTE */
List *aliasp = rte->eref->colnames;
List *tlistitem;
varattno = 0;
foreach(tlistitem, rte->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
if (te->resdom->resjunk)
continue;
varattno++;
Assert(varattno == te->resdom->resno);
if (colnames)
{
/* Assume there is one alias per target item */
char *label = strVal(lfirst(aliasp));
*colnames = lappend(*colnames, makeString(pstrdup(label)));
aliasp = lnext(aliasp);
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, varattno,
te->resdom->restype,
te->resdom->restypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
}
else if (rte->rtekind == RTE_JOIN)
{
/* Join RTE */
List *aliasp = rte->eref->colnames;
List *aliasvars = rte->joinaliasvars;
varattno = 0;
while (aliasp)
{
Assert(aliasvars);
varattno++;
if (colnames)
{
char *label = strVal(lfirst(aliasp));
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
if (colvars)
{
Node *aliasvar = (Node *) lfirst(aliasvars);
Var *varnode;
varnode = makeVar(rtindex, varattno,
exprType(aliasvar),
exprTypmod(aliasvar),
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
aliasp = lnext(aliasp);
aliasvars = lnext(aliasvars);
}
Assert(aliasvars == NIL);
}
else
elog(ERROR, "expandRTE: unsupported RTE kind %d",
(int) rte->rtekind);
}
/*
@ -1044,57 +1232,101 @@ void
get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
Oid *vartype, int32 *vartypmod)
{
if (rte->rtekind == RTE_RELATION)
switch (rte->rtekind)
{
/* Plain relation RTE --- get the attribute's type info */
HeapTuple tp;
Form_pg_attribute att_tup;
case RTE_RELATION:
{
/* Plain relation RTE --- get the attribute's type info */
HeapTuple tp;
Form_pg_attribute att_tup;
tp = SearchSysCache(ATTNUM,
ObjectIdGetDatum(rte->relid),
Int16GetDatum(attnum),
0, 0);
/* this shouldn't happen... */
if (!HeapTupleIsValid(tp))
elog(ERROR, "Relation %s does not have attribute %d",
get_rel_name(rte->relid), attnum);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
ReleaseSysCache(tp);
tp = SearchSysCache(ATTNUM,
ObjectIdGetDatum(rte->relid),
Int16GetDatum(attnum),
0, 0);
/* this shouldn't happen... */
if (!HeapTupleIsValid(tp))
elog(ERROR, "Relation %s does not have attribute %d",
get_rel_name(rte->relid), attnum);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
ReleaseSysCache(tp);
}
break;
case RTE_SUBQUERY:
{
/* Subselect RTE --- get type info from subselect's tlist */
List *tlistitem;
foreach(tlistitem, rte->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
if (te->resdom->resjunk || te->resdom->resno != attnum)
continue;
*vartype = te->resdom->restype;
*vartypmod = te->resdom->restypmod;
return;
}
/* falling off end of list shouldn't happen... */
elog(ERROR, "Subquery %s does not have attribute %d",
rte->eref->aliasname, attnum);
}
break;
case RTE_FUNCTION:
{
/* Function RTE */
Oid funcrettype = exprType(rte->funcexpr);
Oid funcrelid = typeidTypeRelid(funcrettype);
if (OidIsValid(funcrelid))
{
/*
* Composite data type, i.e. a table's row type
* Same as ordinary relation RTE
*/
HeapTuple tp;
Form_pg_attribute att_tup;
tp = SearchSysCache(ATTNUM,
ObjectIdGetDatum(funcrelid),
Int16GetDatum(attnum),
0, 0);
/* this shouldn't happen... */
if (!HeapTupleIsValid(tp))
elog(ERROR, "Relation %s does not have attribute %d",
get_rel_name(funcrelid), attnum);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
ReleaseSysCache(tp);
}
else
{
/*
* Must be a base data type, i.e. scalar
*/
*vartype = funcrettype;
*vartypmod = -1;
}
}
break;
case RTE_JOIN:
{
/* Join RTE --- get type info from join RTE's alias variable */
Node *aliasvar;
Assert(attnum > 0 && attnum <= length(rte->joinaliasvars));
aliasvar = (Node *) nth(attnum-1, rte->joinaliasvars);
*vartype = exprType(aliasvar);
*vartypmod = exprTypmod(aliasvar);
}
break;
default:
elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d",
(int) rte->rtekind);
}
else if (rte->rtekind == RTE_SUBQUERY)
{
/* Subselect RTE --- get type info from subselect's tlist */
List *tlistitem;
foreach(tlistitem, rte->subquery->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
if (te->resdom->resjunk || te->resdom->resno != attnum)
continue;
*vartype = te->resdom->restype;
*vartypmod = te->resdom->restypmod;
return;
}
/* falling off end of list shouldn't happen... */
elog(ERROR, "Subquery %s does not have attribute %d",
rte->eref->aliasname, attnum);
}
else if (rte->rtekind == RTE_JOIN)
{
/* Join RTE --- get type info from join RTE's alias variable */
Node *aliasvar;
Assert(attnum > 0 && attnum <= length(rte->joinaliasvars));
aliasvar = (Node *) nth(attnum-1, rte->joinaliasvars);
*vartype = exprType(aliasvar);
*vartypmod = exprTypmod(aliasvar);
}
else
elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d",
(int) rte->rtekind);
}
/*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.40 2002/04/20 21:56:14 petere Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.41 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -56,7 +56,8 @@ LookupTypeName(const TypeName *typename)
switch (length(typename->names))
{
case 1:
elog(ERROR, "Improper %%TYPE reference (too few dotted names)");
elog(ERROR, "Improper %%TYPE reference (too few dotted names): %s",
NameListToString(typename->names));
break;
case 2:
rel->relname = strVal(lfirst(typename->names));
@ -74,7 +75,8 @@ LookupTypeName(const TypeName *typename)
field = strVal(lfirst(lnext(lnext(lnext(typename->names)))));
break;
default:
elog(ERROR, "Improper %%TYPE reference (too many dotted names)");
elog(ERROR, "Improper %%TYPE reference (too many dotted names): %s",
NameListToString(typename->names));
break;
}
@ -121,7 +123,8 @@ LookupTypeName(const TypeName *typename)
elog(ERROR, "Cross-database references are not implemented");
break;
default:
elog(ERROR, "Improper type name (too many dotted names)");
elog(ERROR, "Improper type name (too many dotted names): %s",
NameListToString(typename->names));
break;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.69 2002/04/27 03:45:03 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.70 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -388,7 +388,7 @@ setRuleCheckAsUser(Query *qry, Oid userid)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
if (rte->subquery)
if (rte->rtekind == RTE_SUBQUERY)
{
/* Recurse into subquery in FROM */
setRuleCheckAsUser(rte->subquery, userid);

View File

@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.102 2002/05/03 20:15:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.103 2002/05/12 20:10:04 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -705,25 +705,16 @@ deparse_context_for_plan(int outer_varno, Node *outercontext,
}
/*
* deparse_context_for_relation - Build deparse context for 1 relation
* deparse_context_for_rte - Build deparse context for 1 relation
*
* Helper routine to build one of the inputs for deparse_context_for_plan.
* Pass the reference name (alias) and OID of a relation.
*
* The returned node is actually a RangeTblEntry, but we declare it as just
* Node to discourage callers from assuming anything.
* The returned node is actually the given RangeTblEntry, but we declare it
* as just Node to discourage callers from assuming anything.
*/
Node *
deparse_context_for_relation(const char *aliasname, Oid relid)
deparse_context_for_rte(RangeTblEntry *rte)
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
rte->rtekind = RTE_RELATION;
rte->relid = relid;
rte->eref = makeAlias(aliasname, NIL);
rte->inh = false;
rte->inFromCl = true;
return (Node *) rte;
}
@ -2398,6 +2389,10 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
get_query_def(rte->subquery, buf, context->namespaces);
appendStringInfoChar(buf, ')');
break;
case RTE_FUNCTION:
/* Function RTE */
get_rule_expr(rte->funcexpr, context);
break;
default:
elog(ERROR, "unexpected rte kind %d", (int) rte->rtekind);
break;

View File

@ -383,7 +383,7 @@ If a function is marked in pg_proc as returning a set, then it is called
with fcinfo->resultinfo pointing to a node of type ReturnSetInfo. A
function that desires to return a set should raise an error "called in
context that does not accept a set result" if resultinfo is NULL or does
not point to a ReturnSetInfo node. ReturnSetInfo contains a single field
not point to a ReturnSetInfo node. ReturnSetInfo contains a field
"isDone", which should be set to one of these values:
ExprSingleResult /* expression does not return a set */
@ -396,6 +396,11 @@ After all elements have been returned, the next call should set
isDone to ExprEndResult and return a null result. (Note it is possible
to return an empty set by doing this on the first call.)
As of 7.3, the ReturnSetInfo node also contains a link to the ExprContext
within which the function is being evaluated. This is useful for functions
that need to close down internal state when they are not run to completion:
they can register a shutdown callback function in the ExprContext.
Notes about function handlers
-----------------------------

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.129 2002/04/28 19:54:28 tgl Exp $
* $Id: catversion.h,v 1.130 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200204281
#define CATALOG_VERSION_NO 200205121
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: executor.h,v 1.63 2002/02/27 19:35:59 tgl Exp $
* $Id: executor.h,v 1.64 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -172,4 +172,11 @@ extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
EState *estate, bool is_update);
extern void RegisterExprContextCallback(ExprContext *econtext,
ExprContextCallbackFunction function,
Datum arg);
extern void UnregisterExprContextCallback(ExprContext *econtext,
ExprContextCallbackFunction function,
Datum arg);
#endif /* EXECUTOR_H */

View File

@ -0,0 +1,27 @@
/*-------------------------------------------------------------------------
*
* nodeFunctionscan.h
*
*
*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: nodeFunctionscan.h,v 1.1 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef NODEFUNCTIONSCAN_H
#define NODEFUNCTIONSCAN_H
#include "nodes/plannodes.h"
extern TupleTableSlot *ExecFunctionScan(FunctionScan *node);
extern void ExecEndFunctionScan(FunctionScan *node);
extern bool ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent);
extern int ExecCountSlotsFunctionScan(FunctionScan *node);
extern void ExecFunctionMarkPos(FunctionScan *node);
extern void ExecFunctionRestrPos(FunctionScan *node);
extern void ExecFunctionReScan(FunctionScan *node, ExprContext *exprCtxt, Plan *parent);
#endif /* NODEFUNCTIONSCAN_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: execnodes.h,v 1.67 2001/11/21 22:57:01 tgl Exp $
* $Id: execnodes.h,v 1.68 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -52,6 +52,21 @@ typedef struct IndexInfo
bool ii_Unique;
} IndexInfo;
/* ----------------
* ExprContext_CB
*
* List of callbacks to be called at ExprContext shutdown.
* ----------------
*/
typedef void (*ExprContextCallbackFunction) (Datum arg);
typedef struct ExprContext_CB
{
struct ExprContext_CB *next;
ExprContextCallbackFunction function;
Datum arg;
} ExprContext_CB;
/* ----------------
* ExprContext
*
@ -77,20 +92,27 @@ typedef struct IndexInfo
*/
typedef struct ExprContext
{
NodeTag type;
NodeTag type;
/* Tuples that Var nodes in expression may refer to */
TupleTableSlot *ecxt_scantuple;
TupleTableSlot *ecxt_innertuple;
TupleTableSlot *ecxt_outertuple;
/* Memory contexts for expression evaluation --- see notes above */
MemoryContext ecxt_per_query_memory;
MemoryContext ecxt_per_tuple_memory;
MemoryContext ecxt_per_query_memory;
MemoryContext ecxt_per_tuple_memory;
/* Values to substitute for Param nodes in expression */
ParamExecData *ecxt_param_exec_vals; /* for PARAM_EXEC params */
ParamListInfo ecxt_param_list_info; /* for other param types */
ParamExecData *ecxt_param_exec_vals; /* for PARAM_EXEC params */
ParamListInfo ecxt_param_list_info; /* for other param types */
/* Values to substitute for Aggref nodes in expression */
Datum *ecxt_aggvalues; /* precomputed values for Aggref nodes */
bool *ecxt_aggnulls; /* null flags for Aggref nodes */
Datum *ecxt_aggvalues; /* precomputed values for Aggref nodes */
bool *ecxt_aggnulls; /* null flags for Aggref nodes */
/* Functions to call back when ExprContext is shut down */
ExprContext_CB *ecxt_callbacks;
} ExprContext;
/*
@ -107,7 +129,8 @@ typedef enum
* When calling a function that might return a set (multiple rows),
* a node of this type is passed as fcinfo->resultinfo to allow
* return status to be passed back. A function returning set should
* raise an error if no such resultinfo is provided.
* raise an error if no such resultinfo is provided. The ExprContext
* in which the function is being called is also made available.
*
* XXX this mechanism is a quick hack and probably needs to be redesigned.
*/
@ -115,9 +138,9 @@ typedef struct ReturnSetInfo
{
NodeTag type;
ExprDoneCond isDone;
ExprContext *econtext;
} ReturnSetInfo;
/* ----------------
* ProjectionInfo node information
*
@ -481,6 +504,36 @@ typedef struct SubqueryScanState
EState *sss_SubEState;
} SubqueryScanState;
/* ----------------
* FunctionScanState information
*
* Function nodes are used to scan the results of a
* function appearing in FROM (typically a function returning set).
*
* functionmode function operating mode:
* - repeated call
* - materialize
* - return query
* tuplestorestate private state of tuplestore.c
* ----------------
*/
typedef enum FunctionMode
{
PM_REPEATEDCALL,
PM_MATERIALIZE,
PM_QUERY
} FunctionMode;
typedef struct FunctionScanState
{
CommonScanState csstate; /* its first field is NodeTag */
FunctionMode functionmode;
TupleDesc tupdesc;
void *tuplestorestate;
Node *funcexpr; /* function expression being evaluated */
bool returnsTuple; /* does function return tuples? */
} FunctionScanState;
/* ----------------------------------------------------------------
* Join State Information
* ----------------------------------------------------------------

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: nodes.h,v 1.105 2002/04/18 20:01:11 tgl Exp $
* $Id: nodes.h,v 1.106 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -49,6 +49,7 @@ typedef enum NodeTag
T_SubPlan,
T_TidScan,
T_SubqueryScan,
T_FunctionScan,
/*
* TAGS FOR PRIMITIVE NODES (primnodes.h)
@ -120,6 +121,7 @@ typedef enum NodeTag
T_SubqueryScanState,
T_SetOpState,
T_LimitState,
T_FunctionScanState,
/*
* TAGS FOR MEMORY NODES (memnodes.h)
@ -212,6 +214,7 @@ typedef enum NodeTag
T_Alias,
T_RangeVar,
T_RangeSubselect,
T_RangeFunction,
T_TypeName,
T_IndexElem,
T_ColumnDef,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parsenodes.h,v 1.175 2002/04/28 19:54:28 tgl Exp $
* $Id: parsenodes.h,v 1.176 2002/05/12 20:10:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -387,6 +387,16 @@ typedef struct RangeSubselect
Alias *alias; /* table alias & optional column aliases */
} RangeSubselect;
/*
* RangeFunction - function call appearing in a FROM clause
*/
typedef struct RangeFunction
{
NodeTag type;
Node *funccallnode; /* untransformed function call tree */
Alias *alias; /* table alias & optional column aliases */
} RangeFunction;
/*
* IndexElem - index parameters (used in CREATE INDEX)
*
@ -482,7 +492,8 @@ typedef enum RTEKind
RTE_RELATION, /* ordinary relation reference */
RTE_SUBQUERY, /* subquery in FROM */
RTE_JOIN, /* join */
RTE_SPECIAL /* special rule relation (NEW or OLD) */
RTE_SPECIAL, /* special rule relation (NEW or OLD) */
RTE_FUNCTION /* function in FROM */
} RTEKind;
typedef struct RangeTblEntry
@ -507,6 +518,11 @@ typedef struct RangeTblEntry
*/
Query *subquery; /* the sub-query */
/*
* Fields valid for a function RTE (else NULL):
*/
Node *funcexpr; /* expression tree for func call */
/*
* Fields valid for a join RTE (else NULL/zero):
*

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: plannodes.h,v 1.55 2002/04/28 19:54:28 tgl Exp $
* $Id: plannodes.h,v 1.56 2002/05/12 20:10:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -32,6 +32,7 @@
* Scan *** CommonScanState scanstate;
* IndexScan IndexScanState indxstate;
* SubqueryScan SubqueryScanState subquerystate;
* FunctionScan FunctionScanState functionstate;
*
* (*** nodes which inherit Scan also inherit scanstate)
*
@ -242,6 +243,17 @@ typedef struct SubqueryScan
Plan *subplan;
} SubqueryScan;
/* ----------------
* FunctionScan node
* ----------------
*/
typedef struct FunctionScan
{
Scan scan;
/* no other fields needed at present */
/* scan.scanstate actually points at a FunctionScanState node */
} FunctionScan;
/*
* ==========
* Join nodes

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: primnodes.h,v 1.61 2002/04/11 20:00:15 tgl Exp $
* $Id: primnodes.h,v 1.62 2002/05/12 20:10:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -514,10 +514,11 @@ typedef struct RelabelType
* rows.) If all joins are inner joins then all the qual positions are
* semantically interchangeable.
*
* NOTE: in the raw output of gram.y, a join tree contains RangeVar and
* RangeSubselect nodes, which are both replaced by RangeTblRef nodes
* during the parse analysis phase. Also, the top-level FromExpr is added
* during parse analysis; the grammar regards FROM and WHERE as separate.
* NOTE: in the raw output of gram.y, a join tree contains RangeVar,
* RangeSubselect, and RangeFunction nodes, which are all replaced by
* RangeTblRef nodes during the parse analysis phase. Also, the top-level
* FromExpr is added during parse analysis; the grammar regards FROM and
* WHERE as separate.
* ----------------------------------------------------------------
*/

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: relation.h,v 1.63 2002/03/12 00:52:02 tgl Exp $
* $Id: relation.h,v 1.64 2002/05/12 20:10:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -92,12 +92,12 @@ typedef enum CostSelector
*
* If the relation is a base relation it will have these fields set:
*
* issubquery - true if baserel is a subquery RTE rather than a table
* rtekind - distinguishes plain relation, subquery, or function RTE
* indexlist - list of IndexOptInfo nodes for relation's indexes
* (always NIL if it's a subquery)
* pages - number of disk pages in relation (zero if a subquery)
* (always NIL if it's not a table)
* pages - number of disk pages in relation (zero if not a table)
* tuples - number of tuples in relation (not considering restrictions)
* subplan - plan for subquery (NULL if it's a plain table)
* subplan - plan for subquery (NULL if it's not a subquery)
*
* Note: for a subquery, tuples and subplan are not set immediately
* upon creation of the RelOptInfo object; they are filled in when
@ -184,11 +184,11 @@ typedef struct RelOptInfo
bool pruneable;
/* information about a base rel (not set for join rels!) */
bool issubquery;
RTEKind rtekind; /* RELATION, SUBQUERY, or FUNCTION */
List *indexlist;
long pages;
double tuples;
struct Plan *subplan;
struct Plan *subplan; /* if subquery */
/* information about a join rel (not set for base rels!) */
Index joinrti;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: cost.h,v 1.43 2001/11/05 17:46:34 momjian Exp $
* $Id: cost.h,v 1.44 2002/05/12 20:10:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -59,6 +59,8 @@ extern void cost_index(Path *path, Query *root,
List *indexQuals, bool is_injoin);
extern void cost_tidscan(Path *path, Query *root,
RelOptInfo *baserel, List *tideval);
extern void cost_functionscan(Path *path, Query *root,
RelOptInfo *baserel);
extern void cost_sort(Path *path, Query *root,
List *pathkeys, double tuples, int width);
extern void cost_nestloop(Path *path, Query *root,
@ -80,6 +82,7 @@ extern void set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
RelOptInfo *inner_rel,
JoinType jointype,
List *restrictlist);
extern void set_function_size_estimates(Query *root, RelOptInfo *rel);
/*
* prototypes for clausesel.c

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pathnode.h,v 1.42 2002/03/12 00:52:03 tgl Exp $
* $Id: pathnode.h,v 1.43 2002/05/12 20:10:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -36,6 +36,7 @@ extern TidPath *create_tidscan_path(Query *root, RelOptInfo *rel,
List *tideval);
extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
extern Path *create_subqueryscan_path(RelOptInfo *rel);
extern Path *create_functionscan_path(Query *root, RelOptInfo *rel);
extern NestPath *create_nestloop_path(Query *root,
RelOptInfo *joinrel,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_relation.h,v 1.32 2002/04/28 19:54:29 tgl Exp $
* $Id: parse_relation.h,v 1.33 2002/05/12 20:10:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -41,6 +41,11 @@ extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
Query *subquery,
Alias *alias,
bool inFromCl);
extern RangeTblEntry *addRangeTableEntryForFunction(ParseState *pstate,
char *funcname,
Node *funcexpr,
Alias *alias,
bool inFromCl);
extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
List *colnames,
JoinType jointype,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: builtins.h,v 1.180 2002/04/26 01:24:08 tgl Exp $
* $Id: builtins.h,v 1.181 2002/05/12 20:10:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -354,7 +354,7 @@ extern char *deparse_expression(Node *expr, List *dpcontext,
extern List *deparse_context_for(const char *aliasname, Oid relid);
extern List *deparse_context_for_plan(int outer_varno, Node *outercontext,
int inner_varno, Node *innercontext);
extern Node *deparse_context_for_relation(const char *aliasname, Oid relid);
extern Node *deparse_context_for_rte(RangeTblEntry *rte);
extern Node *deparse_context_for_subplan(const char *name, List *tlist,
List *rtable);
extern const char *quote_identifier(const char *ident);