#include "utils/memutils.h"
-static bool tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc);
-
/*
* ExecScanFetch -- check interrupts & fetch next potential tuple
ExecAssignScanProjectionInfo(ScanState *node)
{
Scan *scan = (Scan *) node->ps.plan;
+ TupleDesc tupdesc = node->ss_ScanTupleSlot->tts_tupleDescriptor;
- ExecAssignScanProjectionInfoWithVarno(node, scan->scanrelid);
+ ExecConditionalAssignProjectionInfo(&node->ps, tupdesc, scan->scanrelid);
}
/*
void
ExecAssignScanProjectionInfoWithVarno(ScanState *node, Index varno)
{
- Scan *scan = (Scan *) node->ps.plan;
-
- if (tlist_matches_tupdesc(&node->ps,
- scan->plan.targetlist,
- varno,
- node->ss_ScanTupleSlot->tts_tupleDescriptor))
- node->ps.ps_ProjInfo = NULL;
- else
- ExecAssignProjectionInfo(&node->ps,
- node->ss_ScanTupleSlot->tts_tupleDescriptor);
-}
-
-static bool
-tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc)
-{
- int numattrs = tupdesc->natts;
- int attrno;
- bool hasoid;
- ListCell *tlist_item = list_head(tlist);
-
- /* Check the tlist attributes */
- for (attrno = 1; attrno <= numattrs; attrno++)
- {
- Form_pg_attribute att_tup = TupleDescAttr(tupdesc, attrno - 1);
- Var *var;
-
- if (tlist_item == NULL)
- return false; /* tlist too short */
- var = (Var *) ((TargetEntry *) lfirst(tlist_item))->expr;
- if (!var || !IsA(var, Var))
- return false; /* tlist item not a Var */
- /* if these Asserts fail, planner messed up */
- Assert(var->varno == varno);
- Assert(var->varlevelsup == 0);
- if (var->varattno != attrno)
- return false; /* out of order */
- if (att_tup->attisdropped)
- return false; /* table contains dropped columns */
-
- /*
- * Note: usually the Var's type should match the tupdesc exactly, but
- * in situations involving unions of columns that have different
- * typmods, the Var may have come from above the union and hence have
- * typmod -1. This is a legitimate situation since the Var still
- * describes the column, just not as exactly as the tupdesc does. We
- * could change the planner to prevent it, but it'd then insert
- * projection steps just to convert from specific typmod to typmod -1,
- * which is pretty silly.
- */
- if (var->vartype != att_tup->atttypid ||
- (var->vartypmod != att_tup->atttypmod &&
- var->vartypmod != -1))
- return false; /* type mismatch */
-
- tlist_item = lnext(tlist_item);
- }
-
- if (tlist_item)
- return false; /* tlist too long */
-
- /*
- * If the plan context requires a particular hasoid setting, then that has
- * to match, too.
- */
- if (ExecContextForcesOids(ps, &hasoid) &&
- hasoid != tupdesc->tdhasoid)
- return false;
+ TupleDesc tupdesc = node->ss_ScanTupleSlot->tts_tupleDescriptor;
- return true;
+ ExecConditionalAssignProjectionInfo(&node->ps, tupdesc, varno);
}
/*
#include "utils/typcache.h"
+static bool tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc);
static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
}
+/* ----------------
+ * ExecConditionalAssignProjectionInfo
+ *
+ * as ExecAssignProjectionInfo, but store NULL rather than building projection
+ * info if no projection is required
+ * ----------------
+ */
+void
+ExecConditionalAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc,
+ Index varno)
+{
+ if (tlist_matches_tupdesc(planstate,
+ planstate->plan->targetlist,
+ varno,
+ inputDesc))
+ planstate->ps_ProjInfo = NULL;
+ else
+ ExecAssignProjectionInfo(planstate, inputDesc);
+}
+
+static bool
+tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc)
+{
+ int numattrs = tupdesc->natts;
+ int attrno;
+ bool hasoid;
+ ListCell *tlist_item = list_head(tlist);
+
+ /* Check the tlist attributes */
+ for (attrno = 1; attrno <= numattrs; attrno++)
+ {
+ Form_pg_attribute att_tup = TupleDescAttr(tupdesc, attrno - 1);
+ Var *var;
+
+ if (tlist_item == NULL)
+ return false; /* tlist too short */
+ var = (Var *) ((TargetEntry *) lfirst(tlist_item))->expr;
+ if (!var || !IsA(var, Var))
+ return false; /* tlist item not a Var */
+ /* if these Asserts fail, planner messed up */
+ Assert(var->varno == varno);
+ Assert(var->varlevelsup == 0);
+ if (var->varattno != attrno)
+ return false; /* out of order */
+ if (att_tup->attisdropped)
+ return false; /* table contains dropped columns */
+
+ /*
+ * Note: usually the Var's type should match the tupdesc exactly, but
+ * in situations involving unions of columns that have different
+ * typmods, the Var may have come from above the union and hence have
+ * typmod -1. This is a legitimate situation since the Var still
+ * describes the column, just not as exactly as the tupdesc does. We
+ * could change the planner to prevent it, but it'd then insert
+ * projection steps just to convert from specific typmod to typmod -1,
+ * which is pretty silly.
+ */
+ if (var->vartype != att_tup->atttypid ||
+ (var->vartypmod != att_tup->atttypmod &&
+ var->vartypmod != -1))
+ return false; /* type mismatch */
+
+ tlist_item = lnext(tlist_item);
+ }
+
+ if (tlist_item)
+ return false; /* tlist too long */
+
+ /*
+ * If the plan context requires a particular hasoid setting, then that has
+ * to match, too.
+ */
+ if (ExecContextForcesOids(ps, &hasoid) &&
+ hasoid != tupdesc->tdhasoid)
+ return false;
+
+ return true;
+}
+
/* ----------------
* ExecFreeExprContext
*
outerNode = outerPlan(node);
outerPlanState(gatherstate) = ExecInitNode(outerNode, estate, eflags);
- /*
- * Initialize result tuple type and projection info.
- */
- ExecAssignResultTypeFromTL(&gatherstate->ps);
- ExecAssignProjectionInfo(&gatherstate->ps, NULL);
-
/*
* Initialize funnel slot to same tuple descriptor as outer plan.
*/
tupDesc = ExecTypeFromTL(outerNode->targetlist, hasoid);
ExecSetSlotDescriptor(gatherstate->funnel_slot, tupDesc);
+ /*
+ * Initialize result tuple type and projection info.
+ */
+ ExecAssignResultTypeFromTL(&gatherstate->ps);
+ ExecConditionalAssignProjectionInfo(&gatherstate->ps, tupDesc, OUTER_VAR);
+
return gatherstate;
}
if (TupIsNull(slot))
return NULL;
+ /* If no projection is required, we're done. */
+ if (node->ps.ps_ProjInfo == NULL)
+ return slot;
+
/*
* Form the result tuple using ExecProject(), and return it.
*/
outerNode = outerPlan(node);
outerPlanState(gm_state) = ExecInitNode(outerNode, estate, eflags);
+ /*
+ * Store the tuple descriptor into gather merge state, so we can use it
+ * while initializing the gather merge slots.
+ */
+ if (!ExecContextForcesOids(outerPlanState(gm_state), &hasoid))
+ hasoid = false;
+ tupDesc = ExecTypeFromTL(outerNode->targetlist, hasoid);
+ gm_state->tupDesc = tupDesc;
+
/*
* Initialize result tuple type and projection info.
*/
ExecAssignResultTypeFromTL(&gm_state->ps);
- ExecAssignProjectionInfo(&gm_state->ps, NULL);
+ ExecConditionalAssignProjectionInfo(&gm_state->ps, tupDesc, OUTER_VAR);
/*
* initialize sort-key information
}
}
- /*
- * Store the tuple descriptor into gather merge state, so we can use it
- * while initializing the gather merge slots.
- */
- if (!ExecContextForcesOids(outerPlanState(gm_state), &hasoid))
- hasoid = false;
- tupDesc = ExecTypeFromTL(outerNode->targetlist, hasoid);
- gm_state->tupDesc = tupDesc;
-
/* Now allocate the workspace for gather merge */
gather_merge_setup(gm_state);
if (TupIsNull(slot))
return NULL;
+ /* If no projection is required, we're done. */
+ if (node->ps.ps_ProjInfo == NULL)
+ return slot;
+
/*
* Form the result tuple using ExecProject(), and return it.
*/
extern TupleDesc ExecGetResultType(PlanState *planstate);
extern void ExecAssignProjectionInfo(PlanState *planstate,
TupleDesc inputDesc);
+extern void ExecConditionalAssignProjectionInfo(PlanState *planstate,
+ TupleDesc inputDesc, Index varno);
extern void ExecFreeExprContext(PlanState *planstate);
extern void ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc);
extern void ExecAssignScanTypeFromOuterPlan(ScanState *scanstate);