Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/executor/execParallel.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * execParallel.c
4
 *    Support routines for parallel execution.
5
 *
6
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 * This file contains routines that are intended to support setting up,
10
 * using, and tearing down a ParallelContext from within the PostgreSQL
11
 * executor.  The ParallelContext machinery will handle starting the
12
 * workers and ensuring that their state generally matches that of the
13
 * leader; see src/backend/access/transam/README.parallel for details.
14
 * However, we must save and restore relevant executor state, such as
15
 * any ParamListInfo associated with the query, buffer/WAL usage info, and
16
 * the actual plan to be passed down to the worker.
17
 *
18
 * IDENTIFICATION
19
 *    src/backend/executor/execParallel.c
20
 *
21
 *-------------------------------------------------------------------------
22
 */
23
24
#include "postgres.h"
25
26
#include "executor/execParallel.h"
27
#include "executor/executor.h"
28
#include "executor/nodeAgg.h"
29
#include "executor/nodeAppend.h"
30
#include "executor/nodeBitmapHeapscan.h"
31
#include "executor/nodeBitmapIndexscan.h"
32
#include "executor/nodeCustom.h"
33
#include "executor/nodeForeignscan.h"
34
#include "executor/nodeHash.h"
35
#include "executor/nodeHashjoin.h"
36
#include "executor/nodeIncrementalSort.h"
37
#include "executor/nodeIndexonlyscan.h"
38
#include "executor/nodeIndexscan.h"
39
#include "executor/nodeMaterial.h"
40
#include "executor/nodeMemoize.h"
41
#include "executor/nodeSeqscan.h"
42
#include "executor/nodeSort.h"
43
#include "executor/nodeSubplan.h"
44
#include "executor/nodeTidrangescan.h"
45
#include "executor/tqueue.h"
46
#include "jit/jit.h"
47
#include "nodes/nodeFuncs.h"
48
#include "pgstat.h"
49
#include "storage/proc.h"
50
#include "tcop/tcopprot.h"
51
#include "utils/datum.h"
52
#include "utils/dsa.h"
53
#include "utils/lsyscache.h"
54
#include "utils/snapmgr.h"
55
56
/*
57
 * Magic numbers for parallel executor communication.  We use constants
58
 * greater than any 32-bit integer here so that values < 2^32 can be used
59
 * by individual parallel nodes to store their own state.
60
 */
61
0
#define PARALLEL_KEY_EXECUTOR_FIXED   UINT64CONST(0xE000000000000001)
62
0
#define PARALLEL_KEY_PLANNEDSTMT    UINT64CONST(0xE000000000000002)
63
0
#define PARALLEL_KEY_PARAMLISTINFO    UINT64CONST(0xE000000000000003)
64
0
#define PARALLEL_KEY_BUFFER_USAGE   UINT64CONST(0xE000000000000004)
65
0
#define PARALLEL_KEY_TUPLE_QUEUE    UINT64CONST(0xE000000000000005)
66
0
#define PARALLEL_KEY_INSTRUMENTATION  UINT64CONST(0xE000000000000006)
67
0
#define PARALLEL_KEY_DSA        UINT64CONST(0xE000000000000007)
68
0
#define PARALLEL_KEY_QUERY_TEXT   UINT64CONST(0xE000000000000008)
69
0
#define PARALLEL_KEY_JIT_INSTRUMENTATION UINT64CONST(0xE000000000000009)
70
0
#define PARALLEL_KEY_WAL_USAGE      UINT64CONST(0xE00000000000000A)
71
72
0
#define PARALLEL_TUPLE_QUEUE_SIZE   65536
73
74
/*
75
 * Fixed-size random stuff that we need to pass to parallel workers.
76
 */
77
typedef struct FixedParallelExecutorState
78
{
79
  int64   tuples_needed;  /* tuple bound, see ExecSetTupleBound */
80
  dsa_pointer param_exec;
81
  int     eflags;
82
  int     jit_flags;
83
} FixedParallelExecutorState;
84
85
/*
86
 * DSM structure for accumulating per-PlanState instrumentation.
87
 *
88
 * instrument_options: Same meaning here as in instrument.c.
89
 *
90
 * instrument_offset: Offset, relative to the start of this structure,
91
 * of the first NodeInstrumentation object.  This will depend on the length of
92
 * the plan_node_id array.
93
 *
94
 * num_workers: Number of workers.
95
 *
96
 * num_plan_nodes: Number of plan nodes.
97
 *
98
 * plan_node_id: Array of plan nodes for which we are gathering instrumentation
99
 * from parallel workers.  The length of this array is given by num_plan_nodes.
100
 */
101
struct SharedExecutorInstrumentation
102
{
103
  int     instrument_options;
104
  int     instrument_offset;
105
  int     num_workers;
106
  int     num_plan_nodes;
107
  int     plan_node_id[FLEXIBLE_ARRAY_MEMBER];
108
109
  /*
110
   * Array of num_plan_nodes * num_workers NodeInstrumentation objects
111
   * follows.
112
   */
113
};
114
#define GetInstrumentationArray(sei) \
115
0
  (StaticAssertVariableIsOfTypeMacro(sei, SharedExecutorInstrumentation *), \
116
0
   (NodeInstrumentation *) (((char *) sei) + sei->instrument_offset))
117
118
/* Context object for ExecParallelEstimate. */
119
typedef struct ExecParallelEstimateContext
120
{
121
  ParallelContext *pcxt;
122
  int     nnodes;
123
} ExecParallelEstimateContext;
124
125
/* Context object for ExecParallelInitializeDSM. */
126
typedef struct ExecParallelInitializeDSMContext
127
{
128
  ParallelContext *pcxt;
129
  SharedExecutorInstrumentation *instrumentation;
130
  int     nnodes;
131
} ExecParallelInitializeDSMContext;
132
133
/* Helper functions that run in the parallel leader. */
134
static char *ExecSerializePlan(Plan *plan, EState *estate);
135
static bool ExecParallelEstimate(PlanState *planstate,
136
                 ExecParallelEstimateContext *e);
137
static bool ExecParallelInitializeDSM(PlanState *planstate,
138
                    ExecParallelInitializeDSMContext *d);
139
static shm_mq_handle **ExecParallelSetupTupleQueues(ParallelContext *pcxt,
140
                          bool reinitialize);
141
static bool ExecParallelReInitializeDSM(PlanState *planstate,
142
                    ParallelContext *pcxt);
143
static bool ExecParallelRetrieveInstrumentation(PlanState *planstate,
144
                        SharedExecutorInstrumentation *instrumentation);
145
146
/* Helper function that runs in the parallel worker. */
147
static DestReceiver *ExecParallelGetReceiver(dsm_segment *seg, shm_toc *toc);
148
149
/*
150
 * Create a serialized representation of the plan to be sent to each worker.
151
 */
152
static char *
153
ExecSerializePlan(Plan *plan, EState *estate)
154
0
{
155
0
  PlannedStmt *pstmt;
156
0
  ListCell   *lc;
157
158
  /* We can't scribble on the original plan, so make a copy. */
159
0
  plan = copyObject(plan);
160
161
  /*
162
   * The worker will start its own copy of the executor, and that copy will
163
   * insert a junk filter if the toplevel node has any resjunk entries. We
164
   * don't want that to happen, because while resjunk columns shouldn't be
165
   * sent back to the user, here the tuples are coming back to another
166
   * backend which may very well need them.  So mutate the target list
167
   * accordingly.  This is sort of a hack; there might be better ways to do
168
   * this...
169
   */
170
0
  foreach(lc, plan->targetlist)
171
0
  {
172
0
    TargetEntry *tle = lfirst_node(TargetEntry, lc);
173
174
0
    tle->resjunk = false;
175
0
  }
176
177
  /*
178
   * Create a dummy PlannedStmt.  Most of the fields don't need to be valid
179
   * for our purposes, but the worker will need at least a minimal
180
   * PlannedStmt to start the executor.
181
   */
182
0
  pstmt = makeNode(PlannedStmt);
183
0
  pstmt->commandType = CMD_SELECT;
184
0
  pstmt->queryId = pgstat_get_my_query_id();
185
0
  pstmt->planId = pgstat_get_my_plan_id();
186
0
  pstmt->hasReturning = false;
187
0
  pstmt->hasModifyingCTE = false;
188
0
  pstmt->canSetTag = true;
189
0
  pstmt->transientPlan = false;
190
0
  pstmt->dependsOnRole = false;
191
0
  pstmt->parallelModeNeeded = false;
192
0
  pstmt->planTree = plan;
193
0
  pstmt->partPruneInfos = estate->es_part_prune_infos;
194
0
  pstmt->rtable = estate->es_range_table;
195
0
  pstmt->unprunableRelids = estate->es_unpruned_relids;
196
0
  pstmt->permInfos = estate->es_rteperminfos;
197
0
  pstmt->appendRelations = NIL;
198
0
  pstmt->planOrigin = PLAN_STMT_INTERNAL;
199
200
  /*
201
   * Transfer only parallel-safe subplans, leaving a NULL "hole" in the list
202
   * for unsafe ones (so that the list indexes of the safe ones are
203
   * preserved).  This positively ensures that the worker won't try to run,
204
   * or even do ExecInitNode on, an unsafe subplan.  That's important to
205
   * protect, eg, non-parallel-aware FDWs from getting into trouble.
206
   */
207
0
  pstmt->subplans = NIL;
208
0
  foreach(lc, estate->es_plannedstmt->subplans)
209
0
  {
210
0
    Plan     *subplan = (Plan *) lfirst(lc);
211
212
0
    if (subplan && !subplan->parallel_safe)
213
0
      subplan = NULL;
214
0
    pstmt->subplans = lappend(pstmt->subplans, subplan);
215
0
  }
216
217
0
  pstmt->rewindPlanIDs = NULL;
218
0
  pstmt->rowMarks = NIL;
219
220
  /*
221
   * Pass the row mark and result relation relids to parallel workers. They
222
   * may need to check them to inform heuristics.
223
   */
224
0
  pstmt->rowMarkRelids = estate->es_plannedstmt->rowMarkRelids;
225
0
  pstmt->resultRelationRelids = estate->es_plannedstmt->resultRelationRelids;
226
0
  pstmt->relationOids = NIL;
227
0
  pstmt->invalItems = NIL; /* workers can't replan anyway... */
228
0
  pstmt->paramExecTypes = estate->es_plannedstmt->paramExecTypes;
229
0
  pstmt->utilityStmt = NULL;
230
0
  pstmt->stmt_location = -1;
231
0
  pstmt->stmt_len = -1;
232
233
  /* Return serialized copy of our dummy PlannedStmt. */
234
0
  return nodeToString(pstmt);
235
0
}
236
237
/*
238
 * Parallel-aware plan nodes (and occasionally others) may need some state
239
 * which is shared across all parallel workers.  Before we size the DSM, give
240
 * them a chance to call shm_toc_estimate_chunk or shm_toc_estimate_keys on
241
 * &pcxt->estimator.
242
 *
243
 * While we're at it, count the number of PlanState nodes in the tree, so
244
 * we know how many Instrumentation structures we need.
245
 */
