LCOV - code coverage report
Current view: top level - src/backend/executor - nodeAppend.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 168 181 92.8 %
Date: 2020-06-05 19:06:29 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-2020, 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             : 
      82             : static TupleTableSlot *ExecAppend(PlanState *pstate);
      83             : static bool choose_next_subplan_locally(AppendState *node);
      84             : static bool choose_next_subplan_for_leader(AppendState *node);
      85             : static bool choose_next_subplan_for_worker(AppendState *node);
      86             : static void mark_invalid_subplans_as_finished(AppendState *node);
      87             : 
      88             : /* ----------------------------------------------------------------
      89             :  *      ExecInitAppend
      90             :  *
      91             :  *      Begin all of the subscans of the append node.
      92             :  *
      93             :  *     (This is potentially wasteful, since the entire result of the
      94             :  *      append node may not be scanned, but this way all of the
      95             :  *      structures get allocated in the executor's top level memory
      96             :  *      block instead of that of the call to ExecAppend.)
      97             :  * ----------------------------------------------------------------
      98             :  */
      99             : AppendState *
     100        6742 : ExecInitAppend(Append *node, EState *estate, int eflags)
     101             : {
     102        6742 :     AppendState *appendstate = makeNode(AppendState);
     103             :     PlanState **appendplanstates;
     104             :     Bitmapset  *validsubplans;
     105             :     int         nplans;
     106             :     int         firstvalid;
     107             :     int         i,
     108             :                 j;
     109             : 
     110             :     /* check for unsupported flags */
     111             :     Assert(!(eflags & EXEC_FLAG_MARK));
     112             : 
     113             :     /*
     114             :      * create new AppendState for our append node
     115             :      */
     116        6742 :     appendstate->ps.plan = (Plan *) node;
     117        6742 :     appendstate->ps.state = estate;
     118        6742 :     appendstate->ps.ExecProcNode = ExecAppend;
     119             : 
     120             :     /* Let choose_next_subplan_* function handle setting the first subplan */
     121        6742 :     appendstate->as_whichplan = INVALID_SUBPLAN_INDEX;
     122             : 
     123             :     /* If run-time partition pruning is enabled, then set that up now */
     124        6742 :     if (node->part_prune_info != NULL)
     125             :     {
     126             :         PartitionPruneState *prunestate;
     127             : 
     128             :         /* We may need an expression context to evaluate partition exprs */
     129         322 :         ExecAssignExprContext(estate, &appendstate->ps);
     130             : 
     131             :         /* Create the working data structure for pruning. */
     132         322 :         prunestate = ExecCreatePartitionPruneState(&appendstate->ps,
     133         322 :                                                    node->part_prune_info);
     134         322 :         appendstate->as_prune_state = prunestate;
     135             : 
     136             :         /* Perform an initial partition prune, if required. */
     137         322 :         if (prunestate->do_initial_prune)
     138             :         {
     139             :             /* Determine which subplans survive initial pruning */
     140         140 :             validsubplans = ExecFindInitialMatchingSubPlans(prunestate,
     141         140 :                                                             list_length(node->appendplans));
     142             : 
     143         140 :             nplans = bms_num_members(validsubplans);
     144             :         }
     145             :         else
     146             :         {
     147             :             /* We'll need to initialize all subplans */
     148         182 :             nplans = list_length(node->appendplans);
     149             :             Assert(nplans > 0);
     150         182 :             validsubplans = bms_add_range(NULL, 0, nplans - 1);
     151             :         }
     152             : 
     153             :         /*
     154             :          * When no run-time pruning is required and there's at least one
     155             :          * subplan, we can fill as_valid_subplans immediately, preventing
     156             :          * later calls to ExecFindMatchingSubPlans.
     157             :          */
     158         322 :         if (!prunestate->do_exec_prune && nplans > 0)
     159          84 :             appendstate->as_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
     160             :     }
     161             :     else
     162             :     {
     163        6420 :         nplans = list_length(node->appendplans);
     164             : 
     165             :         /*
     166             :          * When run-time partition pruning is not enabled we can just mark all
     167             :          * subplans as valid; they must also all be initialized.
     168             :          */
     169             :         Assert(nplans > 0);
     170        6420 :         appendstate->as_valid_subplans = validsubplans =
     171        6420 :             bms_add_range(NULL, 0, nplans - 1);
     172        6420 :         appendstate->as_prune_state = NULL;
     173             :     }
     174             : 
     175             :     /*
     176             :      * Initialize result tuple type and slot.
     177             :      */
     178        6742 :     ExecInitResultTupleSlotTL(&appendstate->ps, &TTSOpsVirtual);
     179             : 
     180             :     /* node returns slots from each of its subnodes, therefore not fixed */
     181        6742 :     appendstate->ps.resultopsset = true;
     182        6742 :     appendstate->ps.resultopsfixed = false;
     183             : 
     184        6742 :     appendplanstates = (PlanState **) palloc(nplans *
     185             :                                              sizeof(PlanState *));
     186             : 
     187             :     /*
     188             :      * call ExecInitNode on each of the valid plans to be executed and save
     189             :      * the results into the appendplanstates array.
     190             :      *
     191             :      * While at it, find out the first valid partial plan.
     192             :      */
     193        6742 :     j = 0;
     194        6742 :     firstvalid = nplans;
     195        6742 :     i = -1;
     196       25532 :     while ((i = bms_next_member(validsubplans, i)) >= 0)
     197             :     {
     198       18790 :         Plan       *initNode = (Plan *) list_nth(node->appendplans, i);
     199             : 
     200             :         /*
     201             :          * Record the lowest appendplans index which is a valid partial plan.
     202             :          */
     203       18790 :         if (i >= node->first_partial_plan && j < firstvalid)
     204         204 :             firstvalid = j;
     205             : 
     206       18790 :         appendplanstates[j++] = ExecInitNode(initNode, estate, eflags);
     207             :     }
     208             : 
     209        6742 :     appendstate->as_first_partial_plan = firstvalid;
     210        6742 :     appendstate->appendplans = appendplanstates;
     211        6742 :     appendstate->as_nplans = nplans;
     212             : 
     213             :     /*
     214             :      * Miscellaneous initialization
     215             :      */
     216             : 
     217        6742 :     appendstate->ps.ps_ProjInfo = NULL;
     218             : 
     219             :     /* For parallel query, this will be overridden later. */
     220        6742 :     appendstate->choose_next_subplan = choose_next_subplan_locally;
     221             : 
     222        6742 :     return appendstate;
     223             : }
     224             : 
     225             : /* ----------------------------------------------------------------
     226             :  *     ExecAppend
     227             :  *
     228             :  *      Handles iteration over multiple subplans.
     229             :  * ----------------------------------------------------------------
     230             :  */
     231             : static TupleTableSlot *
     232     1733336 : ExecAppend(PlanState *pstate)
     233             : {
     234     1733336 :     AppendState *node = castNode(AppendState, pstate);
     235             : 
     236     1733336 :     if (node->as_whichplan < 0)
     237             :     {
     238             :         /* Nothing to do if there are no subplans */
     239       51412 :         if (node->as_nplans == 0)
     240          24 :             return ExecClearTuple(node->ps.ps_ResultTupleSlot);
     241             : 
     242             :         /*
     243             :          * If no subplan has been chosen, we must choose one before
     244             :          * proceeding.
     245             :          */
     246       51388 :         if (node->as_whichplan == INVALID_SUBPLAN_INDEX &&
     247       51388 :             !node->choose_next_subplan(node))
     248        2150 :             return ExecClearTuple(node->ps.ps_ResultTupleSlot);
     249             :     }
     250             : 
     251             :     for (;;)
     252       51374 :     {
     253             :         PlanState  *subnode;
     254             :         TupleTableSlot *result;
     255             : 
     256     1782536 :         CHECK_FOR_INTERRUPTS();
     257             : 
     258             :         /*
     259             :          * figure out which subplan we are currently processing
     260             :          */
     261             :         Assert(node->as_whichplan >= 0 && node->as_whichplan < node->as_nplans);
     262     1782536 :         subnode = node->appendplans[node->as_whichplan];
     263             : 
     264             :         /*
     265             :          * get a tuple from the subplan
     266             :          */
     267     1782536 :         result = ExecProcNode(subnode);
     268             : 
     269     1782536 :         if (!TupIsNull(result))
     270             :         {
     271             :             /*
     272             :              * If the subplan gave us something then return it as-is. We do
     273             :              * NOT make use of the result slot that was set up in
     274             :              * ExecInitAppend; there's no need for it.
     275             :              */
     276     1682110 :             return result;
     277             :         }
     278             : 
     279             :         /* choose new subplan; if none, we're done */
     280      100426 :         if (!node->choose_next_subplan(node))
     281       49052 :             return ExecClearTuple(node->ps.ps_ResultTupleSlot);
     282             :     }
     283             : }
     284             : 
     285             : /* ----------------------------------------------------------------
     286             :  *      ExecEndAppend
     287             :  *
     288             :  *      Shuts down the subscans of the append node.
     289             :  *
     290             :  *      Returns nothing of interest.
     291             :  * ----------------------------------------------------------------
     292             :  */
     293             : void
     294        6688 : ExecEndAppend(AppendState *node)
     295             : {
     296             :     PlanState **appendplans;
     297             :     int         nplans;
     298             :     int         i;
     299             : 
     300             :     /*
     301             :      * get information from the node
     302             :      */
     303        6688 :     appendplans = node->appendplans;
     304        6688 :     nplans = node->as_nplans;
     305             : 
     306             :     /*
     307             :      * shut down each of the subscans
     308             :      */
     309       25366 :     for (i = 0; i < nplans; i++)
     310       18678 :         ExecEndNode(appendplans[i]);
     311        6688 : }
     312             : 
     313             : void
     314       47256 : ExecReScanAppend(AppendState *node)
     315             : {
     316             :     int         i;
     317             : 
     318             :     /*
     319             :      * If any PARAM_EXEC Params used in pruning expressions have changed, then
     320             :      * we'd better unset the valid subplans so that they are reselected for
     321             :      * the new parameter values.
     322             :      */
     323       49432 :     if (node->as_prune_state &&
     324        2176 :         bms_overlap(node->ps.chgParam,
     325        2176 :                     node->as_prune_state->execparamids))
     326             :     {
     327        2176 :         bms_free(node->as_valid_subplans);
     328        2176 :         node->as_valid_subplans = NULL;
     329             :     }
     330             : 
     331      156616 :     for (i = 0; i < node->as_nplans; i++)
     332             :     {
     333      109360 :         PlanState  *subnode = node->appendplans[i];
     334             : 
     335             :         /*
     336             :          * ExecReScan doesn't know about my subplans, so I have to do
     337             :          * changed-parameter signaling myself.
     338             :          */
     339      109360 :         if (node->ps.chgParam != NULL)
     340      105396 :             UpdateChangedParamSet(subnode, node->ps.chgParam);
     341             : 
     342             :         /*
     343             :          * If chgParam of subnode is not null then plan will be re-scanned by
     344             :          * first ExecProcNode.
     345             :          */
     346      109360 :         if (subnode->chgParam == NULL)
     347       45870 :             ExecReScan(subnode);
     348             :     }
     349             : 
     350             :     /* Let choose_next_subplan_* function handle setting the first subplan */
     351       47256 :     node->as_whichplan = INVALID_SUBPLAN_INDEX;
     352       47256 : }
     353             : 
     354             : /* ----------------------------------------------------------------
     355             :  *                      Parallel Append Support
     356             :  * ----------------------------------------------------------------
     357             :  */
     358             : 
     359             : /* ----------------------------------------------------------------
     360             :  *      ExecAppendEstimate
     361             :  *
     362             :  *      Compute the amount of space we'll need in the parallel
     363             :  *      query DSM, and inform pcxt->estimator about our needs.
     364             :  * ----------------------------------------------------------------
     365             :  */
     366             : void
     367          68 : ExecAppendEstimate(AppendState *node,
     368             :                    ParallelContext *pcxt)
     369             : {
     370          68 :     node->pstate_len =
     371          68 :         add_size(offsetof(ParallelAppendState, pa_finished),
     372          68 :                  sizeof(bool) * node->as_nplans);
     373             : 
     374          68 :     shm_toc_estimate_chunk(&pcxt->estimator, node->pstate_len);
     375          68 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     376          68 : }
     377             : 
     378             : 
     379             : /* ----------------------------------------------------------------
     380             :  *      ExecAppendInitializeDSM
     381             :  *
     382             :  *      Set up shared state for Parallel Append.
     383             :  * ----------------------------------------------------------------
     384             :  */
     385             : void
     386          68 : ExecAppendInitializeDSM(AppendState *node,
     387             :                         ParallelContext *pcxt)
     388             : {
     389             :     ParallelAppendState *pstate;
     390             : 
     391          68 :     pstate = shm_toc_allocate(pcxt->toc, node->pstate_len);
     392          68 :     memset(pstate, 0, node->pstate_len);
     393          68 :     LWLockInitialize(&pstate->pa_lock, LWTRANCHE_PARALLEL_APPEND);
     394          68 :     shm_toc_insert(pcxt->toc, node->ps.plan->plan_node_id, pstate);
     395             : 
     396          68 :     node->as_pstate = pstate;
     397          68 :     node->choose_next_subplan = choose_next_subplan_for_leader;
     398          68 : }
     399             : 
     400             : /* ----------------------------------------------------------------
     401             :  *      ExecAppendReInitializeDSM
     402             :  *
     403             :  *      Reset shared state before beginning a fresh scan.
     404             :  * ----------------------------------------------------------------
     405             :  */
     406             : void
     407           0 : ExecAppendReInitializeDSM(AppendState *node, ParallelContext *pcxt)
     408             : {
     409           0 :     ParallelAppendState *pstate = node->as_pstate;
     410             : 
     411           0 :     pstate->pa_next_plan = 0;
     412           0 :     memset(pstate->pa_finished, 0, sizeof(bool) * node->as_nplans);
     413           0 : }
     414             : 
     415             : /* ----------------------------------------------------------------
     416             :  *      ExecAppendInitializeWorker
     417             :  *
     418             :  *      Copy relevant information from TOC into planstate, and initialize
     419             :  *      whatever is required to choose and execute the optimal subplan.
     420             :  * ----------------------------------------------------------------
     421             :  */
     422             : void
     423         148 : ExecAppendInitializeWorker(AppendState *node, ParallelWorkerContext *pwcxt)
     424             : {
     425         148 :     node->as_pstate = shm_toc_lookup(pwcxt->toc, node->ps.plan->plan_node_id, false);
     426         148 :     node->choose_next_subplan = choose_next_subplan_for_worker;
     427         148 : }
     428             : 
     429             : /* ----------------------------------------------------------------
     430             :  *      choose_next_subplan_locally
     431             :  *
     432             :  *      Choose next subplan for a non-parallel-aware Append,
     433             :  *      returning false if there are no more.
     434             :  * ----------------------------------------------------------------
     435             :  */
     436             : static bool
     437      151364 : choose_next_subplan_locally(AppendState *node)
     438             : {
     439      151364 :     int         whichplan = node->as_whichplan;
     440             :     int         nextplan;
     441             : 
     442             :     /* We should never be called when there are no subplans */
     443             :     Assert(node->as_nplans > 0);
     444             : 
     445             :     /*
     446             :      * If first call then have the bms member function choose the first valid
     447             :      * subplan by initializing whichplan to -1.  If there happen to be no
     448             :      * valid subplans then the bms member function will handle that by
     449             :      * returning a negative number which will allow us to exit returning a
     450             :      * false value.
     451             :      */
     452      151364 :     if (whichplan == INVALID_SUBPLAN_INDEX)
     453             :     {
     454       51184 :         if (node->as_valid_subplans == NULL)
     455        2256 :             node->as_valid_subplans =
     456        2256 :                 ExecFindMatchingSubPlans(node->as_prune_state);
     457             : 
     458       51184 :         whichplan = -1;
     459             :     }
     460             : 
     461             :     /* Ensure whichplan is within the expected range */
     462             :     Assert(whichplan >= -1 && whichplan <= node->as_nplans);
     463             : 
     464      151364 :     if (ScanDirectionIsForward(node->ps.state->es_direction))
     465      151352 :         nextplan = bms_next_member(node->as_valid_subplans, whichplan);
     466             :     else
     467          12 :         nextplan = bms_prev_member(node->as_valid_subplans, whichplan);
     468             : 
     469      151364 :     if (nextplan < 0)
     470       50998 :         return false;
     471             : 
     472      100366 :     node->as_whichplan = nextplan;
     473             : 
     474      100366 :     return true;
     475             : }
     476             : 
     477             : /* ----------------------------------------------------------------
     478             :  *      choose_next_subplan_for_leader
     479             :  *
     480             :  *      Try to pick a plan which doesn't commit us to doing much
     481             :  *      work locally, so that as much work as possible is done in
     482             :  *      the workers.  Cheapest subplans are at the end.
     483             :  * ----------------------------------------------------------------
     484             :  */
     485             : static bool
     486         308 : choose_next_subplan_for_leader(AppendState *node)
     487             : {
     488         308 :     ParallelAppendState *pstate = node->as_pstate;
     489             : 
     490             :     /* Backward scan is not supported by parallel-aware plans */
     491             :     Assert(ScanDirectionIsForward(node->ps.state->es_direction));
     492             : 
     493             :     /* We should never be called when there are no subplans */
     494             :     Assert(node->as_nplans > 0);
     495             : 
     496         308 :     LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
     497             : 
     498         308 :     if (node->as_whichplan != INVALID_SUBPLAN_INDEX)
     499             :     {
     500             :         /* Mark just-completed subplan as finished. */
     501         244 :         node->as_pstate->pa_finished[node->as_whichplan] = true;
     502             :     }
     503             :     else
     504             :     {
     505             :         /* Start with last subplan. */
     506          64 :         node->as_whichplan = node->as_nplans - 1;
     507             : 
     508             :         /*
     509             :          * If we've yet to determine the valid subplans then do so now.  If
     510             :          * run-time pruning is disabled then the valid subplans will always be
     511             :          * set to all subplans.
     512             :          */
     513          64 :         if (node->as_valid_subplans == NULL)
     514             :         {
     515           4 :             node->as_valid_subplans =
     516           4 :                 ExecFindMatchingSubPlans(node->as_prune_state);
     517             : 
     518             :             /*
     519             :              * Mark each invalid plan as finished to allow the loop below to
     520             :              * select the first valid subplan.
     521             :              */
     522           4 :             mark_invalid_subplans_as_finished(node);
     523             :         }
     524             :     }
     525             : 
     526             :     /* Loop until we find a subplan to execute. */
     527         492 :     while (pstate->pa_finished[node->as_whichplan])
     528             :     {
     529         248 :         if (node->as_whichplan == 0)
     530             :         {
     531          64 :             pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
     532          64 :             node->as_whichplan = INVALID_SUBPLAN_INDEX;
     533          64 :             LWLockRelease(&pstate->pa_lock);
     534          64 :             return false;
     535             :         }
     536             : 
     537             :         /*
     538             :          * We needn't pay attention to as_valid_subplans here as all invalid
     539             :          * plans have been marked as finished.
     540             :          */
     541         184 :         node->as_whichplan--;
     542             :     }
     543             : 
     544             :     /* If non-partial, immediately mark as finished. */
     545         244 :     if (node->as_whichplan < node->as_first_partial_plan)
     546          84 :         node->as_pstate->pa_finished[node->as_whichplan] = true;
     547             : 
     548         244 :     LWLockRelease(&pstate->pa_lock);
     549             : 
     550         244 :     return true;
     551             : }
     552             : 
     553             : /* ----------------------------------------------------------------
     554             :  *      choose_next_subplan_for_worker
     555             :  *
     556             :  *      Choose next subplan for a parallel-aware Append, returning
     557             :  *      false if there are no more.
     558             :  *
     559             :  *      We start from the first plan and advance through the list;
     560             :  *      when we get back to the end, we loop back to the first
     561             :  *      partial plan.  This assigns the non-partial plans first in
     562             :  *      order of descending cost and then spreads out the workers
     563             :  *      as evenly as possible across the remaining partial plans.
     564             :  * ----------------------------------------------------------------
     565             :  */
     566             : static bool
     567         142 : choose_next_subplan_for_worker(AppendState *node)
     568             : {
     569         142 :     ParallelAppendState *pstate = node->as_pstate;
     570             : 
     571             :     /* Backward scan is not supported by parallel-aware plans */
     572             :     Assert(ScanDirectionIsForward(node->ps.state->es_direction));
     573             : 
     574             :     /* We should never be called when there are no subplans */
     575             :     Assert(node->as_nplans > 0);
     576             : 
     577         142 :     LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
     578             : 
     579             :     /* Mark just-completed subplan as finished. */
     580         142 :     if (node->as_whichplan != INVALID_SUBPLAN_INDEX)
     581           2 :         node->as_pstate->pa_finished[node->as_whichplan] = true;
     582             : 
     583             :     /*
     584             :      * If we've yet to determine the valid subplans then do so now.  If
     585             :      * run-time pruning is disabled then the valid subplans will always be set
     586             :      * to all subplans.
     587             :      */
     588         140 :     else if (node->as_valid_subplans == NULL)
     589             :     {
     590           8 :         node->as_valid_subplans =
     591           8 :             ExecFindMatchingSubPlans(node->as_prune_state);
     592           8 :         mark_invalid_subplans_as_finished(node);
     593             :     }
     594             : 
     595             :     /* If all the plans are already done, we have nothing to do */
     596         142 :     if (pstate->pa_next_plan == INVALID_SUBPLAN_INDEX)
     597             :     {
     598         138 :         LWLockRelease(&pstate->pa_lock);
     599         138 :         return false;
     600             :     }
     601             : 
     602             :     /* Save the plan from which we are starting the search. */
     603           4 :     node->as_whichplan = pstate->pa_next_plan;
     604             : 
     605             :     /* Loop until we find a valid subplan to execute. */
     606           8 :     while (pstate->pa_finished[pstate->pa_next_plan])
     607             :     {
     608             :         int         nextplan;
     609             : 
     610           6 :         nextplan = bms_next_member(node->as_valid_subplans,
     611             :                                    pstate->pa_next_plan);
     612           6 :         if (nextplan >= 0)
     613             :         {
     614             :             /* Advance to the next valid plan. */
     615           4 :             pstate->pa_next_plan = nextplan;
     616             :         }
     617           2 :         else if (node->as_whichplan > node->as_first_partial_plan)
     618             :         {
     619             :             /*
     620             :              * Try looping back to the first valid partial plan, if there is
     621             :              * one.  If there isn't, arrange to bail out below.
     622             :              */
     623           2 :             nextplan = bms_next_member(node->as_valid_subplans,
     624           2 :                                        node->as_first_partial_plan - 1);
     625           2 :             pstate->pa_next_plan =
     626           2 :                 nextplan < 0 ? node->as_whichplan : nextplan;
     627             :         }
     628             :         else
     629             :         {
     630             :             /*
     631             :              * At last plan, and either there are no partial plans or we've
     632             :              * tried them all.  Arrange to bail out.
     633             :              */
     634           0 :             pstate->pa_next_plan = node->as_whichplan;
     635             :         }
     636             : 
     637           6 :         if (pstate->pa_next_plan == node->as_whichplan)
     638             :         {
     639             :             /* We've tried everything! */
     640           2 :             pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
     641           2 :             LWLockRelease(&pstate->pa_lock);
     642           2 :             return false;
     643             :         }
     644             :     }
     645             : 
     646             :     /* Pick the plan we found, and advance pa_next_plan one more time. */
     647           2 :     node->as_whichplan = pstate->pa_next_plan;
     648           2 :     pstate->pa_next_plan = bms_next_member(node->as_valid_subplans,
     649             :                                            pstate->pa_next_plan);
     650             : 
     651             :     /*
     652             :      * If there are no more valid plans then try setting the next plan to the
     653             :      * first valid partial plan.
     654             :      */
     655           2 :     if (pstate->pa_next_plan < 0)
     656             :     {
     657           0 :         int         nextplan = bms_next_member(node->as_valid_subplans,
     658           0 :                                                node->as_first_partial_plan - 1);
     659             : 
     660           0 :         if (nextplan >= 0)
     661           0 :             pstate->pa_next_plan = nextplan;
     662             :         else
     663             :         {
     664             :             /*
     665             :              * There are no valid partial plans, and we already chose the last
     666             :              * non-partial plan; so flag that there's nothing more for our
     667             :              * fellow workers to do.
     668             :              */
     669           0 :             pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
     670             :         }
     671             :     }
     672             : 
     673             :     /* If non-partial, immediately mark as finished. */
     674           2 :     if (node->as_whichplan < node->as_first_partial_plan)
     675           0 :         node->as_pstate->pa_finished[node->as_whichplan] = true;
     676             : 
     677           2 :     LWLockRelease(&pstate->pa_lock);
     678             : 
     679           2 :     return true;
     680             : }
     681             : 
     682             : /*
     683             :  * mark_invalid_subplans_as_finished
     684             :  *      Marks the ParallelAppendState's pa_finished as true for each invalid
     685             :  *      subplan.
     686             :  *
     687             :  * This function should only be called for parallel Append with run-time
     688             :  * pruning enabled.
     689             :  */
     690             : static void
     691          12 : mark_invalid_subplans_as_finished(AppendState *node)
     692             : {
     693             :     int         i;
     694             : 
     695             :     /* Only valid to call this while in parallel Append mode */
     696             :     Assert(node->as_pstate);
     697             : 
     698             :     /* Shouldn't have been called when run-time pruning is not enabled */
     699             :     Assert(node->as_prune_state);
     700             : 
     701             :     /* Nothing to do if all plans are valid */
     702          12 :     if (bms_num_members(node->as_valid_subplans) == node->as_nplans)
     703           0 :         return;
     704             : 
     705             :     /* Mark all non-valid plans as finished */
     706          48 :     for (i = 0; i < node->as_nplans; i++)
     707             :     {
     708          36 :         if (!bms_is_member(i, node->as_valid_subplans))
     709          12 :             node->as_pstate->pa_finished[i] = true;
     710             :     }
     711             : }

Generated by: LCOV version 1.13