diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index e45e07f0a1a..c979a557749 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -91,10 +91,14 @@ ExecInitGather(Gather *node, EState *estate, int eflags) outerPlanState(gatherstate) = ExecInitNode(outerNode, estate, eflags); tupDesc = ExecGetResultType(outerPlanState(gatherstate)); - /* this node uses tuples from the tuple queue as scan slot */ - gatherstate->ps.scanops = &TTSOpsHeapTuple; - gatherstate->ps.scanopsfixed = true; - gatherstate->ps.scanopsset = true; + /* + * Leader may access ExecProcNode result directly (if + * need_to_scan_locally), or from workers via tuple queue. So we can't + * trivially rely on the slot type being fixed for expressions evaluated + * within this node. + */ + gatherstate->ps.outeropsset = true; + gatherstate->ps.outeropsfixed = false; /* * Initialize result type and projection. @@ -102,6 +106,16 @@ ExecInitGather(Gather *node, EState *estate, int eflags) ExecInitResultTypeTL(&gatherstate->ps); ExecConditionalAssignProjectionInfo(&gatherstate->ps, tupDesc, OUTER_VAR); + /* + * Without projections result slot type is not trivially known, see + * comment above. + */ + if (gatherstate->ps.ps_ProjInfo == NULL) + { + gatherstate->ps.resultopsset = true; + gatherstate->ps.resultopsfixed = false; + } + /* * Initialize funnel slot to same tuple descriptor as outer plan. */ diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c index 651565123be..51d910bd5ee 100644 --- a/src/backend/executor/nodeGatherMerge.c +++ b/src/backend/executor/nodeGatherMerge.c @@ -109,6 +109,15 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags) outerNode = outerPlan(node); outerPlanState(gm_state) = ExecInitNode(outerNode, estate, eflags); + /* + * Leader may access ExecProcNode result directly (if + * need_to_scan_locally), or from workers via tuple queue. So we can't + * trivially rely on the slot type being fixed for expressions evaluated + * within this node. + */ + gm_state->ps.outeropsset = true; + gm_state->ps.outeropsfixed = false; + /* * Store the tuple descriptor into gather merge state, so we can use it * while initializing the gather merge slots. @@ -122,7 +131,10 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags) ExecInitResultTypeTL(&gm_state->ps); ExecConditionalAssignProjectionInfo(&gm_state->ps, tupDesc, OUTER_VAR); - /* leader accesses ExecProcNode result directly, others go through tuple queue */ + /* + * Without projections result slot type is not trivially known, see + * comment above. + */ if (gm_state->ps.ps_ProjInfo == NULL) { gm_state->ps.resultopsset = true;