246
static bool
247
ExecParallelEstimate(PlanState *planstate, ExecParallelEstimateContext *e)
248
0
{
249
0
  if (planstate == NULL)
250
0
    return false;
251
252
  /* Count this node. */
253
0
  e->nnodes++;
254
255
0
  switch (nodeTag(planstate))
256
0
  {
257
0
    case T_SeqScanState:
258
0
      if (planstate->plan->parallel_aware)
259
0
        ExecSeqScanEstimate((SeqScanState *) planstate,
260
0
                  e->pcxt);
261
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
262
0
      ExecSeqScanInstrumentEstimate((SeqScanState *) planstate,
263
0
                      e->pcxt);
264
0
      break;
265
0
    case T_IndexScanState:
266
0
      if (planstate->plan->parallel_aware)
267
0
        ExecIndexScanEstimate((IndexScanState *) planstate,
268
0
                    e->pcxt);
269
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
270
0
      ExecIndexScanInstrumentEstimate((IndexScanState *) planstate,
271
0
                      e->pcxt);
272
0
      break;
273
0
    case T_IndexOnlyScanState:
274
0
      if (planstate->plan->parallel_aware)
275
0
        ExecIndexOnlyScanEstimate((IndexOnlyScanState *) planstate,
276
0
                      e->pcxt);
277
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
278
0
      ExecIndexOnlyScanInstrumentEstimate((IndexOnlyScanState *) planstate,
279
0
                        e->pcxt);
280
0
      break;
281
0
    case T_BitmapIndexScanState:
282
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
283
0
      ExecBitmapIndexScanEstimate((BitmapIndexScanState *) planstate,
284
0
                    e->pcxt);
285
0
      break;
286
0
    case T_ForeignScanState:
287
0
      if (planstate->plan->parallel_aware)
288
0
        ExecForeignScanEstimate((ForeignScanState *) planstate,
289
0
                    e->pcxt);
290
0
      break;
291
0
    case T_TidRangeScanState:
292
0
      if (planstate->plan->parallel_aware)
293
0
        ExecTidRangeScanEstimate((TidRangeScanState *) planstate,
294
0
                     e->pcxt);
295
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
296
0
      ExecTidRangeScanInstrumentEstimate((TidRangeScanState *) planstate,
297
0
                         e->pcxt);
298
0
      break;
299
0
    case T_AppendState:
300
0
      if (planstate->plan->parallel_aware)
301
0
        ExecAppendEstimate((AppendState *) planstate,
302
0
                   e->pcxt);
303
0
      break;
304
0
    case T_CustomScanState:
305
0
      if (planstate->plan->parallel_aware)
306
0
        ExecCustomScanEstimate((CustomScanState *) planstate,
307
0
                     e->pcxt);
308
0
      break;
309
0
    case T_BitmapHeapScanState:
310
0
      if (planstate->plan->parallel_aware)
311
0
        ExecBitmapHeapEstimate((BitmapHeapScanState *) planstate,
312
0
                     e->pcxt);
313
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
314
0
      ExecBitmapHeapInstrumentEstimate((BitmapHeapScanState *) planstate,
315
0
                       e->pcxt);
316
0
      break;
317
0
    case T_HashJoinState:
318
0
      if (planstate->plan->parallel_aware)
319
0
        ExecHashJoinEstimate((HashJoinState *) planstate,
320
0
                   e->pcxt);
321
0
      break;
322
0
    case T_HashState:
323
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
324
0
      ExecHashEstimate((HashState *) planstate, e->pcxt);
325
0
      break;
326
0
    case T_SortState:
327
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
328
0
      ExecSortEstimate((SortState *) planstate, e->pcxt);
329
0
      break;
330
0
    case T_IncrementalSortState:
331
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
332
0
      ExecIncrementalSortEstimate((IncrementalSortState *) planstate, e->pcxt);
333
0
      break;
334
0
    case T_AggState:
335
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
336
0
      ExecAggEstimate((AggState *) planstate, e->pcxt);
337
0
      break;
338
0
    case T_MemoizeState:
339
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
340
0
      ExecMemoizeEstimate((MemoizeState *) planstate, e->pcxt);
341
0
      break;
342
0
    case T_MaterialState:
343
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
344
0
      ExecMaterialEstimate((MaterialState *) planstate, e->pcxt);
345
0
      break;
346
0
    default:
347
0
      break;
348
0
  }
349
350
0
  return planstate_tree_walker(planstate, ExecParallelEstimate, e);
351
0
}
352
353
/*
354
 * Estimate the amount of space required to serialize the indicated parameters.
355
 */
356
static Size
357
EstimateParamExecSpace(EState *estate, Bitmapset *params)
358
0
{
359
0
  int     paramid;
360
0
  Size    sz = sizeof(int);
361
362
0
  paramid = -1;
363
0
  while ((paramid = bms_next_member(params, paramid)) >= 0)
364
0
  {
365
0
    Oid     typeOid;
366
0
    int16   typLen;
367
0
    bool    typByVal;
368
0
    ParamExecData *prm;
369
370
0
    prm = &(estate->es_param_exec_vals[paramid]);
371
0
    typeOid = list_nth_oid(estate->es_plannedstmt->paramExecTypes,
372
0
                 paramid);
373
374
0
    sz = add_size(sz, sizeof(int)); /* space for paramid */
375
376
    /* space for datum/isnull */
377
0
    if (OidIsValid(typeOid))
378
0
      get_typlenbyval(typeOid, &typLen, &typByVal);
379
0
    else
380
0
    {
381
      /* If no type OID, assume by-value, like copyParamList does. */
382
0
      typLen = sizeof(Datum);
383
0
      typByVal = true;
384
0
    }
385
0
    sz = add_size(sz,
386
0
            datumEstimateSpace(prm->value, prm->isnull,
387
0
                     typByVal, typLen));
388
0
  }
389
0
  return sz;
390
0
}
391
392
/*
393
 * Serialize specified PARAM_EXEC parameters.
394
 *
395
 * We write the number of parameters first, as a 4-byte integer, and then
396
 * write details for each parameter in turn.  The details for each parameter
397
 * consist of a 4-byte paramid (location of param in execution time internal
398
 * parameter array) and then the datum as serialized by datumSerialize().
399
 */
