]> granicus.if.org Git - postgresql/blobdiff - src/backend/executor/nodeAppend.c
Make some small planner API cleanups.
[postgresql] / src / backend / executor / nodeAppend.c
index 64a17fb03271ac72f8bcbaedb54ccbf12225230b..f3be2429dbeb122d186e941d26b2094982441bdc 100644 (file)
@@ -3,7 +3,7 @@
  * nodeAppend.c
  *       routines to handle append nodes.
  *
- * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
@@ -58,6 +58,7 @@
 #include "postgres.h"
 
 #include "executor/execdebug.h"
+#include "executor/execPartition.h"
 #include "executor/nodeAppend.h"
 #include "miscadmin.h"
 
@@ -77,11 +78,13 @@ struct ParallelAppendState
 };
 
 #define INVALID_SUBPLAN_INDEX          -1
+#define NO_MATCHING_SUBPLANS           -2
 
 static TupleTableSlot *ExecAppend(PlanState *pstate);
 static bool choose_next_subplan_locally(AppendState *node);
 static bool choose_next_subplan_for_leader(AppendState *node);
 static bool choose_next_subplan_for_worker(AppendState *node);
+static void mark_invalid_subplans_as_finished(AppendState *node);
 
 /* ----------------------------------------------------------------
  *             ExecInitAppend
@@ -99,76 +102,146 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
 {
        AppendState *appendstate = makeNode(AppendState);
        PlanState **appendplanstates;
+       Bitmapset  *validsubplans;
        int                     nplans;
-       int                     i;
+       int                     firstvalid;
+       int                     i,
+                               j;
        ListCell   *lc;
 
        /* check for unsupported flags */
        Assert(!(eflags & EXEC_FLAG_MARK));
 
-       /*
-        * Lock the non-leaf tables in the partition tree controlled by this node.
-        * It's a no-op for non-partitioned parent tables.
-        */
-       ExecLockNonLeafAppendTables(node->partitioned_rels, estate);
-
-       /*
-        * Set up empty vector of subplan states
-        */
-       nplans = list_length(node->appendplans);
-
-       appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *));
-
        /*
         * create new AppendState for our append node
         */
        appendstate->ps.plan = (Plan *) node;
        appendstate->ps.state = estate;
        appendstate->ps.ExecProcNode = ExecAppend;
-       appendstate->appendplans = appendplanstates;
-       appendstate->as_nplans = nplans;
 
