Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/executor/nodeAppend.c
Line
Count
Source
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
0
#define INVALID_SUBPLAN_INDEX   -1
87
0
#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
ExecInitAppend(Append *node, EState *estate, int eflags)
114
0
{
115
0
  AppendState *appendstate = makeNode(AppendState);
116
0
  PlanState **appendplanstates;
117
0
  const TupleTableSlotOps *appendops;
118
0
  Bitmapset  *validsubplans;
119
0
  Bitmapset  *asyncplans;
120
0
  int     nplans;
121
0
  int     nasyncplans;
122
0
  int     firstvalid;
123
0
  int     i,
124
0
        j;
125
126
  /* check for unsupported flags */
127
0
  Assert(!(eflags & EXEC_FLAG_MARK));
128
129
  /*
130
   * create new AppendState for our append node
131
   */
132
0
  appendstate->ps.plan = (Plan *) node;
133
0
  appendstate->ps.state = estate;
134
0
  appendstate->ps.ExecProcNode = ExecAppend;
135
136
  /* Let choose_next_subplan_* function handle setting the first subplan */
137
0
  appendstate->as_whichplan = INVALID_SUBPLAN_INDEX;
138
0
  appendstate->as_syncdone = false;
139
0
  appendstate->as_begun = false;
140
141
  /* If run-time partition pruning is enabled, then set that up now */
142
0
  if (node->part_prune_index >= 0)
143
0
  {
144
0
    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
0
    prunestate = ExecInitPartitionExecPruning(&appendstate->ps,
152
0
                          list_length(node->appendplans),
153
0
                          node->part_prune_index,
154
0
                          node->apprelids,
155
0
                          &validsubplans);
156
0
    appendstate->as_prune_state = prunestate;
157
0
    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
0
    if (!prunestate->do_exec_prune && nplans > 0)
165
0
    {
166
0
      appendstate->as_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
167
0
      appendstate->as_valid_subplans_identified = true;
168
0
    }
169
0
  }
170
0
  else
171
0
  {
172
0
    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
0
    Assert(nplans > 0);
179
0
    appendstate->as_valid_subplans = validsubplans =
180
0
      bms_add_range(NULL, 0, nplans - 1);
181
0
    appendstate->as_valid_subplans_identified = true;
182
0
    appendstate->as_prune_state = NULL;
183
0
  }
184
185
0
  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
0
  j = 0;
194
0
  asyncplans = NULL;
195
0
  nasyncplans = 0;
196
0
  firstvalid = nplans;
197
0
  i = -1;
198
0
  while ((i = bms_next_member(validsubplans, i)) >= 0)
199
0
  {
200
0
    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
0
    if (initNode->async_capable && estate->es_epq_active == NULL)
208
0
    {
209
0
      asyncplans = bms_add_member(asyncplans, j);
210
0
      nasyncplans++;
211
0
    }
212
213
    /*
214
     * Record the lowest appendplans index which is a valid partial plan.
215
     */
216
0
    if (i >= node->first_partial_plan && j < firstvalid)
217
0
      firstvalid = j;
218
219
0
    appendplanstates[j++] = ExecInitNode(initNode, estate, eflags);
220
0
  }
221
222
0
  appendstate->as_first_partial_plan = firstvalid;
223
0
  appendstate->appendplans = appendplanstates;
224
0
  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
0
  appendops = ExecGetCommonSlotOps(appendplanstates, j);
236
0
  if (appendops != NULL)
237
0
  {
238
0
    ExecInitResultTupleSlotTL(&appendstate->ps, appendops);
239
0
  }
240
0
  else
241
0
  {
242
0
    ExecInitResultTupleSlotTL(&appendstate->ps, &TTSOpsVirtual);
243
    /* show that the output slot type is not fixed */
244
0
    appendstate->ps.resultopsset = true;
245
0
    appendstate->ps.resultopsfixed = false;
246
0
  }
247
248
  /* Initialize async state */
249
0
  appendstate->as_asyncplans = asyncplans;
250
0
  appendstate->as_nasyncplans = nasyncplans;
251
0
  appendstate->as_asyncrequests = NULL;
252
0
  appendstate->as_asyncresults = NULL;
253
0
  appendstate->as_nasyncresults = 0;
254
0
  appendstate->as_nasyncremain = 0;
255
0
  appendstate->as_needrequest = NULL;
256
0
  appendstate->as_eventset = NULL;
257
0
  appendstate->as_valid_asyncplans = NULL;
258
259
0
  if (nasyncplans > 0)
260
0
  {
261
0
    appendstate->as_asyncrequests = palloc0_array(AsyncRequest *, nplans);
262
263
0
    i = -1;
264
0
    while ((i = bms_next_member(asyncplans, i)) >= 0)
265
0
    {
266
0
      AsyncRequest *areq;
267
268
0
      areq = palloc_object(AsyncRequest);
269
0
      areq->requestor = (PlanState *) appendstate;
270
0
      areq->requestee = appendplanstates[i];
271
0
      areq->request_index = i;
272
0
      areq->callback_pending = false;
273
0
      areq->request_complete = false;
274
0
      areq->result = NULL;
275
276
0
      appendstate->as_asyncrequests[i] = areq;
277
0
    }
278
279
0
    appendstate->as_asyncresults = palloc0_array(TupleTableSlot *, nasyncplans);
280
281
0
    if (appendstate->as_valid_subplans_identified)
282
0
      classify_matching_subplans(appendstate);
283
0
  }
284
285
  /*
286
   * Miscellaneous initialization
287
   */
288
289
0
  appendstate->ps.ps_ProjInfo = NULL;
290
291
  /* For parallel query, this will be overridden later. */
292
0
  appendstate->choose_next_subplan = choose_next_subplan_locally;
293
294
0
  return appendstate;
295
0
}
296
297
/* ----------------------------------------------------------------
298
 *     ExecAppend
299
 *
300
 *    Handles iteration over multiple subplans.
301
 * ----------------------------------------------------------------
302
 */
