3636#include "executor/nodeGather.h"
3737#include "executor/nodeSubplan.h"
3838#include "executor/tqueue.h"
39+ #include "utils/memutils.h"
3940#include "utils/rel.h"
4041
4142
@@ -50,6 +51,9 @@ GatherState *
5051ExecInitGather (Gather * node , EState * estate , int eflags )
5152{
5253 GatherState * gatherstate ;
54+ Plan * outerNode ;
55+ bool hasoid ;
56+ TupleDesc tupDesc ;
5357
5458 /* Gather node doesn't have innerPlan node. */
5559 Assert (innerPlan (node ) == NULL );
@@ -82,13 +86,14 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
8286 /*
8387 * tuple table initialization
8488 */
89+ gatherstate -> funnel_slot = ExecInitExtraTupleSlot (estate );
8590 ExecInitResultTupleSlot (estate , & gatherstate -> ps );
8691
8792 /*
8893 * now initialize outer plan
8994 */
90- outerPlanState ( gatherstate ) = ExecInitNode ( outerPlan (node ), estate , eflags );
91-
95+ outerNode = outerPlan (node );
96+ outerPlanState ( gatherstate ) = ExecInitNode ( outerNode , estate , eflags );
9297
9398 gatherstate -> ps .ps_TupFromTlist = false;
9499
@@ -98,6 +103,14 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
98103 ExecAssignResultTypeFromTL (& gatherstate -> ps );
99104 ExecAssignProjectionInfo (& gatherstate -> ps , NULL );
100105
106+ /*
107+ * Initialize funnel slot to same tuple descriptor as outer plan.
108+ */
109+ if (!ExecContextForcesOids (& gatherstate -> ps , & hasoid ))
110+ hasoid = false;
111+ tupDesc = ExecTypeFromTL (outerNode -> targetlist , hasoid );
112+ ExecSetSlotDescriptor (gatherstate -> funnel_slot , tupDesc );
113+
101114 return gatherstate ;
102115}
103116
@@ -113,6 +126,9 @@ ExecGather(GatherState *node)
113126{
114127 int i ;
115128 TupleTableSlot * slot ;
129+ TupleTableSlot * resultSlot ;
130+ ExprDoneCond isDone ;
131+ ExprContext * econtext ;
116132
117133 /*
118134 * Initialize the parallel context and workers on first execution. We do
@@ -169,7 +185,53 @@ ExecGather(GatherState *node)
169185 node -> initialized = true;
170186 }
171187
172- slot = gather_getnext (node );
188+ /*
189+ * Check to see if we're still projecting out tuples from a previous scan
190+ * tuple (because there is a function-returning-set in the projection
191+ * expressions). If so, try to project another one.
192+ */
193+ if (node -> ps .ps_TupFromTlist )
194+ {
195+ resultSlot = ExecProject (node -> ps .ps_ProjInfo , & isDone );
196+ if (isDone == ExprMultipleResult )
197+ return resultSlot ;
198+ /* Done with that source tuple... */
199+ node -> ps .ps_TupFromTlist = false;
200+ }
201+
202+ /*
203+ * Reset per-tuple memory context to free any expression evaluation
204+ * storage allocated in the previous tuple cycle. Note we can't do this
205+ * until we're done projecting.
206+ */
207+ econtext = node -> ps .ps_ExprContext ;
208+ ResetExprContext (econtext );
209+
210+ /* Get and return the next tuple, projecting if necessary. */
211+ for (;;)
212+ {
213+ /*
214+ * Get next tuple, either from one of our workers, or by running the
215+ * plan ourselves.
216+ */
217+ slot = gather_getnext (node );
218+ if (TupIsNull (slot ))
219+ return NULL ;
220+
221+ /*
222+ * form the result tuple using ExecProject(), and return it --- unless
223+ * the projection produces an empty set, in which case we must loop
224+ * back around for another tuple
225+ */
226+ econtext -> ecxt_outertuple = slot ;
227+ resultSlot = ExecProject (node -> ps .ps_ProjInfo , & isDone );
228+
229+ if (isDone != ExprEndResult )
230+ {
231+ node -> ps .ps_TupFromTlist = (isDone == ExprMultipleResult );
232+ return resultSlot ;
233+ }
234+ }
173235
174236 return slot ;
175237}
@@ -201,18 +263,11 @@ ExecEndGather(GatherState *node)
201263static TupleTableSlot *
202264gather_getnext (GatherState * gatherstate )
203265{
204- PlanState * outerPlan ;
266+ PlanState * outerPlan = outerPlanState ( gatherstate ) ;
205267 TupleTableSlot * outerTupleSlot ;
206- TupleTableSlot * slot ;
268+ TupleTableSlot * fslot = gatherstate -> funnel_slot ;
207269 HeapTuple tup ;
208270
209- /*
210- * We can use projection info of Gather for the tuples received from
211- * worker backends as currently for all cases worker backends sends the
212- * projected tuple as required by Gather node.
213- */
214- slot = gatherstate -> ps .ps_ProjInfo -> pi_slot ;
215-
216271 while (gatherstate -> funnel != NULL || gatherstate -> need_to_scan_locally )
217272 {
218273 if (gatherstate -> funnel != NULL )
@@ -229,19 +284,17 @@ gather_getnext(GatherState *gatherstate)
229284 if (HeapTupleIsValid (tup ))
230285 {
231286 ExecStoreTuple (tup , /* tuple to store */
232- slot , /* slot to store in */
287+ fslot , /* slot in which to store the tuple */
233288 InvalidBuffer , /* buffer associated with this
234289 * tuple */
235290 true); /* pfree this pointer if not from heap */
236291
237- return slot ;
292+ return fslot ;
238293 }
239294 }
240295
241296 if (gatherstate -> need_to_scan_locally )
242297 {
243- outerPlan = outerPlanState (gatherstate );
244-
245298 outerTupleSlot = ExecProcNode (outerPlan );
246299
247300 if (!TupIsNull (outerTupleSlot ))
@@ -251,7 +304,7 @@ gather_getnext(GatherState *gatherstate)
251304 }
252305 }
253306
254- return ExecClearTuple (slot );
307+ return ExecClearTuple (fslot );
255308}
256309
257310/* ----------------------------------------------------------------
0 commit comments