400
static dsa_pointer
401
SerializeParamExecParams(EState *estate, Bitmapset *params, dsa_area *area)
402
0
{
403
0
  Size    size;
404
0
  int     nparams;
405
0
  int     paramid;
406
0
  ParamExecData *prm;
407
0
  dsa_pointer handle;
408
0
  char     *start_address;
409
410
  /* Allocate enough space for the current parameter values. */
411
0
  size = EstimateParamExecSpace(estate, params);
412
0
  handle = dsa_allocate(area, size);
413
0
  start_address = dsa_get_address(area, handle);
414
415
  /* First write the number of parameters as a 4-byte integer. */
416
0
  nparams = bms_num_members(params);
417
0
  memcpy(start_address, &nparams, sizeof(int));
418
0
  start_address += sizeof(int);
419
420
  /* Write details for each parameter in turn. */
421
0
  paramid = -1;
422
0
  while ((paramid = bms_next_member(params, paramid)) >= 0)
423
0
  {
424
0
    Oid     typeOid;
425
0
    int16   typLen;
426
0
    bool    typByVal;
427
428
0
    prm = &(estate->es_param_exec_vals[paramid]);
429
0
    typeOid = list_nth_oid(estate->es_plannedstmt->paramExecTypes,
430
0
                 paramid);
431
432
    /* Write paramid. */
433
0
    memcpy(start_address, &paramid, sizeof(int));
434
0
    start_address += sizeof(int);
435
436
    /* Write datum/isnull */
437
0
    if (OidIsValid(typeOid))
438
0
      get_typlenbyval(typeOid, &typLen, &typByVal);
439
0
    else
440
0
    {
441
      /* If no type OID, assume by-value, like copyParamList does. */
442
0
      typLen = sizeof(Datum);
443
0
      typByVal = true;
444
0
    }
445
0
    datumSerialize(prm->value, prm->isnull, typByVal, typLen,
446
0
             &start_address);
447
0
  }
448
449
0
  return handle;
450
0
}
451
452
/*
453
 * Restore specified PARAM_EXEC parameters.
454
 */
455
static void
456
RestoreParamExecParams(char *start_address, EState *estate)
457
0
{
458
0
  int     nparams;
459
0
  int     i;
460
0
  int     paramid;
461
462
0
  memcpy(&nparams, start_address, sizeof(int));
463
0
  start_address += sizeof(int);
464
465
0
  for (i = 0; i < nparams; i++)
466
0
  {
467
0
    ParamExecData *prm;
468
469
    /* Read paramid */
470
0
    memcpy(&paramid, start_address, sizeof(int));
471
0
    start_address += sizeof(int);
472
0
    prm = &(estate->es_param_exec_vals[paramid]);
473
474
    /* Read datum/isnull. */
475
0
    prm->value = datumRestore(&start_address, &prm->isnull);
476
0
    prm->execPlan = NULL;
477
0
  }
478
0
}
479
480
/*
481
 * Initialize the dynamic shared memory segment that will be used to control
482
 * parallel execution.
483
 */
484
static bool
485
ExecParallelInitializeDSM(PlanState *planstate,
486
              ExecParallelInitializeDSMContext *d)
487
0
{
488
0
  if (planstate == NULL)
489
0
    return false;
490
491
  /* If instrumentation is enabled, initialize slot for this node. */
492
0
  if (d->instrumentation != NULL)
493
0
    d->instrumentation->plan_node_id[d->nnodes] =
494
0
      planstate->plan->plan_node_id;
495
496
  /* Count this node. */
497
0
  d->nnodes++;
498
499
  /*
500
   * Call initializers for DSM-using plan nodes.
501
   *
502
   * Most plan nodes won't do anything here, but plan nodes that allocated
503
   * DSM may need to initialize shared state in the DSM before parallel
504
   * workers are launched.  They can allocate the space they previously
505
   * estimated using shm_toc_allocate, and add the keys they previously
506
   * estimated using shm_toc_insert, in each case targeting pcxt->toc.
507
   */
508
0
  switch (nodeTag(planstate))
509
0
  {
510
0
    case T_SeqScanState:
511
0
      if (planstate->plan->parallel_aware)
512
0
        ExecSeqScanInitializeDSM((SeqScanState *) planstate,
513
0
                     d->pcxt);
514
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
515
0
      ExecSeqScanInstrumentInitDSM((SeqScanState *) planstate,
516
0
                     d->pcxt);
517
0
      break;
518
0
    case T_IndexScanState:
519
0
      if (planstate->plan->parallel_aware)
520
0
        ExecIndexScanInitializeDSM((IndexScanState *) planstate,
521
0
                       d->pcxt);
522
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
523
0
      ExecIndexScanInstrumentInitDSM((IndexScanState *) planstate,
524
0
                       d->pcxt);
525
0
      break;
526
0
    case T_IndexOnlyScanState:
527
0
      if (planstate->plan->parallel_aware)
528
0
        ExecIndexOnlyScanInitializeDSM((IndexOnlyScanState *) planstate,
529
0
                         d->pcxt);
530
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
531
0
      ExecIndexOnlyScanInstrumentInitDSM((IndexOnlyScanState *) planstate,
532
0
                         d->pcxt);
533
0
      break;
534
0
    case T_BitmapIndexScanState:
535
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
536
0
      ExecBitmapIndexScanInitializeDSM((BitmapIndexScanState *) planstate, d->pcxt);
537
0
      break;
538
0
    case T_ForeignScanState:
539
0
      if (planstate->plan->parallel_aware)
540
0
        ExecForeignScanInitializeDSM((ForeignScanState *) planstate,
541
0
                       d->pcxt);
542
0
      break;
543
0
    case T_TidRangeScanState:
544
0
      if (planstate->plan->parallel_aware)
545
0
        ExecTidRangeScanInitializeDSM((TidRangeScanState *) planstate,
546
0
                        d->pcxt);
547
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
548
0
      ExecTidRangeScanInstrumentInitDSM((TidRangeScanState *) planstate,
549
0
                        d->pcxt);
550
0
      break;
551
0
    case T_AppendState:
552
0
      if (planstate->plan->parallel_aware)
553
0
        ExecAppendInitializeDSM((AppendState *) planstate,
554
0
                    d->pcxt);
555
0
      break;
556
0
    case T_CustomScanState:
557
0
      if (planstate->plan->parallel_aware)
558
0
        ExecCustomScanInitializeDSM((CustomScanState *) planstate,
559
0
                      d->pcxt);
560
0
      break;
561
0
    case T_BitmapHeapScanState:
562
0
      if (planstate->plan->parallel_aware)
563
0
        ExecBitmapHeapInitializeDSM((BitmapHeapScanState *) planstate,
564
0
                      d->pcxt);
565
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
566
0
      ExecBitmapHeapInstrumentInitDSM((BitmapHeapScanState *) planstate,
567
0
                      d->pcxt);
568
0
      break;
569
0
    case T_HashJoinState:
570
0
      if (planstate->plan->parallel_aware)
571
0
        ExecHashJoinInitializeDSM((HashJoinState *) planstate,
572
0
                      d->pcxt);
573
0
      break;
574
0
    case T_HashState:
575
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
576
0
      ExecHashInitializeDSM((HashState *) planstate, d->pcxt);
577
0
      break;
578
0
    case T_SortState:
579
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
580
0
      ExecSortInitializeDSM((SortState *) planstate, d->pcxt);
581
0
      break;
582
0
    case T_IncrementalSortState:
583
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
584
0
      ExecIncrementalSortInitializeDSM((IncrementalSortState *) planstate, d->pcxt);
585
0
      break;
586
0
    case T_AggState:
587
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
588
0
      ExecAggInitializeDSM((AggState *) planstate, d->pcxt);
589
0
      break;
590
0
    case T_MemoizeState:
591
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
592
0
      ExecMemoizeInitializeDSM((MemoizeState *) planstate, d->pcxt);
593
0
      break;
594
0
    case T_MaterialState:
595
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
596
0
      ExecMaterialInitializeDSM((MaterialState *) planstate, d->pcxt);
597
0
      break;
598
0
    default:
599
0
      break;
600
0
  }
601
602
0
  return planstate_tree_walker(planstate, ExecParallelInitializeDSM, d);
603
0
}
604
605
/*
606
 * It sets up the response queues for backend workers to return tuples
607
 * to the main backend and start the workers.
608
 */
609
static shm_mq_handle **
610
ExecParallelSetupTupleQueues(ParallelContext *pcxt, bool reinitialize)
611
0
{
612
0
  shm_mq_handle **responseq;
613
0
  char     *tqueuespace;
614
0
  int     i;
615
616
  /* Skip this if no workers. */
617
0
  if (pcxt->nworkers == 0)
618
0
    return NULL;
619
620
  /* Allocate memory for shared memory queue handles. */
621
0
  responseq = palloc_array(shm_mq_handle *, pcxt->nworkers);
622
623
  /*
624
   * If not reinitializing, allocate space from the DSM for the queues;
625
   * otherwise, find the already allocated space.
626
   */
627
0
  if (!reinitialize)
628
0
    tqueuespace =
629
0
      shm_toc_allocate(pcxt->toc,
630
0
               mul_size(PARALLEL_TUPLE_QUEUE_SIZE,
631
0
                    pcxt->nworkers));
632
0
  else
633
0
    tqueuespace = shm_toc_lookup(pcxt->toc, PARALLEL_KEY_TUPLE_QUEUE, false);
634
635
  /* Create the queues, and become the receiver for each. */
636
0
  for (i = 0; i < pcxt->nworkers; ++i)
637
0
  {
638
0
    shm_mq     *mq;
639
640
0
    mq = shm_mq_create(tqueuespace +
641
0
               ((Size) i) * PARALLEL_TUPLE_QUEUE_SIZE,
642
0
               (Size) PARALLEL_TUPLE_QUEUE_SIZE);
643
644
0
    shm_mq_set_receiver(mq, MyProc);
645
0
    responseq[i] = shm_mq_attach(mq, pcxt->seg, NULL);
646
0
  }
647
648
  /* Add array of queues to shm_toc, so others can find it. */
649
0
  if (!reinitialize)
650
0
    shm_toc_insert(pcxt->toc, PARALLEL_KEY_TUPLE_QUEUE, tqueuespace);
651
652
  /* Return array of handles. */
653
0
  return responseq;
654
0
}
655
656
/*
657
 * Sets up the required infrastructure for backend workers to perform
658
 * execution and return results to the main backend.
659
 */
