]> granicus.if.org Git - postgresql/commitdiff
First pass at set-returning-functions in FROM, by Joe Conway with
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 12 May 2002 20:10:05 +0000 (20:10 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 12 May 2002 20:10:05 +0000 (20:10 +0000)
some kibitzing from Tom Lane.  Not everything works yet, and there's
no documentation or regression test, but let's commit this so Joe
doesn't need to cope with tracking changes in so many files ...

48 files changed:
src/backend/catalog/namespace.c
src/backend/commands/explain.c
src/backend/commands/indexcmds.c
src/backend/executor/Makefile
src/backend/executor/execAmi.c
src/backend/executor/execMain.c
src/backend/executor/execProcnode.c
src/backend/executor/execQual.c
src/backend/executor/execUtils.c
src/backend/executor/functions.c
src/backend/executor/nodeFunctionscan.c [new file with mode: 0644]
src/backend/executor/nodeSubqueryscan.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/print.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/clausesel.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/pathnode.c
src/backend/optimizer/util/relnode.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/parse_clause.c
src/backend/parser/parse_func.c
src/backend/parser/parse_relation.c
src/backend/parser/parse_type.c
src/backend/rewrite/rewriteDefine.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/fmgr/README
src/include/catalog/catversion.h
src/include/executor/executor.h
src/include/executor/nodeFunctionscan.h [new file with mode: 0644]
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/plannodes.h
src/include/nodes/primnodes.h
src/include/nodes/relation.h
src/include/optimizer/cost.h
src/include/optimizer/pathnode.h
src/include/parser/parse_relation.h
src/include/utils/builtins.h

index 9e213edeaed4e3b5863d9887aacbd0947cafeb27..9e5002e37f169f27bc34e05ad7de8e69f5af931f 100644 (file)
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.18 2002/05/05 00:03:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.19 2002/05/12 20:10:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -492,7 +492,8 @@ FuncnameGetCandidates(List *names, int nargs)
                                elog(ERROR, "Cross-database references are not implemented");
                        break;
                default:
-                       elog(ERROR, "Improper qualified name (too many dotted names)");
+                       elog(ERROR, "Improper qualified name (too many dotted names): %s",
+                                NameListToString(names));
                        break;
        }
 
@@ -746,7 +747,8 @@ OpernameGetCandidates(List *names, char oprkind)
                                elog(ERROR, "Cross-database references are not implemented");
                        break;
                default:
-                       elog(ERROR, "Improper qualified name (too many dotted names)");
+                       elog(ERROR, "Improper qualified name (too many dotted names): %s",
+                                NameListToString(names));
                        break;
        }
 
@@ -1199,7 +1201,8 @@ QualifiedNameGetCreationNamespace(List *names, char **objname_p)
                                elog(ERROR, "Cross-database references are not implemented");
                        break;
                default:
-                       elog(ERROR, "Improper qualified name (too many dotted names)");
+                       elog(ERROR, "Improper qualified name (too many dotted names): %s",
+                                NameListToString(names));
                        break;
        }
 
index 03f494d63f44d69bb9e850dc7ed23b3a4b595afe..cb2b2d0f23e3a8b820f2d7053a9bd62fefd57acd 100644 (file)
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.76 2002/05/03 15:56:45 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.77 2002/05/12 20:10:02 tgl Exp $
  *
  */
 
@@ -262,6 +262,9 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
                case T_SubqueryScan:
                        pname = "Subquery Scan";
                        break;
+               case T_FunctionScan:
+                       pname = "Function Scan";
+                       break;
                case T_Material:
                        pname = "Materialize";
                        break;
@@ -336,7 +339,7 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
                                char   *relname;
 
                                /* Assume it's on a real relation */
-                               Assert(rte->relid);
+                               Assert(rte->rtekind == RTE_RELATION);
 
                                /* We only show the rel name, not schema name */
                                relname = get_rel_name(rte->relid);
@@ -358,6 +361,33 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
                                                                 quote_identifier(rte->eref->aliasname));
                        }
                        break;
+               case T_FunctionScan:
+                       if (((Scan *) plan)->scanrelid > 0)
+                       {
+                               RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
+                                                                                         es->rtable);
+                               Expr   *expr;
+                               Func   *funcnode;
+                               Oid             funcid;
+                               char   *proname;
+
+                               /* Assert it's on a RangeFunction */
+                               Assert(rte->rtekind == RTE_FUNCTION);
+
+                               expr = (Expr *) rte->funcexpr;
+                               funcnode = (Func *) expr->oper;
+                               funcid = funcnode->funcid;
+
+                               /* We only show the func name, not schema name */
+                               proname = get_func_name(funcid);
+
+                               appendStringInfo(str, " on %s",
+                                                                quote_identifier(proname));
+                               if (strcmp(rte->eref->aliasname, proname) != 0)
+                                       appendStringInfo(str, " %s",
+                                                                        quote_identifier(rte->eref->aliasname));
+                       }
+                       break;
                default:
                        break;
        }
@@ -397,6 +427,7 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
                        break;
                case T_SeqScan:
                case T_TidScan:
+               case T_FunctionScan:
                        show_scan_qual(plan->qual, false,
                                                   "Filter",
                                                   ((Scan *) plan)->scanrelid,
@@ -545,7 +576,7 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
                                                                          es->rtable);
                List       *saved_rtable = es->rtable;
 
-               Assert(rte->subquery != NULL);
+               Assert(rte->rtekind == RTE_SUBQUERY);
                es->rtable = rte->subquery->rtable;
 
                for (i = 0; i < indent; i++)
@@ -623,11 +654,7 @@ show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
        /* Generate deparse context */
        Assert(scanrelid > 0 && scanrelid <= length(es->rtable));
        rte = rt_fetch(scanrelid, es->rtable);
-
-       /* Assume it's on a real relation */
-       Assert(rte->relid);
-       scancontext = deparse_context_for_relation(rte->eref->aliasname,
-                                                                                          rte->relid);
+       scancontext = deparse_context_for_rte(rte);
 
        /*
         * If we have an outer plan that is referenced by the qual, add it to
index df6f81cd1dbc7702c3b8145a40cf08fcd53f9607..e2ae7f06314dd0509b6916af22cdc99fa464b697 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.72 2002/04/27 03:45:01 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.73 2002/05/12 20:10:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -452,7 +452,8 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
                                elog(ERROR, "Cross-database references are not implemented");
                        break;
                default:
-                       elog(ERROR, "Improper opclass name (too many dotted names)");
+                       elog(ERROR, "Improper opclass name (too many dotted names): %s",
+                                NameListToString(attribute->opclass));
                        break;
        }
 
index 363ea342e9f46c4b541a90d4ba7e61af9c149338..0a66e1be03e08ae0ef9fb1bad32ccf1e706c60b5 100644 (file)
@@ -4,7 +4,7 @@
 #    Makefile for executor
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.17 2001/09/18 01:59:06 tgl Exp $
+#    $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.18 2002/05/12 20:10:02 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -16,9 +16,9 @@ OBJS = execAmi.o execFlatten.o execJunk.o execMain.o \
        execProcnode.o execQual.o execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o nodeHash.o \
        nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
-       nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSetOp.o nodeSort.o \
-       nodeUnique.o nodeLimit.o nodeGroup.o nodeSubplan.o \
-       nodeSubqueryscan.o nodeTidscan.o spi.o
+       nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \
+       nodeSetOp.o nodeSort.o nodeUnique.o nodeLimit.o nodeGroup.o \
+       nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o spi.o
 
 all: SUBSYS.o
 
index 119c89b1c27fdf386f6cc9c786b61e673d95a39f..d2fe9da9ada52e17efc0034ed7f2593abbdc7f21 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: execAmi.c,v 1.62 2002/03/02 21:39:24 momjian Exp $
+ *     $Id: execAmi.c,v 1.63 2002/05/12 20:10:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,6 +35,7 @@
 #include "executor/nodeSort.h"
 #include "executor/nodeSubplan.h"
 #include "executor/nodeSubqueryscan.h"
+#include "executor/nodeFunctionscan.h"
 #include "executor/nodeUnique.h"
 
 
@@ -100,6 +101,10 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
                        ExecSubqueryReScan((SubqueryScan *) node, exprCtxt, parent);
                        break;
 
+               case T_FunctionScan:
+                       ExecFunctionReScan((FunctionScan *) node, exprCtxt, parent);
+                       break;
+
                case T_Material:
                        ExecMaterialReScan((Material *) node, exprCtxt, parent);
                        break;
@@ -187,6 +192,10 @@ ExecMarkPos(Plan *node)
                        ExecIndexMarkPos((IndexScan *) node);
                        break;
 
+               case T_FunctionScan:
+                       ExecFunctionMarkPos((FunctionScan *) node);
+                       break;
+
                case T_Material:
                        ExecMaterialMarkPos((Material *) node);
                        break;
@@ -229,6 +238,10 @@ ExecRestrPos(Plan *node)
                        ExecIndexRestrPos((IndexScan *) node);
                        break;
 
+               case T_FunctionScan:
+                       ExecFunctionRestrPos((FunctionScan *) node);
+                       break;
+
                case T_Material:
                        ExecMaterialRestrPos((Material *) node);
                        break;
index 68fcb325a63666a4e4029301d3fe635b7dc97571..a2c43bc0359d76103de2dfc39c278cd4ed94f94b 100644 (file)
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.160 2002/04/27 21:24:34 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.161 2002/05/12 20:10:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -311,7 +311,7 @@ ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
 
                                /* Recursively check the subquery */
                                rte = rt_fetch(scan->scan.scanrelid, rangeTable);
-                               Assert(rte->subquery != NULL);
+                               Assert(rte->rtekind == RTE_SUBQUERY);
                                ExecCheckQueryPerms(operation, rte->subquery, scan->subplan);
                                break;
                        }
@@ -362,10 +362,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
        Oid                     userid;
        AclResult       aclcheck_result;
 
-       /*
-        * If it's a subquery RTE, ignore it --- it will be checked when
-        * ExecCheckPlanPerms finds the SubqueryScan node for it.
-        */
+       /*
+        * Only plain-relation RTEs need to be checked here.  Subquery RTEs
+        * will be checked when ExecCheckPlanPerms finds the SubqueryScan node,
+        * and function RTEs are checked by init_fcache when the function is
+        * prepared for execution.  Join and special RTEs need no checks.
+        */
        if (rte->rtekind != RTE_RELATION)
                return;
 
index 5cbd2ea5622e5a9a17a20750f451437bc7c765b0..c0f005f1e675a51087519026e47b712310f93836 100644 (file)
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.28 2001/10/25 05:49:27 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.29 2002/05/12 20:10:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -96,6 +96,7 @@
 #include "executor/nodeSort.h"
 #include "executor/nodeSubplan.h"
 #include "executor/nodeSubqueryscan.h"