303
static TupleTableSlot *
304
ExecAppend(PlanState *pstate)
305
0
{
306
0
  AppendState *node = castNode(AppendState, pstate);
307
0
  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
0
  if (!node->as_begun)
314
0
  {
315
0
    Assert(node->as_whichplan == INVALID_SUBPLAN_INDEX);
316
0
    Assert(!node->as_syncdone);
317
318
    /* Nothing to do if there are no subplans */
319
0
    if (node->as_nplans == 0)
320
0
      return ExecClearTuple(node->ps.ps_ResultTupleSlot);
321
322
    /* If there are any async subplans, begin executing them. */
323
0
    if (node->as_nasyncplans > 0)
324
0
      ExecAppendAsyncBegin(node);
325
326
    /*
327
     * If no sync subplan has been chosen, we must choose one before
328
     * proceeding.
329
     */
330
0
    if (!node->choose_next_subplan(node) && node->as_nasyncremain == 0)
331
0
      return ExecClearTuple(node->ps.ps_ResultTupleSlot);
332
333
0
    Assert(node->as_syncdone ||
334
0
         (node->as_whichplan >= 0 &&
335
0
        node->as_whichplan < node->as_nplans));
336
337
    /* And we're initialized. */
338
0
    node->as_begun = true;
339
0
  }
340
341
0
  for (;;)
342
0
  {
343
0
    PlanState  *subnode;
344
345
0
    CHECK_FOR_INTERRUPTS();
346
347
    /*
348
     * try to get a tuple from an async subplan if any
349
     */
350
0
    if (node->as_syncdone || !bms_is_empty(node->as_needrequest))
351
0
    {
352
0
      if (ExecAppendAsyncGetNext(node, &result))
353
0
        return result;
354
0
      Assert(!node->as_syncdone);
355
0
      Assert(bms_is_empty(node->as_needrequest));
356
0
    }
357
358
    /*
359
     * figure out which sync subplan we are currently processing
360
     */
361
0
    Assert(node->as_whichplan >= 0 && node->as_whichplan < node->as_nplans);
362
0
    subnode = node->appendplans[node->as_whichplan];
363
364
    /*
365
     * get a tuple from the subplan
366
     */
367
0
    result = ExecProcNode(subnode);
368
369
0
    if (!TupIsNull(result))
370
0
    {
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
0
      return result;
377
0
    }
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
0
    if (node->as_nasyncremain > 0)
385
0
      ExecAppendAsyncEventWait(node);
386
387
    /* choose new sync subplan; if no sync/async subplans, we're done */
388
0
    if (!node->choose_next_subplan(node) && node->as_nasyncremain == 0)
389
0
      return ExecClearTuple(node->ps.ps_ResultTupleSlot);
390
0
  }
391
0
}
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
ExecEndAppend(AppendState *node)
403
0
{
404
0
  PlanState **appendplans;
405
0
  int     nplans;
406
0
  int     i;
407
408
  /*
409
   * get information from the node
410
   */
411
0
  appendplans = node->appendplans;
412
0
  nplans = node->as_nplans;
413
414
  /*
415
   * shut down each of the subscans
416
   */
417
0
  for (i = 0; i < nplans; i++)
418
0
    ExecEndNode(appendplans[i]);
419
0
}
420
421
void
422
ExecReScanAppend(AppendState *node)
423
0
{
424
0
  int     nasyncplans = node->as_nasyncplans;
425
0
  int     i;
426
427
  /* If there are any async subplans, reset async requests made for them. */
428
0
  if (nasyncplans > 0)
429
0
    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
0
  if (node->as_prune_state &&
437
0
    bms_overlap(node->ps.chgParam,
438
0
          node->as_prune_state->execparamids))
439
0
  {
440
0
    node->as_valid_subplans_identified = false;
441
0
    bms_free(node->as_valid_subplans);
442
0
    node->as_valid_subplans = NULL;
443
0
    bms_free(node->as_valid_asyncplans);
444
0
    node->as_valid_asyncplans = NULL;
445
0
  }
446
447
0
  for (i = 0; i < node->as_nplans; i++)
448
0
  {
449
0
    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
0
    if (node->ps.chgParam != NULL)
456
0
      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
0
    if (subnode->chgParam == NULL)
463
0
      ExecReScan(subnode);
464
0
  }
465
466
  /* Let choose_next_subplan_* function handle setting the first subplan */
467
0
  node->as_whichplan = INVALID_SUBPLAN_INDEX;
468
0
  node->as_syncdone = false;
469
0
  node->as_begun = false;
470
0
}
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
ExecAppendEstimate(AppendState *node,
486
           ParallelContext *pcxt)