660
ParallelExecutorInfo *
661
ExecInitParallelPlan(PlanState *planstate, EState *estate,
662
           Bitmapset *sendParams, int nworkers,
663
           int64 tuples_needed)
664
0
{
665
0
  ParallelExecutorInfo *pei;
666
0
  ParallelContext *pcxt;
667
0
  ExecParallelEstimateContext e;
668
0
  ExecParallelInitializeDSMContext d;
669
0
  FixedParallelExecutorState *fpes;
670
0
  char     *pstmt_data;
671
0
  char     *pstmt_space;
672
0
  char     *paramlistinfo_space;
673
0
  BufferUsage *bufusage_space;
674
0
  WalUsage   *walusage_space;
675
0
  SharedExecutorInstrumentation *instrumentation = NULL;
676
0
  SharedJitInstrumentation *jit_instrumentation = NULL;
677
0
  int     pstmt_len;
678
0
  int     paramlistinfo_len;
679
0
  int     instrumentation_len = 0;
680
0
  int     jit_instrumentation_len = 0;
681
0
  int     instrument_offset = 0;
682
0
  Size    dsa_minsize = dsa_minimum_size();
683
0
  char     *query_string;
684
0
  int     query_len;
685
686
  /*
687
   * Force any initplan outputs that we're going to pass to workers to be
688
   * evaluated, if they weren't already.
689
   *
690
   * For simplicity, we use the EState's per-output-tuple ExprContext here.
691
   * That risks intra-query memory leakage, since we might pass through here
692
   * many times before that ExprContext gets reset; but ExecSetParamPlan
693
   * doesn't normally leak any memory in the context (see its comments), so
694
   * it doesn't seem worth complicating this function's API to pass it a
695
   * shorter-lived ExprContext.  This might need to change someday.
696
   */
697
0
  ExecSetParamPlanMulti(sendParams, GetPerTupleExprContext(estate));
698
699
  /* Allocate object for return value. */
700
0
  pei = palloc0_object(ParallelExecutorInfo);
701
0
  pei->finished = false;
702
0
  pei->planstate = planstate;
703
704
  /* Fix up and serialize plan to be sent to workers. */
705
0
  pstmt_data = ExecSerializePlan(planstate->plan, estate);
706
707
  /* Create a parallel context. */
708
0
  pcxt = CreateParallelContext("postgres", "ParallelQueryMain", nworkers);
709
0
  pei->pcxt = pcxt;
710
711
  /*
712
   * Before telling the parallel context to create a dynamic shared memory
713
   * segment, we need to figure out how big it should be.  Estimate space
714
   * for the various things we need to store.
715
   */
716
717
  /* Estimate space for fixed-size state. */
718
0
  shm_toc_estimate_chunk(&pcxt->estimator,
719
0
               sizeof(FixedParallelExecutorState));
720
0
  shm_toc_estimate_keys(&pcxt->estimator, 1);
721
722
  /* Estimate space for query text. */
723
0
  query_len = strlen(estate->es_sourceText);
724
0
  shm_toc_estimate_chunk(&pcxt->estimator, query_len + 1);
725
0
  shm_toc_estimate_keys(&pcxt->estimator, 1);
726
727
  /* Estimate space for serialized PlannedStmt. */
728
0
  pstmt_len = strlen(pstmt_data) + 1;
729
0
  shm_toc_estimate_chunk(&pcxt->estimator, pstmt_len);
730
0
  shm_toc_estimate_keys(&pcxt->estimator, 1);
731
732
  /* Estimate space for serialized ParamListInfo. */
733
0
  paramlistinfo_len = EstimateParamListSpace(estate->es_param_list_info);
734
0
  shm_toc_estimate_chunk(&pcxt->estimator, paramlistinfo_len);
735
0
  shm_toc_estimate_keys(&pcxt->estimator, 1);
736
737
  /*
738
   * Estimate space for BufferUsage.
739
   *
740
   * If EXPLAIN is not in use and there are no extensions loaded that care,
741
   * we could skip this.  But we have no way of knowing whether anyone's
742
   * looking at pgBufferUsage, so do it unconditionally.
743
   */
744
0
  shm_toc_estimate_chunk(&pcxt->estimator,
745
0
               mul_size(sizeof(BufferUsage), pcxt->nworkers));
746
0
  shm_toc_estimate_keys(&pcxt->estimator, 1);
747
748
  /*
749
   * Same thing for WalUsage.
750
   */
751
0
  shm_toc_estimate_chunk(&pcxt->estimator,
752
0
               mul_size(sizeof(WalUsage), pcxt->nworkers));
753
0
  shm_toc_estimate_keys(&pcxt->estimator, 1);
754
755
  /* Estimate space for tuple queues. */
756
0
  shm_toc_estimate_chunk(&pcxt->estimator,
757
0
               mul_size(PARALLEL_TUPLE_QUEUE_SIZE, pcxt->nworkers));
758
0
  shm_toc_estimate_keys(&pcxt->estimator, 1);
759
760
  /*
761
   * Give parallel-aware nodes a chance to add to the estimates, and get a
762
   * count of how many PlanState nodes there are.
763
   */
764
0
  e.pcxt = pcxt;
765
0
  e.nnodes = 0;
766
0
  ExecParallelEstimate(planstate, &e);
767
768
  /* Estimate space for instrumentation, if required. */
769
0
  if (estate->es_instrument)
770
0
  {
771
0
    instrumentation_len =
772
0
      offsetof(SharedExecutorInstrumentation, plan_node_id) +
773
0
      sizeof(int) * e.nnodes;
774
0
    instrumentation_len = MAXALIGN(instrumentation_len);
775
0
    instrument_offset = instrumentation_len;
776
0
    instrumentation_len +=
777
0
      mul_size(sizeof(NodeInstrumentation),
778
0
           mul_size(e.nnodes, nworkers));
779
0
    shm_toc_estimate_chunk(&pcxt->estimator, instrumentation_len);
780
0
    shm_toc_estimate_keys(&pcxt->estimator, 1);
781
782
    /* Estimate space for JIT instrumentation, if required. */
783
0
    if (estate->es_jit_flags != PGJIT_NONE)
784
0
    {
785
0
      jit_instrumentation_len =
786
0
        offsetof(SharedJitInstrumentation, jit_instr) +
787
0
        sizeof(JitInstrumentation) * nworkers;
788
0
      shm_toc_estimate_chunk(&pcxt->estimator, jit_instrumentation_len);
789
0
      shm_toc_estimate_keys(&pcxt->estimator, 1);
790
0
    }
791
0
  }
792
793
  /* Estimate space for DSA area. */
794
0
  shm_toc_estimate_chunk(&pcxt->estimator, dsa_minsize);
795
0
  shm_toc_estimate_keys(&pcxt->estimator, 1);
796
797
  /*
798
   * InitializeParallelDSM() passes the active snapshot to the parallel
799
   * worker, which uses it to set es_snapshot.  Make sure we don't set
800
   * es_snapshot differently in the child.
801
   */
802
0
  Assert(GetActiveSnapshot() == estate->es_snapshot);
803
804
  /* Everyone's had a chance to ask for space, so now create the DSM. */
805
0
  InitializeParallelDSM(pcxt);
806
807
  /*
808
   * OK, now we have a dynamic shared memory segment, and it should be big
809
   * enough to store all of the data we estimated we would want to put into
810
   * it, plus whatever general stuff (not specifically executor-related) the
811
   * ParallelContext itself needs to store there.  None of the space we
812
   * asked for has been allocated or initialized yet, though, so do that.
813
   */
814
815
  /* Store fixed-size state. */
816
0
  fpes = shm_toc_allocate(pcxt->toc, sizeof(FixedParallelExecutorState));
817
0
  fpes->tuples_needed = tuples_needed;
818
0
  fpes->param_exec = InvalidDsaPointer;
819
0
  fpes->eflags = estate->es_top_eflags;
820
0
  fpes->jit_flags = estate->es_jit_flags;
821
0
  shm_toc_insert(pcxt->toc, PARALLEL_KEY_EXECUTOR_FIXED, fpes);
822
823
  /* Store query string */
824
0
  query_string = shm_toc_allocate(pcxt->toc, query_len + 1);
825
0
  memcpy(query_string, estate->es_sourceText, query_len + 1);
826
0
  shm_toc_insert(pcxt->toc, PARALLEL_KEY_QUERY_TEXT, query_string);
827
828
  /* Store serialized PlannedStmt. */
829
0
  pstmt_space = shm_toc_allocate(pcxt->toc, pstmt_len);
830
0
  memcpy(pstmt_space, pstmt_data, pstmt_len);
831
0
  shm_toc_insert(pcxt->toc, PARALLEL_KEY_PLANNEDSTMT, pstmt_space);
832
833
  /* Store serialized ParamListInfo. */
834
0
  paramlistinfo_space = shm_toc_allocate(pcxt->toc, paramlistinfo_len);
835
0
  shm_toc_insert(pcxt->toc, PARALLEL_KEY_PARAMLISTINFO, paramlistinfo_space);
836
0
  SerializeParamList(estate->es_param_list_info, &paramlistinfo_space);
837
838
  /* Allocate space for each worker's BufferUsage; no need to initialize. */
839
0
  bufusage_space = shm_toc_allocate(pcxt->toc,
840
0
                    mul_size(sizeof(BufferUsage), pcxt->nworkers));
841
0
  shm_toc_insert(pcxt->toc, PARALLEL_KEY_BUFFER_USAGE, bufusage_space);
842
0
  pei->buffer_usage = bufusage_space;
843
844
  /* Same for WalUsage. */
845
0
  walusage_space = shm_toc_allocate(pcxt->toc,
846
0
                    mul_size(sizeof(WalUsage), pcxt->nworkers));
847
0
  shm_toc_insert(pcxt->toc, PARALLEL_KEY_WAL_USAGE, walusage_space);
848
0
  pei->wal_usage = walusage_space;
849
850
  /* Set up the tuple queues that the workers will write into. */
851
0
  pei->tqueue = ExecParallelSetupTupleQueues(pcxt, false);
852
853
  /* We don't need the TupleQueueReaders yet, though. */
854
0
  pei->reader = NULL;
855
856
  /*
857
   * If instrumentation options were supplied, allocate space for the data.
858
   * It only gets partially initialized here; the rest happens during
859
   * ExecParallelInitializeDSM.
860
   */
861
0
  if (estate->es_instrument)
862
0
  {
863
0
    NodeInstrumentation *instrument;
864
0
    int     i;
865
866
0
    instrumentation = shm_toc_allocate(pcxt->toc, instrumentation_len);
867
0
    instrumentation->instrument_options = estate->es_instrument;
868
0
    instrumentation->instrument_offset = instrument_offset;
869
0
    instrumentation->num_workers = nworkers;
870
0
    instrumentation->num_plan_nodes = e.nnodes;
871
0
    instrument = GetInstrumentationArray(instrumentation);
872
0
    for (i = 0; i < nworkers * e.nnodes; ++i)
873
0
      InstrInitNode(&instrument[i], estate->es_instrument, false);
874
0
    shm_toc_insert(pcxt->toc, PARALLEL_KEY_INSTRUMENTATION,
875
0
             instrumentation);
876
0
    pei->instrumentation = instrumentation;
877
878
0
    if (estate->es_jit_flags != PGJIT_NONE)
879
0
    {
880
0
      jit_instrumentation = shm_toc_allocate(pcxt->toc,
881
0
                           jit_instrumentation_len);
882
0
      jit_instrumentation->num_workers = nworkers;
883
0
      memset(jit_instrumentation->jit_instr, 0,
884
0
           sizeof(JitInstrumentation) * nworkers);
885
0
      shm_toc_insert(pcxt->toc, PARALLEL_KEY_JIT_INSTRUMENTATION,
886
0
               jit_instrumentation);
887
0
      pei->jit_instrumentation = jit_instrumentation;
888
0
    }
889
0
  }
890
891
  /*
892
   * Create a DSA area that can be used by the leader and all workers.
893
   * (However, if we failed to create a DSM and are using private memory
894
   * instead, then skip this.)
895
   */
896
0
  if (pcxt->seg != NULL)
897
0
  {
898
0
    char     *area_space;
899
900
0
    area_space = shm_toc_allocate(pcxt->toc, dsa_minsize);
901
0
    shm_toc_insert(pcxt->toc, PARALLEL_KEY_DSA, area_space);
902
0
    pei->area = dsa_create_in_place(area_space, dsa_minsize,
903
0
                    LWTRANCHE_PARALLEL_QUERY_DSA,
904
0
                    pcxt->seg);
905
906
    /*
907
     * Serialize parameters, if any, using DSA storage.  We don't dare use
908
     * the main parallel query DSM for this because we might relaunch
909
     * workers after the values have changed (and thus the amount of
910
     * storage required has changed).
911
     */
912
0
    if (!bms_is_empty(sendParams))
913
0
    {
914
0
      pei->param_exec = SerializeParamExecParams(estate, sendParams,
915
0
                             pei->area);
916
0
      fpes->param_exec = pei->param_exec;
917
0
    }
918
0
  }
919
920
  /*
921
   * Give parallel-aware nodes a chance to initialize their shared data.
922
   * This also initializes the elements of instrumentation->ps_instrument,
923
   * if it exists.
924
   */
925
0
  d.pcxt = pcxt;
926
0
  d.instrumentation = instrumentation;
927
0
  d.nnodes = 0;
928
929
  /* Install our DSA area while initializing the plan. */
930
0
  estate->es_query_dsa = pei->area;
931
0
  ExecParallelInitializeDSM(planstate, &d);
932
0
  estate->es_query_dsa = NULL;
933
934
  /*
935
   * Make sure that the world hasn't shifted under our feet.  This could
936
   * probably just be an Assert(), but let's be conservative for now.
937
   */
938
0
  if (e.nnodes != d.nnodes)
939
0
    elog(ERROR, "inconsistent count of PlanState nodes");
940
941
  /* OK, we're ready to rock and roll. */
942
0
  return pei;
943
0
}
944
945
/*
946
 * Set up tuple queue readers to read the results of a parallel subplan.
947
 *
948
 * This is separate from ExecInitParallelPlan() because we can launch the
949
 * worker processes and let them start doing something before we do this.
950
 */
