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