-       /*
-        * Miscellaneous initialization
-        *
-        * Append plans don't have expression contexts because they never call
-        * ExecQual or ExecProject.
-        */
+       /* Let choose_next_subplan_* function handle setting the first subplan */
+       appendstate->as_whichplan = INVALID_SUBPLAN_INDEX;
+
+       /* If run-time partition pruning is enabled, then set that up now */
+       if (node->part_prune_info != NULL)
+       {
+               PartitionPruneState *prunestate;
+
+               /* We may need an expression context to evaluate partition exprs */
+               ExecAssignExprContext(estate, &appendstate->ps);
+
+               /* Create the working data structure for pruning. */
+               prunestate = ExecCreatePartitionPruneState(&appendstate->ps,
+                                                                                                  node->part_prune_info);
+               appendstate->as_prune_state = prunestate;
+
+               /* Perform an initial partition prune, if required. */
+               if (prunestate->do_initial_prune)
+               {
+                       /* Determine which subplans survive initial pruning */
+                       validsubplans = ExecFindInitialMatchingSubPlans(prunestate,
+                                                                                                                       list_length(node->appendplans));
+
+                       /*
+                        * The case where no subplans survive pruning must be handled
+                        * specially.  The problem here is that code in explain.c requires
+                        * an Append to have at least one subplan in order for it to
+                        * properly determine the Vars in that subplan's targetlist.  We
+                        * sidestep this issue by just initializing the first subplan and
+                        * setting as_whichplan to NO_MATCHING_SUBPLANS to indicate that
+                        * we don't really need to scan any subnodes.
+                        */
+                       if (bms_is_empty(validsubplans))
+                       {
+                               appendstate->as_whichplan = NO_MATCHING_SUBPLANS;
+
+                               /* Mark the first as valid so that it's initialized below */
+                               validsubplans = bms_make_singleton(0);
+                       }
+
+                       nplans = bms_num_members(validsubplans);
+               }
+               else
+               {
+                       /* We'll need to initialize all subplans */
+                       nplans = list_length(node->appendplans);
+                       Assert(nplans > 0);
+                       validsubplans = bms_add_range(NULL, 0, nplans - 1);
+               }
+
+               /*
+                * If no runtime pruning is required, we can fill as_valid_subplans
+                * immediately, preventing later calls to ExecFindMatchingSubPlans.
+                */
+               if (!prunestate->do_exec_prune)
+               {
+                       Assert(nplans > 0);
+                       appendstate->as_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
+               }
+       }
+       else
+       {
+               nplans = list_length(node->appendplans);
+
+               /*
+                * When run-time partition pruning is not enabled we can just mark all
+                * subplans as valid; they must also all be initialized.
+                */
+               Assert(nplans > 0);
+               appendstate->as_valid_subplans = validsubplans =
+                       bms_add_range(NULL, 0, nplans - 1);
+               appendstate->as_prune_state = NULL;
+       }
 
        /*
-        * append nodes still have Result slots, which hold pointers to tuples, so
-        * we have to initialize them.
+        * Initialize result tuple type and slot.
         */
-       ExecInitResultTupleSlot(estate, &appendstate->ps);
+       ExecInitResultTupleSlotTL(&appendstate->ps, &TTSOpsVirtual);
+
+       /* node returns slots from each of its subnodes, therefore not fixed */
+       appendstate->ps.resultopsset = true;
+       appendstate->ps.resultopsfixed = false;
+
+       appendplanstates = (PlanState **) palloc(nplans *
+                                                                                        sizeof(PlanState *));
 
        /*
-        * call ExecInitNode on each of the plans to be executed and save the
-        * results into the array "appendplans".
+        * call ExecInitNode on each of the valid plans to be executed and save
+        * the results into the appendplanstates array.
+        *
+        * While at it, find out the first valid partial plan.
         */
-       i = 0;
+       j = i = 0;
+       firstvalid = nplans;
        foreach(lc, node->appendplans)
        {
-               Plan       *initNode = (Plan *) lfirst(lc);
+               if (bms_is_member(i, validsubplans))
+               {
+                       Plan       *initNode = (Plan *) lfirst(lc);
+
+                       /*
+                        * Record the lowest appendplans index which is a valid partial
+                        * plan.
+                        */
+                       if (i >= node->first_partial_plan && j < firstvalid)
+                               firstvalid = j;
 
-               appendplanstates[i] = ExecInitNode(initNode, estate, eflags);
+                       appendplanstates[j++] = ExecInitNode(initNode, estate, eflags);
+               }
                i++;
        }
 
-       /*
-        * initialize output tuple type
-        */
-       ExecAssignResultTypeFromTL(&appendstate->ps);
-       appendstate->ps.ps_ProjInfo = NULL;
+       appendstate->as_first_partial_plan = firstvalid;
+       appendstate->appendplans = appendplanstates;
+       appendstate->as_nplans = nplans;
 
        /*
-        * Parallel-aware append plans must choose the first subplan to execute by
-        * looking at shared memory, but non-parallel-aware append plans can
-        * always start with the first subplan.
+        * Miscellaneous initialization
         */
-       appendstate->as_whichplan =
-               appendstate->ps.plan->parallel_aware ? INVALID_SUBPLAN_INDEX : 0;
 
-       /* If parallel-aware, this will be overridden later. */
+       appendstate->ps.ps_ProjInfo = NULL;
+
+       /* For parallel query, this will be overridden later. */
        appendstate->choose_next_subplan = choose_next_subplan_locally;
 
        return appendstate;