951
void
952
ExecParallelCreateReaders(ParallelExecutorInfo *pei)
953
0
{
954
0
  int     nworkers = pei->pcxt->nworkers_launched;
955
0
  int     i;
956
957
0
  Assert(pei->reader == NULL);
958
959
0
  if (nworkers > 0)
960
0
  {
961
0
    pei->reader = palloc_array(TupleQueueReader *, nworkers);
962
963
0
    for (i = 0; i < nworkers; i++)
964
0
    {
965
0
      shm_mq_set_handle(pei->tqueue[i],
966
0
                pei->pcxt->worker[i].bgwhandle);
967
0
      pei->reader[i] = CreateTupleQueueReader(pei->tqueue[i]);
968
0
    }
969
0
  }
970
0
}
971
972
/*
973
 * Re-initialize the parallel executor shared memory state before launching
974
 * a fresh batch of workers.
975
 */
976
void
977
ExecParallelReinitialize(PlanState *planstate,
978
             ParallelExecutorInfo *pei,
979
             Bitmapset *sendParams)
980
0
{
981
0
  EState     *estate = planstate->state;
982
0
  FixedParallelExecutorState *fpes;
983
984
  /* Old workers must already be shut down */
985
0
  Assert(pei->finished);
986
987
  /*
988
   * Force any initplan outputs that we're going to pass to workers to be
989
   * evaluated, if they weren't already (see comments in
990
   * ExecInitParallelPlan).
991
   */
992
0
  ExecSetParamPlanMulti(sendParams, GetPerTupleExprContext(estate));
993
994
0
  ReinitializeParallelDSM(pei->pcxt);
995
0
  pei->tqueue = ExecParallelSetupTupleQueues(pei->pcxt, true);
996
0
  pei->reader = NULL;
997
0
  pei->finished = false;
998
999
0
  fpes = shm_toc_lookup(pei->pcxt->toc, PARALLEL_KEY_EXECUTOR_FIXED, false);
1000
1001
  /* Free any serialized parameters from the last round. */
1002
0
  if (DsaPointerIsValid(fpes->param_exec))
1003
0
  {
1004
0
    dsa_free(pei->area, fpes->param_exec);
1005
0
    fpes->param_exec = InvalidDsaPointer;
1006
0
  }
1007
1008
  /* Serialize current parameter values if required. */
1009
0
  if (!bms_is_empty(sendParams))
1010
0
  {
1011
0
    pei->param_exec = SerializeParamExecParams(estate, sendParams,
1012
0
                           pei->area);
1013
0
    fpes->param_exec = pei->param_exec;
1014
0
  }
1015
1016
  /* Traverse plan tree and let each child node reset associated state. */
1017
0
  estate->es_query_dsa = pei->area;
1018
0
  ExecParallelReInitializeDSM(planstate, pei->pcxt);
1019
0
  estate->es_query_dsa = NULL;
1020
0
}
1021
1022
/*
1023
 * Traverse plan tree to reinitialize per-node dynamic shared memory state
1024
 */
1025
static bool
1026
ExecParallelReInitializeDSM(PlanState *planstate,
1027
              ParallelContext *pcxt)