+#include "executor/nodeFunctionscan.h"
 #include "executor/nodeUnique.h"
 #include "miscadmin.h"
 #include "tcop/tcopprot.h"
@@ -168,6 +169,11 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
                                                                                  parent);
                        break;
 
+               case T_FunctionScan:
+                       result = ExecInitFunctionScan((FunctionScan *) node, estate,
+                                                                                 parent);
+                       break;
+
                        /*
                         * join nodes
                         */
@@ -297,6 +303,10 @@ ExecProcNode(Plan *node, Plan *parent)
                        result = ExecSubqueryScan((SubqueryScan *) node);
                        break;
 
+               case T_FunctionScan:
+                       result = ExecFunctionScan((FunctionScan *) node);
+                       break;
+
                        /*
                         * join nodes
                         */
@@ -392,6 +402,9 @@ ExecCountSlotsNode(Plan *node)
                case T_SubqueryScan:
                        return ExecCountSlotsSubqueryScan((SubqueryScan *) node);
 
+               case T_FunctionScan:
+                       return ExecCountSlotsFunctionScan((FunctionScan *) node);
+
                        /*
                         * join nodes
                         */
@@ -503,6 +516,10 @@ ExecEndNode(Plan *node, Plan *parent)
                        ExecEndSubqueryScan((SubqueryScan *) node);
                        break;
 
+               case T_FunctionScan:
+                       ExecEndFunctionScan((FunctionScan *) node);
+                       break;
+
                        /*
                         * join nodes
                         */
@@ -640,6 +657,14 @@ ExecGetTupType(Plan *node)
                        }
                        break;
 
+               case T_FunctionScan:
+                       {
+                               CommonScanState *scanstate = ((FunctionScan *) node)->scan.scanstate;
+
+                               slot = scanstate->cstate.cs_ResultTupleSlot;
+                       }
+                       break;
+
                case T_Material:
                        {
                                MaterialState *matstate = ((Material *) node)->matstate;
index 5cc1bbaa718679f0c91a6cc9564b45c2e1d1ba04..bebb664f29901679ba9adc02bdb7b1e65a6a9041 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.91 2002/04/27 03:45:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.92 2002/05/12 20:10:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -700,6 +700,7 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
        {
                fcinfo.resultinfo = (Node *) &rsinfo;
                rsinfo.type = T_ReturnSetInfo;
+               rsinfo.econtext = econtext;
        }
 
        /*
index 9b03401e4449451bd45e4fb7e731f5c9b7ea401e..a6b5048326b75eb69771165dff9c7e35b680eb00 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.80 2002/04/12 20:38:26 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.81 2002/05/12 20:10:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,6 +20,9 @@
  *             ExecCloseIndices                 | referenced by InitPlan, EndPlan,
  *             ExecInsertIndexTuples   /  ExecAppend, ExecReplace
  *
+ *             RegisterExprContextCallback    Register function shutdown callback
+ *             UnregisterExprContextCallback  Deregister function shutdown callback
+ *
  *      NOTES
  *             This file has traditionally been the place to stick misc.
  *             executor support stuff that doesn't really go anyplace else.
@@ -58,6 +61,9 @@ extern int    NIndexTupleProcessed;           /* have to be defined in the
                                                                                 * access method level so that the
                                                                                 * cinterface.a will link ok. */
 
+
+static void ShutdownExprContext(ExprContext *econtext);
+
 /* ----------------------------------------------------------------
  *                                             statistic functions
  * ----------------------------------------------------------------
@@ -120,8 +126,6 @@ DisplayTupleCount(FILE *statfp)
 
 /* ----------------------------------------------------------------
  *                              miscellaneous node-init support functions
- *
- *             ExecAssignExprContext   - assigns the node's expression context
  * ----------------------------------------------------------------
  */
 
@@ -160,6 +164,7 @@ ExecAssignExprContext(EState *estate, CommonState *commonstate)
        econtext->ecxt_param_list_info = estate->es_param_list_info;
        econtext->ecxt_aggvalues = NULL;
        econtext->ecxt_aggnulls = NULL;
+       econtext->ecxt_callbacks = NULL;
 
        commonstate->cs_ExprContext = econtext;
 }
@@ -204,6 +209,7 @@ MakeExprContext(TupleTableSlot *slot,
        econtext->ecxt_param_list_info = NULL;
        econtext->ecxt_aggvalues = NULL;
        econtext->ecxt_aggnulls = NULL;
+       econtext->ecxt_callbacks = NULL;
 
        return econtext;
 }
@@ -216,6 +222,9 @@ MakeExprContext(TupleTableSlot *slot,
 void
 FreeExprContext(ExprContext *econtext)
 {
+       /* Call any registered callbacks */
+       ShutdownExprContext(econtext);
+       /* And clean up the memory used */
        MemoryContextDelete(econtext->ecxt_per_tuple_memory);
        pfree(econtext);
 }
@@ -369,6 +378,11 @@ ExecFreeExprContext(CommonState *commonstate)
        if (econtext == NULL)
                return;
 
+       /*
+        * clean up any registered callbacks
+        */
+       ShutdownExprContext(econtext);
+
        /*
         * clean up memory used.
         */
@@ -689,3 +703,85 @@ SetChangedParamList(Plan *node, List *newchg)
                node->chgParam = lappendi(node->chgParam, paramId);
        }
 }
+
+/*
+ * Register a shutdown callback in an ExprContext.
+ *
+ * Shutdown callbacks will be called (in reverse order of registration)
+ * when the ExprContext is deleted or rescanned.  This provides a hook
+ * for functions called in the context to do any cleanup needed --- it's
+ * particularly useful for functions returning sets.  Note that the
+ * callback will *not* be called in the event that execution is aborted
+ * by an error.
+ */
+void
+RegisterExprContextCallback(ExprContext *econtext,
+                                                       ExprContextCallbackFunction function,
+                                                       Datum arg)
+{
+       ExprContext_CB   *ecxt_callback;
+
+       /* Save the info in appropriate memory context */
+       ecxt_callback = (ExprContext_CB *)
+               MemoryContextAlloc(econtext->ecxt_per_query_memory,
+                                                  sizeof(ExprContext_CB));
+
+       ecxt_callback->function = function;
+       ecxt_callback->arg = arg;
+
+       /* link to front of list for appropriate execution order */
+       ecxt_callback->next = econtext->ecxt_callbacks;
+       econtext->ecxt_callbacks = ecxt_callback;
+}
+
+/*
+ * Deregister a shutdown callback in an ExprContext.
+ *
+ * Any list entries matching the function and arg will be removed.
+ * This can be used if it's no longer necessary to call the callback.
+ */
+void
+UnregisterExprContextCallback(ExprContext *econtext,
+                                                         ExprContextCallbackFunction function,
+                                                         Datum arg)
+{
+       ExprContext_CB   **prev_callback;
+       ExprContext_CB   *ecxt_callback;
+
+       prev_callback = &econtext->ecxt_callbacks;
+
+       while ((ecxt_callback = *prev_callback) != NULL)
+       {
+               if (ecxt_callback->function == function && ecxt_callback->arg == arg)
+               {
+                       *prev_callback = ecxt_callback->next;
+                       pfree(ecxt_callback);
+               }
+               else
+               {
+                       prev_callback = &ecxt_callback->next;
+               }
+       }
+}
+
+/*
+ * Call all the shutdown callbacks registered in an ExprContext.
+ *
+ * The callback list is emptied (important in case this is only a rescan
+ * reset, and not deletion of the ExprContext).
+ */
+static void
+ShutdownExprContext(ExprContext *econtext)
+{
+       ExprContext_CB   *ecxt_callback;
+
+       /*
+        * Call each callback function in reverse registration order.
+        */
+       while ((ecxt_callback = econtext->ecxt_callbacks) != NULL)
+       {
+               econtext->ecxt_callbacks = ecxt_callback->next;
+               (*ecxt_callback->function) (ecxt_callback->arg);
+               pfree(ecxt_callback);
+       }
+}
index 885d93a2afffd29a0fe6deaed55de083a33a7750..938f7e17f931ba31ef8398fd8cfce21c7ea50a21 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.49 2002/02/27 19:34:51 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.50 2002/05/12 20:10:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,7 +28,7 @@
 
 
 /*
- * We have an execution_state record for each query in the function.
+ * We have an execution_state record for each query in a function.
  */
 typedef enum
 {
@@ -56,6 +56,7 @@ typedef struct
        int                     typlen;                 /* length of the return type */
        bool            typbyval;               /* true if return type is pass by value */
        bool            returnsTuple;   /* true if return type is a tuple */
+       bool            shutdown_reg;   /* true if registered shutdown callback */
 
        TupleTableSlot *funcSlot;       /* if one result we need to copy it before
                                                                 * we end execution of the function and
@@ -79,6 +80,7 @@ static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo);
 static Datum postquel_execute(execution_state *es,
                                 FunctionCallInfo fcinfo,
                                 SQLFunctionCachePtr fcache);
+static void ShutdownSQLFunction(Datum arg);
 
 
 static execution_state *
@@ -546,6 +548,15 @@ fmgr_sql(PG_FUNCTION_ARGS)
                                elog(ERROR, "Set-valued function called in context that cannot accept a set");
                        fcinfo->isnull = true;
                        result = (Datum) 0;
+
+                       /* Deregister shutdown callback, if we made one */
+                       if (fcache->shutdown_reg)
+                       {
+                               UnregisterExprContextCallback(rsi->econtext,
+                                                                                         ShutdownSQLFunction,
+                                                                                         PointerGetDatum(fcache));
+                               fcache->shutdown_reg = false;
+                       }
                }
 
                MemoryContextSwitchTo(oldcontext);
@@ -570,9 +581,45 @@ fmgr_sql(PG_FUNCTION_ARGS)
                        rsi->isDone = ExprMultipleResult;
                else
                        elog(ERROR, "Set-valued function called in context that cannot accept a set");
+
+               /*
+                * Ensure we will get shut down cleanly if the exprcontext is
+                * not run to completion.
+                */
+               if (!fcache->shutdown_reg)
+               {
+                       RegisterExprContextCallback(rsi->econtext,
+                                                                               ShutdownSQLFunction,
+                                                                               PointerGetDatum(fcache));
+                       fcache->shutdown_reg = true;
+               }
        }
 
        MemoryContextSwitchTo(oldcontext);
 
        return result;
 }