@@ -185,10 +258,20 @@ ExecAppend(PlanState *pstate)
 {
        AppendState *node = castNode(AppendState, pstate);
 
-       /* If no subplan has been chosen, we must choose one before proceeding. */
-       if (node->as_whichplan == INVALID_SUBPLAN_INDEX &&
-               !node->choose_next_subplan(node))
-               return ExecClearTuple(node->ps.ps_ResultTupleSlot);
+       if (node->as_whichplan < 0)
+       {
+               /*
+                * If no subplan has been chosen, we must choose one before
+                * proceeding.
+                */
+               if (node->as_whichplan == INVALID_SUBPLAN_INDEX &&
+                       !node->choose_next_subplan(node))
+                       return ExecClearTuple(node->ps.ps_ResultTupleSlot);
+
+               /* Nothing to do if there are no matching subplans */
+               else if (node->as_whichplan == NO_MATCHING_SUBPLANS)
+                       return ExecClearTuple(node->ps.ps_ResultTupleSlot);
+       }
 
        for (;;)
        {
@@ -257,6 +340,19 @@ ExecReScanAppend(AppendState *node)
 {
        int                     i;
 
+       /*
+        * If any PARAM_EXEC Params used in pruning expressions have changed, then
+        * we'd better unset the valid subplans so that they are reselected for
+        * the new parameter values.
+        */
+       if (node->as_prune_state &&
+               bms_overlap(node->ps.chgParam,
+                                       node->as_prune_state->execparamids))
+       {
+               bms_free(node->as_valid_subplans);
+               node->as_valid_subplans = NULL;
+       }
+
        for (i = 0; i < node->as_nplans; i++)
        {
                PlanState  *subnode = node->appendplans[i];
@@ -276,8 +372,8 @@ ExecReScanAppend(AppendState *node)
                        ExecReScan(subnode);
        }
 
-       node->as_whichplan =
-               node->ps.plan->parallel_aware ? INVALID_SUBPLAN_INDEX : 0;
+       /* Let choose_next_subplan_* function handle setting the first subplan */
+       node->as_whichplan = INVALID_SUBPLAN_INDEX;
 }
 
 /* ----------------------------------------------------------------
@@ -366,22 +462,39 @@ static bool
 choose_next_subplan_locally(AppendState *node)
 {
        int                     whichplan = node->as_whichplan;
+       int                     nextplan;
 
-       /* We should never see INVALID_SUBPLAN_INDEX in this case. */
-       Assert(whichplan >= 0 && whichplan <= node->as_nplans);
+       /* We should never be called when there are no subplans */
+       Assert(whichplan != NO_MATCHING_SUBPLANS);
 
-       if (ScanDirectionIsForward(node->ps.state->es_direction))
+       /*
+        * If first call then have the bms member function choose the first valid
+        * subplan by initializing whichplan to -1.  If there happen to be no
+        * valid subplans then the bms member function will handle that by
+        * returning a negative number which will allow us to exit returning a
+        * false value.
+        */
+       if (whichplan == INVALID_SUBPLAN_INDEX)
        {
-               if (whichplan >= node->as_nplans - 1)
-                       return false;
-               node->as_whichplan++;
+               if (node->as_valid_subplans == NULL)
+                       node->as_valid_subplans =
+                               ExecFindMatchingSubPlans(node->as_prune_state);
+
+               whichplan = -1;
        }
+
+       /* Ensure whichplan is within the expected range */
+       Assert(whichplan >= -1 && whichplan <= node->as_nplans);
+
+       if (ScanDirectionIsForward(node->ps.state->es_direction))
+               nextplan = bms_next_member(node->as_valid_subplans, whichplan);
        else
-       {
-               if (whichplan <= 0)
-                       return false;
-               node->as_whichplan--;
-       }
+               nextplan = bms_prev_member(node->as_valid_subplans, whichplan);
+
+       if (nextplan < 0)
+               return false;
+
+       node->as_whichplan = nextplan;
 
        return true;
 }
@@ -398,11 +511,13 @@ static bool
 choose_next_subplan_for_leader(AppendState *node)
 {
        ParallelAppendState *pstate = node->as_pstate;
-       Append     *append = (Append *) node->ps.plan;
 
        /* Backward scan is not supported by parallel-aware plans */
        Assert(ScanDirectionIsForward(node->ps.state->es_direction));
 
+       /* We should never be called when there are no subplans */
+       Assert(node->as_whichplan != NO_MATCHING_SUBPLANS);
+
        LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
 
        if (node->as_whichplan != INVALID_SUBPLAN_INDEX)
@@ -414,6 +529,23 @@ choose_next_subplan_for_leader(AppendState *node)
        {
                /* Start with last subplan. */
                node->as_whichplan = node->as_nplans - 1;
+
+               /*
+                * If we've yet to determine the valid subplans then do so now.  If
+                * run-time pruning is disabled then the valid subplans will always be
+                * set to all subplans.
+                */
+               if (node->as_valid_subplans == NULL)
+               {
+                       node->as_valid_subplans =
+                               ExecFindMatchingSubPlans(node->as_prune_state);
+
+                       /*
+                        * Mark each invalid plan as finished to allow the loop below to
+                        * select the first valid subplan.
+                        */
+                       mark_invalid_subplans_as_finished(node);
+               }
        }
 
        /* Loop until we find a subplan to execute. */
@@ -426,11 +558,16 @@ choose_next_subplan_for_leader(AppendState *node)
                        LWLockRelease(&pstate->pa_lock);
                        return false;
                }
+
+               /*
+                * We needn't pay attention to as_valid_subplans here as all invalid
+                * plans have been marked as finished.
+                */
                node->as_whichplan--;
        }
 
        /* If non-partial, immediately mark as finished. */