1028
0
{
1029
0
  if (planstate == NULL)
1030
0
    return false;
1031
1032
  /*
1033
   * Call reinitializers for DSM-using plan nodes.
1034
   */
1035
0
  switch (nodeTag(planstate))
1036
0
  {
1037
0
    case T_SeqScanState:
1038
0
      if (planstate->plan->parallel_aware)
1039
0
        ExecSeqScanReInitializeDSM((SeqScanState *) planstate,
1040
0
                       pcxt);
1041
0
      break;
1042
0
    case T_IndexScanState:
1043
0
      if (planstate->plan->parallel_aware)
1044
0
        ExecIndexScanReInitializeDSM((IndexScanState *) planstate,
1045
0
                       pcxt);
1046
0
      break;
1047
0
    case T_IndexOnlyScanState:
1048
0
      if (planstate->plan->parallel_aware)
1049
0
        ExecIndexOnlyScanReInitializeDSM((IndexOnlyScanState *) planstate,
1050
0
                         pcxt);
1051
0
      break;
1052
0
    case T_ForeignScanState:
1053
0
      if (planstate->plan->parallel_aware)
1054
0
        ExecForeignScanReInitializeDSM((ForeignScanState *) planstate,
1055
0
                         pcxt);
1056
0
      break;
1057
0
    case T_TidRangeScanState:
1058
0
      if (planstate->plan->parallel_aware)
1059
0
        ExecTidRangeScanReInitializeDSM((TidRangeScanState *) planstate,
1060
0
                        pcxt);
1061
0
      break;
1062
0
    case T_AppendState:
1063
0
      if (planstate->plan->parallel_aware)
1064
0
        ExecAppendReInitializeDSM((AppendState *) planstate, pcxt);
1065
0
      break;
1066
0
    case T_CustomScanState:
1067
0
      if (planstate->plan->parallel_aware)
1068
0
        ExecCustomScanReInitializeDSM((CustomScanState *) planstate,
1069
0
                        pcxt);
1070
0
      break;
1071
0
    case T_BitmapHeapScanState:
1072
0
      if (planstate->plan->parallel_aware)
1073
0
        ExecBitmapHeapReInitializeDSM((BitmapHeapScanState *) planstate,
1074
0
                        pcxt);
1075
0
      break;
1076
0
    case T_HashJoinState:
1077
0
      if (planstate->plan->parallel_aware)
1078
0
        ExecHashJoinReInitializeDSM((HashJoinState *) planstate,
1079
0
                      pcxt);
1080
0
      break;
1081
0
    case T_BitmapIndexScanState:
1082
0
    case T_HashState:
1083
0
    case T_SortState:
1084
0
    case T_IncrementalSortState:
1085
0
    case T_MemoizeState:
1086
0
    case T_MaterialState:
1087
      /* these nodes have DSM state, but no reinitialization is required */
1088
0
      break;
1089
1090
0
    default:
1091
0
      break;
1092
0
  }
1093
1094
0
  return planstate_tree_walker(planstate, ExecParallelReInitializeDSM, pcxt);
1095
0
}
1096
1097
/*
1098
 * Copy instrumentation information about this node and its descendants from
1099
 * dynamic shared memory.
1100
 */
1101
static bool
1102
ExecParallelRetrieveInstrumentation(PlanState *planstate,
1103
                  SharedExecutorInstrumentation *instrumentation)
1104
0
{
1105
0
  NodeInstrumentation *instrument;
1106
0
  int     i;
1107
0
  int     n;
1108
0
  int     ibytes;
1109
0
  int     plan_node_id = planstate->plan->plan_node_id;
1110
0
  MemoryContext oldcontext;
1111
1112
  /* Find the instrumentation for this node. */
1113
0
  for (i = 0; i < instrumentation->num_plan_nodes; ++i)
1114
0
    if (instrumentation->plan_node_id[i] == plan_node_id)
1115
0
      break;
1116
0
  if (i >= instrumentation->num_plan_nodes)
1117
0
    elog(ERROR, "plan node %d not found", plan_node_id);
1118
1119
  /* Accumulate the statistics from all workers. */
1120
0
  instrument = GetInstrumentationArray(instrumentation);
1121
0
  instrument += i * instrumentation->num_workers;
1122
0
  for (n = 0; n < instrumentation->num_workers; ++n)
1123
0
    InstrAggNode(planstate->instrument, &instrument[n]);
1124
1125
  /*
1126
   * Also store the per-worker detail.
1127
   *
1128
   * Worker instrumentation should be allocated in the same context as the
1129
   * regular instrumentation information, which is the per-query context.
1130
   * Switch into per-query memory context.
1131
   */
1132
0
  oldcontext = MemoryContextSwitchTo(planstate->state->es_query_cxt);
1133
0
  ibytes = mul_size(instrumentation->num_workers, sizeof(NodeInstrumentation));
1134
0
  planstate->worker_instrument =
1135
0
    palloc(ibytes + offsetof(WorkerNodeInstrumentation, instrument));
1136
0
  MemoryContextSwitchTo(oldcontext);
1137
1138
0
  planstate->worker_instrument->num_workers = instrumentation->num_workers;
1139
0
  memcpy(planstate->worker_instrument->instrument, instrument, ibytes);
1140
1141
  /* Perform any node-type-specific work that needs to be done. */
1142
0
  switch (nodeTag(planstate))
1143
0
  {
1144
0
    case T_IndexScanState:
1145
0
      ExecIndexScanRetrieveInstrumentation((IndexScanState *) planstate);
1146
0
      break;
1147
0
    case T_IndexOnlyScanState:
1148
0
      ExecIndexOnlyScanRetrieveInstrumentation((IndexOnlyScanState *) planstate);
1149
0
      break;
1150
0
    case T_BitmapIndexScanState:
1151
0
      ExecBitmapIndexScanRetrieveInstrumentation((BitmapIndexScanState *) planstate);
1152
0
      break;
1153
0
    case T_SortState:
1154
0
      ExecSortRetrieveInstrumentation((SortState *) planstate);
1155
0
      break;
1156
0
    case T_IncrementalSortState:
1157
0
      ExecIncrementalSortRetrieveInstrumentation((IncrementalSortState *) planstate);
1158
0
      break;
1159
0
    case T_HashState:
1160
0
      ExecHashRetrieveInstrumentation((HashState *) planstate);
1161
0
      break;
1162
0
    case T_AggState:
1163
0
      ExecAggRetrieveInstrumentation((AggState *) planstate);
1164
0
      break;
1165
0
    case T_MemoizeState:
1166
0
      ExecMemoizeRetrieveInstrumentation((MemoizeState *) planstate);
1167
0
      break;
1168
0
    case T_MaterialState:
1169
0
      ExecMaterialRetrieveInstrumentation((MaterialState *) planstate);
1170
0
      break;
1171
0
    case T_BitmapHeapScanState:
1172
0
      ExecBitmapHeapRetrieveInstrumentation((BitmapHeapScanState *) planstate);
1173
0
      break;
1174
0
    case T_SeqScanState:
1175
0
      ExecSeqScanRetrieveInstrumentation((SeqScanState *) planstate);
1176
0
      break;
1177
0
    case T_TidRangeScanState:
1178
0
      ExecTidRangeScanRetrieveInstrumentation((TidRangeScanState *) planstate);
1179
0
      break;
1180
0
    default:
1181
0
      break;
1182
0
  }
1183
1184
0
  return planstate_tree_walker(planstate, ExecParallelRetrieveInstrumentation,
1185
0
                 instrumentation);
1186
0
}
1187
1188
/*
1189
 * Add up the workers' JIT instrumentation from dynamic shared memory.
1190
 */
1191
static void
1192
ExecParallelRetrieveJitInstrumentation(PlanState *planstate,
1193
                     SharedJitInstrumentation *shared_jit)
1194
0
{
1195
0
  JitInstrumentation *combined;
1196
0
  int     ibytes;
1197
1198
0
  int     n;
1199
1200
  /*
1201
   * Accumulate worker JIT instrumentation into the combined JIT
1202
   * instrumentation, allocating it if required.
1203
   */
1204
0
  if (!planstate->state->es_jit_worker_instr)
1205
0
    planstate->state->es_jit_worker_instr =
1206
0
      MemoryContextAllocZero(planstate->state->es_query_cxt, sizeof(JitInstrumentation));
1207
0
  combined = planstate->state->es_jit_worker_instr;
1208
1209
  /* Accumulate all the workers' instrumentations. */
1210
0
  for (n = 0; n < shared_jit->num_workers; ++n)
1211
0
    InstrJitAgg(combined, &shared_jit->jit_instr[n]);
1212
1213
  /*
1214
   * Store the per-worker detail.
1215
   *
1216
   * Similar to ExecParallelRetrieveInstrumentation(), allocate the
1217
   * instrumentation in per-query context.
1218
   */
1219
0
  ibytes = offsetof(SharedJitInstrumentation, jit_instr)
1220
0
    + mul_size(shared_jit->num_workers, sizeof(JitInstrumentation));
1221
0
  planstate->worker_jit_instrument =
1222
0
    MemoryContextAlloc(planstate->state->es_query_cxt, ibytes);
1223
1224
0
  memcpy(planstate->worker_jit_instrument, shared_jit, ibytes);
1225
0
}
1226
1227
/*
1228
 * Finish parallel execution.  We wait for parallel workers to finish, and
1229
 * accumulate their buffer/WAL usage.
1230
 */
