aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/namespace.c11
-rw-r--r--src/backend/commands/explain.c43
-rw-r--r--src/backend/commands/indexcmds.c5
-rw-r--r--src/backend/executor/Makefile8
-rw-r--r--src/backend/executor/execAmi.c15
-rw-r--r--src/backend/executor/execMain.c14
-rw-r--r--src/backend/executor/execProcnode.c27
-rw-r--r--src/backend/executor/execQual.c3
-rw-r--r--src/backend/executor/execUtils.c102
-rw-r--r--src/backend/executor/functions.c51
-rw-r--r--src/backend/executor/nodeFunctionscan.c469
-rw-r--r--src/backend/executor/nodeSubqueryscan.c4
-rw-r--r--src/backend/nodes/copyfuncs.c39
-rw-r--r--src/backend/nodes/equalfuncs.c18
-rw-r--r--src/backend/nodes/outfuncs.c21
-rw-r--r--src/backend/nodes/print.c52
-rw-r--r--src/backend/nodes/readfuncs.c27
-rw-r--r--src/backend/optimizer/path/allpaths.c28
-rw-r--r--src/backend/optimizer/path/clausesel.c4
-rw-r--r--src/backend/optimizer/path/costsize.c91
-rw-r--r--src/backend/optimizer/plan/createplan.c67
-rw-r--r--src/backend/optimizer/plan/setrefs.c6
-rw-r--r--src/backend/optimizer/plan/subselect.c9
-rw-r--r--src/backend/optimizer/util/clauses.c4
-rw-r--r--src/backend/optimizer/util/pathnode.c21
-rw-r--r--src/backend/optimizer/util/relnode.c10
-rw-r--r--src/backend/parser/analyze.c6
-rw-r--r--src/backend/parser/gram.y72
-rw-r--r--src/backend/parser/parse_clause.c85
-rw-r--r--src/backend/parser/parse_func.c43
-rw-r--r--src/backend/parser/parse_relation.c510
-rw-r--r--src/backend/parser/parse_type.c11
-rw-r--r--src/backend/rewrite/rewriteDefine.c4
-rw-r--r--src/backend/utils/adt/ruleutils.c23
-rw-r--r--src/backend/utils/fmgr/README7
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/executor/executor.h9
-rw-r--r--src/include/executor/nodeFunctionscan.h27
-rw-r--r--src/include/nodes/execnodes.h73
-rw-r--r--src/include/nodes/nodes.h5
-rw-r--r--src/include/nodes/parsenodes.h20
-rw-r--r--src/include/nodes/plannodes.h14
-rw-r--r--src/include/nodes/primnodes.h11
-rw-r--r--src/include/nodes/relation.h14
-rw-r--r--src/include/optimizer/cost.h5
-rw-r--r--src/include/optimizer/pathnode.h3
-rw-r--r--src/include/parser/parse_relation.h7
-rw-r--r--src/include/utils/builtins.h4
48 files changed, 1795 insertions, 311 deletions
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 9e213edeaed..9e5002e37f1 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -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;
}
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 03f494d63f4..cb2b2d0f23e 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -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
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index df6f81cd1db..e2ae7f06314 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -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;
}
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 363ea342e9f..0a66e1be03e 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -4,7 +4,7 @@
# Makefile for executor
#
# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.17 2001/09/18 01:59:06 tgl Exp $
+# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.18 2002/05/12 20:10:02 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -16,9 +16,9 @@ OBJS = execAmi.o execFlatten.o execJunk.o execMain.o \
execProcnode.o execQual.o execScan.o execTuples.o \
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o nodeHash.o \
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
- nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSetOp.o nodeSort.o \
- nodeUnique.o nodeLimit.o nodeGroup.o nodeSubplan.o \
- nodeSubqueryscan.o nodeTidscan.o spi.o
+ nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \
+ nodeSetOp.o nodeSort.o nodeUnique.o nodeLimit.o nodeGroup.o \
+ nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o spi.o
all: SUBSYS.o
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 119c89b1c27..d2fe9da9ada 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: execAmi.c,v 1.62 2002/03/02 21:39:24 momjian Exp $
+ * $Id: execAmi.c,v 1.63 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -35,6 +35,7 @@
#include "executor/nodeSort.h"
#include "executor/nodeSubplan.h"
#include "executor/nodeSubqueryscan.h"
+#include "executor/nodeFunctionscan.h"
#include "executor/nodeUnique.h"
@@ -100,6 +101,10 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
ExecSubqueryReScan((SubqueryScan *) node, exprCtxt, parent);
break;
+ case T_FunctionScan:
+ ExecFunctionReScan((FunctionScan *) node, exprCtxt, parent);
+ break;
+
case T_Material:
ExecMaterialReScan((Material *) node, exprCtxt, parent);
break;
@@ -187,6 +192,10 @@ ExecMarkPos(Plan *node)
ExecIndexMarkPos((IndexScan *) node);
break;
+ case T_FunctionScan:
+ ExecFunctionMarkPos((FunctionScan *) node);
+ break;
+
case T_Material:
ExecMaterialMarkPos((Material *) node);
break;
@@ -229,6 +238,10 @@ ExecRestrPos(Plan *node)
ExecIndexRestrPos((IndexScan *) node);
break;
+ case T_FunctionScan:
+ ExecFunctionRestrPos((FunctionScan *) node);
+ break;
+
case T_Material:
ExecMaterialRestrPos((Material *) node);
break;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 68fcb325a63..a2c43bc0359 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -27,7 +27,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.160 2002/04/27 21:24:34 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.161 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -311,7 +311,7 @@ ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
/* Recursively check the subquery */
rte = rt_fetch(scan->scan.scanrelid, rangeTable);
- Assert(rte->subquery != NULL);
+ Assert(rte->rtekind == RTE_SUBQUERY);
ExecCheckQueryPerms(operation, rte->subquery, scan->subplan);
break;
}
@@ -362,10 +362,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
Oid userid;
AclResult aclcheck_result;
- /*
- * If it's a subquery RTE, ignore it --- it will be checked when
- * ExecCheckPlanPerms finds the SubqueryScan node for it.
- */
+ /*
+ * Only plain-relation RTEs need to be checked here. Subquery RTEs
+ * will be checked when ExecCheckPlanPerms finds the SubqueryScan node,
+ * and function RTEs are checked by init_fcache when the function is
+ * prepared for execution. Join and special RTEs need no checks.
+ */
if (rte->rtekind != RTE_RELATION)
return;
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 5cbd2ea5622..c0f005f1e67 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.28 2001/10/25 05:49:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.29 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -96,6 +96,7 @@
#include "executor/nodeSort.h"
#include "executor/nodeSubplan.h"
#include "executor/nodeSubqueryscan.h"
+#include "executor/nodeFunctionscan.h"
#include "executor/nodeUnique.h"
#include "miscadmin.h"
#include "tcop/tcopprot.h"
@@ -168,6 +169,11 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
parent);
break;
+ case T_FunctionScan:
+ result = ExecInitFunctionScan((FunctionScan *) node, estate,
+ parent);
+ break;
+
/*
* join nodes
*/
@@ -297,6 +303,10 @@ ExecProcNode(Plan *node, Plan *parent)
result = ExecSubqueryScan((SubqueryScan *) node);
break;
+ case T_FunctionScan:
+ result = ExecFunctionScan((FunctionScan *) node);
+ break;
+
/*
* join nodes
*/
@@ -392,6 +402,9 @@ ExecCountSlotsNode(Plan *node)
case T_SubqueryScan:
return ExecCountSlotsSubqueryScan((SubqueryScan *) node);
+ case T_FunctionScan:
+ return ExecCountSlotsFunctionScan((FunctionScan *) node);
+
/*
* join nodes
*/
@@ -503,6 +516,10 @@ ExecEndNode(Plan *node, Plan *parent)
ExecEndSubqueryScan((SubqueryScan *) node);
break;
+ case T_FunctionScan:
+ ExecEndFunctionScan((FunctionScan *) node);
+ break;
+
/*
* join nodes
*/
@@ -640,6 +657,14 @@ ExecGetTupType(Plan *node)
}
break;
+ case T_FunctionScan:
+ {
+ CommonScanState *scanstate = ((FunctionScan *) node)->scan.scanstate;
+
+ slot = scanstate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
case T_Material:
{
MaterialState *matstate = ((Material *) node)->matstate;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 5cc1bbaa718..bebb664f299 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.91 2002/04/27 03:45:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.92 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -700,6 +700,7 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
{
fcinfo.resultinfo = (Node *) &rsinfo;
rsinfo.type = T_ReturnSetInfo;
+ rsinfo.econtext = econtext;
}
/*
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 9b03401e444..a6b5048326b 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.80 2002/04/12 20:38:26 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.81 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,6 +20,9 @@
* ExecCloseIndices | referenced by InitPlan, EndPlan,
* ExecInsertIndexTuples / ExecAppend, ExecReplace
*
+ * RegisterExprContextCallback Register function shutdown callback
+ * UnregisterExprContextCallback Deregister function shutdown callback
+ *
* NOTES
* This file has traditionally been the place to stick misc.
* executor support stuff that doesn't really go anyplace else.
@@ -58,6 +61,9 @@ extern int NIndexTupleProcessed; /* have to be defined in the
* access method level so that the
* cinterface.a will link ok. */
+
+static void ShutdownExprContext(ExprContext *econtext);
+
/* ----------------------------------------------------------------
* statistic functions
* ----------------------------------------------------------------
@@ -120,8 +126,6 @@ DisplayTupleCount(FILE *statfp)
/* ----------------------------------------------------------------
* miscellaneous node-init support functions
- *
- * ExecAssignExprContext - assigns the node's expression context
* ----------------------------------------------------------------
*/
@@ -160,6 +164,7 @@ ExecAssignExprContext(EState *estate, CommonState *commonstate)
econtext->ecxt_param_list_info = estate->es_param_list_info;
econtext->ecxt_aggvalues = NULL;
econtext->ecxt_aggnulls = NULL;
+ econtext->ecxt_callbacks = NULL;
commonstate->cs_ExprContext = econtext;
}
@@ -204,6 +209,7 @@ MakeExprContext(TupleTableSlot *slot,
econtext->ecxt_param_list_info = NULL;
econtext->ecxt_aggvalues = NULL;
econtext->ecxt_aggnulls = NULL;
+ econtext->ecxt_callbacks = NULL;
return econtext;
}
@@ -216,6 +222,9 @@ MakeExprContext(TupleTableSlot *slot,
void
FreeExprContext(ExprContext *econtext)
{
+ /* Call any registered callbacks */
+ ShutdownExprContext(econtext);
+ /* And clean up the memory used */
MemoryContextDelete(econtext->ecxt_per_tuple_memory);
pfree(econtext);
}
@@ -370,6 +379,11 @@ ExecFreeExprContext(CommonState *commonstate)
return;
/*
+ * clean up any registered callbacks
+ */
+ ShutdownExprContext(econtext);
+
+ /*
* clean up memory used.
*/
MemoryContextDelete(econtext->ecxt_per_tuple_memory);
@@ -689,3 +703,85 @@ SetChangedParamList(Plan *node, List *newchg)
node->chgParam = lappendi(node->chgParam, paramId);
}
}
+
+/*
+ * Register a shutdown callback in an ExprContext.
+ *
+ * Shutdown callbacks will be called (in reverse order of registration)
+ * when the ExprContext is deleted or rescanned. This provides a hook
+ * for functions called in the context to do any cleanup needed --- it's
+ * particularly useful for functions returning sets. Note that the
+ * callback will *not* be called in the event that execution is aborted
+ * by an error.
+ */
+void
+RegisterExprContextCallback(ExprContext *econtext,
+ ExprContextCallbackFunction function,
+ Datum arg)
+{
+ ExprContext_CB *ecxt_callback;
+
+ /* Save the info in appropriate memory context */
+ ecxt_callback = (ExprContext_CB *)
+ MemoryContextAlloc(econtext->ecxt_per_query_memory,
+ sizeof(ExprContext_CB));
+
+ ecxt_callback->function = function;
+ ecxt_callback->arg = arg;
+
+ /* link to front of list for appropriate execution order */
+ ecxt_callback->next = econtext->ecxt_callbacks;
+ econtext->ecxt_callbacks = ecxt_callback;
+}
+
+/*
+ * Deregister a shutdown callback in an ExprContext.
+ *
+ * Any list entries matching the function and arg will be removed.
+ * This can be used if it's no longer necessary to call the callback.
+ */
+void
+UnregisterExprContextCallback(ExprContext *econtext,
+ ExprContextCallbackFunction function,
+ Datum arg)
+{
+ ExprContext_CB **prev_callback;
+ ExprContext_CB *ecxt_callback;
+
+ prev_callback = &econtext->ecxt_callbacks;
+
+ while ((ecxt_callback = *prev_callback) != NULL)
+ {
+ if (ecxt_callback->function == function && ecxt_callback->arg == arg)
+ {
+ *prev_callback = ecxt_callback->next;
+ pfree(ecxt_callback);
+ }
+ else
+ {
+ prev_callback = &ecxt_callback->next;
+ }
+ }
+}
+
+/*
+ * Call all the shutdown callbacks registered in an ExprContext.
+ *
+ * The callback list is emptied (important in case this is only a rescan
+ * reset, and not deletion of the ExprContext).
+ */
+static void
+ShutdownExprContext(ExprContext *econtext)
+{
+ ExprContext_CB *ecxt_callback;
+
+ /*
+ * Call each callback function in reverse registration order.
+ */
+ while ((ecxt_callback = econtext->ecxt_callbacks) != NULL)
+ {
+ econtext->ecxt_callbacks = ecxt_callback->next;
+ (*ecxt_callback->function) (ecxt_callback->arg);
+ pfree(ecxt_callback);
+ }
+}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 885d93a2aff..938f7e17f93 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.49 2002/02/27 19:34:51 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.50 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,7 +28,7 @@
/*
- * We have an execution_state record for each query in the function.
+ * We have an execution_state record for each query in a function.
*/
typedef enum
{
@@ -56,6 +56,7 @@ typedef struct
int typlen; /* length of the return type */
bool typbyval; /* true if return type is pass by value */
bool returnsTuple; /* true if return type is a tuple */
+ bool shutdown_reg; /* true if registered shutdown callback */
TupleTableSlot *funcSlot; /* if one result we need to copy it before
* we end execution of the function and
@@ -79,6 +80,7 @@ static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo);
static Datum postquel_execute(execution_state *es,
FunctionCallInfo fcinfo,
SQLFunctionCachePtr fcache);
+static void ShutdownSQLFunction(Datum arg);
static execution_state *
@@ -546,6 +548,15 @@ fmgr_sql(PG_FUNCTION_ARGS)
elog(ERROR, "Set-valued function called in context that cannot accept a set");
fcinfo->isnull = true;
result = (Datum) 0;
+
+ /* Deregister shutdown callback, if we made one */
+ if (fcache->shutdown_reg)
+ {
+ UnregisterExprContextCallback(rsi->econtext,
+ ShutdownSQLFunction,
+ PointerGetDatum(fcache));
+ fcache->shutdown_reg = false;
+ }
}
MemoryContextSwitchTo(oldcontext);
@@ -570,9 +581,45 @@ fmgr_sql(PG_FUNCTION_ARGS)
rsi->isDone = ExprMultipleResult;
else
elog(ERROR, "Set-valued function called in context that cannot accept a set");
+
+ /*
+ * Ensure we will get shut down cleanly if the exprcontext is
+ * not run to completion.
+ */
+ if (!fcache->shutdown_reg)
+ {
+ RegisterExprContextCallback(rsi->econtext,
+ ShutdownSQLFunction,
+ PointerGetDatum(fcache));
+ fcache->shutdown_reg = true;
+ }
}
MemoryContextSwitchTo(oldcontext);
return result;
}
+
+/*
+ * callback function in case a function-returning-set needs to be shut down
+ * before it has been run to completion
+ */
+static void
+ShutdownSQLFunction(Datum arg)
+{
+ SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) DatumGetPointer(arg);
+ execution_state *es = fcache->func_state;
+
+ while (es != NULL)
+ {
+ /* Shut down anything still running */
+ if (es->status == F_EXEC_RUN)
+ postquel_end(es);
+ /* Reset states to START in case we're called again */
+ es->status = F_EXEC_START;
+ es = es->next;
+ }
+
+ /* execUtils will deregister the callback... */
+ fcache->shutdown_reg = false;
+}
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
new file mode 100644
index 00000000000..7b6d466a812
--- /dev/null
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -0,0 +1,469 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeFunctionscan.c
+ * Support routines for scanning RangeFunctions (functions in rangetable).
+ *
+ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.1 2002/05/12 20:10:02 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecFunctionScan scans a function.
+ * ExecFunctionNext retrieve next tuple in sequential order.
+ * ExecInitFunctionScan creates and initializes a functionscan node.
+ * ExecEndFunctionScan releases any storage allocated.
+ * ExecFunctionReScan rescans the function
+ */
+#include "postgres.h"
+
+#include "miscadmin.h"
+#include "access/heapam.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/execdefs.h"
+#include "executor/execdesc.h"
+#include "executor/nodeFunctionscan.h"
+#include "parser/parsetree.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_type.h"
+#include "storage/lmgr.h"
+#include "tcop/pquery.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+#include "utils/tuplestore.h"
+
+static TupleTableSlot *FunctionNext(FunctionScan *node);
+static TupleTableSlot *function_getonetuple(TupleTableSlot *slot,
+ Node *expr,
+ ExprContext *econtext,
+ TupleDesc tupdesc,
+ bool returnsTuple,
+ bool *isNull,
+ ExprDoneCond *isDone);
+static FunctionMode get_functionmode(Node *expr);
+
+/* ----------------------------------------------------------------
+ * Scan Support
+ * ----------------------------------------------------------------
+ */
+/* ----------------------------------------------------------------
+ * FunctionNext
+ *
+ * This is a workhorse for ExecFunctionScan
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+FunctionNext(FunctionScan *node)
+{
+ TupleTableSlot *slot;
+ Node *expr;
+ ExprContext *econtext;
+ TupleDesc tupdesc;
+ EState *estate;
+ ScanDirection direction;
+ Tuplestorestate *tuplestorestate;
+ FunctionScanState *scanstate;
+ bool should_free;
+ HeapTuple heapTuple;
+
+ /*
+ * get information from the estate and scan state
+ */
+ scanstate = (FunctionScanState *) node->scan.scanstate;
+ estate = node->scan.plan.state;
+ direction = estate->es_direction;
+ econtext = scanstate->csstate.cstate.cs_ExprContext;
+
+ tuplestorestate = scanstate->tuplestorestate;
+ tupdesc = scanstate->tupdesc;
+ expr = scanstate->funcexpr;
+
+ /*
+ * If first time through, read all tuples from function and pass them to
+ * tuplestore.c. Subsequent calls just fetch tuples from tuplestore.
+ */
+ if (tuplestorestate == NULL)
+ {
+ /*
+ * Initialize tuplestore module.
+ */
+ tuplestorestate = tuplestore_begin_heap(true, /* randomAccess */
+ SortMem);
+
+ scanstate->tuplestorestate = (void *) tuplestorestate;
+
+ /*
+ * Compute all the function tuples and pass to tuplestore.
+ */
+ for (;;)
+ {
+ bool isNull;
+ ExprDoneCond isDone;
+
+ isNull = false;
+ isDone = ExprSingleResult;
+ slot = function_getonetuple(scanstate->csstate.css_ScanTupleSlot,
+ expr, econtext, tupdesc,
+ scanstate->returnsTuple,
+ &isNull, &isDone);
+ if (TupIsNull(slot))
+ break;
+
+ tuplestore_puttuple(tuplestorestate, (void *) slot->val);
+ ExecClearTuple(slot);
+
+ if (isDone != ExprMultipleResult)
+ break;
+ }
+
+ /*
+ * Complete the store.
+ */
+ tuplestore_donestoring(tuplestorestate);
+ }
+
+ /*
+ * Get the next tuple from tuplestore. Return NULL if no more tuples.
+ */
+ slot = scanstate->csstate.css_ScanTupleSlot;
+ heapTuple = tuplestore_getheaptuple(tuplestorestate,
+ ScanDirectionIsForward(direction),
+ &should_free);
+
+ return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
+}
+
+/* ----------------------------------------------------------------
+ * ExecFunctionScan(node)
+ *
+ * Scans the Function sequentially and returns the next qualifying
+ * tuple.
+ * It calls the ExecScan() routine and passes it the access method
+ * which retrieve tuples sequentially.
+ *
+ */
+
+TupleTableSlot *
+ExecFunctionScan(FunctionScan *node)
+{
+ /*
+ * use FunctionNext as access method
+ */
+ return ExecScan(&node->scan, (ExecScanAccessMtd) FunctionNext);
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitFunctionScan
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
+{
+ FunctionScanState *scanstate;
+ RangeTblEntry *rte;
+ Oid funcrettype;
+ Oid funcrelid;
+ TupleDesc tupdesc;
+
+ /*
+ * FunctionScan should not have any children.
+ */
+ Assert(outerPlan((Plan *) node) == NULL);
+ Assert(innerPlan((Plan *) node) == NULL);
+
+ /*
+ * assign the node's execution state
+ */
+ node->scan.plan.state = estate;
+
+ /*
+ * create new ScanState for node
+ */
+ scanstate = makeNode(FunctionScanState);
+ node->scan.scanstate = &scanstate->csstate;
+
+ /*
+ * Miscellaneous initialization
+ *
+ * create expression context for node
+ */
+ ExecAssignExprContext(estate, &scanstate->csstate.cstate);
+
+#define FUNCTIONSCAN_NSLOTS 2
+
+ /*
+ * tuple table initialization
+ */
+ ExecInitResultTupleSlot(estate, &scanstate->csstate.cstate);
+ ExecInitScanTupleSlot(estate, &scanstate->csstate);
+
+ /*
+ * get info about function
+ */
+ rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
+ Assert(rte->rtekind == RTE_FUNCTION);
+ funcrettype = exprType(rte->funcexpr);
+ funcrelid = typeidTypeRelid(funcrettype);
+
+ /*
+ * Build a suitable tupledesc representing the output rows
+ */
+ if (OidIsValid(funcrelid))
+ {
+ /*
+ * Composite data type, i.e. a table's row type
+ * Same as ordinary relation RTE
+ */
+ Relation rel;
+
+ rel = relation_open(funcrelid, AccessShareLock);
+ tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
+ relation_close(rel, AccessShareLock);
+ scanstate->returnsTuple = true;
+ }
+ else
+ {
+ /*
+ * Must be a base data type, i.e. scalar
+ */
+ char *attname = strVal(lfirst(rte->eref->colnames));
+
+ tupdesc = CreateTemplateTupleDesc(1);
+ TupleDescInitEntry(tupdesc,
+ (AttrNumber) 1,
+ attname,
+ funcrettype,
+ -1,
+ 0,
+ false);
+ scanstate->returnsTuple = false;
+ }
+ scanstate->tupdesc = tupdesc;
+ ExecSetSlotDescriptor(scanstate->csstate.css_ScanTupleSlot,
+ tupdesc, false);
+
+ /*
+ * Other node-specific setup
+ */
+ scanstate->tuplestorestate = NULL;
+ scanstate->funcexpr = rte->funcexpr;
+
+ scanstate->functionmode = get_functionmode(rte->funcexpr);
+
+ scanstate->csstate.cstate.cs_TupFromTlist = false;
+
+ /*
+ * initialize tuple type
+ */
+ ExecAssignResultTypeFromTL((Plan *) node, &scanstate->csstate.cstate);
+ ExecAssignProjectionInfo((Plan *) node, &scanstate->csstate.cstate);
+
+ return TRUE;
+}
+
+int
+ExecCountSlotsFunctionScan(FunctionScan *node)
+{
+ return ExecCountSlotsNode(outerPlan(node)) +
+ ExecCountSlotsNode(innerPlan(node)) +
+ FUNCTIONSCAN_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndFunctionScan
+ *
+ * frees any storage allocated through C routines.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndFunctionScan(FunctionScan *node)
+{
+ FunctionScanState *scanstate;
+ EState *estate;
+
+ /*
+ * get information from node
+ */
+ scanstate = (FunctionScanState *) node->scan.scanstate;
+ estate = node->scan.plan.state;
+
+ /*
+ * Free the projection info and the scan attribute info
+ *
+ * Note: we don't ExecFreeResultType(scanstate) because the rule manager
+ * depends on the tupType returned by ExecMain(). So for now, this is
+ * freed at end-transaction time. -cim 6/2/91
+ */
+ ExecFreeProjectionInfo(&scanstate->csstate.cstate);
+ ExecFreeExprContext(&scanstate->csstate.cstate);
+
+ /*
+ * clean out the tuple table
+ */
+ ExecClearTuple(scanstate->csstate.cstate.cs_ResultTupleSlot);
+ ExecClearTuple(scanstate->csstate.css_ScanTupleSlot);
+
+ /*
+ * Release tuplestore resources
+ */
+ if (scanstate->tuplestorestate != NULL)
+ tuplestore_end((Tuplestorestate *) scanstate->tuplestorestate);
+ scanstate->tuplestorestate = NULL;
+}
+
+/* ----------------------------------------------------------------
+ * ExecFunctionMarkPos
+ *
+ * Calls tuplestore to save the current position in the stored file.
+ * ----------------------------------------------------------------
+ */
+void
+ExecFunctionMarkPos(FunctionScan *node)
+{
+ FunctionScanState *scanstate;
+
+ scanstate = (FunctionScanState *) node->scan.scanstate;
+
+ /*
+ * if we haven't materialized yet, just return.
+ */
+ if (!scanstate->tuplestorestate)
+ return;
+
+ tuplestore_markpos((Tuplestorestate *) scanstate->tuplestorestate);
+}
+
+/* ----------------------------------------------------------------
+ * ExecFunctionRestrPos
+ *
+ * Calls tuplestore to restore the last saved file position.
+ * ----------------------------------------------------------------
+ */
+void
+ExecFunctionRestrPos(FunctionScan *node)
+{
+ FunctionScanState *scanstate;
+
+ scanstate = (FunctionScanState *) node->scan.scanstate;
+
+ /*
+ * if we haven't materialized yet, just return.
+ */
+ if (!scanstate->tuplestorestate)
+ return;
+
+ tuplestore_restorepos((Tuplestorestate *) scanstate->tuplestorestate);
+}
+
+/* ----------------------------------------------------------------
+ * ExecFunctionReScan
+ *
+ * Rescans the relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecFunctionReScan(FunctionScan *node, ExprContext *exprCtxt, Plan *parent)
+{
+ FunctionScanState *scanstate;
+
+ /*
+ * get information from node
+ */
+ scanstate = (FunctionScanState *) node->scan.scanstate;
+
+ ExecClearTuple(scanstate->csstate.cstate.cs_ResultTupleSlot);
+
+ /*
+ * If we haven't materialized yet, just return.
+ */
+ if (!scanstate->tuplestorestate)
+ return;
+
+ /*
+ * Here we have a choice whether to drop the tuplestore (and recompute
+ * the function outputs) or just rescan it. This should depend on
+ * whether the function expression contains parameters and/or is
+ * marked volatile. FIXME soon.
+ */
+ if (node->scan.plan.chgParam != NULL)
+ {
+ tuplestore_end((Tuplestorestate *) scanstate->tuplestorestate);
+ scanstate->tuplestorestate = NULL;
+ }
+ else
+ tuplestore_rescan((Tuplestorestate *) scanstate->tuplestorestate);
+}
+
+/*
+ * Run the underlying function to get the next tuple
+ */
+static TupleTableSlot *
+function_getonetuple(TupleTableSlot *slot,
+ Node *expr,
+ ExprContext *econtext,
+ TupleDesc tupdesc,
+ bool returnsTuple,
+ bool *isNull,
+ ExprDoneCond *isDone)
+{
+ HeapTuple tuple;
+ Datum retDatum;
+ char nullflag;
+
+ /*
+ * get the next Datum from the function
+ */
+ retDatum = ExecEvalExprSwitchContext(expr, econtext, isNull, isDone);
+
+ /*
+ * check to see if we're really done
+ */
+ if (*isDone == ExprEndResult)
+ slot = NULL;
+ else
+ {
+ if (returnsTuple)
+ {
+ /*
+ * Composite data type, i.e. a table's row type
+ * function returns pointer to tts??
+ */
+ slot = (TupleTableSlot *) retDatum;
+ }
+ else
+ {
+ /*
+ * Must be a base data type, i.e. scalar
+ * turn it into a tuple
+ */
+ nullflag = *isNull ? 'n' : ' ';
+ tuple = heap_formtuple(tupdesc, &retDatum, &nullflag);
+
+ /*
+ * save the tuple in the scan tuple slot and return the slot.
+ */
+ slot = ExecStoreTuple(tuple, /* tuple to store */
+ slot, /* slot to store in */
+ InvalidBuffer, /* buffer associated with
+ * this tuple */
+ true); /* pfree this pointer */
+ }
+ }
+
+ return slot;
+}
+
+static FunctionMode
+get_functionmode(Node *expr)
+{
+ /*
+ * for the moment, hardwire this
+ */
+ return PM_REPEATEDCALL;
+}
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index edd4640ba41..12f659847c6 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.11 2001/10/25 05:49:29 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.12 2002/05/12 20:10:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -146,7 +146,7 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent)
* This should agree with ExecInitSubPlan
*/
rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
- Assert(rte->subquery != NULL);
+ Assert(rte->rtekind == RTE_SUBQUERY);
sp_estate = CreateExecutorState();
subquerystate->sss_SubEState = sp_estate;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c461f147ef8..cfcf00a4d47 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 31ee46e0603..185a16beb5a 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -1574,6 +1574,17 @@ _equalRangeSubselect(RangeSubselect *a, RangeSubselect *b)
}
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)
{
if (!equal(a->names, b->names))
@@ -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;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 34264db11e9..698beb5d999 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -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
@@ -552,6 +552,18 @@ _outSubqueryScan(StringInfo str, SubqueryScan *node)
}
/*
+ * 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
*/
static void
@@ -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;
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index a1fd2b93870..49bf55e3518 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -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')
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 1d4236fc847..27604dcb4be 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -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
@@ -652,6 +652,24 @@ _readSubqueryScan(void)
}
/* ----------------
+ * _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
*
* Sort is a subclass of Plan
@@ -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)
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 9368723188f..1e9074186c2 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -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)
{
@@ -386,6 +393,23 @@ set_subquery_pathlist(Query *root, RelOptInfo *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.
*/
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 85584bfdcfc..35a98031004 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -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
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 2bc0bb8a137..b14322f45b5 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -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;
@@ -387,6 +392,36 @@ cost_tidscan(Path *path, Query *root,
}
/*
+ * 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.
*
@@ -1299,6 +1334,54 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
}
/*
+ * 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.
*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index c20abacca39..cd8b0e6612f 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -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)
{
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 807876e4a72..3e07b2425e5 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -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);
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index a67eb5d8e54..57930e9a502 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -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:
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 1b7cc14e2e7..1a278e9ca2e 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -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:
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index c86ee450082..07d05b38a87 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -455,6 +455,25 @@ create_subqueryscan_path(RelOptInfo *rel)
}
/*
+ * 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
* relations.
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 0fad16fbdd0..38e988b26fc 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -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;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a3acf294534..72a0c8be513 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -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));
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 258a11c1183..6b7469f2bd7 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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
- {
- $$ = 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;
+ }
}
;
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 6df4a4fd7dc..19aa688ff94 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -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)
{
@@ -454,6 +457,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
* range table list being built in the ParseState, and return 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 */
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index f74f5be2f7b..339577f39ca 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -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,
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index b822a2378ba..857adf05691 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -673,6 +673,117 @@ addRangeTableEntryForSubquery(ParseState *pstate,
}
/*
+ * 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).
*
* This is much like addRangeTableEntry() except that it makes a join RTE.
@@ -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;
+ 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);
}
/*
@@ -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;
-
- 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);
}
/*
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 0901164389d..a8a466ac3ba 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -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;
}
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 6d4c81c70a2..f35540128cf 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -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);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index b1f012d4d9d..b4bd3b2f514 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -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;
diff --git a/src/backend/utils/fmgr/README b/src/backend/utils/fmgr/README
index c50e33b9d10..043a3a7e8d9 100644
--- a/src/backend/utils/fmgr/README
+++ b/src/backend/utils/fmgr/README
@@ -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
-----------------------------
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index e135782bd58..3120fb5fae0 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -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
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 63bc10f79dc..6752d72ca76 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -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 */
diff --git a/src/include/executor/nodeFunctionscan.h b/src/include/executor/nodeFunctionscan.h
new file mode 100644
index 00000000000..4decbfe6b5e
--- /dev/null
+++ b/src/include/executor/nodeFunctionscan.h
@@ -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 */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 97e76ccdfd5..66f93df451d 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,21 @@ typedef struct IndexInfo
} 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
*
* This class holds the "current context" information
@@ -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
* ----------------------------------------------------------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 1664e769003..492619e194d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -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,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 5bd0e89b185..0c5672dd677 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -388,6 +388,16 @@ typedef struct RangeSubselect
} 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)
*
* For a plain index, each 'name' is an attribute name in the heap relation;
@@ -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
@@ -508,6 +519,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):
*
* joinaliasvars is a list of Vars or COALESCE expressions corresponding
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 9ba1caca327..20413e08ae5 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -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
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 903245cf51f..6eed89a7042 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -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.
* ----------------------------------------------------------------
*/
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index d26d60c71be..28ad8bc2eb7 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -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;
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 7c4c22a5959..690fa3f129c 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -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
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index d9419df47d3..5a6646db88c 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -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,
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index 353ce8957c7..be02997ba5e 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -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,
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index d46e6d8262b..7a8d1fb3873 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -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);