-       if (node->as_whichplan < append->first_partial_plan)
+       if (node->as_whichplan < node->as_first_partial_plan)
                node->as_pstate->pa_finished[node->as_whichplan] = true;
 
        LWLockRelease(&pstate->pa_lock);
@@ -455,17 +592,31 @@ static bool
 choose_next_subplan_for_worker(AppendState *node)
 {
        ParallelAppendState *pstate = node->as_pstate;
-       Append     *append = (Append *) node->ps.plan;
 
        /* Backward scan is not supported by parallel-aware plans */
        Assert(ScanDirectionIsForward(node->ps.state->es_direction));
 
+       /* We should never be called when there are no subplans */
+       Assert(node->as_whichplan != NO_MATCHING_SUBPLANS);
+
        LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
 
        /* Mark just-completed subplan as finished. */
        if (node->as_whichplan != INVALID_SUBPLAN_INDEX)
                node->as_pstate->pa_finished[node->as_whichplan] = true;
 
+       /*
+        * If we've yet to determine the valid subplans then do so now.  If
+        * run-time pruning is disabled then the valid subplans will always be set
+        * to all subplans.
+        */
+       else if (node->as_valid_subplans == NULL)
+       {
+               node->as_valid_subplans =
+                       ExecFindMatchingSubPlans(node->as_prune_state);
+               mark_invalid_subplans_as_finished(node);
+       }
+
        /* If all the plans are already done, we have nothing to do */
        if (pstate->pa_next_plan == INVALID_SUBPLAN_INDEX)
        {
@@ -473,22 +624,38 @@ choose_next_subplan_for_worker(AppendState *node)
                return false;
        }
 
-       /* Loop until we find a subplan to execute. */
+       /* Save the plan from which we are starting the search. */
+       node->as_whichplan = pstate->pa_next_plan;
+
+       /* Loop until we find a valid subplan to execute. */
        while (pstate->pa_finished[pstate->pa_next_plan])
        {
-               if (pstate->pa_next_plan < node->as_nplans - 1)
+               int                     nextplan;
+
+               nextplan = bms_next_member(node->as_valid_subplans,
+                                                                  pstate->pa_next_plan);
+               if (nextplan >= 0)
                {
-                       /* Advance to next plan. */
-                       pstate->pa_next_plan++;
+                       /* Advance to the next valid plan. */
+                       pstate->pa_next_plan = nextplan;
                }
-               else if (append->first_partial_plan < node->as_nplans)
+               else if (node->as_whichplan > node->as_first_partial_plan)
                {
-                       /* Loop back to first partial plan. */
-                       pstate->pa_next_plan = append->first_partial_plan;
+                       /*
+                        * Try looping back to the first valid partial plan, if there is
+                        * one.  If there isn't, arrange to bail out below.
+                        */
+                       nextplan = bms_next_member(node->as_valid_subplans,
+                                                                          node->as_first_partial_plan - 1);
+                       pstate->pa_next_plan =
+                               nextplan < 0 ? node->as_whichplan : nextplan;
                }
                else
                {
-                       /* At last plan, no partial plans, arrange to bail out. */
+                       /*
+                        * At last plan, and either there are no partial plans or we've
+                        * tried them all.  Arrange to bail out.
+                        */
                        pstate->pa_next_plan = node->as_whichplan;
                }
 
@@ -502,26 +669,68 @@ choose_next_subplan_for_worker(AppendState *node)
        }
 
        /* Pick the plan we found, and advance pa_next_plan one more time. */
-       node->as_whichplan = pstate->pa_next_plan++;
-       if (pstate->pa_next_plan >= node->as_nplans)
+       node->as_whichplan = pstate->pa_next_plan;
+       pstate->pa_next_plan = bms_next_member(node->as_valid_subplans,
+                                                                                  pstate->pa_next_plan);
+
+       /*
+        * If there are no more valid plans then try setting the next plan to the
+        * first valid partial plan.
+        */
+       if (pstate->pa_next_plan < 0)
        {
-               if (append->first_partial_plan < node->as_nplans)
-                       pstate->pa_next_plan = append->first_partial_plan;
+               int                     nextplan = bms_next_member(node->as_valid_subplans,
+                                                                                          node->as_first_partial_plan - 1);
+
+               if (nextplan >= 0)
+                       pstate->pa_next_plan = nextplan;
                else
                {
                        /*
-                        * We have only non-partial plans, and we already chose the last
-                        * one; so arrange for the other workers to immediately bail out.
+                        * There are no valid partial plans, and we already chose the last
+                        * non-partial plan; so flag that there's nothing more for our
+                        * fellow workers to do.
                         */
                        pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
                }
        }
 
        /* If non-partial, immediately mark as finished. */
-       if (node->as_whichplan < append->first_partial_plan)
+       if (node->as_whichplan < node->as_first_partial_plan)
                node->as_pstate->pa_finished[node->as_whichplan] = true;
 
        LWLockRelease(&pstate->pa_lock);
 
        return true;
 }
+
+/*
+ * mark_invalid_subplans_as_finished
+ *             Marks the ParallelAppendState's pa_finished as true for each invalid
+ *             subplan.
+ *
+ * This function should only be called for parallel Append with run-time
+ * pruning enabled.
+ */
+static void
+mark_invalid_subplans_as_finished(AppendState *node)
+{
+       int                     i;
+
+       /* Only valid to call this while in parallel Append mode */
+       Assert(node->as_pstate);
+
+       /* Shouldn't have been called when run-time pruning is not enabled */
+       Assert(node->as_prune_state);
+
+       /* Nothing to do if all plans are valid */
+       if (bms_num_members(node->as_valid_subplans) == node->as_nplans)
+               return;
+
+       /* Mark all non-valid plans as finished */
+       for (i = 0; i < node->as_nplans; i++)
+       {
+               if (!bms_is_member(i, node->as_valid_subplans))
+                       node->as_pstate->pa_finished[i] = true;
+       }
+}