+
+/*
+ * callback function in case a function-returning-set needs to be shut down
+ * before it has been run to completion
+ */
+static void
+ShutdownSQLFunction(Datum arg)
+{
+       SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) DatumGetPointer(arg);
+       execution_state *es = fcache->func_state;
+
+       while (es != NULL)
+       {
+               /* Shut down anything still running */
+               if (es->status == F_EXEC_RUN)
+                       postquel_end(es);
+               /* Reset states to START in case we're called again */
+               es->status = F_EXEC_START;
+               es = es->next;
+       }
+
+       /* execUtils will deregister the callback... */
+       fcache->shutdown_reg = false;
+}
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
new file mode 100644 (file)
index 0000000..7b6d466
--- /dev/null
@@ -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;
+}
index edd4640ba41d98e910fe8991bc9725a93dc90255..12f659847c690540fab44bf1e10fdd7643f87b1d 100644 (file)
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.11 2001/10/25 05:49:29 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.12 2002/05/12 20:10:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -146,7 +146,7 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent)
         * This should agree with ExecInitSubPlan
         */
        rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
-       Assert(rte->subquery != NULL);
+       Assert(rte->rtekind == RTE_SUBQUERY);
 
        sp_estate = CreateExecutorState();
        subquerystate->sss_SubEState = sp_estate;
index c461f147ef89c9b9a486718422d74d839f1adef5..cfcf00a4d47a8694d91e188e70e8b1c2550554b6 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.182 2002/04/28 19:54:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.183 2002/05/12 20:10:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -310,6 +310,23 @@ _copySubqueryScan(SubqueryScan *from)
        return newnode;
 }
 
+/* ----------------
+ *             _copyFunctionScan
+ * ----------------
+ */
+static FunctionScan *
+_copyFunctionScan(FunctionScan *from)
+{
+       FunctionScan *newnode = makeNode(FunctionScan);
+
+       /*
+        * copy node superclass fields
+        */
+       CopyPlanFields((Plan *) from, (Plan *) newnode);
+       CopyScanFields((Scan *) from, (Scan *) newnode);
+
+       return newnode;
+}
 
 /* ----------------
  *             CopyJoinFields
@@ -1083,7 +1100,7 @@ _copyRelOptInfo(RelOptInfo *from)
        Node_Copy(from, newnode, cheapest_total_path);
        newnode->pruneable = from->pruneable;
 
-       newnode->issubquery = from->issubquery;
+       newnode->rtekind = from->rtekind;
        Node_Copy(from, newnode, indexlist);
        newnode->pages = from->pages;
        newnode->tuples = from->tuples;
@@ -1473,6 +1490,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
        newnode->rtekind = from->rtekind;
        newnode->relid = from->relid;
        Node_Copy(from, newnode, subquery);
+       Node_Copy(from, newnode, funcexpr);
        newnode->jointype = from->jointype;
        Node_Copy(from, newnode, joinaliasvars);
        Node_Copy(from, newnode, alias);
@@ -1690,6 +1708,17 @@ _copyRangeSubselect(RangeSubselect *from)
        return newnode;
 }
 
+static RangeFunction *
+_copyRangeFunction(RangeFunction *from)
+{
+       RangeFunction   *newnode = makeNode(RangeFunction);
+
+       Node_Copy(from, newnode, funccallnode);
+       Node_Copy(from, newnode, alias);
+
+       return newnode;
+}
+
 static TypeCast *
 _copyTypeCast(TypeCast *from)
 {
@@ -2621,6 +2650,9 @@ copyObject(void *from)
                case T_SubqueryScan:
                        retval = _copySubqueryScan(from);
                        break;
+               case T_FunctionScan:
+                       retval = _copyFunctionScan(from);
+                       break;
                case T_Join:
                        retval = _copyJoin(from);
                        break;
@@ -3001,6 +3033,9 @@ copyObject(void *from)
                case T_RangeSubselect:
                        retval = _copyRangeSubselect(from);
                        break;
+               case T_RangeFunction:
+                       retval = _copyRangeFunction(from);
+                       break;
                case T_TypeName:
                        retval = _copyTypeName(from);
                        break;
index 31ee46e06035635d786f59cf9ad1fd21bf15eddb..185a16beb5a9df6d542cb75df6c172688bd05841 100644 (file)
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.130 2002/04/28 19:54:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.131 2002/05/12 20:10:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1573,6 +1573,17 @@ _equalRangeSubselect(RangeSubselect *a, RangeSubselect *b)
        return true;
 }
 
+static bool
+_equalRangeFunction(RangeFunction *a, RangeFunction *b)
+{
+       if (!equal(a->funccallnode, b->funccallnode))
+               return false;
+       if (!equal(a->alias, b->alias))
+               return false;
+
+       return true;
+}
+
 static bool
 _equalTypeName(TypeName *a, TypeName *b)
 {
@@ -1678,6 +1689,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
                return false;
        if (!equal(a->subquery, b->subquery))
                return false;
+       if (!equal(a->funcexpr, b->funcexpr))
+               return false;
        if (a->jointype != b->jointype)
                return false;
        if (!equal(a->joinaliasvars, b->joinaliasvars))
@@ -2166,6 +2179,9 @@ equal(void *a, void *b)
                case T_RangeSubselect:
                        retval = _equalRangeSubselect(a, b);
                        break;
+               case T_RangeFunction:
+                       retval = _equalRangeFunction(a, b);
+                       break;
                case T_TypeName:
                        retval = _equalTypeName(a, b);
                        break;
index 34264db11e9dad3246ace5e3c4a8fbf3927aea29..698beb5d999a597d56a1a912da33a1c1238f9461 100644 (file)
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.157 2002/04/28 19:54:28 tgl Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.158 2002/05/12 20:10:03 tgl Exp $
  *
  * NOTES
  *       Every (plan) node in POSTGRES has an associated "out" routine which
@@ -551,6 +551,18 @@ _outSubqueryScan(StringInfo str, SubqueryScan *node)
        _outNode(str, node->subplan);
 }
 
+/*
+ *     FunctionScan is a subclass of Scan
+ */
+static void
+_outFunctionScan(StringInfo str, FunctionScan *node)
+{
+       appendStringInfo(str, " FUNCTIONSCAN ");
+       _outPlanInfo(str, (Plan *) node);
+
+       appendStringInfo(str, " :scanrelid %u ", node->scan.scanrelid);
+}
+
 /*
  *     Material is a subclass of Plan
  */
@@ -980,6 +992,10 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
                        appendStringInfo(str, ":subquery ");
                        _outNode(str, node->subquery);
                        break;
+               case RTE_FUNCTION:
+                       appendStringInfo(str, ":funcexpr ");
+                       _outNode(str, node->funcexpr);
+                       break;
                case RTE_JOIN:
                        appendStringInfo(str, ":jointype %d :joinaliasvars ",
                                                         (int) node->jointype);
@@ -1598,6 +1614,9 @@ _outNode(StringInfo str, void *obj)
                        case T_SubqueryScan:
                                _outSubqueryScan(str, obj);
                                break;
+                       case T_FunctionScan:
+                               _outFunctionScan(str, obj);
+                               break;
                        case T_Material:
                                _outMaterial(str, obj);
                                break;
index a1fd2b9387081db754e060e2f13b4d9f57e6f0ef..49bf55e35184ea48a00193b9fc7b1b1a76c96f16 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.54 2002/03/24 04:31:07 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.55 2002/05/12 20:10:03 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -254,12 +254,33 @@ print_rt(List *rtable)
        {
                RangeTblEntry *rte = lfirst(l);
 
-               if (rte->rtekind == RTE_RELATION)
-                       printf("%d\t%s\t%u",
-                                  i, rte->eref->aliasname, rte->relid);
-               else
-                       printf("%d\t%s\t[subquery]",
-                                  i, rte->eref->aliasname);
+               switch (rte->rtekind)
+               {
+                       case RTE_RELATION:
+                               printf("%d\t%s\t%u",
+                                          i, rte->eref->aliasname, rte->relid);
+                               break;
+                       case RTE_SUBQUERY:
+                               printf("%d\t%s\t[subquery]",
+                                          i, rte->eref->aliasname);
+                               break;
+                       case RTE_FUNCTION:
+                               printf("%d\t%s\t[rangefunction]",
+                                          i, rte->eref->aliasname);
+                               break;
+                       case RTE_JOIN:
+                               printf("%d\t%s\t[join]",
+                                          i, rte->eref->aliasname);
+                               break;
+                       case RTE_SPECIAL:
+                               printf("%d\t%s\t[special]",
+                                          i, rte->eref->aliasname);
+                               break;
+                       default:
+                               printf("%d\t%s\t[unknown rtekind]",
+                                          i, rte->eref->aliasname);
+               }
+
                printf("\t%s\t%s\n",
                           (rte->inh ? "inh" : ""),
                           (rte->inFromCl ? "inFromCl" : ""));
@@ -459,6 +480,8 @@ plannode_type(Plan *p)
                        return "TIDSCAN";
                case T_SubqueryScan:
                        return "SUBQUERYSCAN";
+               case T_FunctionScan:
+                       return "FUNCTIONSCAN";
                case T_Join:
                        return "JOIN";
                case T_NestLoop:
@@ -489,12 +512,8 @@ plannode_type(Plan *p)
 }
 
 /*
-   prints the ascii description of the plan nodes
-   does this recursively by doing a depth-first traversal of the
-   plan tree.  for SeqScan and IndexScan, the name of the table is also
-   printed out
-
-*/
+ * Recursively prints a simple text description of the plan tree
+ */
 void
 print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
 {
@@ -523,6 +542,13 @@ print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
                rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
                StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
        }
+       else if (IsA(p, FunctionScan))
+       {
+               RangeTblEntry *rte;
+
+               rte = rt_fetch(((FunctionScan *) p)->scan.scanrelid, parsetree->rtable);
+               StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
+       }
        else
                extraInfo[0] = '\0';
        if (extraInfo[0] != '\0')
index 1d4236fc8479cddbca67870ea2d8517c6ae15acb..27604dcb4be0ee0d894ed3bec840aad87663cfb0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.120 2002/04/28 19:54:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.121 2002/05/12 20:10:03 tgl Exp $
  *
  * NOTES
  *       Most of the read functions for plan nodes are tested. (In fact, they
@@ -651,6 +651,24 @@ _readSubqueryScan(void)
        return local_node;
 }
 
+/* ----------------
+ *             _readFunctionScan
+ *
+ *     FunctionScan is a subclass of Scan
+ * ----------------
+ */
+static FunctionScan *
+_readFunctionScan(void)
+{
+       FunctionScan *local_node;
+
+       local_node = makeNode(FunctionScan);
+
+       _getScan((Scan *) local_node);
+
+       return local_node;
+}
+
 /* ----------------
  *             _readSort
  *
@@ -1514,6 +1532,11 @@ _readRangeTblEntry(void)
                        local_node->subquery = nodeRead(true);          /* now read it */
                        break;
 
