LCOV - code coverage report
Current view: top level - src/backend/executor - nodeAppend.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 95.0 % 360 342
Test Date: 2026-08-19 14:15:54 Functions: 94.7 % 19 18
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 89.2 % 186 166

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * nodeAppend.c
       4                 :             :  *    routines to handle append nodes.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, 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                 :             : /*
      16                 :             :  * INTERFACE ROUTINES
      17                 :             :  *      ExecInitAppend  - initialize the append node
      18                 :             :  *      ExecAppend      - retrieve the next tuple from the node
      19                 :             :  *      ExecEndAppend   - shut down the append node
      20                 :             :  *      ExecReScanAppend - rescan the append node
      21                 :             :  *
      22                 :             :  *   NOTES
      23                 :             :  *      Each append node contains a list of one or more subplans which
      24                 :             :  *      must be iteratively processed (forwards or backwards).
      25                 :             :  *      Tuples are retrieved by executing the 'whichplan'th subplan
      26                 :             :  *      until the subplan stops returning tuples, at which point that
      27                 :             :  *      plan is shut down and the next started up.
      28                 :             :  *
      29                 :             :  *      Append nodes don't make use of their left and right
      30                 :             :  *      subtrees, rather they maintain a list of subplans so
      31                 :             :  *      a typical append node looks like this in the plan tree:
      32                 :             :  *
      33                 :             :  *                 ...
      34                 :             :  *                 /
      35                 :             :  *              Append -------+------+------+--- nil
      36                 :             :  *              /   \         |      |      |
      37                 :             :  *            nil   nil      ...    ...    ...
      38                 :             :  *                               subplans
      39                 :             :  *
      40                 :             :  *      Append nodes are currently used for unions, and to support
      41                 :             :  *      inheritance queries, where several relations need to be scanned.
      42                 :             :  *      For example, in our standard person/student/employee/student-emp
      43                 :             :  *      example, where student and employee inherit from person
      44                 :             :  *      and student-emp inherits from student and employee, the
      45                 :             :  *      query:
      46                 :             :  *
      47                 :             :  *              select name from person
      48                 :             :  *
      49                 :             :  *      generates the plan:
      50                 :             :  *
      51                 :             :  *                |
      52                 :             :  *              Append -------+-------+--------+--------+
      53                 :             :  *              /   \         |       |        |        |
      54                 :             :  *            nil   nil      Scan    Scan     Scan     Scan
      55                 :             :  *                            |       |        |        |
      56                 :             :  *                          person employee student student-emp
      57                 :             :  */
      58                 :             : 
      59                 :             : #include "postgres.h"
      60                 :             : 
      61                 :             : #include "executor/execAsync.h"
      62                 :             : #include "executor/execPartition.h"
      63                 :             : #include "executor/executor.h"
      64                 :             : #include "executor/nodeAppend.h"
      65                 :             : #include "miscadmin.h"
      66                 :             : #include "pgstat.h"
      67                 :             : #include "storage/latch.h"
      68                 :             : #include "storage/lwlock.h"
      69                 :             : #include "utils/wait_event.h"
      70                 :             : 
      71                 :             : /* Shared state for parallel-aware Append. */
      72                 :             : struct ParallelAppendState
      73                 :             : {
      74                 :             :     LWLock      pa_lock;        /* mutual exclusion to choose next subplan */
      75                 :             :     int         pa_next_plan;   /* next plan to choose by any worker */
      76                 :             : 
      77                 :             :     /*
      78                 :             :      * pa_finished[i] should be true if no more workers should select subplan
      79                 :             :      * i.  for a non-partial plan, this should be set to true as soon as a
      80                 :             :      * worker selects the plan; for a partial plan, it remains false until
      81                 :             :      * some worker executes the plan to completion.
      82                 :             :      */
      83                 :             :     bool        pa_finished[FLEXIBLE_ARRAY_MEMBER];
      84                 :             : };
      85                 :             : 
      86                 :             : #define INVALID_SUBPLAN_INDEX       -1
      87                 :             : #define EVENT_BUFFER_SIZE           16
      88                 :             : 
      89                 :             : static TupleTableSlot *ExecAppend(PlanState *pstate);
      90                 :             : static bool choose_next_subplan_locally(AppendState *node);
      91                 :             : static bool choose_next_subplan_for_leader(AppendState *node);
      92                 :             : static bool choose_next_subplan_for_worker(AppendState *node);
      93                 :             : static void mark_invalid_subplans_as_finished(AppendState *node);
      94                 :             : static void ExecAppendAsyncBegin(AppendState *node);
      95                 :             : static bool ExecAppendAsyncGetNext(AppendState *node, TupleTableSlot **result);
      96                 :             : static bool ExecAppendAsyncRequest(AppendState *node, TupleTableSlot **result);
      97                 :             : static void ExecAppendAsyncEventWait(AppendState *node);
      98                 :             : static void ExecAppendAsyncReset(AppendState *node);
      99                 :             : static void classify_matching_subplans(AppendState *node);
     100                 :             : 
     101                 :             : /* ----------------------------------------------------------------
     102                 :             :  *      ExecInitAppend
     103                 :             :  *
     104                 :             :  *      Begin all of the subscans of the append node.
     105                 :             :  *
     106                 :             :  *     (This is potentially wasteful, since the entire result of the
     107                 :             :  *      append node may not be scanned, but this way all of the
     108                 :             :  *      structures get allocated in the executor's top level memory
     109                 :             :  *      block instead of that of the call to ExecAppend.)
     110                 :             :  * ----------------------------------------------------------------
     111                 :             :  */
     112                 :             : AppendState *
     113                 :       12686 : ExecInitAppend(Append *node, EState *estate, int eflags)
     114                 :             : {
     115                 :       12686 :     AppendState *appendstate = makeNode(AppendState);
     116                 :             :     PlanState **appendplanstates;
     117                 :             :     const TupleTableSlotOps *appendops;
     118                 :             :     Bitmapset  *validsubplans;
     119                 :             :     Bitmapset  *asyncplans;
     120                 :             :     int         nplans;
     121                 :             :     int         nasyncplans;
     122                 :             :     int         firstvalid;
     123                 :             :     int         i,
     124                 :             :                 j;
     125                 :             : 
     126                 :             :     /* check for unsupported flags */
     127                 :             :     Assert(!(eflags & EXEC_FLAG_MARK));
     128                 :             : 
     129                 :             :     /*
     130                 :             :      * create new AppendState for our append node
     131                 :             :      */
     132                 :       12686 :     appendstate->ps.plan = (Plan *) node;
     133                 :       12686 :     appendstate->ps.state = estate;
     134                 :       12686 :     appendstate->ps.ExecProcNode = ExecAppend;
     135                 :             : 
     136                 :             :     /* Let choose_next_subplan_* function handle setting the first subplan */
     137                 :       12686 :     appendstate->as_whichplan = INVALID_SUBPLAN_INDEX;
     138                 :       12686 :     appendstate->as_syncdone = false;
     139                 :       12686 :     appendstate->as_begun = false;
     140                 :             : 
     141                 :             :     /* If run-time partition pruning is enabled, then set that up now */
     142         [ +  + ]:       12686 :     if (node->part_prune_index >= 0)
     143                 :             :     {
     144                 :             :         PartitionPruneState *prunestate;
     145                 :             : 
     146                 :             :         /*
     147                 :             :          * Set up pruning data structure.  This also initializes the set of
     148                 :             :          * subplans to initialize (validsubplans) by taking into account the
     149                 :             :          * result of performing initial pruning if any.
     150                 :             :          */
     151                 :         496 :         prunestate = ExecInitPartitionExecPruning(&appendstate->ps,
     152                 :         496 :                                                   list_length(node->appendplans),
     153                 :             :                                                   node->part_prune_index,
     154                 :             :                                                   node->apprelids,
     155                 :             :                                                   &validsubplans);
     156                 :         496 :         appendstate->as_prune_state = prunestate;
     157                 :         496 :         nplans = bms_num_members(validsubplans);
     158                 :             : 
     159                 :             :         /*
     160                 :             :          * When no run-time pruning is required and there's at least one
     161                 :             :          * subplan, we can fill as_valid_subplans immediately, preventing
     162                 :             :          * later calls to ExecFindMatchingSubPlans.
     163                 :             :          */
     164   [ +  +  +  + ]:         496 :         if (!prunestate->do_exec_prune && nplans > 0)
     165                 :             :         {
     166                 :         182 :             appendstate->as_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
     167                 :         182 :             appendstate->as_valid_subplans_identified = true;
     168                 :             :         }
     169                 :             :     }
     170                 :             :     else
     171                 :             :     {
     172                 :       12190 :         nplans = list_length(node->appendplans);
     173                 :             : 
     174                 :             :         /*
     175                 :             :          * When run-time partition pruning is not enabled we can just mark all
     176                 :             :          * subplans as valid; they must also all be initialized.
     177                 :             :          */
     178                 :             :         Assert(nplans > 0);
     179                 :       12190 :         appendstate->as_valid_subplans = validsubplans =
     180                 :       12190 :             bms_add_range(NULL, 0, nplans - 1);
     181                 :       12190 :         appendstate->as_valid_subplans_identified = true;
     182                 :       12190 :         appendstate->as_prune_state = NULL;
     183                 :             :     }
     184                 :             : 
     185                 :       12686 :     appendplanstates = palloc_array(PlanState *, nplans);
     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                 :       12686 :     j = 0;
     194                 :       12686 :     asyncplans = NULL;
     195                 :       12686 :     nasyncplans = 0;
     196                 :       12686 :     firstvalid = nplans;
     197                 :       12686 :     i = -1;
     198         [ +  + ]:       51792 :     while ((i = bms_next_member(validsubplans, i)) >= 0)
     199                 :             :     {
     200                 :       39106 :         Plan       *initNode = (Plan *) list_nth(node->appendplans, i);
     201                 :             : 
     202                 :             :         /*
     203                 :             :          * Record async subplans.  When executing EvalPlanQual, we treat them
     204                 :             :          * as sync ones; don't do this when initializing an EvalPlanQual plan
     205                 :             :          * tree.
     206                 :             :          */
     207   [ +  +  +  - ]:       39106 :         if (initNode->async_capable && estate->es_epq_active == NULL)
     208                 :             :         {
     209                 :         105 :             asyncplans = bms_add_member(asyncplans, j);
     210                 :         105 :             nasyncplans++;
     211                 :             :         }
     212                 :             : 
     213                 :             :         /*
     214                 :             :          * Record the lowest appendplans index which is a valid partial plan.
     215                 :             :          */
     216   [ +  +  +  + ]:       39106 :         if (i >= node->first_partial_plan && j < firstvalid)
     217                 :         335 :             firstvalid = j;
     218                 :             : 
     219                 :       39106 :         appendplanstates[j++] = ExecInitNode(initNode, estate, eflags);
     220                 :             :     }
     221                 :             : 
     222                 :       12686 :     appendstate->as_first_partial_plan = firstvalid;
     223                 :       12686 :     appendstate->appendplans = appendplanstates;
     224                 :       12686 :     appendstate->as_nplans = nplans;
     225                 :             : 
     226                 :             :     /*
     227                 :             :      * Initialize Append's result tuple type and slot.  If the child plans all
     228                 :             :      * produce the same fixed slot type, we can use that slot type; otherwise
     229                 :             :      * make a virtual slot.  (Note that the result slot itself is used only to
     230                 :             :      * return a null tuple at end of execution; real tuples are returned to
     231                 :             :      * the caller in the children's own result slots.  What we are doing here
     232                 :             :      * is allowing the parent plan node to optimize if the Append will return
     233                 :             :      * only one kind of slot.)
     234                 :             :      */
     235                 :       12686 :     appendops = ExecGetCommonSlotOps(appendplanstates, j);
     236         [ +  + ]:       12686 :     if (appendops != NULL)
     237                 :             :     {
     238                 :       12032 :         ExecInitResultTupleSlotTL(&appendstate->ps, appendops);
     239                 :             :     }
     240                 :             :     else
     241                 :             :     {
     242                 :         654 :         ExecInitResultTupleSlotTL(&appendstate->ps, &TTSOpsVirtual);
     243                 :             :         /* show that the output slot type is not fixed */
     244                 :         654 :         appendstate->ps.resultopsset = true;
     245                 :         654 :         appendstate->ps.resultopsfixed = false;
     246                 :             :     }
     247                 :             : 
     248                 :             :     /* Initialize async state */
     249                 :       12686 :     appendstate->as_asyncplans = asyncplans;
     250                 :       12686 :     appendstate->as_nasyncplans = nasyncplans;
     251                 :       12686 :     appendstate->as_asyncrequests = NULL;
     252                 :       12686 :     appendstate->as_asyncresults = NULL;
     253                 :       12686 :     appendstate->as_nasyncresults = 0;
     254                 :       12686 :     appendstate->as_nasyncremain = 0;
     255                 :       12686 :     appendstate->as_needrequest = NULL;
     256                 :       12686 :     appendstate->as_eventset = NULL;
     257                 :       12686 :     appendstate->as_valid_asyncplans = NULL;
     258                 :             : 
     259         [ +  + ]:       12686 :     if (nasyncplans > 0)
     260                 :             :     {
     261                 :          51 :         appendstate->as_asyncrequests = palloc0_array(AsyncRequest *, nplans);
     262                 :             : 
     263                 :          51 :         i = -1;
     264         [ +  + ]:         156 :         while ((i = bms_next_member(asyncplans, i)) >= 0)
     265                 :             :         {
     266                 :             :             AsyncRequest *areq;
     267                 :             : 
     268                 :         105 :             areq = palloc_object(AsyncRequest);
     269                 :         105 :             areq->requestor = (PlanState *) appendstate;
     270                 :         105 :             areq->requestee = appendplanstates[i];
     271                 :         105 :             areq->request_index = i;
     272                 :         105 :             areq->callback_pending = false;
     273                 :         105 :             areq->request_complete = false;
     274                 :         105 :             areq->result = NULL;
     275                 :             : 
     276                 :         105 :             appendstate->as_asyncrequests[i] = areq;
     277                 :             :         }
     278                 :             : 
     279                 :          51 :         appendstate->as_asyncresults = palloc0_array(TupleTableSlot *, nasyncplans);
     280                 :             : 
     281         [ +  + ]:          51 :         if (appendstate->as_valid_subplans_identified)
     282                 :          44 :             classify_matching_subplans(appendstate);
     283                 :             :     }
     284                 :             : 
     285                 :             :     /*
     286                 :             :      * Miscellaneous initialization
     287                 :             :      */
     288                 :             : 
     289                 :       12686 :     appendstate->ps.ps_ProjInfo = NULL;
     290                 :             : 
     291                 :             :     /* For parallel query, this will be overridden later. */
     292                 :       12686 :     appendstate->choose_next_subplan = choose_next_subplan_locally;
     293                 :             : 
     294                 :       12686 :     return appendstate;
     295                 :             : }
     296                 :             : 
     297                 :             : /* ----------------------------------------------------------------
     298                 :             :  *     ExecAppend
     299                 :             :  *
     300                 :             :  *      Handles iteration over multiple subplans.
     301                 :             :  * ----------------------------------------------------------------
     302                 :             :  */
     303                 :             : static TupleTableSlot *
     304                 :     1697210 : ExecAppend(PlanState *pstate)
     305                 :             : {
     306                 :     1697210 :     AppendState *node = castNode(AppendState, pstate);
     307                 :             :     TupleTableSlot *result;
     308                 :             : 
     309                 :             :     /*
     310                 :             :      * If this is the first call after Init or ReScan, we need to do the
     311                 :             :      * initialization work.
     312                 :             :      */
     313         [ +  + ]:     1697210 :     if (!node->as_begun)
     314                 :             :     {
     315                 :             :         Assert(node->as_whichplan == INVALID_SUBPLAN_INDEX);
     316                 :             :         Assert(!node->as_syncdone);
     317                 :             : 
     318                 :             :         /* Nothing to do if there are no subplans */
     319         [ +  + ]:       22236 :         if (node->as_nplans == 0)
     320                 :          39 :             return ExecClearTuple(node->ps.ps_ResultTupleSlot);
     321                 :             : 
     322                 :             :         /* If there are any async subplans, begin executing them. */
     323         [ +  + ]:       22197 :         if (node->as_nasyncplans > 0)
     324                 :          41 :             ExecAppendAsyncBegin(node);
     325                 :             : 
     326                 :             :         /*
     327                 :             :          * If no sync subplan has been chosen, we must choose one before
     328                 :             :          * proceeding.
     329                 :             :          */
     330   [ +  +  +  + ]:       22197 :         if (!node->choose_next_subplan(node) && node->as_nasyncremain == 0)
     331                 :        2154 :             return ExecClearTuple(node->ps.ps_ResultTupleSlot);
     332                 :             : 
     333                 :             :         Assert(node->as_syncdone ||
     334                 :             :                (node->as_whichplan >= 0 &&
     335                 :             :                 node->as_whichplan < node->as_nplans));
     336                 :             : 
     337                 :             :         /* And we're initialized. */
     338                 :       20043 :         node->as_begun = true;
     339                 :             :     }
     340                 :             : 
     341                 :             :     for (;;)
     342                 :       29416 :     {
     343                 :             :         PlanState  *subnode;
     344                 :             : 
     345         [ +  + ]:     1724433 :         CHECK_FOR_INTERRUPTS();
     346                 :             : 
     347                 :             :         /*
     348                 :             :          * try to get a tuple from an async subplan if any
     349                 :             :          */
     350   [ +  +  -  + ]:     1724433 :         if (node->as_syncdone || !bms_is_empty(node->as_needrequest))
     351                 :             :         {
     352         [ +  - ]:        6142 :             if (ExecAppendAsyncGetNext(node, &result))
     353                 :        6141 :                 return result;
     354                 :             :             Assert(!node->as_syncdone);
     355                 :             :             Assert(bms_is_empty(node->as_needrequest));
     356                 :             :         }
     357                 :             : 
     358                 :             :         /*
     359                 :             :          * figure out which sync subplan we are currently processing
     360                 :             :          */
     361                 :             :         Assert(node->as_whichplan >= 0 && node->as_whichplan < node->as_nplans);
     362                 :     1718291 :         subnode = node->appendplans[node->as_whichplan];
     363                 :             : 
     364                 :             :         /*
     365                 :             :          * get a tuple from the subplan
     366                 :             :          */
     367                 :     1718291 :         result = ExecProcNode(subnode);
     368                 :             : 
     369   [ +  +  +  + ]:     1718257 :         if (!TupIsNull(result))
     370                 :             :         {
     371                 :             :             /*
     372                 :             :              * If the subplan gave us something then return it as-is. We do
     373                 :             :              * NOT make use of the result slot that was set up in
     374                 :             :              * ExecInitAppend; there's no need for it.
     375                 :             :              */
     376                 :     1669220 :             return result;
     377                 :             :         }
     378                 :             : 
     379                 :             :         /*
     380                 :             :          * wait or poll for async events if any. We do this before checking
     381                 :             :          * for the end of iteration, because it might drain the remaining
     382                 :             :          * async subplans.
     383                 :             :          */
     384         [ +  + ]:       49037 :         if (node->as_nasyncremain > 0)
     385                 :          17 :             ExecAppendAsyncEventWait(node);
     386                 :             : 
     387                 :             :         /* choose new sync subplan; if no sync/async subplans, we're done */
     388   [ +  +  +  + ]:       49037 :         if (!node->choose_next_subplan(node) && node->as_nasyncremain == 0)
     389                 :       19621 :             return ExecClearTuple(node->ps.ps_ResultTupleSlot);
     390                 :             :     }
     391                 :             : }
     392                 :             : 
     393                 :             : /* ----------------------------------------------------------------
     394                 :             :  *      ExecEndAppend
     395                 :             :  *
     396                 :             :  *      Shuts down the subscans of the append node.
     397                 :             :  *
     398                 :             :  *      Returns nothing of interest.
     399                 :             :  * ----------------------------------------------------------------
     400                 :             :  */
     401                 :             : void
     402                 :       12485 : ExecEndAppend(AppendState *node)
     403                 :             : {
     404                 :             :     PlanState **appendplans;
     405                 :             :     int         nplans;
     406                 :             :     int         i;
     407                 :             : 
     408                 :             :     /*
     409                 :             :      * get information from the node
     410                 :             :      */
     411                 :       12485 :     appendplans = node->appendplans;
     412                 :       12485 :     nplans = node->as_nplans;
     413                 :             : 
     414                 :             :     /*
     415                 :             :      * shut down each of the subscans
     416                 :             :      */
     417         [ +  + ]:       51111 :     for (i = 0; i < nplans; i++)
     418                 :       38626 :         ExecEndNode(appendplans[i]);
     419                 :       12485 : }
     420                 :             : 
     421                 :             : void
     422                 :       13412 : ExecReScanAppend(AppendState *node)
     423                 :             : {
     424                 :       13412 :     int         nasyncplans = node->as_nasyncplans;
     425                 :             :     int         i;
     426                 :             : 
     427                 :             :     /* If there are any async subplans, reset async requests made for them. */
     428         [ +  + ]:       13412 :     if (nasyncplans > 0)
     429                 :          21 :         ExecAppendAsyncReset(node);
     430                 :             : 
     431                 :             :     /*
     432                 :             :      * If any PARAM_EXEC Params used in pruning expressions have changed, then
     433                 :             :      * we'd better unset the valid subplans so that they are reselected for
     434                 :             :      * the new parameter values.
     435                 :             :      */
     436   [ +  +  +  + ]:       15596 :     if (node->as_prune_state &&
     437                 :        2184 :         bms_overlap(node->ps.chgParam,
     438                 :        2184 :                     node->as_prune_state->execparamids))
     439                 :             :     {
     440                 :        2182 :         node->as_valid_subplans_identified = false;
     441                 :        2182 :         bms_free(node->as_valid_subplans);
     442                 :        2182 :         node->as_valid_subplans = NULL;
     443                 :        2182 :         bms_free(node->as_valid_asyncplans);
     444                 :        2182 :         node->as_valid_asyncplans = NULL;
     445                 :             :     }
     446                 :             : 
     447         [ +  + ]:       55665 :     for (i = 0; i < node->as_nplans; i++)
     448                 :             :     {
     449                 :       42253 :         PlanState  *subnode = node->appendplans[i];
     450                 :             : 
     451                 :             :         /*
     452                 :             :          * ExecReScan doesn't know about my subplans, so I have to do
     453                 :             :          * changed-parameter signaling myself.
     454                 :             :          */
     455         [ +  + ]:       42253 :         if (node->ps.chgParam != NULL)
     456                 :       37339 :             UpdateChangedParamSet(subnode, node->ps.chgParam);
     457                 :             : 
     458                 :             :         /*
     459                 :             :          * If chgParam of subnode is not null then plan will be re-scanned by
     460                 :             :          * first ExecProcNode or by first ExecAsyncRequest.
     461                 :             :          */
     462         [ +  + ]:       42253 :         if (subnode->chgParam == NULL)
     463                 :       12514 :             ExecReScan(subnode);
     464                 :             :     }
     465                 :             : 
     466                 :             :     /* Let choose_next_subplan_* function handle setting the first subplan */
     467                 :       13412 :     node->as_whichplan = INVALID_SUBPLAN_INDEX;
     468                 :       13412 :     node->as_syncdone = false;
     469                 :       13412 :     node->as_begun = false;
     470                 :       13412 : }
     471                 :             : 
     472                 :             : /* ----------------------------------------------------------------
     473                 :             :  *                      Parallel Append Support
     474                 :             :  * ----------------------------------------------------------------
     475                 :             :  */
     476                 :             : 
     477                 :             : /* ----------------------------------------------------------------
     478                 :             :  *      ExecAppendEstimate
     479                 :             :  *
     480                 :             :  *      Compute the amount of space we'll need in the parallel
     481                 :             :  *      query DSM, and inform pcxt->estimator about our needs.
     482                 :             :  * ----------------------------------------------------------------
     483                 :             :  */
     484                 :             : void
     485                 :         108 : ExecAppendEstimate(AppendState *node,
     486                 :             :                    ParallelContext *pcxt)
     487                 :             : {
     488                 :         108 :     node->pstate_len =
     489                 :         108 :         add_size(offsetof(ParallelAppendState, pa_finished),
     490                 :         108 :                  sizeof(bool) * node->as_nplans);
     491                 :             : 
     492                 :         108 :     shm_toc_estimate_chunk(&pcxt->estimator, node->pstate_len);
     493                 :         108 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     494                 :         108 : }
     495                 :             : 
     496                 :             : 
     497                 :             : /* ----------------------------------------------------------------
     498                 :             :  *      ExecAppendInitializeDSM
     499                 :             :  *
     500                 :             :  *      Set up shared state for Parallel Append.
     501                 :             :  * ----------------------------------------------------------------
     502                 :             :  */
     503                 :             : void
     504                 :         108 : ExecAppendInitializeDSM(AppendState *node,
     505                 :             :                         ParallelContext *pcxt)
     506                 :             : {
     507                 :             :     ParallelAppendState *pstate;
     508                 :             : 
     509                 :         108 :     pstate = shm_toc_allocate(pcxt->toc, node->pstate_len);
     510                 :         108 :     memset(pstate, 0, node->pstate_len);
     511                 :         108 :     LWLockInitialize(&pstate->pa_lock, LWTRANCHE_PARALLEL_APPEND);
     512                 :         108 :     shm_toc_insert(pcxt->toc, node->ps.plan->plan_node_id, pstate);
     513                 :             : 
     514                 :         108 :     node->as_pstate = pstate;
     515                 :         108 :     node->choose_next_subplan = choose_next_subplan_for_leader;
     516                 :         108 : }
     517                 :             : 
     518                 :             : /* ----------------------------------------------------------------
     519                 :             :  *      ExecAppendReInitializeDSM
     520                 :             :  *
     521                 :             :  *      Reset shared state before beginning a fresh scan.
     522                 :             :  * ----------------------------------------------------------------
     523                 :             :  */
     524                 :             : void
     525                 :           0 : ExecAppendReInitializeDSM(AppendState *node, ParallelContext *pcxt)
     526                 :             : {
     527                 :           0 :     ParallelAppendState *pstate = node->as_pstate;
     528                 :             : 
     529                 :           0 :     pstate->pa_next_plan = 0;
     530                 :           0 :     memset(pstate->pa_finished, 0, sizeof(bool) * node->as_nplans);
     531                 :           0 : }
     532                 :             : 
     533                 :             : /* ----------------------------------------------------------------
     534                 :             :  *      ExecAppendInitializeWorker
     535                 :             :  *
     536                 :             :  *      Copy relevant information from TOC into planstate, and initialize
     537                 :             :  *      whatever is required to choose and execute the optimal subplan.
     538                 :             :  * ----------------------------------------------------------------
     539                 :             :  */
     540                 :             : void
     541                 :         242 : ExecAppendInitializeWorker(AppendState *node, ParallelWorkerContext *pwcxt)
     542                 :             : {
     543                 :         242 :     node->as_pstate = shm_toc_lookup(pwcxt->toc, node->ps.plan->plan_node_id, false);
     544                 :         242 :     node->choose_next_subplan = choose_next_subplan_for_worker;
     545                 :         242 : }
     546                 :             : 
     547                 :             : /* ----------------------------------------------------------------
     548                 :             :  *      choose_next_subplan_locally
     549                 :             :  *
     550                 :             :  *      Choose next sync subplan for a non-parallel-aware Append,
     551                 :             :  *      returning false if there are no more.
     552                 :             :  * ----------------------------------------------------------------
     553                 :             :  */
     554                 :             : static bool
     555                 :       69946 : choose_next_subplan_locally(AppendState *node)
     556                 :             : {
     557                 :       69946 :     int         whichplan = node->as_whichplan;
     558                 :             :     int         nextplan;
     559                 :             : 
     560                 :             :     /* We should never be called when there are no subplans */
     561                 :             :     Assert(node->as_nplans > 0);
     562                 :             : 
     563                 :             :     /* Nothing to do if syncdone */
     564         [ +  + ]:       69946 :     if (node->as_syncdone)
     565                 :          22 :         return false;
     566                 :             : 
     567                 :             :     /*
     568                 :             :      * If first call then have the bms member function choose the first valid
     569                 :             :      * sync subplan by initializing whichplan to -1.  If there happen to be no
     570                 :             :      * valid sync subplans then the bms member function will handle that by
     571                 :             :      * returning a negative number which will allow us to exit returning a
     572                 :             :      * false value.
     573                 :             :      */
     574         [ +  + ]:       69924 :     if (whichplan == INVALID_SUBPLAN_INDEX)
     575                 :             :     {
     576         [ +  + ]:       21860 :         if (node->as_nasyncplans > 0)
     577                 :             :         {
     578                 :             :             /* We'd have filled as_valid_subplans already */
     579                 :             :             Assert(node->as_valid_subplans_identified);
     580                 :             :         }
     581         [ +  + ]:       21841 :         else if (!node->as_valid_subplans_identified)
     582                 :             :         {
     583                 :        2255 :             node->as_valid_subplans =
     584                 :        2255 :                 ExecFindMatchingSubPlans(node->as_prune_state, false, NULL);
     585                 :        2255 :             node->as_valid_subplans_identified = true;
     586                 :             :         }
     587                 :             : 
     588                 :       21860 :         whichplan = -1;
     589                 :             :     }
     590                 :             : 
     591                 :             :     /* Ensure whichplan is within the expected range */
     592                 :             :     Assert(whichplan >= -1 && whichplan <= node->as_nplans);
     593                 :             : 
     594         [ +  + ]:       69924 :     if (ScanDirectionIsForward(node->ps.state->es_direction))
     595                 :       69912 :         nextplan = bms_next_member(node->as_valid_subplans, whichplan);
     596                 :             :     else
     597                 :          12 :         nextplan = bms_prev_member(node->as_valid_subplans, whichplan);
     598                 :             : 
     599         [ +  + ]:       69924 :     if (nextplan < 0)
     600                 :             :     {
     601                 :             :         /* Set as_syncdone if in async mode */
     602         [ +  + ]:       21477 :         if (node->as_nasyncplans > 0)
     603                 :          17 :             node->as_syncdone = true;
     604                 :       21477 :         return false;
     605                 :             :     }
     606                 :             : 
     607                 :       48447 :     node->as_whichplan = nextplan;
     608                 :             : 
     609                 :       48447 :     return true;
     610                 :             : }
     611                 :             : 
     612                 :             : /* ----------------------------------------------------------------
     613                 :             :  *      choose_next_subplan_for_leader
     614                 :             :  *
     615                 :             :  *      Try to pick a plan which doesn't commit us to doing much
     616                 :             :  *      work locally, so that as much work as possible is done in
     617                 :             :  *      the workers.  Cheapest subplans are at the end.
     618                 :             :  * ----------------------------------------------------------------
     619                 :             :  */
     620                 :             : static bool
     621                 :         953 : choose_next_subplan_for_leader(AppendState *node)
     622                 :             : {
     623                 :         953 :     ParallelAppendState *pstate = node->as_pstate;
     624                 :             : 
     625                 :             :     /* Backward scan is not supported by parallel-aware plans */
     626                 :             :     Assert(ScanDirectionIsForward(node->ps.state->es_direction));
     627                 :             : 
     628                 :             :     /* We should never be called when there are no subplans */
     629                 :             :     Assert(node->as_nplans > 0);
     630                 :             : 
     631                 :         953 :     LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
     632                 :             : 
     633         [ +  + ]:         953 :     if (node->as_whichplan != INVALID_SUBPLAN_INDEX)
     634                 :             :     {
     635                 :             :         /* Mark just-completed subplan as finished. */
     636                 :         857 :         node->as_pstate->pa_finished[node->as_whichplan] = true;
     637                 :             :     }
     638                 :             :     else
     639                 :             :     {
     640                 :             :         /* Start with last subplan. */
     641                 :          96 :         node->as_whichplan = node->as_nplans - 1;
     642                 :             : 
     643                 :             :         /*
     644                 :             :          * If we've yet to determine the valid subplans then do so now.  If
     645                 :             :          * run-time pruning is disabled then the valid subplans will always be
     646                 :             :          * set to all subplans.
     647                 :             :          */
     648         [ +  + ]:          96 :         if (!node->as_valid_subplans_identified)
     649                 :             :         {
     650                 :          16 :             node->as_valid_subplans =
     651                 :          16 :                 ExecFindMatchingSubPlans(node->as_prune_state, false, NULL);
     652                 :          16 :             node->as_valid_subplans_identified = true;
     653                 :             : 
     654                 :             :             /*
     655                 :             :              * Mark each invalid plan as finished to allow the loop below to
     656                 :             :              * select the first valid subplan.
     657                 :             :              */
     658                 :          16 :             mark_invalid_subplans_as_finished(node);
     659                 :             :         }
     660                 :             :     }
     661                 :             : 
     662                 :             :     /* Loop until we find a subplan to execute. */
     663         [ +  + ]:        1753 :     while (pstate->pa_finished[node->as_whichplan])
     664                 :             :     {
     665         [ +  + ]:         896 :         if (node->as_whichplan == 0)
     666                 :             :         {
     667                 :          96 :             pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
     668                 :          96 :             node->as_whichplan = INVALID_SUBPLAN_INDEX;
     669                 :          96 :             LWLockRelease(&pstate->pa_lock);
     670                 :          96 :             return false;
     671                 :             :         }
     672                 :             : 
     673                 :             :         /*
     674                 :             :          * We needn't pay attention to as_valid_subplans here as all invalid
     675                 :             :          * plans have been marked as finished.
     676                 :             :          */
     677                 :         800 :         node->as_whichplan--;
     678                 :             :     }
     679                 :             : 
     680                 :             :     /* If non-partial, immediately mark as finished. */
     681         [ +  + ]:         857 :     if (node->as_whichplan < node->as_first_partial_plan)
     682                 :         119 :         node->as_pstate->pa_finished[node->as_whichplan] = true;
     683                 :             : 
     684                 :         857 :     LWLockRelease(&pstate->pa_lock);
     685                 :             : 
     686                 :         857 :     return true;
     687                 :             : }
     688                 :             : 
     689                 :             : /* ----------------------------------------------------------------
     690                 :             :  *      choose_next_subplan_for_worker
     691                 :             :  *
     692                 :             :  *      Choose next subplan for a parallel-aware Append, returning
     693                 :             :  *      false if there are no more.
     694                 :             :  *
     695                 :             :  *      We start from the first plan and advance through the list;
     696                 :             :  *      when we get back to the end, we loop back to the first
     697                 :             :  *      partial plan.  This assigns the non-partial plans first in
     698                 :             :  *      order of descending cost and then spreads out the workers
     699                 :             :  *      as evenly as possible across the remaining partial plans.
     700                 :             :  * ----------------------------------------------------------------
     701                 :             :  */
     702                 :             : static bool
     703                 :         335 : choose_next_subplan_for_worker(AppendState *node)
     704                 :             : {
     705                 :         335 :     ParallelAppendState *pstate = node->as_pstate;
     706                 :             : 
     707                 :             :     /* Backward scan is not supported by parallel-aware plans */
     708                 :             :     Assert(ScanDirectionIsForward(node->ps.state->es_direction));
     709                 :             : 
     710                 :             :     /* We should never be called when there are no subplans */
     711                 :             :     Assert(node->as_nplans > 0);
     712                 :             : 
     713                 :         335 :     LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
     714                 :             : 
     715                 :             :     /* Mark just-completed subplan as finished. */
     716         [ +  + ]:         335 :     if (node->as_whichplan != INVALID_SUBPLAN_INDEX)
     717                 :         116 :         node->as_pstate->pa_finished[node->as_whichplan] = true;
     718                 :             : 
     719                 :             :     /*
     720                 :             :      * If we've yet to determine the valid subplans then do so now.  If
     721                 :             :      * run-time pruning is disabled then the valid subplans will always be set
     722                 :             :      * to all subplans.
     723                 :             :      */
     724         [ +  + ]:         219 :     else if (!node->as_valid_subplans_identified)
     725                 :             :     {
     726                 :          15 :         node->as_valid_subplans =
     727                 :          15 :             ExecFindMatchingSubPlans(node->as_prune_state, false, NULL);
     728                 :          15 :         node->as_valid_subplans_identified = true;
     729                 :             : 
     730                 :          15 :         mark_invalid_subplans_as_finished(node);
     731                 :             :     }
     732                 :             : 
     733                 :             :     /* If all the plans are already done, we have nothing to do */
     734         [ +  + ]:         335 :     if (pstate->pa_next_plan == INVALID_SUBPLAN_INDEX)
     735                 :             :     {
     736                 :         194 :         LWLockRelease(&pstate->pa_lock);
     737                 :         194 :         return false;
     738                 :             :     }
     739                 :             : 
     740                 :             :     /* Save the plan from which we are starting the search. */
     741                 :         141 :     node->as_whichplan = pstate->pa_next_plan;
     742                 :             : 
     743                 :             :     /* Loop until we find a valid subplan to execute. */
     744         [ +  + ]:         255 :     while (pstate->pa_finished[pstate->pa_next_plan])
     745                 :             :     {
     746                 :             :         int         nextplan;
     747                 :             : 
     748                 :         139 :         nextplan = bms_next_member(node->as_valid_subplans,
     749                 :             :                                    pstate->pa_next_plan);
     750         [ +  + ]:         139 :         if (nextplan >= 0)
     751                 :             :         {
     752                 :             :             /* Advance to the next valid plan. */
     753                 :         100 :             pstate->pa_next_plan = nextplan;
     754                 :             :         }
     755         [ +  + ]:          39 :         else if (node->as_whichplan > node->as_first_partial_plan)
     756                 :             :         {
     757                 :             :             /*
     758                 :             :              * Try looping back to the first valid partial plan, if there is
     759                 :             :              * one.  If there isn't, arrange to bail out below.
     760                 :             :              */
     761                 :          22 :             nextplan = bms_next_member(node->as_valid_subplans,
     762                 :          22 :                                        node->as_first_partial_plan - 1);
     763                 :          22 :             pstate->pa_next_plan =
     764         [ -  + ]:          22 :                 nextplan < 0 ? node->as_whichplan : nextplan;
     765                 :             :         }
     766                 :             :         else
     767                 :             :         {
     768                 :             :             /*
     769                 :             :              * At last plan, and either there are no partial plans or we've
     770                 :             :              * tried them all.  Arrange to bail out.
     771                 :             :              */
     772                 :          17 :             pstate->pa_next_plan = node->as_whichplan;
     773                 :             :         }
     774                 :             : 
     775         [ +  + ]:         139 :         if (pstate->pa_next_plan == node->as_whichplan)
     776                 :             :         {
     777                 :             :             /* We've tried everything! */
     778                 :          25 :             pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
     779                 :          25 :             LWLockRelease(&pstate->pa_lock);
     780                 :          25 :             return false;
     781                 :             :         }
     782                 :             :     }
     783                 :             : 
     784                 :             :     /* Pick the plan we found, and advance pa_next_plan one more time. */
     785                 :         116 :     node->as_whichplan = pstate->pa_next_plan;
     786                 :         116 :     pstate->pa_next_plan = bms_next_member(node->as_valid_subplans,
     787                 :             :                                            pstate->pa_next_plan);
     788                 :             : 
     789                 :             :     /*
     790                 :             :      * If there are no more valid plans then try setting the next plan to the
     791                 :             :      * first valid partial plan.
     792                 :             :      */
     793         [ +  + ]:         116 :     if (pstate->pa_next_plan < 0)
     794                 :             :     {
     795                 :          20 :         int         nextplan = bms_next_member(node->as_valid_subplans,
     796                 :          20 :                                                node->as_first_partial_plan - 1);
     797                 :             : 
     798         [ +  - ]:          20 :         if (nextplan >= 0)
     799                 :          20 :             pstate->pa_next_plan = nextplan;
     800                 :             :         else
     801                 :             :         {
     802                 :             :             /*
     803                 :             :              * There are no valid partial plans, and we already chose the last
     804                 :             :              * non-partial plan; so flag that there's nothing more for our
     805                 :             :              * fellow workers to do.
     806                 :             :              */
     807                 :           0 :             pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
     808                 :             :         }
     809                 :             :     }
     810                 :             : 
     811                 :             :     /* If non-partial, immediately mark as finished. */
     812         [ +  + ]:         116 :     if (node->as_whichplan < node->as_first_partial_plan)
     813                 :          21 :         node->as_pstate->pa_finished[node->as_whichplan] = true;
     814                 :             : 
     815                 :         116 :     LWLockRelease(&pstate->pa_lock);
     816                 :             : 
     817                 :         116 :     return true;
     818                 :             : }
     819                 :             : 
     820                 :             : /*
     821                 :             :  * mark_invalid_subplans_as_finished
     822                 :             :  *      Marks the ParallelAppendState's pa_finished as true for each invalid
     823                 :             :  *      subplan.
     824                 :             :  *
     825                 :             :  * This function should only be called for parallel Append with run-time
     826                 :             :  * pruning enabled.
     827                 :             :  */
     828                 :             : static void
     829                 :          31 : mark_invalid_subplans_as_finished(AppendState *node)
     830                 :             : {
     831                 :             :     int         i;
     832                 :             : 
     833                 :             :     /* Only valid to call this while in parallel Append mode */
     834                 :             :     Assert(node->as_pstate);
     835                 :             : 
     836                 :             :     /* Shouldn't have been called when run-time pruning is not enabled */
     837                 :             :     Assert(node->as_prune_state);
     838                 :             : 
     839                 :             :     /* Nothing to do if all plans are valid */
     840         [ -  + ]:          31 :     if (bms_num_members(node->as_valid_subplans) == node->as_nplans)
     841                 :           0 :         return;
     842                 :             : 
     843                 :             :     /* Mark all non-valid plans as finished */
     844         [ +  + ]:         104 :     for (i = 0; i < node->as_nplans; i++)
     845                 :             :     {
     846         [ +  + ]:          73 :         if (!bms_is_member(i, node->as_valid_subplans))
     847                 :          31 :             node->as_pstate->pa_finished[i] = true;
     848                 :             :     }
     849                 :             : }
     850                 :             : 
     851                 :             : /* ----------------------------------------------------------------
     852                 :             :  *                      Asynchronous Append Support
     853                 :             :  * ----------------------------------------------------------------
     854                 :             :  */
     855                 :             : 
     856                 :             : /* ----------------------------------------------------------------
     857                 :             :  *      ExecAppendAsyncBegin
     858                 :             :  *
     859                 :             :  *      Begin executing designed async-capable subplans.
     860                 :             :  * ----------------------------------------------------------------
     861                 :             :  */
     862                 :             : static void
     863                 :          41 : ExecAppendAsyncBegin(AppendState *node)
     864                 :             : {
     865                 :             :     int         i;
     866                 :             : 
     867                 :             :     /* Backward scan is not supported by async-aware Appends. */
     868                 :             :     Assert(ScanDirectionIsForward(node->ps.state->es_direction));
     869                 :             : 
     870                 :             :     /* We should never be called when there are no subplans */
     871                 :             :     Assert(node->as_nplans > 0);
     872                 :             : 
     873                 :             :     /* We should never be called when there are no async subplans. */
     874                 :             :     Assert(node->as_nasyncplans > 0);
     875                 :             : 
     876                 :             :     /* If we've yet to determine the valid subplans then do so now. */
     877         [ +  + ]:          41 :     if (!node->as_valid_subplans_identified)
     878                 :             :     {
     879                 :           6 :         node->as_valid_subplans =
     880                 :           6 :             ExecFindMatchingSubPlans(node->as_prune_state, false, NULL);
     881                 :           6 :         node->as_valid_subplans_identified = true;
     882                 :             : 
     883                 :           6 :         classify_matching_subplans(node);
     884                 :             :     }
     885                 :             : 
     886                 :             :     /* Initialize state variables. */
     887                 :          41 :     node->as_syncdone = bms_is_empty(node->as_valid_subplans);
     888                 :          41 :     node->as_nasyncremain = bms_num_members(node->as_valid_asyncplans);
     889                 :             : 
     890                 :             :     /* Nothing to do if there are no valid async subplans. */
     891         [ -  + ]:          41 :     if (node->as_nasyncremain == 0)
     892                 :           0 :         return;
     893                 :             : 
     894                 :             :     /* Make a request for each of the valid async subplans. */
     895                 :          41 :     i = -1;
     896         [ +  + ]:         121 :     while ((i = bms_next_member(node->as_valid_asyncplans, i)) >= 0)
     897                 :             :     {
     898                 :          80 :         AsyncRequest *areq = node->as_asyncrequests[i];
     899                 :             : 
     900                 :             :         Assert(areq->request_index == i);
     901                 :             :         Assert(!areq->callback_pending);
     902                 :             : 
     903                 :             :         /* Do the actual work. */
     904                 :          80 :         ExecAsyncRequest(areq);
     905                 :             :     }
     906                 :             : }
     907                 :             : 
     908                 :             : /* ----------------------------------------------------------------
     909                 :             :  *      ExecAppendAsyncGetNext
     910                 :             :  *
     911                 :             :  *      Get the next tuple from any of the asynchronous subplans.
     912                 :             :  * ----------------------------------------------------------------
     913                 :             :  */
     914                 :             : static bool
     915                 :        6142 : ExecAppendAsyncGetNext(AppendState *node, TupleTableSlot **result)
     916                 :             : {
     917                 :        6142 :     *result = NULL;
     918                 :             : 
     919                 :             :     /* We should never be called when there are no valid async subplans. */
     920                 :             :     Assert(node->as_nasyncremain > 0);
     921                 :             : 
     922                 :             :     /* Request a tuple asynchronously. */
     923         [ +  + ]:        6142 :     if (ExecAppendAsyncRequest(node, result))
     924                 :        6039 :         return true;
     925                 :             : 
     926         [ +  + ]:         149 :     while (node->as_nasyncremain > 0)
     927                 :             :     {
     928         [ -  + ]:         118 :         CHECK_FOR_INTERRUPTS();
     929                 :             : 
     930                 :             :         /* Wait or poll for async events. */
     931                 :         118 :         ExecAppendAsyncEventWait(node);
     932                 :             : 
     933                 :             :         /* Request a tuple asynchronously. */
     934         [ +  + ]:         117 :         if (ExecAppendAsyncRequest(node, result))
     935                 :          71 :             return true;
     936                 :             : 
     937                 :             :         /* Break from loop if there's any sync subplan that isn't complete. */
     938         [ -  + ]:          46 :         if (!node->as_syncdone)
     939                 :           0 :             break;
     940                 :             :     }
     941                 :             : 
     942                 :             :     /*
     943                 :             :      * If all sync subplans are complete, we're totally done scanning the
     944                 :             :      * given node.  Otherwise, we're done with the asynchronous stuff but must
     945                 :             :      * continue scanning the sync subplans.
     946                 :             :      */
     947         [ +  - ]:          31 :     if (node->as_syncdone)
     948                 :             :     {
     949                 :             :         Assert(node->as_nasyncremain == 0);
     950                 :          31 :         *result = ExecClearTuple(node->ps.ps_ResultTupleSlot);
     951                 :          31 :         return true;
     952                 :             :     }
     953                 :             : 
     954                 :           0 :     return false;
     955                 :             : }
     956                 :             : 
     957                 :             : /* ----------------------------------------------------------------
     958                 :             :  *      ExecAppendAsyncRequest
     959                 :             :  *
     960                 :             :  *      Request a tuple asynchronously.
     961                 :             :  * ----------------------------------------------------------------
     962                 :             :  */
     963                 :             : static bool
     964                 :        6259 : ExecAppendAsyncRequest(AppendState *node, TupleTableSlot **result)
     965                 :             : {
     966                 :             :     Bitmapset  *needrequest;
     967                 :             :     int         i;
     968                 :             : 
     969                 :             :     /* Nothing to do if there are no async subplans needing a new request. */
     970         [ +  + ]:        6259 :     if (bms_is_empty(node->as_needrequest))
     971                 :             :     {
     972                 :             :         Assert(node->as_nasyncresults == 0);
     973                 :          72 :         return false;
     974                 :             :     }
     975                 :             : 
     976                 :             :     /*
     977                 :             :      * If there are any asynchronously-generated results that have not yet
     978                 :             :      * been returned, we have nothing to do; just return one of them.
     979                 :             :      */
     980         [ +  + ]:        6187 :     if (node->as_nasyncresults > 0)
     981                 :             :     {
     982                 :        1185 :         --node->as_nasyncresults;
     983                 :        1185 :         *result = node->as_asyncresults[node->as_nasyncresults];
     984                 :        1185 :         return true;
     985                 :             :     }
     986                 :             : 
     987                 :             :     /* Make a new request for each of the async subplans that need it. */
     988                 :        5002 :     needrequest = node->as_needrequest;
     989                 :        5002 :     node->as_needrequest = NULL;
     990                 :        5002 :     i = -1;
     991         [ +  + ]:       11105 :     while ((i = bms_next_member(needrequest, i)) >= 0)
     992                 :             :     {
     993                 :        6103 :         AsyncRequest *areq = node->as_asyncrequests[i];
     994                 :             : 
     995                 :             :         /* Do the actual work. */
     996                 :        6103 :         ExecAsyncRequest(areq);
     997                 :             :     }
     998                 :        5002 :     bms_free(needrequest);
     999                 :             : 
    1000                 :             :     /* Return one of the asynchronously-generated results if any. */
    1001         [ +  + ]:        5002 :     if (node->as_nasyncresults > 0)
    1002                 :             :     {
    1003                 :        4925 :         --node->as_nasyncresults;
    1004                 :        4925 :         *result = node->as_asyncresults[node->as_nasyncresults];
    1005                 :        4925 :         return true;
    1006                 :             :     }
    1007                 :             : 
    1008                 :          77 :     return false;
    1009                 :             : }
    1010                 :             : 
    1011                 :             : /* ----------------------------------------------------------------
    1012                 :             :  *      ExecAppendAsyncEventWait
    1013                 :             :  *
    1014                 :             :  *      Wait or poll for file descriptor events and fire callbacks.
    1015                 :             :  * ----------------------------------------------------------------
    1016                 :             :  */
    1017                 :             : static void
    1018                 :         138 : ExecAppendAsyncEventWait(AppendState *node)
    1019                 :             : {
    1020                 :         138 :     int         nevents = node->as_nasyncplans + 2;
    1021         [ +  + ]:         138 :     long        timeout = node->as_syncdone ? -1 : 0;
    1022                 :             :     WaitEvent   occurred_event[EVENT_BUFFER_SIZE];
    1023                 :             :     int         noccurred;
    1024                 :             :     int         i;
    1025                 :             : 
    1026                 :             :     /* We should never be called when there are no valid async subplans. */
    1027                 :             :     Assert(node->as_nasyncremain > 0);
    1028                 :             : 
    1029                 :             :     Assert(node->as_eventset == NULL);
    1030                 :         138 :     node->as_eventset = CreateWaitEventSet(CurrentResourceOwner, nevents);
    1031                 :         138 :     AddWaitEventToSet(node->as_eventset, WL_EXIT_ON_PM_DEATH, PGINVALID_SOCKET,
    1032                 :             :                       NULL, NULL);
    1033                 :             : 
    1034                 :             :     /* Give each waiting subplan a chance to add an event. */
    1035                 :         138 :     i = -1;
    1036         [ +  + ]:         429 :     while ((i = bms_next_member(node->as_asyncplans, i)) >= 0)
    1037                 :             :     {
    1038                 :         292 :         AsyncRequest *areq = node->as_asyncrequests[i];
    1039                 :             : 
    1040         [ +  + ]:         292 :         if (areq->callback_pending)
    1041                 :         230 :             ExecAsyncConfigureWait(areq);
    1042                 :             :     }
    1043                 :             : 
    1044                 :             :     /*
    1045                 :             :      * No need for further processing if none of the subplans configured any
    1046                 :             :      * events.
    1047                 :             :      */
    1048         [ +  + ]:         137 :     if (GetNumRegisteredWaitEvents(node->as_eventset) == 1)
    1049                 :             :     {
    1050                 :           1 :         FreeWaitEventSet(node->as_eventset);
    1051                 :           1 :         node->as_eventset = NULL;
    1052                 :           3 :         return;
    1053                 :             :     }
    1054                 :             : 
    1055                 :             :     /*
    1056                 :             :      * Add the process latch to the set, so that we wake up to process the
    1057                 :             :      * standard interrupts with CHECK_FOR_INTERRUPTS().
    1058                 :             :      *
    1059                 :             :      * NOTE: For historical reasons, it's important that this is added to the
    1060                 :             :      * WaitEventSet after the ExecAsyncConfigureWait() calls.  Namely,
    1061                 :             :      * postgres_fdw calls "GetNumRegisteredWaitEvents(set) == 1" to check if
    1062                 :             :      * any other events are in the set.  That's a poor design, it's
    1063                 :             :      * questionable for postgres_fdw to be doing that in the first place, but
    1064                 :             :      * we cannot change it now.  The pattern has possibly been copied to other
    1065                 :             :      * extensions too.
    1066                 :             :      */
    1067                 :         136 :     AddWaitEventToSet(node->as_eventset, WL_LATCH_SET, PGINVALID_SOCKET,
    1068                 :             :                       MyLatch, NULL);
    1069                 :             : 
    1070                 :             :     /* Return at most EVENT_BUFFER_SIZE events in one call. */
    1071         [ -  + ]:         136 :     if (nevents > EVENT_BUFFER_SIZE)
    1072                 :           0 :         nevents = EVENT_BUFFER_SIZE;
    1073                 :             : 
    1074                 :             :     /*
    1075                 :             :      * If the timeout is -1, wait until at least one event occurs.  If the
    1076                 :             :      * timeout is 0, poll for events, but do not wait at all.
    1077                 :             :      */
    1078                 :         136 :     noccurred = WaitEventSetWait(node->as_eventset, timeout, occurred_event,
    1079                 :             :                                  nevents, WAIT_EVENT_APPEND_READY);
    1080                 :         136 :     FreeWaitEventSet(node->as_eventset);
    1081                 :         136 :     node->as_eventset = NULL;
    1082         [ +  + ]:         136 :     if (noccurred == 0)
    1083                 :           2 :         return;
    1084                 :             : 
    1085                 :             :     /* Deliver notifications. */
    1086         [ +  + ]:         291 :     for (i = 0; i < noccurred; i++)
    1087                 :             :     {
    1088                 :         157 :         WaitEvent  *w = &occurred_event[i];
    1089                 :             : 
    1090                 :             :         /*
    1091                 :             :          * Each waiting subplan should have registered its wait event with
    1092                 :             :          * user_data pointing back to its AsyncRequest.
    1093                 :             :          */
    1094         [ +  - ]:         157 :         if ((w->events & WL_SOCKET_READABLE) != 0)
    1095                 :             :         {
    1096                 :         157 :             AsyncRequest *areq = (AsyncRequest *) w->user_data;
    1097                 :             : 
    1098         [ +  - ]:         157 :             if (areq->callback_pending)
    1099                 :             :             {
    1100                 :             :                 /*
    1101                 :             :                  * Mark it as no longer needing a callback.  We must do this
    1102                 :             :                  * before dispatching the callback in case the callback resets
    1103                 :             :                  * the flag.
    1104                 :             :                  */
    1105                 :         157 :                 areq->callback_pending = false;
    1106                 :             : 
    1107                 :             :                 /* Do the actual work. */
    1108                 :         157 :                 ExecAsyncNotify(areq);
    1109                 :             :             }
    1110                 :             :         }
    1111                 :             : 
    1112                 :             :         /* Handle standard interrupts */
    1113         [ -  + ]:         157 :         if ((w->events & WL_LATCH_SET) != 0)
    1114                 :             :         {
    1115                 :           0 :             ResetLatch(MyLatch);
    1116         [ #  # ]:           0 :             CHECK_FOR_INTERRUPTS();
    1117                 :             :         }
    1118                 :             :     }
    1119                 :             : }
    1120                 :             : 
    1121                 :             : /* ----------------------------------------------------------------
    1122                 :             :  *      ExecAppendAsyncReset
    1123                 :             :  *
    1124                 :             :  *      Reset asynchronous requests made for async-capable subplans.
    1125                 :             :  * ----------------------------------------------------------------
    1126                 :             :  */
    1127                 :             : static void
    1128                 :          21 : ExecAppendAsyncReset(AppendState *node)
    1129                 :             : {
    1130                 :             :     int         i;
    1131                 :             : 
    1132                 :             :     /* We should never be called when there are no async subplans. */
    1133                 :             :     Assert(node->as_nasyncplans > 0);
    1134                 :             : 
    1135                 :             :     /*
    1136                 :             :      * Drain pending async requests if any.  We force the as_syncdone flag to
    1137                 :             :      * be true so that ExecAppendAsyncEventWait() waits until at least one
    1138                 :             :      * event occurs.
    1139                 :             :      */
    1140                 :          21 :     node->as_syncdone = true;
    1141                 :             :     for (;;)
    1142                 :           3 :     {
    1143                 :          24 :         bool        found = false;
    1144                 :             : 
    1145                 :             :         /*
    1146                 :             :          * When called from ExecAppendAsyncEventWait(), postgres_fdw (and
    1147                 :             :          * possibly other FDWs) will skip configuration of events for pending
    1148                 :             :          * requests in some cases if as_needrequest isn't empty.  To avoid
    1149                 :             :          * that, discard results we already have.  Note that we need to do
    1150                 :             :          * this on every iteration, as the call to that function may produce
    1151                 :             :          * new results.
    1152                 :             :          */
    1153                 :          24 :         node->as_nasyncresults = 0;
    1154                 :          24 :         bms_free(node->as_needrequest);
    1155                 :          24 :         node->as_needrequest = NULL;
    1156                 :             : 
    1157                 :          24 :         i = -1;
    1158         [ +  + ]:          73 :         while ((i = bms_next_member(node->as_asyncplans, i)) >= 0)
    1159                 :             :         {
    1160                 :          52 :             AsyncRequest *areq = node->as_asyncrequests[i];
    1161                 :             : 
    1162         [ +  + ]:          52 :             if (areq->callback_pending)
    1163                 :             :             {
    1164                 :           3 :                 found = true;
    1165                 :           3 :                 break;
    1166                 :             :             }
    1167                 :             :         }
    1168         [ +  + ]:          24 :         if (!found)
    1169                 :          21 :             break;
    1170                 :             : 
    1171         [ -  + ]:           3 :         CHECK_FOR_INTERRUPTS();
    1172                 :             : 
    1173                 :             :         /* Wait or poll for async events. */
    1174                 :           3 :         ExecAppendAsyncEventWait(node);
    1175                 :             :     }
    1176                 :             : 
    1177                 :             :     /* Reset async requests. */
    1178                 :          21 :     i = -1;
    1179         [ +  + ]:          67 :     while ((i = bms_next_member(node->as_asyncplans, i)) >= 0)
    1180                 :             :     {
    1181                 :          46 :         AsyncRequest *areq = node->as_asyncrequests[i];
    1182                 :             : 
    1183                 :             :         Assert(!areq->callback_pending);
    1184                 :          46 :         areq->request_complete = false;
    1185                 :          46 :         areq->result = NULL;
    1186                 :             :     }
    1187                 :             : 
    1188                 :             :     /* Reset state variables. */
    1189                 :             :     Assert(node->as_nasyncresults == 0);
    1190                 :             :     Assert(node->as_needrequest == NULL);
    1191                 :          21 :     node->as_nasyncremain = 0;
    1192                 :          21 : }
    1193                 :             : 
    1194                 :             : /* ----------------------------------------------------------------
    1195                 :             :  *      ExecAsyncAppendResponse
    1196                 :             :  *
    1197                 :             :  *      Receive a response from an asynchronous request we made.
    1198                 :             :  * ----------------------------------------------------------------
    1199                 :             :  */
    1200                 :             : void
    1201                 :        6345 : ExecAsyncAppendResponse(AsyncRequest *areq)
    1202                 :             : {
    1203                 :        6345 :     AppendState *node = (AppendState *) areq->requestor;
    1204                 :        6345 :     TupleTableSlot *slot = areq->result;
    1205                 :             : 
    1206                 :             :     /* The result should be a TupleTableSlot or NULL. */
    1207                 :             :     Assert(slot == NULL || IsA(slot, TupleTableSlot));
    1208                 :             : 
    1209                 :             :     /* Nothing to do if the request is pending. */
    1210         [ +  + ]:        6345 :     if (!areq->request_complete)
    1211                 :             :     {
    1212                 :             :         /* The request would have been pending for a callback. */
    1213                 :             :         Assert(areq->callback_pending);
    1214                 :         168 :         return;
    1215                 :             :     }
    1216                 :             : 
    1217                 :             :     /* If the result is NULL or an empty slot, there's nothing more to do. */
    1218   [ +  +  -  + ]:        6177 :     if (TupIsNull(slot))
    1219                 :             :     {
    1220                 :             :         /* The ending subplan wouldn't have been pending for a callback. */
    1221                 :             :         Assert(!areq->callback_pending);
    1222                 :          62 :         --node->as_nasyncremain;
    1223                 :          62 :         return;
    1224                 :             :     }
    1225                 :             : 
    1226                 :             :     /* Save result so we can return it. */
    1227                 :             :     Assert(node->as_nasyncresults < node->as_nasyncplans);
    1228                 :        6115 :     node->as_asyncresults[node->as_nasyncresults++] = slot;
    1229                 :             : 
    1230                 :             :     /*
    1231                 :             :      * Mark the subplan that returned a result as ready for a new request.  We
    1232                 :             :      * don't launch another one here immediately because it might complete.
    1233                 :             :      */
    1234                 :        6115 :     node->as_needrequest = bms_add_member(node->as_needrequest,
    1235                 :             :                                           areq->request_index);
    1236                 :             : }
    1237                 :             : 
    1238                 :             : /* ----------------------------------------------------------------
    1239                 :             :  *      classify_matching_subplans
    1240                 :             :  *
    1241                 :             :  *      Classify the node's as_valid_subplans into sync ones and
    1242                 :             :  *      async ones, adjust it to contain sync ones only, and save
    1243                 :             :  *      async ones in the node's as_valid_asyncplans.
    1244                 :             :  * ----------------------------------------------------------------
    1245                 :             :  */
    1246                 :             : static void
    1247                 :          50 : classify_matching_subplans(AppendState *node)
    1248                 :             : {
    1249                 :             :     Bitmapset  *valid_asyncplans;
    1250                 :             : 
    1251                 :             :     Assert(node->as_valid_subplans_identified);
    1252                 :             :     Assert(node->as_valid_asyncplans == NULL);
    1253                 :             : 
    1254                 :             :     /* Nothing to do if there are no valid subplans. */
    1255         [ -  + ]:          50 :     if (bms_is_empty(node->as_valid_subplans))
    1256                 :             :     {
    1257                 :           0 :         node->as_syncdone = true;
    1258                 :           0 :         node->as_nasyncremain = 0;
    1259                 :           0 :         return;
    1260                 :             :     }
    1261                 :             : 
    1262                 :             :     /* Nothing to do if there are no valid async subplans. */
    1263         [ -  + ]:          50 :     if (!bms_overlap(node->as_valid_subplans, node->as_asyncplans))
    1264                 :             :     {
    1265                 :           0 :         node->as_nasyncremain = 0;
    1266                 :           0 :         return;
    1267                 :             :     }
    1268                 :             : 
    1269                 :             :     /* Get valid async subplans. */
    1270                 :          50 :     valid_asyncplans = bms_intersect(node->as_asyncplans,
    1271                 :          50 :                                      node->as_valid_subplans);
    1272                 :             : 
    1273                 :             :     /* Adjust the valid subplans to contain sync subplans only. */
    1274                 :          50 :     node->as_valid_subplans = bms_del_members(node->as_valid_subplans,
    1275                 :             :                                               valid_asyncplans);
    1276                 :             : 
    1277                 :             :     /* Save valid async subplans. */
    1278                 :          50 :     node->as_valid_asyncplans = valid_asyncplans;
    1279                 :             : }
        

Generated by: LCOV version 2.0-1