]> granicus.if.org Git - postgresql/commitdiff
Avoid projecting tuples unnecessarily in Gather and Gather Merge.
authorRobert Haas <rhaas@postgresql.org>
Sat, 25 Nov 2017 15:49:17 +0000 (10:49 -0500)
committerRobert Haas <rhaas@postgresql.org>
Sat, 25 Nov 2017 15:49:17 +0000 (10:49 -0500)
It's most often the case that the target list for the Gather (Merge)
node matches the target list supplied by the underlying plan node;
when this is so, we can avoid the overhead of projecting.

This depends on commit f455e1125e2588d4cd4fc663c6a10da4e003a3b5 for
proper functioning.

Idea by Andres Freund.  Patch by me.  Review by Amit Kapila.

Discussion: http://postgr.es/m/CA+TgmoZ0ZL=cesZFq8c9NnfK6bqy-wwUd3_74iYGodYrSoQ7Fw@mail.gmail.com

src/backend/executor/execScan.c
src/backend/executor/execUtils.c
src/backend/executor/nodeGather.c
src/backend/executor/nodeGatherMerge.c
src/include/executor/executor.h

index 5dfc49deb95483ee6dd92f80b2eccd57e96d73d5..837abc0f0179b2d2bf742793e879019bad7ea891 100644 (file)
@@ -23,8 +23,6 @@
 #include "utils/memutils.h"
 
 
-static bool tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc);
-
 
 /*
  * ExecScanFetch -- check interrupts & fetch next potential tuple
@@ -237,8 +235,9 @@ void
 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);
 }
 
 /*
@@ -248,75 +247,9 @@ ExecAssignScanProjectionInfo(ScanState *node)
 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);
 }
 
 /*
index e8c06c76056de57baab9a4790ceb576c5edf8a0c..876439835a356977d698b72a8eeb47b4a8bb49e3 100644 (file)
@@ -56,6 +56,7 @@
 #include "utils/typcache.h"
 
 
+static bool tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc);
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -503,6 +504,85 @@ ExecAssignProjectionInfo(PlanState *planstate,
 }
 
 
+/* ----------------
+ *             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
  *
index 30885e6f5c2015f87e31d88a3021e6fd80445dfd..212612b5351f68d12bc3759d4b292b3f8bb5bd6b 100644 (file)
@@ -103,12 +103,6 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
        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.
         */
@@ -117,6 +111,12 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
        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;
 }
 
@@ -221,6 +221,10 @@ ExecGather(PlanState *pstate)
        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.
         */
index d81462e72b41efdd559269aff6c0ffc44058ea1a..166f2064ff71e5772be8da034c9b14693ffd8180 100644 (file)
@@ -115,11 +115,20 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags)
        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
@@ -151,15 +160,6 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int 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;
-
        /* Now allocate the workspace for gather merge */
        gather_merge_setup(gm_state);
 
@@ -257,6 +257,10 @@ ExecGatherMerge(PlanState *pstate)
        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.
         */
index bee4ebf2693426e781ecfc2df1880535295089a8..b5578f5855feba3c549fe9aef0706d1989f3bc8a 100644 (file)
@@ -485,6 +485,8 @@ extern void ExecAssignResultTypeFromTL(PlanState *planstate);
 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);