+               case RTE_FUNCTION:
+                       token = pg_strtok(&length); /* eat :funcexpr */
+                       local_node->funcexpr = nodeRead(true);          /* now read it */
+                       break;
+
                case RTE_JOIN:
                        token = pg_strtok(&length); /* eat :jointype */
                        token = pg_strtok(&length); /* get jointype */
@@ -2031,6 +2054,8 @@ parsePlanString(void)
                return_value = _readTidScan();
        else if (length == 12 && strncmp(token, "SUBQUERYSCAN", length) == 0)
                return_value = _readSubqueryScan();
+       else if (length == 12 && strncmp(token, "FUNCTIONSCAN", length) == 0)
+               return_value = _readFunctionScan();
        else if (length == 4 && strncmp(token, "SORT", length) == 0)
                return_value = _readSort();
        else if (length == 6 && strncmp(token, "AGGREG", length) == 0)
index 9368723188fe4386e815533f03a492a966cb238e..1e9074186c297139883e7b6262500cd8c4ae63db 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.83 2001/12/10 22:54:12 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.84 2002/05/12 20:10:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,6 +42,8 @@ static void set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
                                                   List *inheritlist);
 static void set_subquery_pathlist(Query *root, RelOptInfo *rel,
                                          Index rti, RangeTblEntry *rte);
+static void set_function_pathlist(Query *root, RelOptInfo *rel,
+                                               RangeTblEntry *rte);
 static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
                                          List *initial_rels);
 
@@ -98,11 +100,16 @@ set_base_rel_pathlists(Query *root)
                rti = lfirsti(rel->relids);
                rte = rt_fetch(rti, root->rtable);
 
-               if (rel->issubquery)
+               if (rel->rtekind == RTE_SUBQUERY)
                {
                        /* Subquery --- generate a separate plan for it */
                        set_subquery_pathlist(root, rel, rti, rte);
                }
+               else if (rel->rtekind == RTE_FUNCTION)
+               {
+                       /* RangeFunction --- generate a separate plan for it */
+                       set_function_pathlist(root, rel, rte);
+               }
                else if ((inheritlist = expand_inherted_rtentry(root, rti, true))
                                 != NIL)
                {
@@ -385,6 +392,23 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
        set_cheapest(rel);
 }
 
+/*
+ * set_function_pathlist
+ *             Build the (single) access path for a function RTE
+ */
+static void
+set_function_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+       /* Mark rel with estimated output rows, width, etc */
+       set_function_size_estimates(root, rel);
+
+       /* Generate appropriate path */
+       add_path(rel, create_functionscan_path(root, rel));
+
+       /* Select cheapest path (pretty easy in this case...) */
+       set_cheapest(rel);
+}
+
 /*
  * make_fromexpr_rel
  *       Build access paths for a FromExpr jointree node.
index 85584bfdcfc4e39cf751ddce6c9b30192c321cd8..35a98031004d0ff55ddd72f8f57a9c70ac741ad0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.49 2002/03/06 06:09:50 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.50 2002/05/12 20:10:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -381,7 +381,7 @@ clause_selectivity(Query *root,
                {
                        RangeTblEntry *rte = rt_fetch(var->varno, root->rtable);
 
-                       if (rte->subquery)
+                       if (rte->rtekind == RTE_SUBQUERY)
                        {
                                /*
                                 * XXX not smart about subquery references... any way to
index 2bc0bb8a137b5daab11e50b048ca2fe8842fbac4..b14322f45b571fa206afe8a9d8e6ab2daf933016 100644 (file)
@@ -42,7 +42,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.83 2002/03/12 00:51:42 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.84 2002/05/12 20:10:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -111,7 +111,7 @@ cost_seqscan(Path *path, Query *root,
 
        /* Should only be applied to base relations */
        Assert(length(baserel->relids) == 1);
-       Assert(!baserel->issubquery);
+       Assert(baserel->rtekind == RTE_RELATION);
 
        if (!enable_seqscan)
                startup_cost += disable_cost;
