* 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 $
*
*-------------------------------------------------------------------------
*/
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;
}
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;
}
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;
}
* 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 $
*
*/
case T_SubqueryScan:
pname = "Subquery Scan";
break;
+ case T_FunctionScan:
+ pname = "Function Scan";
+ break;
case T_Material:
pname = "Materialize";
break;
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);
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;
}
break;
case T_SeqScan:
case T_TidScan:
+ case T_FunctionScan:
show_scan_qual(plan->qual, false,
"Filter",
((Scan *) plan)->scanrelid,
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++)
/* 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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
}
# 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 $
#
#-------------------------------------------------------------------------
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
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "executor/nodeSort.h"
#include "executor/nodeSubplan.h"
#include "executor/nodeSubqueryscan.h"
+#include "executor/nodeFunctionscan.h"
#include "executor/nodeUnique.h"
ExecSubqueryReScan((SubqueryScan *) node, exprCtxt, parent);
break;
+ case T_FunctionScan:
+ ExecFunctionReScan((FunctionScan *) node, exprCtxt, parent);
+ break;
+
case T_Material:
ExecMaterialReScan((Material *) node, exprCtxt, parent);
break;
ExecIndexMarkPos((IndexScan *) node);
break;
+ case T_FunctionScan:
+ ExecFunctionMarkPos((FunctionScan *) node);
+ break;
+
case T_Material:
ExecMaterialMarkPos((Material *) node);
break;
ExecIndexRestrPos((IndexScan *) node);
break;
+ case T_FunctionScan:
+ ExecFunctionRestrPos((FunctionScan *) node);
+ break;
+
case T_Material:
ExecMaterialRestrPos((Material *) node);
break;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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;
}
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
parent);
break;
+ case T_FunctionScan:
+ result = ExecInitFunctionScan((FunctionScan *) node, estate,
+ parent);
+ break;
+
/*
* join nodes
*/
result = ExecSubqueryScan((SubqueryScan *) node);
break;
+ case T_FunctionScan:
+ result = ExecFunctionScan((FunctionScan *) node);
+ break;
+
/*
* join nodes
*/
case T_SubqueryScan:
return ExecCountSlotsSubqueryScan((SubqueryScan *) node);
+ case T_FunctionScan:
+ return ExecCountSlotsFunctionScan((FunctionScan *) node);
+
/*
* join nodes
*/
ExecEndSubqueryScan((SubqueryScan *) node);
break;
+ case T_FunctionScan:
+ ExecEndFunctionScan((FunctionScan *) node);
+ break;
+
/*
* join nodes
*/
}
break;
+ case T_FunctionScan:
+ {
+ CommonScanState *scanstate = ((FunctionScan *) node)->scan.scanstate;
+
+ slot = scanstate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
case T_Material:
{
MaterialState *matstate = ((Material *) node)->matstate;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
fcinfo.resultinfo = (Node *) &rsinfo;
rsinfo.type = T_ReturnSetInfo;
+ rsinfo.econtext = econtext;
}
/*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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.
* access method level so that the
* cinterface.a will link ok. */
+
+static void ShutdownExprContext(ExprContext *econtext);
+
/* ----------------------------------------------------------------
* statistic functions
* ----------------------------------------------------------------
/* ----------------------------------------------------------------
* miscellaneous node-init support functions
- *
- * ExecAssignExprContext - assigns the node's expression context
* ----------------------------------------------------------------
*/
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;
}
econtext->ecxt_param_list_info = NULL;
econtext->ecxt_aggvalues = NULL;
econtext->ecxt_aggnulls = NULL;
+ econtext->ecxt_callbacks = NULL;
return econtext;
}
void
FreeExprContext(ExprContext *econtext)
{
+ /* Call any registered callbacks */
+ ShutdownExprContext(econtext);
+ /* And clean up the memory used */
MemoryContextDelete(econtext->ecxt_per_tuple_memory);
pfree(econtext);
}
if (econtext == NULL)
return;
+ /*
+ * clean up any registered callbacks
+ */
+ ShutdownExprContext(econtext);
+
/*
* clean up memory used.
*/
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);
+ }
+}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
- * 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
{
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
static Datum postquel_execute(execution_state *es,
FunctionCallInfo fcinfo,
SQLFunctionCachePtr fcache);
+static void ShutdownSQLFunction(Datum arg);
static execution_state *
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);
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;
+}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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
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;
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);
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)
{
case T_SubqueryScan:
retval = _copySubqueryScan(from);
break;
+ case T_FunctionScan:
+ retval = _copyFunctionScan(from);
+ break;
case T_Join:
retval = _copyJoin(from);
break;
case T_RangeSubselect:
retval = _copyRangeSubselect(from);
break;
+ case T_RangeFunction:
+ retval = _copyRangeFunction(from);
+ break;
case T_TypeName:
retval = _copyTypeName(from);
break;
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
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))
case T_RangeSubselect:
retval = _equalRangeSubselect(a, b);
break;
+ case T_RangeFunction:
+ retval = _equalRangeFunction(a, b);
+ break;
case T_TypeName:
retval = _equalTypeName(a, b);
break;
* 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
_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
*/
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);
case T_SubqueryScan:
_outSubqueryScan(str, obj);
break;
+ case T_FunctionScan:
+ _outFunctionScan(str, obj);
+ break;
case T_Material:
_outMaterial(str, obj);
break;
*
*
* 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
{
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" : ""));
return "TIDSCAN";
case T_SubqueryScan:
return "SUBQUERYSCAN";
+ case T_FunctionScan:
+ return "FUNCTIONSCAN";
case T_Join:
return "JOIN";
case T_NestLoop:
}
/*
- 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)
{
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')
*
*
* 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
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
*
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 */
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)
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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)
{
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.
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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;
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;
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;
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.
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.
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
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,
case T_SeqScan:
case T_TidScan:
case T_SubqueryScan:
+ case T_FunctionScan:
plan = (Plan *) create_scan_plan(root, best_path);
break;
case T_HashJoin:
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);
/* 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);
/* 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);
/* 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);
/* 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);
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
{
case T_SeqScan:
case T_IndexScan:
+ case T_FunctionScan:
case T_Material:
case T_Sort:
/* OK, these inner plans support mark/restore */
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)
{
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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);
* 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 $
*
*-------------------------------------------------------------------------
*/
}
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;
case T_SetOp:
case T_Limit:
case T_Group:
+ case T_FunctionScan:
break;
default:
*
*
* 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
{
case RTE_RELATION:
case RTE_SPECIAL:
+ case RTE_FUNCTION:
/* nothing to do */
break;
case RTE_SUBQUERY:
{
case RTE_RELATION:
case RTE_SPECIAL:
+ case RTE_FUNCTION:
/* nothing to do, don't bother to make a copy */
break;
case RTE_SUBQUERY:
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
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 */
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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));
++i;
if (strcmp(rte->eref->aliasname, relname) == 0)
{
- if (rte->subquery)
+ if (rte->rtekind == RTE_SUBQUERY)
{
/* propagate to subquery */
transformForUpdate(rte->subquery, makeList1(NULL));
*
*
* 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
#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"
%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
$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
{
/*
}
;
+
/*
* 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-
}
;
+
+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 */ }
;
{ $$ = lappend($1, $3); }
;
-qualified_name: ColId
+qualified_name: relation_name
{
$$ = makeNode(RangeVar);
$$->catalogname = NULL;
$$->schemaname = NULL;
$$->relname = $1;
}
- | ColId '.' ColId
- {
- $$ = makeNode(RangeVar);
- $$->catalogname = NULL;
- $$->schemaname = $1;
- $$->relname = $3;
- }
- | ColId '.' ColId '.' ColId
+ | dotted_name
{
$$ = 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;
+ }
}
;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "access/heapam.h"
#include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/analyze.h"
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,
/*
* 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)
{
}
+/*
+ * 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
*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 */
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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,
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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).
*
/* 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;
+ case RTE_RELATION:
+ {
+ /* Ordinary relation RTE */
+ Relation rel;
+ int maxattrs;
+ int numaliases;
- rel = heap_open(rte->relid, AccessShareLock);
- maxattrs = RelationGetNumberOfAttributes(rel);
- numaliases = length(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];
+ for (varattno = 0; varattno < maxattrs; varattno++)
+ {
+ Form_pg_attribute attr = rel->rd_att->attrs[varattno];
- if (colnames)
- {
- char *label;
+ 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 (varattno < numaliases)
+ label = strVal(nth(varattno, rte->eref->colnames));
+ else
+ label = NameStr(attr->attname);
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ }
- if (colvars)
- {
- Var *varnode;
+ if (colvars)
+ {
+ Var *varnode;
- varnode = makeVar(rtindex, attr->attnum,
- attr->atttypid, attr->atttypmod,
- sublevels_up);
+ varnode = makeVar(rtindex, attr->attnum,
+ attr->atttypid, attr->atttypmod,
+ sublevels_up);
+
+ *colvars = lappend(*colvars, varnode);
+ }
+ }
- *colvars = lappend(*colvars, varnode);
+ heap_close(rel, AccessShareLock);
}
- }
+ break;
+ case RTE_SUBQUERY:
+ {
+ /* Subquery RTE */
+ List *aliasp = rte->eref->colnames;
+ List *tlistitem;
- heap_close(rel, AccessShareLock);
- }
- 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);
- varattno = 0;
- foreach(tlistitem, rte->subquery->targetList)
- {
- TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
+ if (te->resdom->resjunk)
+ continue;
+ varattno++;
+ Assert(varattno == te->resdom->resno);
- 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));
- if (colnames)
- {
- /* Assume there is one alias per target item */
- char *label = strVal(lfirst(aliasp));
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ aliasp = lnext(aliasp);
+ }
- *colnames = lappend(*colnames, makeString(pstrdup(label)));
- aliasp = lnext(aliasp);
- }
+ if (colvars)
+ {
+ Var *varnode;
- if (colvars)
- {
- Var *varnode;
+ varnode = makeVar(rtindex, varattno,
+ te->resdom->restype,
+ te->resdom->restypmod,
+ sublevels_up);
- 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);
- *colvars = lappend(*colvars, varnode);
+ 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
+ {
+ /*
+ * Must be a base data type, i.e. scalar
+ */
+ if (colnames)
+ *colnames = lappend(*colnames,
+ lfirst(rte->eref->colnames));
+
+ if (colvars)
+ {
+ Var *varnode;
+
+ varnode = makeVar(rtindex, 1,
+ funcrettype, -1,
+ sublevels_up);
+
+ *colvars = lappend(*colvars, varnode);
+ }
+ }
}
- }
- }
- else if (rte->rtekind == RTE_JOIN)
- {
- /* Join RTE */
- List *aliasp = rte->eref->colnames;
- List *aliasvars = rte->joinaliasvars;
+ break;
+ case RTE_JOIN:
+ {
+ /* Join RTE */
+ List *aliasp = rte->eref->colnames;
+ List *aliasvars = rte->joinaliasvars;
- varattno = 0;
- while (aliasp)
- {
- Assert(aliasvars);
- varattno++;
+ varattno = 0;
+ while (aliasp)
+ {
+ Assert(aliasvars);
+ varattno++;
- if (colnames)
- {
- char *label = strVal(lfirst(aliasp));
+ if (colnames)
+ {
+ char *label = strVal(lfirst(aliasp));
- *colnames = lappend(*colnames, makeString(pstrdup(label)));
- }
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ }
- if (colvars)
- {
- Node *aliasvar = (Node *) lfirst(aliasvars);
- Var *varnode;
+ if (colvars)
+ {
+ Node *aliasvar = (Node *) lfirst(aliasvars);
+ Var *varnode;
- varnode = makeVar(rtindex, varattno,
- exprType(aliasvar),
- exprTypmod(aliasvar),
- sublevels_up);
+ varnode = makeVar(rtindex, varattno,
+ exprType(aliasvar),
+ exprTypmod(aliasvar),
+ sublevels_up);
- *colvars = lappend(*colvars, varnode);
- }
+ *colvars = lappend(*colvars, varnode);
+ }
- aliasp = lnext(aliasp);
- aliasvars = lnext(aliasvars);
- }
- Assert(aliasvars == NIL);
+ aliasp = lnext(aliasp);
+ aliasvars = lnext(aliasvars);
+ }
+ Assert(aliasvars == NIL);
+ }
+ break;
+ default:
+ elog(ERROR, "expandRTE: unsupported RTE kind %d",
+ (int) rte->rtekind);
}
- else
- elog(ERROR, "expandRTE: unsupported RTE kind %d",
- (int) rte->rtekind);
}
/*
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;
-
- 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);
- }
- else if (rte->rtekind == RTE_SUBQUERY)
- {
- /* Subselect RTE --- get type info from subselect's tlist */
- List *tlistitem;
+ 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);
+ }
+ 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);
+ 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;
+ 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);
+ 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
- elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d",
- (int) rte->rtekind);
}
/*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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));
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;
}
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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
- if (rte->subquery)
+ if (rte->rtekind == RTE_SUBQUERY)
{
/* Recurse into subquery in FROM */
setRuleCheckAsUser(rte->subquery, userid);
* 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.
*
}
/*
- * 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;
}
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;
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 */
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
-----------------------------
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200204281
+#define CATALOG_VERSION_NO 200205121
#endif
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
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
*
*/
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;
/*
* 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.
*/
{
NodeTag type;
ExprDoneCond isDone;
+ ExprContext *econtext;
} ReturnSetInfo;
-
/* ----------------
* ProjectionInfo node information
*
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
* ----------------------------------------------------------------
* 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 $
*
*-------------------------------------------------------------------------
*/
T_SubPlan,
T_TidScan,
T_SubqueryScan,
+ T_FunctionScan,
/*
* TAGS FOR PRIMITIVE NODES (primnodes.h)
T_SubqueryScanState,
T_SetOpState,
T_LimitState,
+ T_FunctionScanState,
/*
* TAGS FOR MEMORY NODES (memnodes.h)
T_Alias,
T_RangeVar,
T_RangeSubselect,
+ T_RangeFunction,
T_TypeName,
T_IndexElem,
T_ColumnDef,
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
*
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
*/
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):
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* Scan *** CommonScanState scanstate;
* IndexScan IndexScanState indxstate;
* SubqueryScan SubqueryScanState subquerystate;
+ * FunctionScan FunctionScanState functionstate;
*
* (*** nodes which inherit Scan also inherit scanstate)
*
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
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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.
* ----------------------------------------------------------------
*/
* 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 $
*
*-------------------------------------------------------------------------
*/
*
* 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
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
RelOptInfo *inner_rel,
JoinType jointype,
List *restrictlist);
+extern void set_function_size_estimates(Query *root, RelOptInfo *rel);
/*
* prototypes for clausesel.c
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
* 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 $
*
*-------------------------------------------------------------------------
*/
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);