* Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.160 2008/08/25 22:42:32 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.161 2008/09/08 00:22:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
    if (node->agg_done)
        return NULL;
 
+   /*
+    * Check to see if we're still projecting out tuples from a previous agg
+    * tuple (because there is a function-returning-set in the projection
+    * expressions).  If so, try to project another one.
+    */
+   if (node->ss.ps.ps_TupFromTlist)
+   {
+       TupleTableSlot *result;
+       ExprDoneCond isDone;
+
+       result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
+       if (isDone == ExprMultipleResult)
+           return result;
+       /* Done with that source tuple... */
+       node->ss.ps.ps_TupFromTlist = false;
+   }
+
    if (((Agg *) node->ss.ps.plan)->aggstrategy == AGG_HASHED)
    {
        if (!node->table_filled)
    PlanState  *outerPlan;
    ExprContext *econtext;
    ExprContext *tmpcontext;
-   ProjectionInfo *projInfo;
    Datum      *aggvalues;
    bool       *aggnulls;
    AggStatePerAgg peragg;
    aggnulls = econtext->ecxt_aggnulls;
    /* tmpcontext is the per-input-tuple expression context */
    tmpcontext = aggstate->tmpcontext;
-   projInfo = aggstate->ss.ps.ps_ProjInfo;
    peragg = aggstate->peragg;
    pergroup = aggstate->pergroup;
    firstSlot = aggstate->ss.ss_ScanTupleSlot;
        {
            /*
             * Form and return a projection tuple using the aggregate results
-            * and the representative input tuple.  Note we do not support
-            * aggregates returning sets ...
+            * and the representative input tuple.
             */
-           return ExecProject(projInfo, NULL);
+           TupleTableSlot *result;
+           ExprDoneCond isDone;
+
+           result = ExecProject(aggstate->ss.ps.ps_ProjInfo, &isDone);
+
+           if (isDone != ExprEndResult)
+           {
+               aggstate->ss.ps.ps_TupFromTlist =
+                   (isDone == ExprMultipleResult);
+               return result;
+           }
        }
    }
 
 agg_retrieve_hash_table(AggState *aggstate)
 {
    ExprContext *econtext;
-   ProjectionInfo *projInfo;
    Datum      *aggvalues;
    bool       *aggnulls;
    AggStatePerAgg peragg;
    econtext = aggstate->ss.ps.ps_ExprContext;
    aggvalues = econtext->ecxt_aggvalues;
    aggnulls = econtext->ecxt_aggnulls;
-   projInfo = aggstate->ss.ps.ps_ProjInfo;
    peragg = aggstate->peragg;
    firstSlot = aggstate->ss.ss_ScanTupleSlot;
 
        {
            /*
             * Form and return a projection tuple using the aggregate results
-            * and the representative input tuple.  Note we do not support
-            * aggregates returning sets ...
+            * and the representative input tuple.
             */
-           return ExecProject(projInfo, NULL);
+           TupleTableSlot *result;
+           ExprDoneCond isDone;
+
+           result = ExecProject(aggstate->ss.ps.ps_ProjInfo, &isDone);
+
+           if (isDone != ExprEndResult)
+           {
+               aggstate->ss.ps.ps_TupFromTlist =
+                   (isDone == ExprMultipleResult);
+               return result;
+           }
        }
    }
 
 
  *   locate group boundaries.
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.70 2008/01/01 19:45:49 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.71 2008/09/08 00:22:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
    numCols = ((Group *) node->ss.ps.plan)->numCols;
    grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
 
+   /*
+    * Check to see if we're still projecting out tuples from a previous group
+    * tuple (because there is a function-returning-set in the projection
+    * expressions).  If so, try to project another one.
+    */
+   if (node->ss.ps.ps_TupFromTlist)
+   {
+       TupleTableSlot *result;
+       ExprDoneCond isDone;
+
+       result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
+       if (isDone == ExprMultipleResult)
+           return result;
+       /* Done with that source tuple... */
+       node->ss.ps.ps_TupFromTlist = false;
+   }
+
    /*
     * The ScanTupleSlot holds the (copied) first tuple of each group.
     */
            /*
             * Form and return a projection tuple using the first input tuple.
             */
-           return ExecProject(node->ss.ps.ps_ProjInfo, NULL);
+           TupleTableSlot *result;
+           ExprDoneCond isDone;
+
+           result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
+
+           if (isDone != ExprEndResult)
+           {
+               node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
+               return result;
+           }
        }
    }
 
            /*
             * Form and return a projection tuple using the first input tuple.
             */
-           return ExecProject(node->ss.ps.ps_ProjInfo, NULL);
+           TupleTableSlot *result;
+           ExprDoneCond isDone;
+
+           result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
+
+           if (isDone != ExprEndResult)
+           {
+               node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
+               return result;
+           }
        }
    }