/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 | } |