1231
void
1232
ExecParallelFinish(ParallelExecutorInfo *pei)
1233
0
{
1234
0
  int     nworkers = pei->pcxt->nworkers_launched;
1235
0
  int     i;
1236
1237
  /* Make this be a no-op if called twice in a row. */
1238
0
  if (pei->finished)
1239
0
    return;
1240
1241
  /*
1242
   * Detach from tuple queues ASAP, so that any still-active workers will
1243
   * notice that no further results are wanted.
1244
   */
1245
0
  if (pei->tqueue != NULL)
1246
0
  {
1247
0
    for (i = 0; i < nworkers; i++)
1248
0
      shm_mq_detach(pei->tqueue[i]);
1249
0
    pfree(pei->tqueue);
1250
0
    pei->tqueue = NULL;
1251
0
  }
1252
1253
  /*
1254
   * While we're waiting for the workers to finish, let's get rid of the
1255
   * tuple queue readers.  (Any other local cleanup could be done here too.)
1256
   */
1257
0
  if (pei->reader != NULL)
1258
0
  {
1259
0
    for (i = 0; i < nworkers; i++)
1260
0
      DestroyTupleQueueReader(pei->reader[i]);
1261
0
    pfree(pei->reader);
1262
0
    pei->reader = NULL;
1263
0
  }
1264
1265
  /* Now wait for the workers to finish. */
1266
0
  WaitForParallelWorkersToFinish(pei->pcxt);
1267
1268
  /*
1269
   * Next, accumulate buffer/WAL usage.  (This must wait for the workers to
1270
   * finish, or we might get incomplete data.)
1271
   */
1272
0
  for (i = 0; i < nworkers; i++)
1273
0
    InstrAccumParallelQuery(&pei->buffer_usage[i], &pei->wal_usage[i]);
1274
1275
0
  pei->finished = true;
1276
0
}
1277
1278
/*
1279
 * Accumulate instrumentation, and then clean up whatever ParallelExecutorInfo
1280
 * resources still exist after ExecParallelFinish.  We separate these
1281
 * routines because someone might want to examine the contents of the DSM
1282
 * after ExecParallelFinish and before calling this routine.
1283
 */
1284
void
1285
ExecParallelCleanup(ParallelExecutorInfo *pei)
1286
0
{
1287
  /* Accumulate instrumentation, if any. */
1288
0
  if (pei->instrumentation)
1289
0
    ExecParallelRetrieveInstrumentation(pei->planstate,
1290
0
                      pei->instrumentation);
1291
1292
  /* Accumulate JIT instrumentation, if any. */
1293
0
  if (pei->jit_instrumentation)
1294
0
    ExecParallelRetrieveJitInstrumentation(pei->planstate,
1295
0
                         pei->jit_instrumentation);
1296
1297
  /* Free any serialized parameters. */
1298
0
  if (DsaPointerIsValid(pei->param_exec))
1299
0
  {
1300
0
    dsa_free(pei->area, pei->param_exec);
1301
0
    pei->param_exec = InvalidDsaPointer;
1302
0
  }
1303
0
  if (pei->area != NULL)
1304
0
  {
1305
0
    dsa_detach(pei->area);
1306
0
    pei->area = NULL;
1307
0
  }
1308
0
  if (pei->pcxt != NULL)
1309
0
  {
1310
0
    DestroyParallelContext(pei->pcxt);
1311
0
    pei->pcxt = NULL;
1312
0
  }
1313
0
  pfree(pei);
1314
0
}
1315
1316
/*
1317
 * Create a DestReceiver to write tuples we produce to the shm_mq designated
1318
 * for that purpose.
1319
 */
1320
static DestReceiver *
1321
ExecParallelGetReceiver(dsm_segment *seg, shm_toc *toc)
1322
0
{
1323
0
  char     *mqspace;
1324
0
  shm_mq     *mq;
1325
1326
0
  mqspace = shm_toc_lookup(toc, PARALLEL_KEY_TUPLE_QUEUE, false);
1327
0
  mqspace += ParallelWorkerNumber * PARALLEL_TUPLE_QUEUE_SIZE;
1328
0
  mq = (shm_mq *) mqspace;
1329
0
  shm_mq_set_sender(mq, MyProc);
1330
0
  return CreateTupleQueueDestReceiver(shm_mq_attach(mq, seg, NULL));
1331
0
}
1332
1333
/*
1334
 * Create a QueryDesc for the PlannedStmt we are to execute, and return it.
1335
 */
1336
static QueryDesc *
1337
ExecParallelGetQueryDesc(shm_toc *toc, DestReceiver *receiver,
1338
             int instrument_options)
1339
0
{
1340
0
  char     *pstmtspace;
1341
0
  char     *paramspace;
1342
0
  PlannedStmt *pstmt;
1343
0
  ParamListInfo paramLI;
1344
0
  char     *queryString;
1345
1346
  /* Get the query string from shared memory */
1347
0
  queryString = shm_toc_lookup(toc, PARALLEL_KEY_QUERY_TEXT, false);
1348
1349
  /* Reconstruct leader-supplied PlannedStmt. */
1350
0
  pstmtspace = shm_toc_lookup(toc, PARALLEL_KEY_PLANNEDSTMT, false);
1351
0
  pstmt = (PlannedStmt *) stringToNode(pstmtspace);
1352
1353
  /* Reconstruct ParamListInfo. */
1354
0
  paramspace = shm_toc_lookup(toc, PARALLEL_KEY_PARAMLISTINFO, false);
1355
0
  paramLI = RestoreParamList(&paramspace);
1356
1357
  /* Create a QueryDesc for the query. */
1358
0
  return CreateQueryDesc(pstmt,
1359
0
               queryString,
1360
0
               GetActiveSnapshot(), InvalidSnapshot,
1361
0
               receiver, paramLI, NULL, instrument_options);
1362
0
}
1363
1364
/*
1365
 * Copy instrumentation information from this node and its descendants into
1366
 * dynamic shared memory, so that the parallel leader can retrieve it.
1367
 */
1368
static bool
1369
ExecParallelReportInstrumentation(PlanState *planstate,
1370
                  SharedExecutorInstrumentation *instrumentation)
1371
0
{
1372
0
  int     i;
1373
0
  int     plan_node_id = planstate->plan->plan_node_id;
1374
0
  NodeInstrumentation *instrument;
1375
1376
0
  InstrEndLoop(planstate->instrument);
1377
1378
  /*
1379
   * If we shuffled the plan_node_id values in ps_instrument into sorted
1380
   * order, we could use binary search here.  This might matter someday if
1381
   * we're pushing down sufficiently large plan trees.  For now, do it the
1382
   * slow, dumb way.
1383
   */
1384
0
  for (i = 0; i < instrumentation->num_plan_nodes; ++i)
1385
0
    if (instrumentation->plan_node_id[i] == plan_node_id)
1386
0
      break;
1387
0
  if (i >= instrumentation->num_plan_nodes)
1388
0
    elog(ERROR, "plan node %d not found", plan_node_id);
1389
1390
  /*
1391
   * Add our statistics to the per-node, per-worker totals.  It's possible
1392
   * that this could happen more than once if we relaunched workers.
1393
   */
1394
0
  instrument = GetInstrumentationArray(instrumentation);
1395
0
  instrument += i * instrumentation->num_workers;
1396
0
  Assert(IsParallelWorker());
1397
0
  Assert(ParallelWorkerNumber < instrumentation->num_workers);
1398
0
  InstrAggNode(&instrument[ParallelWorkerNumber], planstate->instrument);
1399
1400
0
  return planstate_tree_walker(planstate, ExecParallelReportInstrumentation,
1401
0
                 instrumentation);
1402
0
}
1403
1404
/*
1405
 * Initialize the PlanState and its descendants with the information
1406
 * retrieved from shared memory.  This has to be done once the PlanState
1407
 * is allocated and initialized by executor; that is, after ExecutorStart().
1408
 */