@@ -224,9 +224,10 @@ cost_index(Path *path, Query *root,
                                b;
 
        /* Should only be applied to base relations */
-       Assert(IsA(baserel, RelOptInfo) &&IsA(index, IndexOptInfo));
+       Assert(IsA(baserel, RelOptInfo) &&
+                  IsA(index, IndexOptInfo));
        Assert(length(baserel->relids) == 1);
-       Assert(!baserel->issubquery);
+       Assert(baserel->rtekind == RTE_RELATION);
 
        if (!enable_indexscan && !is_injoin)
                startup_cost += disable_cost;
@@ -372,6 +373,10 @@ cost_tidscan(Path *path, Query *root,
        Cost            cpu_per_tuple;
        int                     ntuples = length(tideval);
 
+       /* Should only be applied to base relations */
+       Assert(length(baserel->relids) == 1);
+       Assert(baserel->rtekind == RTE_RELATION);
+
        if (!enable_tidscan)
                startup_cost += disable_cost;
 
@@ -386,6 +391,36 @@ cost_tidscan(Path *path, Query *root,
        path->total_cost = startup_cost + run_cost;
 }
 
+/*
+ * cost_functionscan
+ *       Determines and returns the cost of scanning a function RTE.
+ */
+void
+cost_functionscan(Path *path, Query *root, RelOptInfo *baserel)
+{
+       Cost            startup_cost = 0;
+       Cost            run_cost = 0;
+       Cost            cpu_per_tuple;
+
+       /* Should only be applied to base relations that are functions */
+       Assert(length(baserel->relids) == 1);
+       Assert(baserel->rtekind == RTE_FUNCTION);
+
+       /*
+        * For now, estimate function's cost at one operator eval per function
+        * call.  Someday we should revive the function cost estimate columns in
+        * pg_proc...
+        */
+       cpu_per_tuple = cpu_operator_cost;
+
+       /* Add scanning CPU costs */
+       cpu_per_tuple += cpu_tuple_cost + baserel->baserestrictcost;
+       run_cost += cpu_per_tuple * baserel->tuples;
+
+       path->startup_cost = startup_cost;
+       path->total_cost = startup_cost + run_cost;
+}
+
 /*
  * cost_sort
  *       Determines and returns the cost of sorting a relation.
@@ -1298,6 +1333,54 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
        rel->width = outer_rel->width + inner_rel->width;
 }
 
+/*
+ * set_function_size_estimates
+ *             Set the size estimates for a base relation that is a function call.
+ *
+ * The rel's targetlist and restrictinfo list must have been constructed
+ * already.
+ *
+ * We set the following fields of the rel node:
+ *     rows: the estimated number of output tuples (after applying
+ *               restriction clauses).
+ *     width: the estimated average output tuple width in bytes.
+ *     baserestrictcost: estimated cost of evaluating baserestrictinfo clauses.
+ */
+void
+set_function_size_estimates(Query *root, RelOptInfo *rel)
+{
+       /* Should only be applied to base relations that are functions */
+       Assert(length(rel->relids) == 1);
+       Assert(rel->rtekind == RTE_FUNCTION);
+
+       /*
+        * Estimate number of rows the function itself will return.
+        *
+        * XXX no idea how to do this yet; but should at least check whether
+        * function returns set or not...
+        */
+       rel->tuples = 1000;
+
+       /* Now estimate number of output rows */
+       rel->rows = rel->tuples *
+               restrictlist_selectivity(root,
+                                                                rel->baserestrictinfo,
+                                                                lfirsti(rel->relids));
+
+       /*
+        * Force estimate to be at least one row, to make explain output look
+        * better and to avoid possible divide-by-zero when interpolating
+        * cost.
+        */
+       if (rel->rows < 1.0)
+               rel->rows = 1.0;
+
+       rel->baserestrictcost = cost_qual_eval(rel->baserestrictinfo);
+
+       set_rel_width(root, rel);
+}
+
+
 /*
  * set_rel_width
  *             Set the estimated output width of the relation.
index c20abacca3913e4007083cb33c1f1ce43652e922..cd8b0e6612fda83f2f8a47d95d7e4fddc01440ec 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.113 2002/04/28 19:54:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.114 2002/05/12 20:10:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,6 +43,8 @@ static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist,
                                        List *scan_clauses);
 static SubqueryScan *create_subqueryscan_plan(Path *best_path,
                                                 List *tlist, List *scan_clauses);
+static FunctionScan *create_functionscan_plan(Path *best_path,
+                                                List *tlist, List *scan_clauses);
 static NestLoop *create_nestloop_plan(Query *root,
                                         NestPath *best_path, List *tlist,
                                         List *joinclauses, List *otherclauses,
@@ -77,6 +79,8 @@ static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
                           ScanDirection indexscandir);
 static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
                         List *tideval);
+static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
+                                                                          Index scanrelid);
 static NestLoop *make_nestloop(List *tlist,
                          List *joinclauses, List *otherclauses,
                          Plan *lefttree, Plan *righttree,
@@ -119,6 +123,7 @@ create_plan(Query *root, Path *best_path)
                case T_SeqScan:
                case T_TidScan:
                case T_SubqueryScan:
+               case T_FunctionScan:
                        plan = (Plan *) create_scan_plan(root, best_path);
                        break;
                case T_HashJoin:
@@ -200,6 +205,12 @@ create_scan_plan(Query *root, Path *best_path)
                                                                                                         scan_clauses);
                        break;
 
+               case T_FunctionScan:
+                       plan = (Scan *) create_functionscan_plan(best_path,
+                                                                                               tlist,
+                                                                                               scan_clauses);
+                       break;
+
                default:
                        elog(ERROR, "create_scan_plan: unknown node type: %d",
                                 best_path->pathtype);
@@ -353,7 +364,7 @@ create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
 
        /* there should be exactly one base rel involved... */
        Assert(length(best_path->parent->relids) == 1);
-       Assert(!best_path->parent->issubquery);
+       Assert(best_path->parent->rtekind == RTE_RELATION);
 
        scan_relid = (Index) lfirsti(best_path->parent->relids);
 
@@ -397,7 +408,7 @@ create_indexscan_plan(Query *root,
 
        /* there should be exactly one base rel involved... */
        Assert(length(best_path->path.parent->relids) == 1);
-       Assert(!best_path->path.parent->issubquery);
+       Assert(best_path->path.parent->rtekind == RTE_RELATION);
 
        baserelid = lfirsti(best_path->path.parent->relids);
 
@@ -515,7 +526,7 @@ create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
 
        /* there should be exactly one base rel involved... */
        Assert(length(best_path->path.parent->relids) == 1);
-       Assert(!best_path->path.parent->issubquery);
+       Assert(best_path->path.parent->rtekind == RTE_RELATION);
 
        scan_relid = (Index) lfirsti(best_path->path.parent->relids);
 
@@ -546,7 +557,7 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
        /* there should be exactly one base rel involved... */
        Assert(length(best_path->parent->relids) == 1);
        /* and it must be a subquery */
-       Assert(best_path->parent->issubquery);
+       Assert(best_path->parent->rtekind == RTE_SUBQUERY);
 
        scan_relid = (Index) lfirsti(best_path->parent->relids);
 
@@ -558,6 +569,31 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
        return scan_plan;
 }
 
+/*
+ * create_functionscan_plan
+ *      Returns a functionscan plan for the base relation scanned by 'best_path'
+ *      with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static FunctionScan *
+create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses)
+{
+       FunctionScan *scan_plan;
+       Index           scan_relid;
+
+       /* there should be exactly one base rel involved... */
+       Assert(length(best_path->parent->relids) == 1);
+       /* and it must be a function */
+       Assert(best_path->parent->rtekind == RTE_FUNCTION);
+
+       scan_relid = (Index) lfirsti(best_path->parent->relids);
+
+       scan_plan = make_functionscan(tlist, scan_clauses, scan_relid);
+
+       copy_path_costsize(&scan_plan->scan.plan, best_path);
+
+       return scan_plan;
+}
+
 /*****************************************************************************
  *
  *     JOIN METHODS
@@ -791,6 +827,7 @@ create_mergejoin_plan(Query *root,
        {
                case T_SeqScan:
                case T_IndexScan:
+               case T_FunctionScan:
                case T_Material:
                case T_Sort:
                        /* OK, these inner plans support mark/restore */
@@ -1305,6 +1342,26 @@ make_subqueryscan(List *qptlist,
        return node;
 }
 
+static FunctionScan *
+make_functionscan(List *qptlist,
+                                 List *qpqual,
+                                 Index scanrelid)
+{
+       FunctionScan   *node = makeNode(FunctionScan);
+       Plan               *plan = &node->scan.plan;
+
+       /* cost should be inserted by caller */
+       plan->state = (EState *) NULL;
+       plan->targetlist = qptlist;
+       plan->qual = qpqual;
+       plan->lefttree = NULL;
+       plan->righttree = NULL;
+       node->scan.scanrelid = scanrelid;
+       node->scan.scanstate = (CommonScanState *) NULL;
+
+       return node;
+}
+
 Append *
 make_append(List *appendplans, bool isTarget, List *tlist)
 {
index 807876e4a726ed4b7f172d63545519150ce07092..3e07b2425e5d0e3f60651dd26f6e6aac9b26ab33 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.75 2002/04/28 19:54:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.76 2002/05/12 20:10:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -120,6 +120,10 @@ set_plan_references(Query *root, Plan *plan)
                        /* Recurse into subplan too */
                        set_plan_references(root, ((SubqueryScan *) plan)->subplan);
                        break;
+               case T_FunctionScan:
+                       fix_expr_references(plan, (Node *) plan->targetlist);
+                       fix_expr_references(plan, (Node *) plan->qual);
+                       break;
                case T_NestLoop:
                        set_join_references(root, (Join *) plan);
                        fix_expr_references(plan, (Node *) plan->targetlist);
index a67eb5d8e54d82e60654e3867c2e5eead0a3ba29..57930e9a5021b896c3d600da251b3ff856dde45f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.51 2002/04/16 23:08:11 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.52 2002/05/12 20:10:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -352,13 +352,13 @@ make_subplan(SubLink *slink)
                                        }
                                        break;
                                case T_Material:
+                               case T_FunctionScan:
                                case T_Sort:
 
                                        /*
                                         * Don't add another Material node if there's one
-                                        * already, nor if the top node is a Sort, since Sort
-                                        * materializes its output anyway.      (I doubt either
-                                        * case can happen in practice for a subplan, but...)
+                                        * already, nor if the top node is any other type that
+                                        * materializes its output anyway.
                                         */
                                        use_material = false;
                                        break;
@@ -686,6 +686,7 @@ SS_finalize_plan(Plan *plan)
                case T_SetOp:
                case T_Limit:
                case T_Group:
+               case T_FunctionScan:
                        break;
 
                default:
index 1b7cc14e2e74d6d934aa3e81d82d80acfd37d539..1a278e9ca2e46721a6fe0c3dbfc80bffcceeb7e0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.97 2002/04/28 19:54:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.98 2002/05/12 20:10:03 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -1922,6 +1922,7 @@ query_tree_walker(Query *query,
                {
                        case RTE_RELATION:
                        case RTE_SPECIAL:
+                       case RTE_FUNCTION:
                                /* nothing to do */
                                break;
                        case RTE_SUBQUERY:
@@ -2309,6 +2310,7 @@ query_tree_mutator(Query *query,
                {
                        case RTE_RELATION:
                        case RTE_SPECIAL:
+                       case RTE_FUNCTION:
                                /* nothing to do, don't bother to make a copy */
                                break;
                        case RTE_SUBQUERY:
index c86ee450082e0c5f2bc26c3efe365431e59c0f56..07d05b38a877aabc6d959d1f81316162d0feaa73 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.76 2001/10/25 05:49:34 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.77 2002/05/12 20:10:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -454,6 +454,25 @@ create_subqueryscan_path(RelOptInfo *rel)
        return pathnode;
 }
 
+/*
+ * create_functionscan_path
+ *       Creates a path corresponding to a sequential scan of a function,
+ *       returning the pathnode.
+ */
+Path *
+create_functionscan_path(Query *root, RelOptInfo *rel)
+{
+       Path       *pathnode = makeNode(Path);
+
+       pathnode->pathtype = T_FunctionScan;
+       pathnode->parent = rel;
+       pathnode->pathkeys = NIL;       /* for now, assume unordered result */
+
+       cost_functionscan(pathnode, root, rel);
+
+       return pathnode;
+}
+
 /*
  * create_nestloop_path
  *       Creates a pathnode corresponding to a nestloop join between two
index 0fad16fbdd082bbb4bef7a3fadf61bd27f5e8414..38e988b26fc64224cf936764a813fe3b54b8d94d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.36 2002/03/12 00:51:51 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.37 2002/05/12 20:10:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -140,7 +140,7 @@ make_base_rel(Query *root, int relid)
        rel->cheapest_startup_path = NULL;
        rel->cheapest_total_path = NULL;
        rel->pruneable = true;
-       rel->issubquery = false;
+       rel->rtekind = rte->rtekind;
        rel->indexlist = NIL;
        rel->pages = 0;
        rel->tuples = 0;
@@ -168,8 +168,8 @@ make_base_rel(Query *root, int relid)
                        break;
                }
                case RTE_SUBQUERY:
-                       /* Subquery --- mark it as such for later processing */
-                       rel->issubquery = true;
+               case RTE_FUNCTION:
+                       /* Subquery or function --- nothing to do here */
                        break;
                case RTE_JOIN:
                        /* Join --- must be an otherrel */
@@ -351,7 +351,7 @@ build_join_rel(Query *root,
        joinrel->cheapest_startup_path = NULL;
        joinrel->cheapest_total_path = NULL;
        joinrel->pruneable = true;
-       joinrel->issubquery = false;
+       joinrel->rtekind = RTE_JOIN;
        joinrel->indexlist = NIL;
        joinrel->pages = 0;
        joinrel->tuples = 0;
index a3acf294534378f2a2aeda3b136c19495d45faac..72a0c8be513d2f6437c02d1f39bd8b5f7abd8f6c 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.233 2002/04/28 19:54:28 tgl Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.234 2002/05/12 20:10:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2679,7 +2679,7 @@ transformForUpdate(Query *qry, List *forUpdate)
                        RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
 
                        ++i;
-                       if (rte->subquery)
+                       if (rte->rtekind == RTE_SUBQUERY)
                        {
                                /* FOR UPDATE of subquery is propagated to subquery's rels */
                                transformForUpdate(rte->subquery, makeList1(NULL));
@@ -2707,7 +2707,7 @@ transformForUpdate(Query *qry, List *forUpdate)
                                ++i;
                                if (strcmp(rte->eref->aliasname, relname) == 0)
                                {
-                                       if (rte->subquery)
+                                       if (rte->rtekind == RTE_SUBQUERY)
                                        {
                                                /* propagate to subquery */
                                                transformForUpdate(rte->subquery, makeList1(NULL));
index 258a11c11832c2cec6ff72442b3367d826a65480..6b7469f2bd71fc47a5c7912e7dd018b712ad4921 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.313 2002/05/06 19:47:30 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.314 2002/05/12 20:10:04 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -52,6 +52,7 @@
 
 #include "access/htup.h"
 #include "catalog/index.h"
+#include "catalog/namespace.h"
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
 #include "nodes/params.h"
@@ -250,7 +251,7 @@ static void doNegateFloat(Value *v);
 %type <defelt> def_elem
 %type <node>   def_arg, columnElem, where_clause, insert_column_item,
                                a_expr, b_expr, c_expr, AexprConst,
-                               in_expr, having_clause
+                               in_expr, having_clause, func_table
 %type <list>   row_descriptor, row_list, in_expr_nodes
 %type <node>   row_expr
 %type <node>   case_expr, case_arg, when_clause, case_default
@@ -4074,6 +4075,19 @@ table_ref:  relation_expr
                                        $1->alias = $2;
                                        $$ = (Node *) $1;
                                }
+               | func_table
+                               {
+                                       RangeFunction *n = makeNode(RangeFunction);
+                                       n->funccallnode = $1;
+                                       $$ = (Node *) n;
+                               }
+               | func_table alias_clause
+                               {
+                                       RangeFunction *n = makeNode(RangeFunction);
+                                       n->funccallnode = $1;
+                                       n->alias = $2;
+                                       $$ = (Node *) n;
+                               }
                | select_with_parens
                                {
                                        /*
@@ -4109,6 +4123,7 @@ table_ref:  relation_expr
                                }
                ;
 
+
 /*
  * It may seem silly to separate joined_table from table_ref, but there is
  * method in SQL92's madness: if you don't do it this way you get reduce-
@@ -4280,6 +4295,28 @@ relation_expr:   qualified_name
                 }
                ;
 
+
+func_table:  func_name '(' ')'
+                               {
+                                       FuncCall *n = makeNode(FuncCall);
+                                       n->funcname = $1;
+                                       n->args = NIL;
+                                       n->agg_star = FALSE;
+                                       n->agg_distinct = FALSE;
+                                       $$ = (Node *)n;
+                               }
+               | func_name '(' expr_list ')'
+                               {
+                                       FuncCall *n = makeNode(FuncCall);
+                                       n->funcname = $1;
+                                       n->args = $3;
+                                       n->agg_star = FALSE;
+                                       n->agg_distinct = FALSE;
+                                       $$ = (Node *)n;
+                               }
+               ;
+
+
 where_clause:  WHERE a_expr                                            { $$ = $2; }
                | /*EMPTY*/                                                             { $$ = NULL;  /* no qualifiers */ }
                ;
@@ -5845,26 +5882,33 @@ qualified_name_list:  qualified_name
                                { $$ = lappend($1, $3); }
                ;
 
-qualified_name: ColId
+qualified_name: relation_name
                                {
                                        $$ = makeNode(RangeVar);
                                        $$->catalogname = NULL;
                                        $$->schemaname = NULL;
                                        $$->relname = $1;
                                }
-               | ColId '.' ColId
-                               {
-                                       $$ = 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;
+                                       }
                                }
                ;
 
index 6df4a4fd7dc03b7e9e9c6b6902e26b678827f48a..19aa688ff949d9609eba31e8eab968678e34b5c3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.90 2002/04/28 19:54:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.91 2002/05/12 20:10:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 
 #include "access/heapam.h"
 #include "nodes/makefuncs.h"
+#include "optimizer/clauses.h"
 #include "optimizer/tlist.h"
 #include "optimizer/var.h"
 #include "parser/analyze.h"
@@ -49,6 +50,8 @@ static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j,
 static RangeTblRef *transformTableEntry(ParseState *pstate, RangeVar *r);
 static RangeTblRef *transformRangeSubselect(ParseState *pstate,
                                                RangeSubselect *r);
+static RangeTblRef *transformRangeFunction(ParseState *pstate,
+                                                                                  RangeFunction *r);
 static Node *transformFromClauseItem(ParseState *pstate, Node *n,
                                                List **containedRels);
 static Node *buildMergedJoinVar(JoinType jointype,
@@ -82,9 +85,9 @@ transformFromClause(ParseState *pstate, List *frmList)
 
        /*
         * The grammar will have produced a list of RangeVars,
-        * RangeSubselects, and/or JoinExprs. Transform each one (possibly
-        * adding entries to the rtable), check for duplicate refnames, and
-        * then add it to the joinlist and namespace.
+        * RangeSubselects, RangeFunctions, and/or JoinExprs. Transform each one
+        * (possibly adding entries to the rtable), check for duplicate refnames,
+        * and then add it to the joinlist and namespace.
         */
        foreach(fl, frmList)
        {
@@ -453,6 +456,71 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 }
 
 
+/*
+ * transformRangeFunction --- transform a function call appearing in FROM
+ */
+static RangeTblRef *
+transformRangeFunction(ParseState *pstate, RangeFunction *r)
+{
+       Node       *funcexpr;
+       char       *funcname;
+       RangeTblEntry *rte;
+       RangeTblRef *rtr;
+
+       /*
+        * Transform the raw FuncCall node
+        */
+       funcexpr = transformExpr(pstate, r->funccallnode);
+
+       Assert(IsA(r->funccallnode, FuncCall));
+       funcname = strVal(llast(((FuncCall *) r->funccallnode)->funcname));
+
+       /*
+        * Disallow aggregate functions and subselects in the expression.
+        * (Aggregates clearly make no sense; perhaps later we could support
+        * subselects, though.)
+        */
+       if (contain_agg_clause(funcexpr))
+               elog(ERROR, "cannot use aggregate function in FROM function expression");
+       if (contain_subplans(funcexpr))
+               elog(ERROR, "cannot use subselect in FROM function expression");
+
+       /*
+        * Remove any Iter nodes added by parse_func.c.  We oughta get rid of
+        * Iter completely ...
+        */
+       while (funcexpr && IsA(funcexpr, Iter))
+               funcexpr = ((Iter *) funcexpr)->iterexpr;
+
+       /*
+        * Insist we now have a bare function call (explain.c is the only place
+        * that depends on this, I think).  If this fails, it's probably because
+        * transformExpr interpreted the function notation as a type coercion.
+        */
+       if (!funcexpr ||
+               !IsA(funcexpr, Expr) ||
+               ((Expr *) funcexpr)->opType != FUNC_EXPR)
+               elog(ERROR, "Coercion function not allowed in FROM clause");
+
+       /*
+        * OK, build an RTE for the function.
+        */
+       rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
+                                                                               r->alias, true);
+
+       /*
+        * We create a RangeTblRef, but we do not add it to the joinlist or
+        * namespace; our caller must do that if appropriate.
+        */
+       rtr = makeNode(RangeTblRef);
+       /* assume new rte is at end */
+       rtr->rtindex = length(pstate->p_rtable);
+       Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
+
+       return rtr;
+}
+
+
 /*
  * transformFromClauseItem -
  *       Transform a FROM-clause item, adding any required entries to the
@@ -486,6 +554,15 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
                *containedRels = makeListi1(rtr->rtindex);
                return (Node *) rtr;
        }
+       else if (IsA(n, RangeFunction))
+       {
+               /* function is like a plain relation */
+               RangeTblRef *rtr;
+
+               rtr = transformRangeFunction(pstate, (RangeFunction *) n);
+               *containedRels = makeListi1(rtr->rtindex);
+               return (Node *) rtr;
+       }
        else if (IsA(n, JoinExpr))
        {
                /* A newfangled join expression */
index f74f5be2f7bec4637c3a209bbfb37546e4325cdf..339577f39ca09273c2e633a83d8538bae6952972 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.127 2002/05/03 20:15:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.128 2002/05/12 20:10:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -181,27 +181,32 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                         * sizeof(Pointer) to signal that the runtime representation
                         * will be a pointer not an Oid.
                         */
-                       if (rte->rtekind != RTE_RELATION)
+                       switch (rte->rtekind)
                        {
-                               /*
-                                * RTE is a join or subselect; must fail for lack of a
-                                * named tuple type
-                                */
-                               if (is_column)
-                                       elog(ERROR, "No such attribute %s.%s",
-                                                refname, strVal(lfirst(funcname)));
-                               else
-                               {
-                                       elog(ERROR, "Cannot pass result of sub-select or join %s to a function",
-                                                refname);
-                               }
+                               case RTE_RELATION:
+                                       toid = get_rel_type_id(rte->relid);
+                                       if (!OidIsValid(toid))
+                                               elog(ERROR, "Cannot find type OID for relation %u",
+                                                        rte->relid);
+                                       break;
+                               case RTE_FUNCTION:
+                                       toid = exprType(rte->funcexpr);
+                                       break;
+                               default:
+                                       /*
+                                        * RTE is a join or subselect; must fail for lack of a
+                                        * named tuple type
+                                        */
+                                       if (is_column)
+                                               elog(ERROR, "No such attribute %s.%s",
+                                                        refname, strVal(lfirst(funcname)));
+                                       else
+                                               elog(ERROR, "Cannot pass result of sub-select or join %s to a function",
+                                                        refname);
+                                       toid = InvalidOid; /* keep compiler quiet */
+                                       break;
                        }
 
-                       toid = get_rel_type_id(rte->relid);
-                       if (!OidIsValid(toid))
-                               elog(ERROR, "Cannot find type OID for relation %u",
-                                        rte->relid);
-
                        /* replace RangeVar in the arg list */
                        lfirst(i) = makeVar(vnum,
                                                                InvalidAttrNumber,
index b822a2378ba021ba21df59651a8415fea34533ec..857adf05691bae1ca11a27dba2e3d3ebe1428b1c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.68 2002/04/28 19:54:28 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.69 2002/05/12 20:10:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -672,6 +672,117 @@ addRangeTableEntryForSubquery(ParseState *pstate,
        return rte;
 }
 
+/*
+ * Add an entry for a function to the pstate's range table (p_rtable).
+ *
+ * This is just like addRangeTableEntry() except that it makes a function RTE.
+ */
+RangeTblEntry *
+addRangeTableEntryForFunction(ParseState *pstate,
+                                                         char *funcname,
+                                                         Node *funcexpr,
+                                                         Alias *alias,
+                                                         bool inFromCl)
+{
+       RangeTblEntry *rte = makeNode(RangeTblEntry);
+       Oid                     funcrettype = exprType(funcexpr);
+       Oid                     funcrelid;
+       Alias      *eref;
+       int                     numaliases;
+       int                     varattno;
+
+       rte->rtekind = RTE_FUNCTION;
+       rte->relid = InvalidOid;
+       rte->subquery = NULL;
+       rte->funcexpr = funcexpr;
+       rte->alias = alias;
+
+       eref = alias ? (Alias *) copyObject(alias) : makeAlias(funcname, NIL);
+       rte->eref = eref;
+
+       numaliases = length(eref->colnames);
+
+       /*
+        * Now determine if the function returns a simple or composite type,
+        * and check/add column aliases.
+        */
+       funcrelid = typeidTypeRelid(funcrettype);
+
+       if (OidIsValid(funcrelid))
+       {
+               /*
+                * Composite data type, i.e. a table's row type
+                *
+                * Get the rel's relcache entry.  This access ensures that we have an
+                * up-to-date relcache entry for the rel.
+                */
+               Relation        rel;
+               int                     maxattrs;
+
+               rel = heap_open(funcrelid, AccessShareLock);
+
+               /*
+                * Since the rel is open anyway, let's check that the number of column
+                * aliases is reasonable.
+                */
+               maxattrs = RelationGetNumberOfAttributes(rel);
+               if (maxattrs < numaliases)
+                       elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
+                                RelationGetRelationName(rel), maxattrs, numaliases);
+
+               /* fill in alias columns using actual column names */
+               for (varattno = numaliases; varattno < maxattrs; varattno++)
+               {
+                       char       *attrname;
+
+                       attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
+                       eref->colnames = lappend(eref->colnames, makeString(attrname));
+               }
+
+               /*
+                * Drop the rel refcount, but keep the access lock till end of
+                * transaction so that the table can't be deleted or have its schema
+                * modified underneath us.
+                */
+               heap_close(rel, NoLock);
+       }
+       else
+       {
+               /*
+                * Must be a base data type, i.e. scalar.
+                * Just add one alias column named for the function.
+                */
+               if (numaliases > 1)
+                       elog(ERROR, "Too many column aliases specified for function %s",
+                                funcname);
+               if (numaliases == 0)
+                       eref->colnames = makeList1(makeString(funcname));
+       }
+
+       /*----------
+        * Flags:
+        * - this RTE should be expanded to include descendant tables,
+        * - this RTE is in the FROM clause,
+        * - this RTE should be checked for read/write access rights.
+        *----------
+        */
+       rte->inh = false;                       /* never true for functions */
+       rte->inFromCl = inFromCl;
+       rte->checkForRead = true;
+       rte->checkForWrite = false;
+
+       rte->checkAsUser = InvalidOid;
+
+       /*
+        * Add completed RTE to pstate's range table list, but not to join
+        * list nor namespace --- caller must do that if appropriate.
+        */
+       if (pstate != NULL)
+               pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+       return rte;
+}
+
 /*
  * Add an entry for a join to the pstate's range table (p_rtable).
  *
@@ -834,124 +945,201 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
        /* Need the RT index of the entry for creating Vars */
        rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
 
-       if (rte->rtekind == RTE_RELATION)
+       switch (rte->rtekind)
        {
-               /* Ordinary relation RTE */
-               Relation        rel;
-               int                     maxattrs;
-               int                     numaliases;
+               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);
 }
 
 /*
index 0901164389d84d64e3561063d5beb4a090af1984..a8a466ac3ba94990366d25f6a7b5ff01c22e9bfa 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.40 2002/04/20 21:56:14 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.41 2002/05/12 20:10:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,7 +56,8 @@ LookupTypeName(const TypeName *typename)
                switch (length(typename->names))
                {
                        case 1:
-                               elog(ERROR, "Improper %%TYPE reference (too few dotted names)");
+                               elog(ERROR, "Improper %%TYPE reference (too few dotted names): %s",
+                                        NameListToString(typename->names));
                                break;
                        case 2:
                                rel->relname = strVal(lfirst(typename->names));
@@ -74,7 +75,8 @@ LookupTypeName(const TypeName *typename)
                                field = strVal(lfirst(lnext(lnext(lnext(typename->names)))));
                                break;
                        default:
-                               elog(ERROR, "Improper %%TYPE reference (too many dotted names)");
+                               elog(ERROR, "Improper %%TYPE reference (too many dotted names): %s",
+                                        NameListToString(typename->names));
                                break;
                }
 
@@ -121,7 +123,8 @@ LookupTypeName(const TypeName *typename)
                                        elog(ERROR, "Cross-database references are not implemented");
                                break;
                        default:
-                               elog(ERROR, "Improper type name (too many dotted names)");
+                               elog(ERROR, "Improper type name (too many dotted names): %s",
+                                        NameListToString(typename->names));
                                break;
                }
 
index 6d4c81c70a2a9136f0dd7448e3db37161e72f080..f35540128cfa627e1df7f9d3a25055b24a474d0d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.69 2002/04/27 03:45:03 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.70 2002/05/12 20:10:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -388,7 +388,7 @@ setRuleCheckAsUser(Query *qry, Oid userid)
        {
                RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
 
-               if (rte->subquery)
+               if (rte->rtekind == RTE_SUBQUERY)
                {
                        /* Recurse into subquery in FROM */
                        setRuleCheckAsUser(rte->subquery, userid);
index b1f012d4d9d85434a58d52e949b0c397ca28b67a..b4bd3b2f5145f7ec6f0fafbd023a49154f3b80b4 100644 (file)
@@ -3,7 +3,7 @@
  *                             back to source text
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.102 2002/05/03 20:15:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.103 2002/05/12 20:10:04 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -705,25 +705,16 @@ deparse_context_for_plan(int outer_varno, Node *outercontext,
 }
 
 /*
- * deparse_context_for_relation                - Build deparse context for 1 relation
+ * deparse_context_for_rte             - Build deparse context for 1 relation
  *
  * Helper routine to build one of the inputs for deparse_context_for_plan.
- * Pass the reference name (alias) and OID of a relation.
  *
- * The returned node is actually a RangeTblEntry, but we declare it as just
- * Node to discourage callers from assuming anything.
+ * The returned node is actually the given RangeTblEntry, but we declare it
+ * as just Node to discourage callers from assuming anything.
  */
 Node *
-deparse_context_for_relation(const char *aliasname, Oid relid)
+deparse_context_for_rte(RangeTblEntry *rte)
 {
-       RangeTblEntry *rte = makeNode(RangeTblEntry);
-
-       rte->rtekind = RTE_RELATION;
-       rte->relid = relid;
-       rte->eref = makeAlias(aliasname, NIL);
-       rte->inh = false;
-       rte->inFromCl = true;
-
        return (Node *) rte;
 }
 
@@ -2398,6 +2389,10 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                                get_query_def(rte->subquery, buf, context->namespaces);
                                appendStringInfoChar(buf, ')');
                                break;
+                       case RTE_FUNCTION:
+                               /* Function RTE */
+                               get_rule_expr(rte->funcexpr, context);
+                               break;
                        default:
                                elog(ERROR, "unexpected rte kind %d", (int) rte->rtekind);
                                break;
index c50e33b9d1098ca679b3a80c2c7c253045621978..043a3a7e8d918d5f56a0d7557395700a9f2ee5ad 100644 (file)
@@ -383,7 +383,7 @@ If a function is marked in pg_proc as returning a set, then it is called
 with fcinfo->resultinfo pointing to a node of type ReturnSetInfo.  A
 function that desires to return a set should raise an error "called in
 context that does not accept a set result" if resultinfo is NULL or does
-not point to a ReturnSetInfo node.  ReturnSetInfo contains a single field
+not point to a ReturnSetInfo node.  ReturnSetInfo contains a field
 "isDone", which should be set to one of these values:
 
     ExprSingleResult             /* expression does not return a set */
@@ -396,6 +396,11 @@ After all elements have been returned, the next call should set
 isDone to ExprEndResult and return a null result.  (Note it is possible
 to return an empty set by doing this on the first call.)
 
+As of 7.3, the ReturnSetInfo node also contains a link to the ExprContext
+within which the function is being evaluated.  This is useful for functions
+that need to close down internal state when they are not run to completion:
+they can register a shutdown callback function in the ExprContext.
+
 
 Notes about function handlers
 -----------------------------
index e135782bd5825161205d6558e009c6616eb2a826..3120fb5fae0c34e7bffad96579db4e5d239ba64b 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.129 2002/04/28 19:54:28 tgl Exp $
+ * $Id: catversion.h,v 1.130 2002/05/12 20:10:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200204281
+#define CATALOG_VERSION_NO     200205121
 
 #endif
index 63bc10f79dcde17fb25258129feadc84352f2e77..6752d72ca768029c5322191408aaa9e8cd8a6575 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.63 2002/02/27 19:35:59 tgl Exp $
+ * $Id: executor.h,v 1.64 2002/05/12 20:10:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -172,4 +172,11 @@ extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
 extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
                                          EState *estate, bool is_update);
 
+extern void RegisterExprContextCallback(ExprContext *econtext,
+                                                                               ExprContextCallbackFunction function,
+                                                                               Datum arg);
+extern void UnregisterExprContextCallback(ExprContext *econtext,
+                                                                                 ExprContextCallbackFunction function,
+                                                                                 Datum arg);
+
 #endif   /* EXECUTOR_H  */
diff --git a/src/include/executor/nodeFunctionscan.h b/src/include/executor/nodeFunctionscan.h
new file mode 100644 (file)
index 0000000..4decbfe
--- /dev/null
@@ -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 */
index 97e76ccdfd5b8b929560fbbbcfd1ab8b257e168a..66f93df451da719cc8e88175df7b8b94ada02239 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.67 2001/11/21 22:57:01 tgl Exp $
+ * $Id: execnodes.h,v 1.68 2002/05/12 20:10:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,6 +52,21 @@ typedef struct IndexInfo
        bool            ii_Unique;
 } IndexInfo;
 
+/* ----------------
+ *       ExprContext_CB
+ *
+ *             List of callbacks to be called at ExprContext shutdown.
+ * ----------------
+ */
+typedef void (*ExprContextCallbackFunction) (Datum arg);
+
+typedef struct ExprContext_CB
+{
+       struct ExprContext_CB *next;
+       ExprContextCallbackFunction function;
+       Datum           arg;
+} ExprContext_CB;
+
 /* ----------------
  *       ExprContext
  *
@@ -77,20 +92,27 @@ typedef struct IndexInfo
  */
 typedef struct ExprContext
 {
-       NodeTag         type;
+       NodeTag                 type;
+
        /* Tuples that Var nodes in expression may refer to */
        TupleTableSlot *ecxt_scantuple;
        TupleTableSlot *ecxt_innertuple;
        TupleTableSlot *ecxt_outertuple;
+
        /* Memory contexts for expression evaluation --- see notes above */
-       MemoryContext ecxt_per_query_memory;
-       MemoryContext ecxt_per_tuple_memory;
+       MemoryContext   ecxt_per_query_memory;
+       MemoryContext   ecxt_per_tuple_memory;
+
        /* Values to substitute for Param nodes in expression */
-       ParamExecData *ecxt_param_exec_vals;            /* for PARAM_EXEC params */
-       ParamListInfo ecxt_param_list_info; /* for other param types */
+       ParamExecData  *ecxt_param_exec_vals;   /* for PARAM_EXEC params */
+       ParamListInfo   ecxt_param_list_info;   /* for other param types */
+
        /* Values to substitute for Aggref nodes in expression */
-       Datum      *ecxt_aggvalues; /* precomputed values for Aggref nodes */
-       bool       *ecxt_aggnulls;      /* null flags for Aggref nodes */
+       Datum              *ecxt_aggvalues;     /* precomputed values for Aggref nodes */
+       bool               *ecxt_aggnulls;      /* null flags for Aggref nodes */
+
+       /* Functions to call back when ExprContext is shut down */
+       ExprContext_CB *ecxt_callbacks;
 } ExprContext;
 
 /*
@@ -107,7 +129,8 @@ typedef enum
  * When calling a function that might return a set (multiple rows),
  * a node of this type is passed as fcinfo->resultinfo to allow
  * return status to be passed back.  A function returning set should
- * raise an error if no such resultinfo is provided.
+ * raise an error if no such resultinfo is provided.  The ExprContext
+ * in which the function is being called is also made available.
  *
  * XXX this mechanism is a quick hack and probably needs to be redesigned.
  */
@@ -115,9 +138,9 @@ typedef struct ReturnSetInfo
 {
        NodeTag         type;
        ExprDoneCond isDone;
+       ExprContext *econtext;
 } ReturnSetInfo;
 
-
 /* ----------------
  *             ProjectionInfo node information
  *
@@ -481,6 +504,36 @@ typedef struct SubqueryScanState
        EState     *sss_SubEState;
 } SubqueryScanState;
 
+/* ----------------
+ *      FunctionScanState information
+ *
+ *             Function nodes are used to scan the results of a
+ *             function appearing in FROM (typically a function returning set).
+ *
+ *             functionmode                    function operating mode:
+ *                                                     - repeated call
+ *                                                     - materialize
+ *                                                     - return query
+ *             tuplestorestate         private state of tuplestore.c
+ * ----------------
+ */
+typedef enum FunctionMode
+{
+       PM_REPEATEDCALL,
+       PM_MATERIALIZE,
+       PM_QUERY
+} FunctionMode;
+
+typedef struct FunctionScanState
+{
+       CommonScanState csstate;        /* its first field is NodeTag */
+       FunctionMode    functionmode;
+       TupleDesc               tupdesc;
+       void               *tuplestorestate;
+       Node               *funcexpr;   /* function expression being evaluated */
+       bool                    returnsTuple; /* does function return tuples? */
+} FunctionScanState;
+
 /* ----------------------------------------------------------------
  *                              Join State Information
  * ----------------------------------------------------------------
index 1664e769003acc7e8d291c6f6ad68bc1a8f687c3..492619e194d06bdfdaff02237d43b1602d2b2ad2 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.105 2002/04/18 20:01:11 tgl Exp $
+ * $Id: nodes.h,v 1.106 2002/05/12 20:10:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,6 +49,7 @@ typedef enum NodeTag
        T_SubPlan,
        T_TidScan,
        T_SubqueryScan,
+       T_FunctionScan,
 
        /*
         * TAGS FOR PRIMITIVE NODES (primnodes.h)
@@ -120,6 +121,7 @@ typedef enum NodeTag
        T_SubqueryScanState,
        T_SetOpState,
        T_LimitState,
+       T_FunctionScanState,
 
        /*
         * TAGS FOR MEMORY NODES (memnodes.h)
@@ -212,6 +214,7 @@ typedef enum NodeTag
        T_Alias,
        T_RangeVar,
        T_RangeSubselect,
+       T_RangeFunction,
        T_TypeName,
        T_IndexElem,
        T_ColumnDef,
index 5bd0e89b185af19783ef24dfb56c0d8e0c743be0..0c5672dd67782c34decdc974d786b0c4b0b0a379 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.175 2002/04/28 19:54:28 tgl Exp $
+ * $Id: parsenodes.h,v 1.176 2002/05/12 20:10:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -387,6 +387,16 @@ typedef struct RangeSubselect
        Alias      *alias;                      /* table alias & optional column aliases */
 } RangeSubselect;
 
+/*
+ * RangeFunction - function call appearing in a FROM clause
+ */
+typedef struct RangeFunction
+{
+       NodeTag         type;
+       Node       *funccallnode;       /* untransformed function call tree */
+       Alias      *alias;                      /* table alias & optional column aliases */
+} RangeFunction;
+
 /*
  * IndexElem - index parameters (used in CREATE INDEX)
  *
@@ -482,7 +492,8 @@ typedef enum RTEKind
        RTE_RELATION,                           /* ordinary relation reference */
        RTE_SUBQUERY,                           /* subquery in FROM */
        RTE_JOIN,                                       /* join */
-       RTE_SPECIAL                                     /* special rule relation (NEW or OLD) */
+       RTE_SPECIAL,                            /* special rule relation (NEW or OLD) */
+       RTE_FUNCTION                            /* function in FROM */
 } RTEKind;
 
 typedef struct RangeTblEntry
@@ -507,6 +518,11 @@ typedef struct RangeTblEntry
         */
        Query      *subquery;           /* the sub-query */
 
+       /*
+        * Fields valid for a function RTE (else NULL):
+        */
+       Node       *funcexpr;           /* expression tree for func call */
+
        /*
         * Fields valid for a join RTE (else NULL/zero):
         *
index 9ba1caca327b77106ccb8441bd2c0a8c0c73c1b9..20413e08ae5046f170be2f3d0b49e80798ae91ef 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: plannodes.h,v 1.55 2002/04/28 19:54:28 tgl Exp $
+ * $Id: plannodes.h,v 1.56 2002/05/12 20:10:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +32,7 @@
  *             Scan ***                                CommonScanState                 scanstate;
  *             IndexScan                               IndexScanState                  indxstate;
  *             SubqueryScan                    SubqueryScanState               subquerystate;
+ *             FunctionScan                    FunctionScanState               functionstate;
  *
  *               (*** nodes which inherit Scan also inherit scanstate)
  *
@@ -242,6 +243,17 @@ typedef struct SubqueryScan
        Plan       *subplan;
 } SubqueryScan;
 
+/* ----------------
+ *             FunctionScan node
+ * ----------------
+ */
+typedef struct FunctionScan
+{
+       Scan                            scan;
+       /* no other fields needed at present */
+       /* scan.scanstate actually points at a FunctionScanState node */
+} FunctionScan;
+
 /*
  * ==========
  * Join nodes
index 903245cf51f8e2f6667aaed8f7ee480762d92cc5..6eed89a70424e409dd4e1f605c874c07e947ffd4 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.61 2002/04/11 20:00:15 tgl Exp $
+ * $Id: primnodes.h,v 1.62 2002/05/12 20:10:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -514,10 +514,11 @@ typedef struct RelabelType
  * rows.)  If all joins are inner joins then all the qual positions are
  * semantically interchangeable.
  *
- * NOTE: in the raw output of gram.y, a join tree contains RangeVar and
- * RangeSubselect nodes, which are both replaced by RangeTblRef nodes
- * during the parse analysis phase.  Also, the top-level FromExpr is added
- * during parse analysis; the grammar regards FROM and WHERE as separate.
+ * NOTE: in the raw output of gram.y, a join tree contains RangeVar,
+ * RangeSubselect, and RangeFunction nodes, which are all replaced by
+ * RangeTblRef nodes during the parse analysis phase.  Also, the top-level
+ * FromExpr is added during parse analysis; the grammar regards FROM and
+ * WHERE as separate.
  * ----------------------------------------------------------------
  */
 
index d26d60c71be1b20b491ba443708474e070c6d6d5..28ad8bc2eb727a019c90bc829d839254d906d9f2 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relation.h,v 1.63 2002/03/12 00:52:02 tgl Exp $
+ * $Id: relation.h,v 1.64 2002/05/12 20:10:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -92,12 +92,12 @@ typedef enum CostSelector
  *
  * If the relation is a base relation it will have these fields set:
  *
- *             issubquery - true if baserel is a subquery RTE rather than a table
+ *             rtekind - distinguishes plain relation, subquery, or function RTE
  *             indexlist - list of IndexOptInfo nodes for relation's indexes
- *                                     (always NIL if it's a subquery)
- *             pages - number of disk pages in relation (zero if a subquery)
+ *                                     (always NIL if it's not a table)
+ *             pages - number of disk pages in relation (zero if not a table)
  *             tuples - number of tuples in relation (not considering restrictions)
- *             subplan - plan for subquery (NULL if it's a plain table)
+ *             subplan - plan for subquery (NULL if it's not a subquery)
  *
  *             Note: for a subquery, tuples and subplan are not set immediately
  *             upon creation of the RelOptInfo object; they are filled in when
@@ -184,11 +184,11 @@ typedef struct RelOptInfo
        bool            pruneable;
 
        /* information about a base rel (not set for join rels!) */
-       bool            issubquery;
+       RTEKind         rtekind;                /* RELATION, SUBQUERY, or FUNCTION */
        List       *indexlist;
        long            pages;
        double          tuples;
-       struct Plan *subplan;
+       struct Plan *subplan;           /* if subquery */
 
        /* information about a join rel (not set for base rels!) */
        Index           joinrti;
index 7c4c22a595962a43cff8cc0a70da0112f9f2c3e3..690fa3f129cb885a193794832bdf423a7bd7fbb1 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: cost.h,v 1.43 2001/11/05 17:46:34 momjian Exp $
+ * $Id: cost.h,v 1.44 2002/05/12 20:10:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,6 +59,8 @@ extern void cost_index(Path *path, Query *root,
                   List *indexQuals, bool is_injoin);
 extern void cost_tidscan(Path *path, Query *root,
                         RelOptInfo *baserel, List *tideval);
+extern void cost_functionscan(Path *path, Query *root,
+                                                         RelOptInfo *baserel);
 extern void cost_sort(Path *path, Query *root,
                  List *pathkeys, double tuples, int width);
 extern void cost_nestloop(Path *path, Query *root,
@@ -80,6 +82,7 @@ extern void set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
                                                   RelOptInfo *inner_rel,
                                                   JoinType jointype,
                                                   List *restrictlist);
+extern void set_function_size_estimates(Query *root, RelOptInfo *rel);
 
 /*
  * prototypes for clausesel.c
index d9419df47d3dedabc4b3d456e1e0199b9b4ab599..5a6646db88ceca77cd2f2c7d01782ce1264b0f70 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pathnode.h,v 1.42 2002/03/12 00:52:03 tgl Exp $
+ * $Id: pathnode.h,v 1.43 2002/05/12 20:10:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,6 +36,7 @@ extern TidPath *create_tidscan_path(Query *root, RelOptInfo *rel,
                                        List *tideval);
 extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
 extern Path *create_subqueryscan_path(RelOptInfo *rel);
+extern Path *create_functionscan_path(Query *root, RelOptInfo *rel);
 
 extern NestPath *create_nestloop_path(Query *root,
                                         RelOptInfo *joinrel,
index 353ce8957c70a5dba5dfca09b63491b7f406fe4f..be02997ba5e167af0838733bb1aa22236827a1f3 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_relation.h,v 1.32 2002/04/28 19:54:29 tgl Exp $
+ * $Id: parse_relation.h,v 1.33 2002/05/12 20:10:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,6 +41,11 @@ extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
                                                          Query *subquery,
                                                          Alias *alias,
                                                          bool inFromCl);
+extern RangeTblEntry *addRangeTableEntryForFunction(ParseState *pstate,
+                                                                                                       char *funcname,
+                                                                                                       Node *funcexpr,
+                                                                                                       Alias *alias,
+                                                                                                       bool inFromCl);
 extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
                                                  List *colnames,
                                                  JoinType jointype,
index d46e6d8262b9c62a9e0ce622f38596229fe1a6d8..7a8d1fb387312603b6be5d9e6b0b74278fafc86c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.180 2002/04/26 01:24:08 tgl Exp $
+ * $Id: builtins.h,v 1.181 2002/05/12 20:10:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -354,7 +354,7 @@ extern char *deparse_expression(Node *expr, List *dpcontext,
 extern List *deparse_context_for(const char *aliasname, Oid relid);
 extern List *deparse_context_for_plan(int outer_varno, Node *outercontext,
                                                                          int inner_varno, Node *innercontext);
-extern Node *deparse_context_for_relation(const char *aliasname, Oid relid);
+extern Node *deparse_context_for_rte(RangeTblEntry *rte);
 extern Node *deparse_context_for_subplan(const char *name, List *tlist,
                                                                                 List *rtable);
 extern const char *quote_identifier(const char *ident);