Skip to content

Commit 8538a63

Browse files
committed
Make Gather node projection-capable.
The original Gather code failed to mark a Gather node as not able to do projection, but it couldn't, even though it did call initialize its projection info via ExecAssignProjectionInfo. There doesn't seem to be any good reason for this node not to have projection capability, so clean things up so that it does. Without this, plans using Gather nodes might need to carry extra Result nodes to do projection.
1 parent c15898c commit 8538a63

File tree

3 files changed

+75
-18
lines changed

3 files changed

+75
-18
lines changed

src/backend/executor/nodeGather.c

+70-17
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
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 *
5051
ExecInitGather(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)
201263
static TupleTableSlot *
202264
gather_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
/* ----------------------------------------------------------------

src/backend/optimizer/plan/setrefs.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -602,12 +602,15 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
602602
set_join_references(root, (Join *) plan, rtoffset);
603603
break;
604604

605+
case T_Gather:
606+
set_upper_references(root, plan, rtoffset);
607+
break;
608+
605609
case T_Hash:
606610
case T_Material:
607611
case T_Sort:
608612
case T_Unique:
609613
case T_SetOp:
610-
case T_Gather:
611614

612615
/*
613616
* These plan types don't actually bother to evaluate their

src/include/nodes/execnodes.h

+1
Original file line numberDiff line numberDiff line change
@@ -1964,6 +1964,7 @@ typedef struct GatherState
19641964
bool initialized;
19651965
struct ParallelExecutorInfo *pei;
19661966
struct TupleQueueFunnel *funnel;
1967+
TupleTableSlot *funnel_slot;
19671968
bool need_to_scan_locally;
19681969
} GatherState;
19691970

0 commit comments

Comments
 (0)