LCOV - code coverage report
Current view: top level - src/backend/executor - nodeAppend.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 170 185 91.9 %
Date: 2019-06-19 16:07:09 Functions: 11 12 91.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * nodeAppend.c
       4             :  *    routines to handle append nodes.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/executor/nodeAppend.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : /* INTERFACE ROUTINES
      16             :  *      ExecInitAppend  - initialize the append node
      17             :  *      ExecAppend      - retrieve the next tuple from the node
      18             :  *      ExecEndAppend   - shut down the append node
      19             :  *      ExecReScanAppend - rescan the append node
      20             :  *
      21             :  *   NOTES
      22             :  *      Each append node contains a list of one or more subplans which
      23             :  *      must be iteratively processed (forwards or backwards).
      24             :  *      Tuples are retrieved by executing the 'whichplan'th subplan
      25             :  *      until the subplan stops returning tuples, at which point that
      26             :  *      plan is shut down and the next started up.
      27             :  *
      28             :  *      Append nodes don't make use of their left and right
      29             :  *      subtrees, rather they maintain a list of subplans so
      30             :  *      a typical append node looks like this in the plan tree:
      31             :  *
      32             :  *                 ...
      33             :  *                 /
      34             :  *              Append -------+------+------+--- nil
      35             :  *              /   \         |      |      |
      36             :  *            nil   nil      ...    ...    ...
      37             :  *                               subplans
      38             :  *
      39             :  *      Append nodes are currently used for unions, and to support
      40             :  *      inheritance queries, where several relations need to be scanned.
      41             :  *      For example, in our standard person/student/employee/student-emp
      42             :  *      example, where student and employee inherit from person
      43             :  *      and student-emp inherits from student and employee, the
      44             :  *      query:
      45             :  *
      46             :  *              select name from person
      47             :  *
      48             :  *      generates the plan:
      49             :  *
      50             :  *                |
      51             :  *              Append -------+-------+--------+--------+
      52             :  *              /   \         |       |        |        |
      53             :  *            nil   nil      Scan    Scan     Scan     Scan
      54             :  *                            |       |        |        |
      55             :  *                          person employee student student-emp
      56             :  */
      57             : 
      58             : #include "postgres.h"
      59             : 
      60             : #include "executor/execdebug.h"
      61             : #include "executor/execPartition.h"
      62             : #include "executor/nodeAppend.h"
      63             : #include "miscadmin.h"
      64             : 
      65             : /* Shared state for parallel-aware Append. */
      66             : struct ParallelAppendState
      67             : {
      68             :     LWLock      pa_lock;        /* mutual exclusion to choose next subplan */
      69             :     int         pa_next_plan;   /* next plan to choose by any worker */
      70             : 
      71             :     /*
      72             :      * pa_finished[i] should be true if no more workers should select subplan
      73             :      * i.  for a non-partial plan, this should be set to true as soon as a
      74             :      * worker selects the plan; for a partial plan, it remains false until
      75             :      * some worker executes the plan to completion.
      76             :      */
      77             :     bool        pa_finished[FLEXIBLE_ARRAY_MEMBER];
      78             : };
      79             : 
      80             : #define INVALID_SUBPLAN_INDEX       -1
      81             : #define NO_MATCHING_SUBPLANS        -2
      82             : 
      83             : static TupleTableSlot *ExecAppend(PlanState *pstate);
      84             : static bool choose_next_subplan_locally(AppendState *node);
      85             : static bool choose_next_subplan_for_leader(AppendState *node);
      86             : static bool choose_next_subplan_for_worker(AppendState *node);
      87             : static void mark_invalid_subplans_as_finished(AppendState *node);
      88             : 
      89             : /* ----------------------------------------------------------------
      90             :  *      ExecInitAppend
      91             :  *
      92             :  *      Begin all of the subscans of the append node.
      93             :  *
      94             :  *     (This is potentially wasteful, since the entire result of the
      95             :  *      append node may not be scanned, but this way all of the
      96             :  *      structures get allocated in the executor's top level memory
      97             :  *      block instead of that of the call to ExecAppend.)
      98             :  * ----------------------------------------------------------------
      99             :  */
     100             : AppendState *
     101        5832 : ExecInitAppend(Append *node, EState *estate, int eflags)
     102             : {
     103        5832 :     AppendState *appendstate = makeNode(AppendState);
     104             :     PlanState **appendplanstates;
     105             :     Bitmapset  *validsubplans;
     106             :     int         nplans;
     107             :     int         firstvalid;
     108             :     int         i,
     109             :                 j;
     110             :     ListCell   *lc;
     111             : 
     112             :     /* check for unsupported flags */
     113             :     Assert(!(eflags & EXEC_FLAG_MARK));
     114             : 
     115             :     /*
     116             :      * create new AppendState for our append node
     117             :      */
     118        5832 :     appendstate->ps.plan = (Plan *) node;
     119        5832 :     appendstate->ps.state = estate;
     120        5832 :     appendstate->ps.ExecProcNode = ExecAppend;
     121             : 
     122             :     /* Let choose_next_subplan_* function handle setting the first subplan */
     123        5832 :     appendstate->as_whichplan = INVALID_SUBPLAN_INDEX;
     124             : 
     125             :     /* If run-time partition pruning is enabled, then set that up now */
     126        5832 :     if (node->part_prune_info != NULL)
     127             :     {
     128             :         PartitionPruneState *prunestate;
     129             : 
     130             :         /* We may need an expression context to evaluate partition exprs */
     131         360 :         ExecAssignExprContext(estate, &appendstate->ps);
     132             : 
     133             :         /* Create the working data structure for pruning. */
     134         360 :         prunestate = ExecCreatePartitionPruneState(&appendstate->ps,
     135         360 :                                                    node->part_prune_info);
     136         360 :         appendstate->as_prune_state = prunestate;
     137             : 
     138             :         /* Perform an initial partition prune, if required. */
     139         360 :         if (prunestate->do_initial_prune)
     140             :         {
     141             :             /* Determine which subplans survive initial pruning */
     142         140 :             validsubplans = ExecFindInitialMatchingSubPlans(prunestate,
     143         140 :                                                             list_length(node->appendplans));
     144             : 
     145             :             /*
     146             :              * The case where no subplans survive pruning must be handled
     147             :              * specially.  The problem here is that code in explain.c requires
     148             :              * an Append to have at least one subplan in order for it to
     149             :              * properly determine the Vars in that subplan's targetlist.  We
     150             :              * sidestep this issue by just initializing the first subplan and
     151             :              * setting as_whichplan to NO_MATCHING_SUBPLANS to indicate that
     152             :              * we don't really need to scan any subnodes.
     153             :              */
     154         140 :             if (bms_is_empty(validsubplans))
     155             :             {
     156          24 :                 appendstate->as_whichplan = NO_MATCHING_SUBPLANS;
     157             : 
     158             :                 /* Mark the first as valid so that it's initialized below */
     159          24 :                 validsubplans = bms_make_singleton(0);
     160             :             }
     161             : 
     162         140 :             nplans = bms_num_members(validsubplans);
     163             :         }
     164             :         else
     165             :         {
     166             :             /* We'll need to initialize all subplans */
     167         220 :             nplans = list_length(node->appendplans);
     168             :             Assert(nplans > 0);
     169         220 :             validsubplans = bms_add_range(NULL, 0, nplans - 1);
     170             :         }
     171             : 
     172             :         /*
     173             :          * If no runtime pruning is required, we can fill as_valid_subplans
     174             :          * immediately, preventing later calls to ExecFindMatchingSubPlans.
     175             :          */
     176         360 :         if (!prunestate->do_exec_prune)
     177             :         {
     178             :             Assert(nplans > 0);
     179         108 :             appendstate->as_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
     180             :         }
     181             :     }
     182             :     else
     183             :     {
     184        5472 :         nplans = list_length(node->appendplans);
     185             : 
     186             :         /*
     187             :          * When run-time partition pruning is not enabled we can just mark all
     188             :          * subplans as valid; they must also all be initialized.
     189             :          */
     190             :         Assert(nplans > 0);
     191        5472 :         appendstate->as_valid_subplans = validsubplans =
     192        5472 :             bms_add_range(NULL, 0, nplans - 1);
     193        5472 :         appendstate->as_prune_state = NULL;
     194             :     }
     195             : 
     196             :     /*
     197             :      * Initialize result tuple type and slot.
     198             :      */
     199        5832 :     ExecInitResultTupleSlotTL(&appendstate->ps, &TTSOpsVirtual);
     200             : 
     201             :     /* node returns slots from each of its subnodes, therefore not fixed */
     202        5832 :     appendstate->ps.resultopsset = true;
     203        5832 :     appendstate->ps.resultopsfixed = false;
     204             : 
     205        5832 :     appendplanstates = (PlanState **) palloc(nplans *
     206             :                                              sizeof(PlanState *));
     207             : 
     208             :     /*
     209             :      * call ExecInitNode on each of the valid plans to be executed and save
     210             :      * the results into the appendplanstates array.
     211             :      *
     212             :      * While at it, find out the first valid partial plan.
     213             :      */
     214        5832 :     j = i = 0;
     215        5832 :     firstvalid = nplans;
     216       24028 :     foreach(lc, node->appendplans)
     217             :     {
     218       18196 :         if (bms_is_member(i, validsubplans))
     219             :         {
     220       17604 :             Plan       *initNode = (Plan *) lfirst(lc);
     221             : 
     222             :             /*
     223             :              * Record the lowest appendplans index which is a valid partial
     224             :              * plan.
     225             :              */
     226       17604 :             if (i >= node->first_partial_plan && j < firstvalid)
     227         336 :                 firstvalid = j;
     228             : 
     229       17604 :             appendplanstates[j++] = ExecInitNode(initNode, estate, eflags);
     230             :         }
     231       18196 :         i++;
     232             :     }
     233             : 
     234        5832 :     appendstate->as_first_partial_plan = firstvalid;
     235        5832 :     appendstate->appendplans = appendplanstates;
     236        5832 :     appendstate->as_nplans = nplans;
     237             : 
     238             :     /*
     239             :      * Miscellaneous initialization
     240             :      */
     241             : 
     242        5832 :     appendstate->ps.ps_ProjInfo = NULL;
     243             : 
     244             :     /* For parallel query, this will be overridden later. */
     245        5832 :     appendstate->choose_next_subplan = choose_next_subplan_locally;
     246             : 
     247        5832 :     return appendstate;
     248             : }
     249             : 
     250             : /* ----------------------------------------------------------------
     251             :  *     ExecAppend
     252             :  *
     253             :  *      Handles iteration over multiple subplans.
     254             :  * ----------------------------------------------------------------
     255             :  */
     256             : static TupleTableSlot *
     257      954698 : ExecAppend(PlanState *pstate)
     258             : {
     259      954698 :     AppendState *node = castNode(AppendState, pstate);
     260             : 
     261      954698 :     if (node->as_whichplan < 0)
     262             :     {
     263             :         /*
     264             :          * If no subplan has been chosen, we must choose one before
     265             :          * proceeding.
     266             :          */
     267       90468 :         if (node->as_whichplan == INVALID_SUBPLAN_INDEX &&
     268       45222 :             !node->choose_next_subplan(node))
     269        2230 :             return ExecClearTuple(node->ps.ps_ResultTupleSlot);
     270             : 
     271             :         /* Nothing to do if there are no matching subplans */
     272       43016 :         else if (node->as_whichplan == NO_MATCHING_SUBPLANS)
     273          24 :             return ExecClearTuple(node->ps.ps_ResultTupleSlot);
     274             :     }
     275             : 
     276             :     for (;;)
     277       45422 :     {
     278             :         PlanState  *subnode;
     279             :         TupleTableSlot *result;
     280             : 
     281      997866 :         CHECK_FOR_INTERRUPTS();
     282             : 
     283             :         /*
     284             :          * figure out which subplan we are currently processing
     285             :          */
     286             :         Assert(node->as_whichplan >= 0 && node->as_whichplan < node->as_nplans);
     287      997866 :         subnode = node->appendplans[node->as_whichplan];
     288             : 
     289             :         /*
     290             :          * get a tuple from the subplan
     291             :          */
     292      997866 :         result = ExecProcNode(subnode);
     293             : 
     294      997866 :         if (!TupIsNull(result))
     295             :         {
     296             :             /*
     297             :              * If the subplan gave us something then return it as-is. We do
     298             :              * NOT make use of the result slot that was set up in
     299             :              * ExecInitAppend; there's no need for it.
     300             :              */
     301      909630 :             return result;
     302             :         }
     303             : 
     304             :         /* choose new subplan; if none, we're done */
     305       88236 :         if (!node->choose_next_subplan(node))
     306       42814 :             return ExecClearTuple(node->ps.ps_ResultTupleSlot);
     307             :     }
     308             : }
     309             : 
     310             : /* ----------------------------------------------------------------
     311             :  *      ExecEndAppend
     312             :  *
     313             :  *      Shuts down the subscans of the append node.
     314             :  *
     315             :  *      Returns nothing of interest.
     316             :  * ----------------------------------------------------------------
     317             :  */
     318             : void
     319        5762 : ExecEndAppend(AppendState *node)
     320             : {
     321             :     PlanState **appendplans;
     322             :     int         nplans;
     323             :     int         i;
     324             : 
     325             :     /*
     326             :      * get information from the node
     327             :      */
     328        5762 :     appendplans = node->appendplans;
     329        5762 :     nplans = node->as_nplans;
     330             : 
     331             :     /*
     332             :      * shut down each of the subscans
     333             :      */
     334       23222 :     for (i = 0; i < nplans; i++)
     335       17460 :         ExecEndNode(appendplans[i]);
     336        5762 : }
     337             : 
     338             : void
     339       41578 : ExecReScanAppend(AppendState *node)
     340             : {
     341             :     int         i;
     342             : 
     343             :     /*
     344             :      * If any PARAM_EXEC Params used in pruning expressions have changed, then
     345             :      * we'd better unset the valid subplans so that they are reselected for
     346             :      * the new parameter values.
     347             :      */
     348       43754 :     if (node->as_prune_state &&
     349        2176 :         bms_overlap(node->ps.chgParam,
     350        2176 :                     node->as_prune_state->execparamids))
     351             :     {
     352        2176 :         bms_free(node->as_valid_subplans);
     353        2176 :         node->as_valid_subplans = NULL;
     354             :     }
     355             : 
     356      139586 :     for (i = 0; i < node->as_nplans; i++)
     357             :     {
     358       98008 :         PlanState  *subnode = node->appendplans[i];
     359             : 
     360             :         /*
     361             :          * ExecReScan doesn't know about my subplans, so I have to do
     362             :          * changed-parameter signaling myself.
     363             :          */
     364       98008 :         if (node->ps.chgParam != NULL)
     365       93972 :             UpdateChangedParamSet(subnode, node->ps.chgParam);
     366             : 
     367             :         /*
     368             :          * If chgParam of subnode is not null then plan will be re-scanned by
     369             :          * first ExecProcNode.
     370             :          */
     371       98008 :         if (subnode->chgParam == NULL)
     372       40626 :             ExecReScan(subnode);
     373             :     }
     374             : 
     375             :     /* Let choose_next_subplan_* function handle setting the first subplan */
     376       41578 :     node->as_whichplan = INVALID_SUBPLAN_INDEX;
     377       41578 : }
     378             : 
     379             : /* ----------------------------------------------------------------
     380             :  *                      Parallel Append Support
     381             :  * ----------------------------------------------------------------
     382             :  */
     383             : 
     384             : /* ----------------------------------------------------------------
     385             :  *      ExecAppendEstimate
     386             :  *
     387             :  *      Compute the amount of space we'll need in the parallel
     388             :  *      query DSM, and inform pcxt->estimator about our needs.
     389             :  * ----------------------------------------------------------------
     390             :  */
     391             : void
     392         108 : ExecAppendEstimate(AppendState *node,
     393             :                    ParallelContext *pcxt)
     394             : {
     395         108 :     node->pstate_len =
     396         108 :         add_size(offsetof(ParallelAppendState, pa_finished),
     397         108 :                  sizeof(bool) * node->as_nplans);
     398             : 
     399         108 :     shm_toc_estimate_chunk(&pcxt->estimator, node->pstate_len);
     400         108 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     401         108 : }
     402             : 
     403             : 
     404             : /* ----------------------------------------------------------------
     405             :  *      ExecAppendInitializeDSM
     406             :  *
     407             :  *      Set up shared state for Parallel Append.
     408             :  * ----------------------------------------------------------------
     409             :  */
     410             : void
     411         108 : ExecAppendInitializeDSM(AppendState *node,
     412             :                         ParallelContext *pcxt)
     413             : {
     414             :     ParallelAppendState *pstate;
     415             : 
     416         108 :     pstate = shm_toc_allocate(pcxt->toc, node->pstate_len);
     417         108 :     memset(pstate, 0, node->pstate_len);
     418         108 :     LWLockInitialize(&pstate->pa_lock, LWTRANCHE_PARALLEL_APPEND);
     419         108 :     shm_toc_insert(pcxt->toc, node->ps.plan->plan_node_id, pstate);
     420             : 
     421         108 :     node->as_pstate = pstate;
     422         108 :     node->choose_next_subplan = choose_next_subplan_for_leader;
     423         108 : }
     424             : 
     425             : /* ----------------------------------------------------------------
     426             :  *      ExecAppendReInitializeDSM
     427             :  *
     428             :  *      Reset shared state before beginning a fresh scan.
     429             :  * ----------------------------------------------------------------
     430             :  */
     431             : void
     432           0 : ExecAppendReInitializeDSM(AppendState *node, ParallelContext *pcxt)
     433             : {
     434           0 :     ParallelAppendState *pstate = node->as_pstate;
     435             : 
     436           0 :     pstate->pa_next_plan = 0;
     437           0 :     memset(pstate->pa_finished, 0, sizeof(bool) * node->as_nplans);
     438           0 : }
     439             : 
     440             : /* ----------------------------------------------------------------
     441             :  *      ExecAppendInitializeWorker
     442             :  *
     443             :  *      Copy relevant information from TOC into planstate, and initialize
     444             :  *      whatever is required to choose and execute the optimal subplan.
     445             :  * ----------------------------------------------------------------
     446             :  */
     447             : void
     448         228 : ExecAppendInitializeWorker(AppendState *node, ParallelWorkerContext *pwcxt)
     449             : {
     450         228 :     node->as_pstate = shm_toc_lookup(pwcxt->toc, node->ps.plan->plan_node_id, false);
     451         228 :     node->choose_next_subplan = choose_next_subplan_for_worker;
     452         228 : }
     453             : 
     454             : /* ----------------------------------------------------------------
     455             :  *      choose_next_subplan_locally
     456             :  *
     457             :  *      Choose next subplan for a non-parallel-aware Append,
     458             :  *      returning false if there are no more.
     459             :  * ----------------------------------------------------------------
     460             :  */
     461             : static bool
     462      132530 : choose_next_subplan_locally(AppendState *node)
     463             : {
     464      132530 :     int         whichplan = node->as_whichplan;
     465             :     int         nextplan;
     466             : 
     467             :     /* We should never be called when there are no subplans */
     468             :     Assert(whichplan != NO_MATCHING_SUBPLANS);
     469             : 
     470             :     /*
     471             :      * If first call then have the bms member function choose the first valid
     472             :      * subplan by initializing whichplan to -1.  If there happen to be no
     473             :      * valid subplans then the bms member function will handle that by
     474             :      * returning a negative number which will allow us to exit returning a
     475             :      * false value.
     476             :      */
     477      132530 :     if (whichplan == INVALID_SUBPLAN_INDEX)
     478             :     {
     479       44898 :         if (node->as_valid_subplans == NULL)
     480        2296 :             node->as_valid_subplans =
     481        2296 :                 ExecFindMatchingSubPlans(node->as_prune_state);
     482             : 
     483       44898 :         whichplan = -1;
     484             :     }
     485             : 
     486             :     /* Ensure whichplan is within the expected range */
     487             :     Assert(whichplan >= -1 && whichplan <= node->as_nplans);
     488             : 
     489      132530 :     if (ScanDirectionIsForward(node->ps.state->es_direction))
     490      132518 :         nextplan = bms_next_member(node->as_valid_subplans, whichplan);
     491             :     else
     492          12 :         nextplan = bms_prev_member(node->as_valid_subplans, whichplan);
     493             : 
     494      132530 :     if (nextplan < 0)
     495       44720 :         return false;
     496             : 
     497       87810 :     node->as_whichplan = nextplan;
     498             : 
     499       87810 :     return true;
     500             : }
     501             : 
     502             : /* ----------------------------------------------------------------
     503             :  *      choose_next_subplan_for_leader
     504             :  *
     505             :  *      Try to pick a plan which doesn't commit us to doing much
     506             :  *      work locally, so that as much work as possible is done in
     507             :  *      the workers.  Cheapest subplans are at the end.
     508             :  * ----------------------------------------------------------------
     509             :  */
     510             : static bool
     511         706 : choose_next_subplan_for_leader(AppendState *node)
     512             : {
     513         706 :     ParallelAppendState *pstate = node->as_pstate;
     514             : 
     515             :     /* Backward scan is not supported by parallel-aware plans */
     516             :     Assert(ScanDirectionIsForward(node->ps.state->es_direction));
     517             : 
     518             :     /* We should never be called when there are no subplans */
     519             :     Assert(node->as_whichplan != NO_MATCHING_SUBPLANS);
     520             : 
     521         706 :     LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
     522             : 
     523         706 :     if (node->as_whichplan != INVALID_SUBPLAN_INDEX)
     524             :     {
     525             :         /* Mark just-completed subplan as finished. */
     526         602 :         node->as_pstate->pa_finished[node->as_whichplan] = true;
     527             :     }
     528             :     else
     529             :     {
     530             :         /* Start with last subplan. */
     531         104 :         node->as_whichplan = node->as_nplans - 1;
     532             : 
     533             :         /*
     534             :          * If we've yet to determine the valid subplans then do so now.  If
     535             :          * run-time pruning is disabled then the valid subplans will always be
     536             :          * set to all subplans.
     537             :          */
     538         104 :         if (node->as_valid_subplans == NULL)
     539             :         {
     540           4 :             node->as_valid_subplans =
     541           4 :                 ExecFindMatchingSubPlans(node->as_prune_state);
     542             : 
     543             :             /*
     544             :              * Mark each invalid plan as finished to allow the loop below to
     545             :              * select the first valid subplan.
     546             :              */
     547           4 :             mark_invalid_subplans_as_finished(node);
     548             :         }
     549             :     }
     550             : 
     551             :     /* Loop until we find a subplan to execute. */
     552        1916 :     while (pstate->pa_finished[node->as_whichplan])
     553             :     {
     554         608 :         if (node->as_whichplan == 0)
     555             :         {
     556         104 :             pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
     557         104 :             node->as_whichplan = INVALID_SUBPLAN_INDEX;
     558         104 :             LWLockRelease(&pstate->pa_lock);
     559         104 :             return false;
     560             :         }
     561             : 
     562             :         /*
     563             :          * We needn't pay attention to as_valid_subplans here as all invalid
     564             :          * plans have been marked as finished.
     565             :          */
     566         504 :         node->as_whichplan--;
     567             :     }
     568             : 
     569             :     /* If non-partial, immediately mark as finished. */
     570         602 :     if (node->as_whichplan < node->as_first_partial_plan)
     571          82 :         node->as_pstate->pa_finished[node->as_whichplan] = true;
     572             : 
     573         602 :     LWLockRelease(&pstate->pa_lock);
     574             : 
     575         602 :     return true;
     576             : }
     577             : 
     578             : /* ----------------------------------------------------------------
     579             :  *      choose_next_subplan_for_worker
     580             :  *
     581             :  *      Choose next subplan for a parallel-aware Append, returning
     582             :  *      false if there are no more.
     583             :  *
     584             :  *      We start from the first plan and advance through the list;
     585             :  *      when we get back to the end, we loop back to the first
     586             :  *      partial plan.  This assigns the non-partial plans first in
     587             :  *      order of descending cost and then spreads out the workers
     588             :  *      as evenly as possible across the remaining partial plans.
     589             :  * ----------------------------------------------------------------
     590             :  */
     591             : static bool
     592         222 : choose_next_subplan_for_worker(AppendState *node)
     593             : {
     594         222 :     ParallelAppendState *pstate = node->as_pstate;
     595             : 
     596             :     /* Backward scan is not supported by parallel-aware plans */
     597             :     Assert(ScanDirectionIsForward(node->ps.state->es_direction));
     598             : 
     599             :     /* We should never be called when there are no subplans */
     600             :     Assert(node->as_whichplan != NO_MATCHING_SUBPLANS);
     601             : 
     602         222 :     LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
     603             : 
     604             :     /* Mark just-completed subplan as finished. */
     605         222 :     if (node->as_whichplan != INVALID_SUBPLAN_INDEX)
     606           2 :         node->as_pstate->pa_finished[node->as_whichplan] = true;
     607             : 
     608             :     /*
     609             :      * If we've yet to determine the valid subplans then do so now.  If
     610             :      * run-time pruning is disabled then the valid subplans will always be set
     611             :      * to all subplans.
     612             :      */
     613         220 :     else if (node->as_valid_subplans == NULL)
     614             :     {
     615           8 :         node->as_valid_subplans =
     616           8 :             ExecFindMatchingSubPlans(node->as_prune_state);
     617           8 :         mark_invalid_subplans_as_finished(node);
     618             :     }
     619             : 
     620             :     /* If all the plans are already done, we have nothing to do */
     621         222 :     if (pstate->pa_next_plan == INVALID_SUBPLAN_INDEX)
     622             :     {
     623         218 :         LWLockRelease(&pstate->pa_lock);
     624         218 :         return false;
     625             :     }
     626             : 
     627             :     /* Save the plan from which we are starting the search. */
     628           4 :     node->as_whichplan = pstate->pa_next_plan;
     629             : 
     630             :     /* Loop until we find a valid subplan to execute. */
     631          16 :     while (pstate->pa_finished[pstate->pa_next_plan])
     632             :     {
     633             :         int         nextplan;
     634             : 
     635          10 :         nextplan = bms_next_member(node->as_valid_subplans,
     636             :                                    pstate->pa_next_plan);
     637          10 :         if (nextplan >= 0)
     638             :         {
     639             :             /* Advance to the next valid plan. */
     640           8 :             pstate->pa_next_plan = nextplan;
     641             :         }
     642           2 :         else if (node->as_whichplan > node->as_first_partial_plan)
     643             :         {
     644             :             /*
     645             :              * Try looping back to the first valid partial plan, if there is
     646             :              * one.  If there isn't, arrange to bail out below.
     647             :              */
     648           0 :             nextplan = bms_next_member(node->as_valid_subplans,
     649           0 :                                        node->as_first_partial_plan - 1);
     650           0 :             pstate->pa_next_plan =
     651           0 :                 nextplan < 0 ? node->as_whichplan : nextplan;
     652             :         }
     653             :         else
     654             :         {
     655             :             /*
     656             :              * At last plan, and either there are no partial plans or we've
     657             :              * tried them all.  Arrange to bail out.
     658             :              */
     659           2 :             pstate->pa_next_plan = node->as_whichplan;
     660             :         }
     661             : 
     662          10 :         if (pstate->pa_next_plan == node->as_whichplan)
     663             :         {
     664             :             /* We've tried everything! */
     665           2 :             pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
     666           2 :             LWLockRelease(&pstate->pa_lock);
     667           2 :             return false;
     668             :         }
     669             :     }
     670             : 
     671             :     /* Pick the plan we found, and advance pa_next_plan one more time. */
     672           2 :     node->as_whichplan = pstate->pa_next_plan;
     673           2 :     pstate->pa_next_plan = bms_next_member(node->as_valid_subplans,
     674             :                                            pstate->pa_next_plan);
     675             : 
     676             :     /*
     677             :      * If there are no more valid plans then try setting the next plan to the
     678             :      * first valid partial plan.
     679             :      */
     680           2 :     if (pstate->pa_next_plan < 0)
     681             :     {
     682           0 :         int         nextplan = bms_next_member(node->as_valid_subplans,
     683           0 :                                                node->as_first_partial_plan - 1);
     684             : 
     685           0 :         if (nextplan >= 0)
     686           0 :             pstate->pa_next_plan = nextplan;
     687             :         else
     688             :         {
     689             :             /*
     690             :              * There are no valid partial plans, and we already chose the last
     691             :              * non-partial plan; so flag that there's nothing more for our
     692             :              * fellow workers to do.
     693             :              */
     694           0 :             pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
     695             :         }
     696             :     }
     697             : 
     698             :     /* If non-partial, immediately mark as finished. */
     699           2 :     if (node->as_whichplan < node->as_first_partial_plan)
     700           2 :         node->as_pstate->pa_finished[node->as_whichplan] = true;
     701             : 
     702           2 :     LWLockRelease(&pstate->pa_lock);
     703             : 
     704           2 :     return true;
     705             : }
     706             : 
     707             : /*
     708             :  * mark_invalid_subplans_as_finished
     709             :  *      Marks the ParallelAppendState's pa_finished as true for each invalid
     710             :  *      subplan.
     711             :  *
     712             :  * This function should only be called for parallel Append with run-time
     713             :  * pruning enabled.
     714             :  */
     715             : static void
     716          12 : mark_invalid_subplans_as_finished(AppendState *node)
     717             : {
     718             :     int         i;
     719             : 
     720             :     /* Only valid to call this while in parallel Append mode */
     721             :     Assert(node->as_pstate);
     722             : 
     723             :     /* Shouldn't have been called when run-time pruning is not enabled */
     724             :     Assert(node->as_prune_state);
     725             : 
     726             :     /* Nothing to do if all plans are valid */
     727          12 :     if (bms_num_members(node->as_valid_subplans) == node->as_nplans)
     728           0 :         return;
     729             : 
     730             :     /* Mark all non-valid plans as finished */
     731          48 :     for (i = 0; i < node->as_nplans; i++)
     732             :     {
     733          36 :         if (!bms_is_member(i, node->as_valid_subplans))
     734          12 :             node->as_pstate->pa_finished[i] = true;
     735             :     }
     736             : }

Generated by: LCOV version 1.13