487
0
{
488
0
  node->pstate_len =
489
0
    add_size(offsetof(ParallelAppendState, pa_finished),
490
0
         sizeof(bool) * node->as_nplans);
491
492
0
  shm_toc_estimate_chunk(&pcxt->estimator, node->pstate_len);
493
0
  shm_toc_estimate_keys(&pcxt->estimator, 1);
494
0
}
495
496
497
/* ----------------------------------------------------------------
498
 *    ExecAppendInitializeDSM
499
 *
500
 *    Set up shared state for Parallel Append.
501
 * ----------------------------------------------------------------
502
 */
503
void
504
ExecAppendInitializeDSM(AppendState *node,
505
            ParallelContext *pcxt)
506
0
{
507
0
  ParallelAppendState *pstate;
508
509
0
  pstate = shm_toc_allocate(pcxt->toc, node->pstate_len);
510
0
  memset(pstate, 0, node->pstate_len);
511
0
  LWLockInitialize(&pstate->pa_lock, LWTRANCHE_PARALLEL_APPEND);
512
0
  shm_toc_insert(pcxt->toc, node->ps.plan->plan_node_id, pstate);
513
514
0
  node->as_pstate = pstate;
515
0
  node->choose_next_subplan = choose_next_subplan_for_leader;
516
0
}
517
518
/* ----------------------------------------------------------------
519
 *    ExecAppendReInitializeDSM
520
 *
521
 *    Reset shared state before beginning a fresh scan.
522
 * ----------------------------------------------------------------
523
 */