1409
static bool
1410
ExecParallelInitializeWorker(PlanState *planstate, ParallelWorkerContext *pwcxt)
1411
0
{
1412
0
  if (planstate == NULL)
1413
0
    return false;
1414
1415
0
  switch (nodeTag(planstate))
1416
0
  {
1417
0
    case T_SeqScanState:
1418
0
      if (planstate->plan->parallel_aware)
1419
0
        ExecSeqScanInitializeWorker((SeqScanState *) planstate, pwcxt);
1420
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
1421
0
      ExecSeqScanInstrumentInitWorker((SeqScanState *) planstate, pwcxt);
1422
0
      break;
1423
0
    case T_IndexScanState:
1424
0
      if (planstate->plan->parallel_aware)
1425
0
        ExecIndexScanInitializeWorker((IndexScanState *) planstate,
1426
0
                        pwcxt);
1427
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
1428
0
      ExecIndexScanInstrumentInitWorker((IndexScanState *) planstate,
1429
0
                        pwcxt);
1430
0
      break;
1431
0
    case T_IndexOnlyScanState:
1432
0
      if (planstate->plan->parallel_aware)
1433
0
        ExecIndexOnlyScanInitializeWorker((IndexOnlyScanState *) planstate,
1434
0
                          pwcxt);
1435
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
1436
0
      ExecIndexOnlyScanInstrumentInitWorker((IndexOnlyScanState *) planstate,
1437
0
                          pwcxt);
1438
0
      break;
1439
0
    case T_BitmapIndexScanState:
1440
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
1441
0
      ExecBitmapIndexScanInitializeWorker((BitmapIndexScanState *) planstate,
1442
0
                        pwcxt);
1443
0
      break;
1444
0
    case T_ForeignScanState:
1445
0
      if (planstate->plan->parallel_aware)
1446
0
        ExecForeignScanInitializeWorker((ForeignScanState *) planstate,
1447
0
                        pwcxt);
1448
0
      break;
1449
0
    case T_TidRangeScanState:
1450
0
      if (planstate->plan->parallel_aware)
1451
0
        ExecTidRangeScanInitializeWorker((TidRangeScanState *) planstate,
1452
0
                         pwcxt);
1453
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
1454
0
      ExecTidRangeScanInstrumentInitWorker((TidRangeScanState *) planstate,
1455
0
                         pwcxt);
1456
0
      break;
1457
0
    case T_AppendState:
1458
0
      if (planstate->plan->parallel_aware)
1459
0
        ExecAppendInitializeWorker((AppendState *) planstate, pwcxt);
1460
0
      break;
1461
0
    case T_CustomScanState:
1462
0
      if (planstate->plan->parallel_aware)
1463
0
        ExecCustomScanInitializeWorker((CustomScanState *) planstate,
1464
0
                         pwcxt);
1465
0
      break;
1466
0
    case T_BitmapHeapScanState:
1467
0
      if (planstate->plan->parallel_aware)
1468
0
        ExecBitmapHeapInitializeWorker((BitmapHeapScanState *) planstate,
1469
0
                         pwcxt);
1470
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
1471
0
      ExecBitmapHeapInstrumentInitWorker((BitmapHeapScanState *) planstate,
1472
0
                         pwcxt);
1473
0
      break;
1474
0
    case T_HashJoinState:
1475
0
      if (planstate->plan->parallel_aware)
1476
0
        ExecHashJoinInitializeWorker((HashJoinState *) planstate,
1477
0
                       pwcxt);
1478
0
      break;
1479
0
    case T_HashState:
1480
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
1481
0
      ExecHashInitializeWorker((HashState *) planstate, pwcxt);
1482
0
      break;
1483
0
    case T_SortState:
1484
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
1485
0
      ExecSortInitializeWorker((SortState *) planstate, pwcxt);
1486
0
      break;
1487
0
    case T_IncrementalSortState:
1488
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
1489
0
      ExecIncrementalSortInitializeWorker((IncrementalSortState *) planstate,
1490
0
                        pwcxt);
1491
0
      break;
1492
0
    case T_AggState:
1493
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
1494
0
      ExecAggInitializeWorker((AggState *) planstate, pwcxt);
1495
0
      break;
1496
0
    case T_MemoizeState:
1497
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
1498
0
      ExecMemoizeInitializeWorker((MemoizeState *) planstate, pwcxt);
1499
0
      break;
1500
0
    case T_MaterialState:
1501
      /* even when not parallel-aware, for EXPLAIN ANALYZE */
1502
0
      ExecMaterialInitializeWorker((MaterialState *) planstate, pwcxt);
1503
0
      break;
1504
0
    default:
1505
0
      break;
1506
0
  }
1507
1508
0
  return planstate_tree_walker(planstate, ExecParallelInitializeWorker,
1509
0
                 pwcxt);
1510
0
}
1511
1512
/*
1513
 * Main entrypoint for parallel query worker processes.
1514
 *
1515
 * We reach this function from ParallelWorkerMain, so the setup necessary to
1516
 * create a sensible parallel environment has already been done;
1517
 * ParallelWorkerMain worries about stuff like the transaction state, combo
1518
 * CID mappings, and GUC values, so we don't need to deal with any of that
1519
 * here.
1520
 *
1521
 * Our job is to deal with concerns specific to the executor.  The parallel
1522
 * group leader will have stored a serialized PlannedStmt, and it's our job
1523
 * to execute that plan and write the resulting tuples to the appropriate
1524
 * tuple queue.  Various bits of supporting information that we need in order
1525
 * to do this are also stored in the dsm_segment and can be accessed through
1526
 * the shm_toc.
1527
 */
1528
void
1529
ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
1530
0
{
1531
0
  FixedParallelExecutorState *fpes;
1532
0
  BufferUsage *buffer_usage;
1533
0
  WalUsage   *wal_usage;
1534
0
  DestReceiver *receiver;
1535
0
  QueryDesc  *queryDesc;
1536
0
  SharedExecutorInstrumentation *instrumentation;
1537
0
  SharedJitInstrumentation *jit_instrumentation;
1538
0
  int     instrument_options = 0;
1539
0
  void     *area_space;
1540
0
  dsa_area   *area;
1541
0
  ParallelWorkerContext pwcxt;
1542
1543
  /* Get fixed-size state. */
1544
0
  fpes = shm_toc_lookup(toc, PARALLEL_KEY_EXECUTOR_FIXED, false);
1545
1546
  /* Set up DestReceiver, SharedExecutorInstrumentation, and QueryDesc. */
1547
0
  receiver = ExecParallelGetReceiver(seg, toc);
1548
0
  instrumentation = shm_toc_lookup(toc, PARALLEL_KEY_INSTRUMENTATION, true);
1549
0
  if (instrumentation != NULL)
1550
0
    instrument_options = instrumentation->instrument_options;
1551
0
  jit_instrumentation = shm_toc_lookup(toc, PARALLEL_KEY_JIT_INSTRUMENTATION,
1552
0
                     true);
1553
0
  queryDesc = ExecParallelGetQueryDesc(toc, receiver, instrument_options);
1554
1555
  /* Setting debug_query_string for individual workers */
1556
0
  debug_query_string = queryDesc->sourceText;
1557
1558
  /* Report workers' query for monitoring purposes */
1559
0
  pgstat_report_activity(STATE_RUNNING, debug_query_string);
1560
1561
  /* Attach to the dynamic shared memory area. */
1562
0
  area_space = shm_toc_lookup(toc, PARALLEL_KEY_DSA, false);
1563
0
  area = dsa_attach_in_place(area_space, seg);
1564
1565
  /* Start up the executor */
1566
0
  queryDesc->plannedstmt->jitFlags = fpes->jit_flags;
1567
0
  ExecutorStart(queryDesc, fpes->eflags);
1568
1569
  /* Special executor initialization steps for parallel workers */
1570
0
  queryDesc->planstate->state->es_query_dsa = area;
1571
0
  if (DsaPointerIsValid(fpes->param_exec))
1572
0
  {
1573
0
    char     *paramexec_space;
1574
1575
0
    paramexec_space = dsa_get_address(area, fpes->param_exec);
1576
0
    RestoreParamExecParams(paramexec_space, queryDesc->estate);
1577
0
  }
1578
0
  pwcxt.toc = toc;
1579
0
  pwcxt.seg = seg;
1580
0
  ExecParallelInitializeWorker(queryDesc->planstate, &pwcxt);
1581
1582
  /* Pass down any tuple bound */
1583
0
  ExecSetTupleBound(fpes->tuples_needed, queryDesc->planstate);
1584
1585
  /*
1586
   * Prepare to track buffer/WAL usage during query execution.
1587
   *
1588
   * We do this after starting up the executor to match what happens in the
1589
   * leader, which also doesn't count buffer accesses and WAL activity that
1590
   * occur during executor startup.
1591
   */
1592
0
  InstrStartParallelQuery();
1593
1594
  /*
1595
   * Run the plan.  If we specified a tuple bound, be careful not to demand
1596
   * more tuples than that.
1597
   */
1598
0
  ExecutorRun(queryDesc,
1599
0
        ForwardScanDirection,
1600
0
        fpes->tuples_needed < 0 ? (int64) 0 : fpes->tuples_needed);
1601
1602
  /* Shut down the executor */
1603
0
  ExecutorFinish(queryDesc);
1604
1605
  /* Report buffer/WAL usage during parallel execution. */
1606
0
  buffer_usage = shm_toc_lookup(toc, PARALLEL_KEY_BUFFER_USAGE, false);
1607
0
  wal_usage = shm_toc_lookup(toc, PARALLEL_KEY_WAL_USAGE, false);
1608
0
  InstrEndParallelQuery(&buffer_usage[ParallelWorkerNumber],
1609
0
              &wal_usage[ParallelWorkerNumber]);
1610
1611
  /* Report instrumentation data if any instrumentation options are set. */
1612
0
  if (instrumentation != NULL)
1613
0
    ExecParallelReportInstrumentation(queryDesc->planstate,
1614
0
                      instrumentation);
1615
1616
  /* Report JIT instrumentation data if any */
1617
0
  if (queryDesc->estate->es_jit && jit_instrumentation != NULL)
1618
0
  {
1619
0
    Assert(ParallelWorkerNumber < jit_instrumentation->num_workers);
1620
0
    jit_instrumentation->jit_instr[ParallelWorkerNumber] =
1621
0
      queryDesc->estate->es_jit->instr;
1622
0
  }
1623
1624
  /* Must do this after capturing instrumentation. */
1625
0
  ExecutorEnd(queryDesc);
1626
1627
  /* Cleanup. */
1628
0
  dsa_detach(area);
1629
0
  FreeQueryDesc(queryDesc);
1630
0
  receiver->rDestroy(receiver);
1631
0
}