524
void
525
ExecAppendReInitializeDSM(AppendState *node, ParallelContext *pcxt)
526
0
{
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
ExecAppendInitializeWorker(AppendState *node, ParallelWorkerContext *pwcxt)
542
0
{
543
0
  node->as_pstate = shm_toc_lookup(pwcxt->toc, node->ps.plan->plan_node_id, false);
544
0
  node->choose_next_subplan = choose_next_subplan_for_worker;
545
0
}
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
choose_next_subplan_locally(AppendState *node)
556
0
{
557
0
  int     whichplan = node->as_whichplan;
558
0
  int     nextplan;
559
560
  /* We should never be called when there are no subplans */
561
0
  Assert(node->as_nplans > 0);
562
563
  /* Nothing to do if syncdone */
564
0
  if (node->as_syncdone)
565
0
    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
0
  if (whichplan == INVALID_SUBPLAN_INDEX)
575
0
  {
576
0
    if (node->as_nasyncplans > 0)
577
0
    {
578
      /* We'd have filled as_valid_subplans already */
579
0
      Assert(node->as_valid_subplans_identified);
580
0
    }
581
0
    else if (!node->as_valid_subplans_identified)
582
0
    {
583
0
      node->as_valid_subplans =
584
0
        ExecFindMatchingSubPlans(node->as_prune_state, false, NULL);
585
0
      node->as_valid_subplans_identified = true;
586
0
    }
587
588
0
    whichplan = -1;
589
0
  }
590
591
  /* Ensure whichplan is within the expected range */
592
0
  Assert(whichplan >= -1 && whichplan <= node->as_nplans);
593
594
0
  if (ScanDirectionIsForward(node->ps.state->es_direction))
595
0
    nextplan = bms_next_member(node->as_valid_subplans, whichplan);
596
0
  else
597
0
    nextplan = bms_prev_member(node->as_valid_subplans, whichplan);
598
599
0
  if (nextplan < 0)
600
0
  {
601
    /* Set as_syncdone if in async mode */
602
0
    if (node->as_nasyncplans > 0)
603
0
      node->as_syncdone = true;
604
0
    return false;
605
0
  }
606
607
0
  node->as_whichplan = nextplan;
608
609
0
  return true;
610
0
}
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
choose_next_subplan_for_leader(AppendState *node)
622
0
{
623
0
  ParallelAppendState *pstate = node->as_pstate;
624
625
  /* Backward scan is not supported by parallel-aware plans */
626
0
  Assert(ScanDirectionIsForward(node->ps.state->es_direction));
627
628
  /* We should never be called when there are no subplans */
629
0
  Assert(node->as_nplans > 0);
630
631
0
  LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
632
633
0
  if (node->as_whichplan != INVALID_SUBPLAN_INDEX)
634
0
  {
635
    /* Mark just-completed subplan as finished. */
636
0
    node->as_pstate->pa_finished[node->as_whichplan] = true;
637
0
  }
638
0
  else
639
0
  {
640
    /* Start with last subplan. */
641
0
    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
0
    if (!node->as_valid_subplans_identified)
649
0
    {
650
0
      node->as_valid_subplans =
651
0
        ExecFindMatchingSubPlans(node->as_prune_state, false, NULL);
652
0
      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
0
      mark_invalid_subplans_as_finished(node);
659
0
    }
660
0
  }
661
662
  /* Loop until we find a subplan to execute. */
663
0
  while (pstate->pa_finished[node->as_whichplan])
664
0
  {
665
0
    if (node->as_whichplan == 0)
666
0
    {
667
0
      pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
668
0
      node->as_whichplan = INVALID_SUBPLAN_INDEX;
669
0
      LWLockRelease(&pstate->pa_lock);
670
0
      return false;
671
0
    }
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
0
    node->as_whichplan--;
678
0
  }
679
680
  /* If non-partial, immediately mark as finished. */
681
0
  if (node->as_whichplan < node->as_first_partial_plan)
682
0
    node->as_pstate->pa_finished[node->as_whichplan] = true;
683
684
0
  LWLockRelease(&pstate->pa_lock);
685
686
0
  return true;
687
0
}
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
choose_next_subplan_for_worker(AppendState *node)
704
0
{
705
0
  ParallelAppendState *pstate = node->as_pstate;
706
707
  /* Backward scan is not supported by parallel-aware plans */
708
0
  Assert(ScanDirectionIsForward(node->ps.state->es_direction));
709
710
  /* We should never be called when there are no subplans */
711
0
  Assert(node->as_nplans > 0);
712
713
0
  LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
714
715
  /* Mark just-completed subplan as finished. */
716
0
  if (node->as_whichplan != INVALID_SUBPLAN_INDEX)
717
0
    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
0
  else if (!node->as_valid_subplans_identified)
725
0
  {
726
0
    node->as_valid_subplans =
727
0
      ExecFindMatchingSubPlans(node->as_prune_state, false, NULL);
728
0
    node->as_valid_subplans_identified = true;
729
730
0
    mark_invalid_subplans_as_finished(node);
731
0
  }
732
733
  /* If all the plans are already done, we have nothing to do */
734
0
  if (pstate->pa_next_plan == INVALID_SUBPLAN_INDEX)
735
0
  {
736
0
    LWLockRelease(&pstate->pa_lock);
737
0
    return false;
738
0
  }
739
740
  /* Save the plan from which we are starting the search. */
741
0
  node->as_whichplan = pstate->pa_next_plan;
742
743
  /* Loop until we find a valid subplan to execute. */
744
0
  while (pstate->pa_finished[pstate->pa_next_plan])
745
0
  {
746
0
    int     nextplan;
747
748
0
    nextplan = bms_next_member(node->as_valid_subplans,
749
0
                   pstate->pa_next_plan);
750
0
    if (nextplan >= 0)
751
0
    {
752
      /* Advance to the next valid plan. */
753
0
      pstate->pa_next_plan = nextplan;
754
0
    }
755
0
    else if (node->as_whichplan > node->as_first_partial_plan)
756
0
    {
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
0
      nextplan = bms_next_member(node->as_valid_subplans,
762
0
                     node->as_first_partial_plan - 1);
763
0
      pstate->pa_next_plan =
764
0
        nextplan < 0 ? node->as_whichplan : nextplan;
765
0
    }
766
0
    else
767
0
    {
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
0
      pstate->pa_next_plan = node->as_whichplan;
773
0
    }
774
775
0
    if (pstate->pa_next_plan == node->as_whichplan)
776
0
    {
777
      /* We've tried everything! */
778
0
      pstate->pa_next_plan = INVALID_SUBPLAN_INDEX;
779
0
      LWLockRelease(&pstate->pa_lock);
780
0
      return false;
781
0
    }
782
0
  }
783
784
  /* Pick the plan we found, and advance pa_next_plan one more time. */
785
0
  node->as_whichplan = pstate->pa_next_plan;
786
0
  pstate->pa_next_plan = bms_next_member(node->as_valid_subplans,
787
0
                       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
0
  if (pstate->pa_next_plan < 0)
794
0
  {
795
0
    int     nextplan = bms_next_member(node->as_valid_subplans,
796
0
                         node->as_first_partial_plan - 1);
797
798
0
    if (nextplan >= 0)
799
0
      pstate->pa_next_plan = nextplan;
800
0
    else
801
0
    {
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
0
    }
809
0
  }
810
811
  /* If non-partial, immediately mark as finished. */
812
0
  if (node->as_whichplan < node->as_first_partial_plan)
813
0
    node->as_pstate->pa_finished[node->as_whichplan] = true;
814
815
0
  LWLockRelease(&pstate->pa_lock);
816
817
0
  return true;
818
0
}
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
mark_invalid_subplans_as_finished(AppendState *node)
830
0
{
831
0
  int     i;
832
833
  /* Only valid to call this while in parallel Append mode */
834
0
  Assert(node->as_pstate);
835
836
  /* Shouldn't have been called when run-time pruning is not enabled */
837
0
  Assert(node->as_prune_state);
838
839
  /* Nothing to do if all plans are valid */
840
0
  if (bms_num_members(node->as_valid_subplans) == node->as_nplans)
841
0
    return;
842
843
  /* Mark all non-valid plans as finished */
844
0
  for (i = 0; i < node->as_nplans; i++)
845
0
  {
846
0
    if (!bms_is_member(i, node->as_valid_subplans))
847
0
      node->as_pstate->pa_finished[i] = true;
848
0
  }
849
0
}
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
ExecAppendAsyncBegin(AppendState *node)
864
0
{
865
0
  int     i;
866
867
  /* Backward scan is not supported by async-aware Appends. */
868
0
  Assert(ScanDirectionIsForward(node->ps.state->es_direction));
869
870
  /* We should never be called when there are no subplans */
871
0
  Assert(node->as_nplans > 0);
872
873
  /* We should never be called when there are no async subplans. */
874
0
  Assert(node->as_nasyncplans > 0);
875
876
  /* If we've yet to determine the valid subplans then do so now. */
877
0
  if (!node->as_valid_subplans_identified)
878
0
  {
879
0
    node->as_valid_subplans =
880
0
      ExecFindMatchingSubPlans(node->as_prune_state, false, NULL);
881
0
    node->as_valid_subplans_identified = true;
882
883
0
    classify_matching_subplans(node);
884
0
  }
885
886
  /* Initialize state variables. */
887
0
  node->as_syncdone = bms_is_empty(node->as_valid_subplans);
888
0
  node->as_nasyncremain = bms_num_members(node->as_valid_asyncplans);
889
890
  /* Nothing to do if there are no valid async subplans. */
891
0
  if (node->as_nasyncremain == 0)
892
0
    return;
893
894
  /* Make a request for each of the valid async subplans. */
895
0
  i = -1;
896
0
  while ((i = bms_next_member(node->as_valid_asyncplans, i)) >= 0)
897
0
  {
898
0
    AsyncRequest *areq = node->as_asyncrequests[i];
899
900
0
    Assert(areq->request_index == i);
901
0
    Assert(!areq->callback_pending);
902
903
    /* Do the actual work. */
904
0
    ExecAsyncRequest(areq);
905
0
  }
906
0
}
907
908
/* ----------------------------------------------------------------
909
 *    ExecAppendAsyncGetNext
910
 *
911
 *    Get the next tuple from any of the asynchronous subplans.
912
 * ----------------------------------------------------------------
913
 */
914
static bool
915
ExecAppendAsyncGetNext(AppendState *node, TupleTableSlot **result)
916
0
{
917
0
  *result = NULL;
918
919
  /* We should never be called when there are no valid async subplans. */
920
0
  Assert(node->as_nasyncremain > 0);
921
922
  /* Request a tuple asynchronously. */
923
0
  if (ExecAppendAsyncRequest(node, result))
924
0
    return true;
925
926
0
  while (node->as_nasyncremain > 0)
927
0
  {
928
0
    CHECK_FOR_INTERRUPTS();
929
930
    /* Wait or poll for async events. */
931
0
    ExecAppendAsyncEventWait(node);
932
933
    /* Request a tuple asynchronously. */
934
0
    if (ExecAppendAsyncRequest(node, result))
935
0
      return true;
936
937
    /* Break from loop if there's any sync subplan that isn't complete. */
938
0
    if (!node->as_syncdone)
939
0
      break;
940
0
  }
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
0
  if (node->as_syncdone)
948
0
  {
949
0
    Assert(node->as_nasyncremain == 0);
950
0
    *result = ExecClearTuple(node->ps.ps_ResultTupleSlot);
951
0
    return true;
952
0
  }
953
954
0
  return false;
955
0
}
956
957
/* ----------------------------------------------------------------
958
 *    ExecAppendAsyncRequest
959
 *
960
 *    Request a tuple asynchronously.
961
 * ----------------------------------------------------------------
962
 */
963
static bool
964
ExecAppendAsyncRequest(AppendState *node, TupleTableSlot **result)
965
0
{
966
0
  Bitmapset  *needrequest;
967
0
  int     i;
968
969
  /* Nothing to do if there are no async subplans needing a new request. */
970
0
  if (bms_is_empty(node->as_needrequest))
971
0
  {
972
0
    Assert(node->as_nasyncresults == 0);
973
0
    return false;
974
0
  }
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
0
  if (node->as_nasyncresults > 0)
981
0
  {
982
0
    --node->as_nasyncresults;
983
0
    *result = node->as_asyncresults[node->as_nasyncresults];
984
0
    return true;
985
0
  }
986
987
  /* Make a new request for each of the async subplans that need it. */
988
0
  needrequest = node->as_needrequest;
989
0
  node->as_needrequest = NULL;
990
0
  i = -1;
991
0
  while ((i = bms_next_member(needrequest, i)) >= 0)
992
0
  {
993
0
    AsyncRequest *areq = node->as_asyncrequests[i];
994
995
    /* Do the actual work. */
996
0
    ExecAsyncRequest(areq);
997
0
  }
998
0
  bms_free(needrequest);
999
1000
  /* Return one of the asynchronously-generated results if any. */
1001
0
  if (node->as_nasyncresults > 0)
1002
0
  {
1003
0
    --node->as_nasyncresults;
1004
0
    *result = node->as_asyncresults[node->as_nasyncresults];
1005
0
    return true;
1006
0
  }
1007
1008
0
  return false;
1009
0
}
1010
1011
/* ----------------------------------------------------------------
1012
 *    ExecAppendAsyncEventWait
1013
 *
1014
 *    Wait or poll for file descriptor events and fire callbacks.
1015
 * ----------------------------------------------------------------
1016
 */
1017
static void
1018
ExecAppendAsyncEventWait(AppendState *node)
1019
0
{
1020
0
  int     nevents = node->as_nasyncplans + 2;
1021
0
  long    timeout = node->as_syncdone ? -1 : 0;
1022
0
  WaitEvent occurred_event[EVENT_BUFFER_SIZE];
1023
0
  int     noccurred;
1024
0
  int     i;
1025
1026
  /* We should never be called when there are no valid async subplans. */
1027
0
  Assert(node->as_nasyncremain > 0);
1028
1029
0
  Assert(node->as_eventset == NULL);
1030
0
  node->as_eventset = CreateWaitEventSet(CurrentResourceOwner, nevents);
1031
0
  AddWaitEventToSet(node->as_eventset, WL_EXIT_ON_PM_DEATH, PGINVALID_SOCKET,
1032
0
            NULL, NULL);
1033
1034
  /* Give each waiting subplan a chance to add an event. */
1035
0
  i = -1;
1036
0
  while ((i = bms_next_member(node->as_asyncplans, i)) >= 0)
1037
0
  {
1038
0
    AsyncRequest *areq = node->as_asyncrequests[i];
1039
1040
0
    if (areq->callback_pending)
1041
0
      ExecAsyncConfigureWait(areq);
1042
0
  }
1043
1044
  /*
1045
   * No need for further processing if none of the subplans configured any
1046
   * events.
1047
   */
1048
0
  if (GetNumRegisteredWaitEvents(node->as_eventset) == 1)
1049
0
  {
1050
0
    FreeWaitEventSet(node->as_eventset);
1051
0
    node->as_eventset = NULL;
1052
0
    return;
1053
0
  }
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
0
  AddWaitEventToSet(node->as_eventset, WL_LATCH_SET, PGINVALID_SOCKET,
1068
0
            MyLatch, NULL);
1069
1070
  /* Return at most EVENT_BUFFER_SIZE events in one call. */
1071
0
  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
0
  noccurred = WaitEventSetWait(node->as_eventset, timeout, occurred_event,
1079
0
                 nevents, WAIT_EVENT_APPEND_READY);
1080
0
  FreeWaitEventSet(node->as_eventset);
1081
0
  node->as_eventset = NULL;
1082
0
  if (noccurred == 0)
1083
0
    return;
1084
1085
  /* Deliver notifications. */
1086
0
  for (i = 0; i < noccurred; i++)
1087
0
  {
1088
0
    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
0
    if ((w->events & WL_SOCKET_READABLE) != 0)
1095
0
    {
1096
0
      AsyncRequest *areq = (AsyncRequest *) w->user_data;
1097
1098
0
      if (areq->callback_pending)
1099
0
      {
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
0
        areq->callback_pending = false;
1106
1107
        /* Do the actual work. */
1108
0
        ExecAsyncNotify(areq);
1109
0
      }
1110
0
    }
1111
1112
    /* Handle standard interrupts */
1113
0
    if ((w->events & WL_LATCH_SET) != 0)
1114
0
    {
1115
0
      ResetLatch(MyLatch);
1116
0
      CHECK_FOR_INTERRUPTS();
1117
0
    }
1118
0
  }
1119
0
}
1120
1121
/* ----------------------------------------------------------------
1122
 *    ExecAppendAsyncReset
1123
 *
1124
 *    Reset asynchronous requests made for async-capable subplans.
1125
 * ----------------------------------------------------------------
1126
 */
1127
static void
1128
ExecAppendAsyncReset(AppendState *node)
1129
0
{
1130
0
  int     i;
1131
1132
  /* We should never be called when there are no async subplans. */
1133
0
  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
0
  node->as_syncdone = true;
1141
0
  for (;;)
1142
0
  {
1143
0
    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
0
    node->as_nasyncresults = 0;
1154
0
    bms_free(node->as_needrequest);
1155
0
    node->as_needrequest = NULL;
1156
1157
0
    i = -1;
1158
0
    while ((i = bms_next_member(node->as_asyncplans, i)) >= 0)
1159
0
    {
1160
0
      AsyncRequest *areq = node->as_asyncrequests[i];
1161
1162
0
      if (areq->callback_pending)
1163
0
      {
1164
0
        found = true;
1165
0
        break;
1166
0
      }
1167
0
    }
1168
0
    if (!found)
1169
0
      break;
1170
1171
0
    CHECK_FOR_INTERRUPTS();
1172
1173
    /* Wait or poll for async events. */
1174
0
    ExecAppendAsyncEventWait(node);
1175
0
  }
1176
1177
  /* Reset async requests. */
1178
0
  i = -1;
1179
0
  while ((i = bms_next_member(node->as_asyncplans, i)) >= 0)
1180
0
  {
1181
0
    AsyncRequest *areq = node->as_asyncrequests[i];
1182
1183
0
    Assert(!areq->callback_pending);
1184
0
    areq->request_complete = false;
1185
0
    areq->result = NULL;
1186
0
  }
1187
1188
  /* Reset state variables. */
1189
0
  Assert(node->as_nasyncresults == 0);
1190
0
  Assert(node->as_needrequest == NULL);
1191
0
  node->as_nasyncremain = 0;
1192
0
}
1193
1194
/* ----------------------------------------------------------------
1195
 *    ExecAsyncAppendResponse
1196
 *
1197
 *    Receive a response from an asynchronous request we made.
1198
 * ----------------------------------------------------------------
1199
 */
1200
void
1201
ExecAsyncAppendResponse(AsyncRequest *areq)
1202
0
{
1203
0
  AppendState *node = (AppendState *) areq->requestor;
1204
0
  TupleTableSlot *slot = areq->result;
1205
1206
  /* The result should be a TupleTableSlot or NULL. */
1207
0
  Assert(slot == NULL || IsA(slot, TupleTableSlot));
1208
1209
  /* Nothing to do if the request is pending. */
1210
0
  if (!areq->request_complete)
1211
0
  {
1212
    /* The request would have been pending for a callback. */
1213
0
    Assert(areq->callback_pending);
1214
0
    return;
1215
0
  }
1216
1217
  /* If the result is NULL or an empty slot, there's nothing more to do. */
1218
0
  if (TupIsNull(slot))
1219
0
  {
1220
    /* The ending subplan wouldn't have been pending for a callback. */
1221
0
    Assert(!areq->callback_pending);
1222
0
    --node->as_nasyncremain;
1223
0
    return;
1224
0
  }
1225
1226
  /* Save result so we can return it. */
1227
0
  Assert(node->as_nasyncresults < node->as_nasyncplans);
1228
0
  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
0
  node->as_needrequest = bms_add_member(node->as_needrequest,
1235
0
                      areq->request_index);
1236
0
}
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
classify_matching_subplans(AppendState *node)
1248
0
{
1249
0
  Bitmapset  *valid_asyncplans;
1250
1251
0
  Assert(node->as_valid_subplans_identified);
1252
0
  Assert(node->as_valid_asyncplans == NULL);
1253
1254
  /* Nothing to do if there are no valid subplans. */
1255
0
  if (bms_is_empty(node->as_valid_subplans))
1256
0
  {
1257
0
    node->as_syncdone = true;
1258
0
    node->as_nasyncremain = 0;
1259
0
    return;
1260
0
  }
1261
1262
  /* Nothing to do if there are no valid async subplans. */
1263
0
  if (!bms_overlap(node->as_valid_subplans, node->as_asyncplans))
1264
0
  {
1265
0
    node->as_nasyncremain = 0;
1266
0
    return;
1267
0
  }
1268
1269
  /* Get valid async subplans. */
1270
0
  valid_asyncplans = bms_intersect(node->as_asyncplans,
1271
0
                   node->as_valid_subplans);
1272
1273
  /* Adjust the valid subplans to contain sync subplans only. */
1274
0
  node->as_valid_subplans = bms_del_members(node->as_valid_subplans,
1275
0
                        valid_asyncplans);
1276
1277
  /* Save valid async subplans. */
1278
0
  node->as_valid_asyncplans = valid_asyncplans;
1279
0
}