Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * nodeTidrangescan.c
4 : : * Routines to support TID range scans of relations
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/nodeTidrangescan.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "access/relscan.h"
18 : : #include "access/sysattr.h"
19 : : #include "access/tableam.h"
20 : : #include "catalog/pg_operator.h"
21 : : #include "executor/execParallel.h"
22 : : #include "executor/executor.h"
23 : : #include "executor/instrument.h"
24 : : #include "executor/nodeTidrangescan.h"
25 : : #include "nodes/nodeFuncs.h"
26 : : #include "utils/rel.h"
27 : :
28 : :
29 : : /*
30 : : * It's sufficient to check varattno to identify the CTID variable, as any
31 : : * Var in the relation scan qual must be for our table. (Even if it's a
32 : : * parameterized scan referencing some other table's CTID, the other table's
33 : : * Var would have become a Param by the time it gets here.)
34 : : */
35 : : #define IsCTIDVar(node) \
36 : : ((node) != NULL && \
37 : : IsA((node), Var) && \
38 : : ((Var *) (node))->varattno == SelfItemPointerAttributeNumber)
39 : :
40 : : typedef enum
41 : : {
42 : : TIDEXPR_UPPER_BOUND,
43 : : TIDEXPR_LOWER_BOUND,
44 : : } TidExprType;
45 : :
46 : : /* Upper or lower range bound for scan */
47 : : typedef struct TidOpExpr
48 : : {
49 : : TidExprType exprtype; /* type of op; lower or upper */
50 : : ExprState *exprstate; /* ExprState for a TID-yielding subexpr */
51 : : bool inclusive; /* whether op is inclusive */
52 : : } TidOpExpr;
53 : :
54 : : /*
55 : : * For the given 'expr', build and return an appropriate TidOpExpr taking into
56 : : * account the expr's operator and operand order.
57 : : */
58 : : static TidOpExpr *
1893 drowley@postgresql.o 59 :CBC 1447 : MakeTidOpExpr(OpExpr *expr, TidRangeScanState *tidstate)
60 : : {
61 : 1447 : Node *arg1 = get_leftop((Expr *) expr);
62 : 1447 : Node *arg2 = get_rightop((Expr *) expr);
63 : 1447 : ExprState *exprstate = NULL;
64 : 1447 : bool invert = false;
65 : : TidOpExpr *tidopexpr;
66 : :
67 [ + - + + : 1447 : if (IsCTIDVar(arg1))
+ - ]
68 : 1423 : exprstate = ExecInitExpr((Expr *) arg2, &tidstate->ss.ps);
69 [ + - + - : 24 : else if (IsCTIDVar(arg2))
+ - ]
70 : : {
71 : 24 : exprstate = ExecInitExpr((Expr *) arg1, &tidstate->ss.ps);
72 : 24 : invert = true;
73 : : }
74 : : else
1893 drowley@postgresql.o 75 [ # # ]:UBC 0 : elog(ERROR, "could not identify CTID variable");
76 : :
146 michael@paquier.xyz 77 :GNC 1447 : tidopexpr = palloc_object(TidOpExpr);
1893 drowley@postgresql.o 78 :CBC 1447 : tidopexpr->inclusive = false; /* for now */
79 : :
80 [ + + + + : 1447 : switch (expr->opno)
- ]
81 : : {
82 : 27 : case TIDLessEqOperator:
83 : 27 : tidopexpr->inclusive = true;
84 : : pg_fallthrough;
85 : 164 : case TIDLessOperator:
86 : 164 : tidopexpr->exprtype = invert ? TIDEXPR_LOWER_BOUND : TIDEXPR_UPPER_BOUND;
87 : 164 : break;
88 : 1191 : case TIDGreaterEqOperator:
89 : 1191 : tidopexpr->inclusive = true;
90 : : pg_fallthrough;
91 : 1283 : case TIDGreaterOperator:
92 : 1283 : tidopexpr->exprtype = invert ? TIDEXPR_UPPER_BOUND : TIDEXPR_LOWER_BOUND;
93 : 1283 : break;
1893 drowley@postgresql.o 94 :UBC 0 : default:
95 [ # # ]: 0 : elog(ERROR, "could not identify CTID operator");
96 : : }
97 : :
1893 drowley@postgresql.o 98 :CBC 1447 : tidopexpr->exprstate = exprstate;
99 : :
100 : 1447 : return tidopexpr;
101 : : }
102 : :
103 : : /*
104 : : * Extract the qual subexpressions that yield TIDs to search for,
105 : : * and compile them into ExprStates if they're ordinary expressions.
106 : : */
107 : : static void
108 : 1398 : TidExprListCreate(TidRangeScanState *tidrangestate)
109 : : {
110 : 1398 : TidRangeScan *node = (TidRangeScan *) tidrangestate->ss.ps.plan;
111 : 1398 : List *tidexprs = NIL;
112 : : ListCell *l;
113 : :
114 [ + - + + : 2845 : foreach(l, node->tidrangequals)
+ + ]
115 : : {
116 : 1447 : OpExpr *opexpr = lfirst(l);
117 : : TidOpExpr *tidopexpr;
118 : :
119 [ - + ]: 1447 : if (!IsA(opexpr, OpExpr))
1893 drowley@postgresql.o 120 [ # # ]:UBC 0 : elog(ERROR, "could not identify CTID expression");
121 : :
1893 drowley@postgresql.o 122 :CBC 1447 : tidopexpr = MakeTidOpExpr(opexpr, tidrangestate);
123 : 1447 : tidexprs = lappend(tidexprs, tidopexpr);
124 : : }
125 : :
126 : 1398 : tidrangestate->trss_tidexprs = tidexprs;
127 : 1398 : }
128 : :
129 : : /* ----------------------------------------------------------------
130 : : * TidRangeEval
131 : : *
132 : : * Compute and set node's block and offset range to scan by evaluating
133 : : * node->trss_tidexprs. Returns false if we detect the range cannot
134 : : * contain any tuples. Returns true if it's possible for the range to
135 : : * contain tuples. We don't bother validating that trss_mintid is less
136 : : * than or equal to trss_maxtid, as the scan_set_tidrange() table AM
137 : : * function will handle that.
138 : : * ----------------------------------------------------------------
139 : : */
140 : : static bool
141 : 1380 : TidRangeEval(TidRangeScanState *node)
142 : : {
143 : 1380 : ExprContext *econtext = node->ss.ps.ps_ExprContext;
144 : : ItemPointerData lowerBound;
145 : : ItemPointerData upperBound;
146 : : ListCell *l;
147 : :
148 : : /*
149 : : * Set the upper and lower bounds to the absolute limits of the range of
150 : : * the ItemPointer type. Below we'll try to narrow this range on either
151 : : * side by looking at the TidOpExprs.
152 : : */
153 : 1380 : ItemPointerSet(&lowerBound, 0, 0);
154 : 1380 : ItemPointerSet(&upperBound, InvalidBlockNumber, PG_UINT16_MAX);
155 : :
156 [ + - + + : 2791 : foreach(l, node->trss_tidexprs)
+ + ]
157 : : {
158 : 1415 : TidOpExpr *tidopexpr = (TidOpExpr *) lfirst(l);
159 : : ItemPointer itemptr;
160 : : bool isNull;
161 : :
162 : : /* Evaluate this bound. */
163 : : itemptr = (ItemPointer)
164 : 1415 : DatumGetPointer(ExecEvalExprSwitchContext(tidopexpr->exprstate,
165 : : econtext,
166 : : &isNull));
167 : :
168 : : /* If the bound is NULL, *nothing* matches the qual. */
169 [ + + ]: 1415 : if (isNull)
170 : 4 : return false;
171 : :
172 [ + + ]: 1411 : if (tidopexpr->exprtype == TIDEXPR_LOWER_BOUND)
173 : : {
174 : : ItemPointerData lb;
175 : :
176 : 1233 : ItemPointerCopy(itemptr, &lb);
177 : :
178 : : /*
179 : : * Normalize non-inclusive ranges to become inclusive. The
180 : : * resulting ItemPointer here may not be a valid item pointer.
181 : : */
182 [ + + ]: 1233 : if (!tidopexpr->inclusive)
183 : 70 : ItemPointerInc(&lb);
184 : :
185 : : /* Check if we can narrow the range using this qual */
186 [ + - ]: 1233 : if (ItemPointerCompare(&lb, &lowerBound) > 0)
187 : 1233 : ItemPointerCopy(&lb, &lowerBound);
188 : : }
189 : :
190 [ + - ]: 178 : else if (tidopexpr->exprtype == TIDEXPR_UPPER_BOUND)
191 : : {
192 : : ItemPointerData ub;
193 : :
194 : 178 : ItemPointerCopy(itemptr, &ub);
195 : :
196 : : /*
197 : : * Normalize non-inclusive ranges to become inclusive. The
198 : : * resulting ItemPointer here may not be a valid item pointer.
199 : : */
200 [ + + ]: 178 : if (!tidopexpr->inclusive)
201 : 103 : ItemPointerDec(&ub);
202 : :
203 : : /* Check if we can narrow the range using this qual */
204 [ + - ]: 178 : if (ItemPointerCompare(&ub, &upperBound) < 0)
205 : 178 : ItemPointerCopy(&ub, &upperBound);
206 : : }
207 : : }
208 : :
209 : 1376 : ItemPointerCopy(&lowerBound, &node->trss_mintid);
210 : 1376 : ItemPointerCopy(&upperBound, &node->trss_maxtid);
211 : :
212 : 1376 : return true;
213 : : }
214 : :
215 : : /* ----------------------------------------------------------------
216 : : * TidRangeNext
217 : : *
218 : : * Retrieve a tuple from the TidRangeScan node's currentRelation
219 : : * using the TIDs in the TidRangeScanState information.
220 : : *
221 : : * ----------------------------------------------------------------
222 : : */
223 : : static TupleTableSlot *
224 : 6743 : TidRangeNext(TidRangeScanState *node)
225 : : {
226 : : TableScanDesc scandesc;
227 : : EState *estate;
228 : : ScanDirection direction;
229 : : TupleTableSlot *slot;
230 : :
231 : : /*
232 : : * extract necessary information from TID scan node
233 : : */
234 : 6743 : scandesc = node->ss.ss_currentScanDesc;
235 : 6743 : estate = node->ss.ps.state;
236 : 6743 : slot = node->ss.ss_ScanTupleSlot;
237 : 6743 : direction = estate->es_direction;
238 : :
239 [ + + ]: 6743 : if (!node->trss_inScan)
240 : : {
241 : : /* First time through, compute TID range to scan */
242 [ + + ]: 1379 : if (!TidRangeEval(node))
243 : 4 : return NULL;
244 : :
245 [ + + ]: 1375 : if (scandesc == NULL)
246 : : {
28 tomas.vondra@postgre 247 :GNC 1235 : uint32 flags = SO_NONE;
248 : :
249 [ + + ]: 1235 : if (ScanRelIsReadOnly(&node->ss))
250 : 1231 : flags |= SO_HINT_REL_READ_ONLY;
251 : :
252 [ - + ]: 1235 : if (estate->es_instrument & INSTRUMENT_IO)
28 tomas.vondra@postgre 253 :UNC 0 : flags |= SO_SCAN_INSTRUMENT;
254 : :
1893 drowley@postgresql.o 255 :CBC 1235 : scandesc = table_beginscan_tidrange(node->ss.ss_currentRelation,
256 : : estate->es_snapshot,
257 : : &node->trss_mintid,
258 : : &node->trss_maxtid,
259 : : flags);
260 : 1235 : node->ss.ss_currentScanDesc = scandesc;
261 : : }
262 : : else
263 : : {
264 : : /* rescan with the updated TID range */
265 : 140 : table_rescan_tidrange(scandesc, &node->trss_mintid,
266 : : &node->trss_maxtid);
267 : : }
268 : :
269 : 1375 : node->trss_inScan = true;
270 : : }
271 : :
272 : : /* Fetch the next tuple. */
273 [ + + ]: 6739 : if (!table_scan_getnextslot_tidrange(scandesc, direction, slot))
274 : : {
275 : 212 : node->trss_inScan = false;
276 : 212 : ExecClearTuple(slot);
277 : : }
278 : :
279 : 6728 : return slot;
280 : : }
281 : :
282 : : /*
283 : : * TidRangeRecheck -- access method routine to recheck a tuple in EvalPlanQual
284 : : */
285 : : static bool
286 : 1 : TidRangeRecheck(TidRangeScanState *node, TupleTableSlot *slot)
287 : : {
230 288 [ - + ]: 1 : if (!TidRangeEval(node))
230 drowley@postgresql.o 289 :UBC 0 : return false;
290 : :
230 drowley@postgresql.o 291 [ - + ]:CBC 1 : Assert(ItemPointerIsValid(&slot->tts_tid));
292 : :
293 : : /* Recheck the ctid is still within range */
294 [ + - + - ]: 2 : if (ItemPointerCompare(&slot->tts_tid, &node->trss_mintid) < 0 ||
295 : 1 : ItemPointerCompare(&slot->tts_tid, &node->trss_maxtid) > 0)
296 : 1 : return false;
297 : :
1893 drowley@postgresql.o 298 :UBC 0 : return true;
299 : : }
300 : :
301 : : /* ----------------------------------------------------------------
302 : : * ExecTidRangeScan(node)
303 : : *
304 : : * Scans the relation using tids and returns the next qualifying tuple.
305 : : * We call the ExecScan() routine and pass it the appropriate
306 : : * access method functions.
307 : : *
308 : : * Conditions:
309 : : * -- the "cursor" maintained by the AMI is positioned at the tuple
310 : : * returned previously.
311 : : *
312 : : * Initial States:
313 : : * -- the relation indicated is opened for TID range scanning.
314 : : * ----------------------------------------------------------------
315 : : */
316 : : static TupleTableSlot *
1893 drowley@postgresql.o 317 :CBC 6744 : ExecTidRangeScan(PlanState *pstate)
318 : : {
319 : 6744 : TidRangeScanState *node = castNode(TidRangeScanState, pstate);
320 : :
321 : 6744 : return ExecScan(&node->ss,
322 : : (ExecScanAccessMtd) TidRangeNext,
323 : : (ExecScanRecheckMtd) TidRangeRecheck);
324 : : }
325 : :
326 : : /* ----------------------------------------------------------------
327 : : * ExecReScanTidRangeScan(node)
328 : : * ----------------------------------------------------------------
329 : : */
330 : : void
331 : 64 : ExecReScanTidRangeScan(TidRangeScanState *node)
332 : : {
333 : : /* mark scan as not in progress, and tid range list as not computed yet */
334 : 64 : node->trss_inScan = false;
335 : :
336 : : /*
337 : : * We must wait until TidRangeNext before calling table_rescan_tidrange.
338 : : */
339 : 64 : ExecScanReScan(&node->ss);
340 : 64 : }
341 : :
342 : : /* ----------------------------------------------------------------
343 : : * ExecEndTidRangeScan
344 : : *
345 : : * Releases any storage allocated through C routines.
346 : : * Returns nothing.
347 : : * ----------------------------------------------------------------
348 : : */
349 : : void
350 : 243 : ExecEndTidRangeScan(TidRangeScanState *node)
351 : : {
352 : 243 : TableScanDesc scan = node->ss.ss_currentScanDesc;
353 : :
354 : : /* Collect IO stats for this process into shared instrumentation */
28 tomas.vondra@postgre 355 [ - + - - ]:GNC 243 : if (node->trss_sinstrument != NULL && IsParallelWorker())
356 : : {
357 : : TidRangeScanInstrumentation *si;
358 : :
28 tomas.vondra@postgre 359 [ # # ]:UNC 0 : Assert(ParallelWorkerNumber < node->trss_sinstrument->num_workers);
360 : 0 : si = &node->trss_sinstrument->sinstrument[ParallelWorkerNumber];
361 : :
362 [ # # # # ]: 0 : if (scan && scan->rs_instrument)
363 : : {
364 : 0 : AccumulateIOStats(&si->stats.io, &scan->rs_instrument->io);
365 : : }
366 : : }
367 : :
1893 drowley@postgresql.o 368 [ + + ]:CBC 243 : if (scan != NULL)
369 : 160 : table_endscan(scan);
370 : 243 : }
371 : :
372 : : /* ----------------------------------------------------------------
373 : : * ExecInitTidRangeScan
374 : : *
375 : : * Initializes the tid range scan's state information, creates
376 : : * scan keys, and opens the scan relation.
377 : : *
378 : : * Parameters:
379 : : * node: TidRangeScan node produced by the planner.
380 : : * estate: the execution state initialized in InitPlan.
381 : : * ----------------------------------------------------------------
382 : : */
383 : : TidRangeScanState *
384 : 1398 : ExecInitTidRangeScan(TidRangeScan *node, EState *estate, int eflags)
385 : : {
386 : : TidRangeScanState *tidrangestate;
387 : : Relation currentRelation;
388 : :
389 : : /*
390 : : * create state structure
391 : : */
392 : 1398 : tidrangestate = makeNode(TidRangeScanState);
393 : 1398 : tidrangestate->ss.ps.plan = (Plan *) node;
394 : 1398 : tidrangestate->ss.ps.state = estate;
395 : 1398 : tidrangestate->ss.ps.ExecProcNode = ExecTidRangeScan;
396 : :
397 : : /*
398 : : * Miscellaneous initialization
399 : : *
400 : : * create expression context for node
401 : : */
402 : 1398 : ExecAssignExprContext(estate, &tidrangestate->ss.ps);
403 : :
404 : : /*
405 : : * mark scan as not in progress, and TID range as not computed yet
406 : : */
407 : 1398 : tidrangestate->trss_inScan = false;
408 : :
409 : : /*
410 : : * open the scan relation
411 : : */
412 : 1398 : currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
413 : :
414 : 1398 : tidrangestate->ss.ss_currentRelation = currentRelation;
415 : 1398 : tidrangestate->ss.ss_currentScanDesc = NULL; /* no table scan here */
416 : :
417 : : /*
418 : : * get the scan type from the relation descriptor.
419 : : */
420 : 1398 : ExecInitScanTupleSlot(estate, &tidrangestate->ss,
421 : : RelationGetDescr(currentRelation),
422 : : table_slot_callbacks(currentRelation),
423 : : TTS_FLAG_OBEYS_NOT_NULL_CONSTRAINTS);
424 : :
425 : : /*
426 : : * Initialize result type and projection.
427 : : */
428 : 1398 : ExecInitResultTypeTL(&tidrangestate->ss.ps);
429 : 1398 : ExecAssignScanProjectionInfo(&tidrangestate->ss);
430 : :
431 : : /*
432 : : * initialize child expressions
433 : : */
434 : 1398 : tidrangestate->ss.ps.qual =
435 : 1398 : ExecInitQual(node->scan.plan.qual, (PlanState *) tidrangestate);
436 : :
437 : 1398 : TidExprListCreate(tidrangestate);
438 : :
439 : : /*
440 : : * all done.
441 : : */
442 : 1398 : return tidrangestate;
443 : : }
444 : :
445 : : /* ----------------------------------------------------------------
446 : : * Parallel Scan Support
447 : : * ----------------------------------------------------------------
448 : : */
449 : :
450 : : /* ----------------------------------------------------------------
451 : : * ExecTidRangeScanEstimate
452 : : *
453 : : * Compute the amount of space we'll need in the parallel
454 : : * query DSM, and inform pcxt->estimator about our needs.
455 : : * ----------------------------------------------------------------
456 : : */
457 : : void
159 drowley@postgresql.o 458 :GNC 16 : ExecTidRangeScanEstimate(TidRangeScanState *node, ParallelContext *pcxt)
459 : : {
460 : 16 : EState *estate = node->ss.ps.state;
461 : :
462 : 16 : node->trss_pscanlen =
463 : 16 : table_parallelscan_estimate(node->ss.ss_currentRelation,
464 : : estate->es_snapshot);
465 : 16 : shm_toc_estimate_chunk(&pcxt->estimator, node->trss_pscanlen);
466 : 16 : shm_toc_estimate_keys(&pcxt->estimator, 1);
467 : 16 : }
468 : :
469 : : /* ----------------------------------------------------------------
470 : : * ExecTidRangeScanInitializeDSM
471 : : *
472 : : * Set up a parallel TID range scan descriptor.
473 : : * ----------------------------------------------------------------
474 : : */
475 : : void
476 : 16 : ExecTidRangeScanInitializeDSM(TidRangeScanState *node, ParallelContext *pcxt)
477 : : {
478 : 16 : EState *estate = node->ss.ps.state;
479 : : ParallelTableScanDesc pscan;
28 tomas.vondra@postgre 480 : 16 : uint32 flags = SO_NONE;
481 : :
482 [ + - ]: 16 : if (ScanRelIsReadOnly(&node->ss))
483 : 16 : flags |= SO_HINT_REL_READ_ONLY;
484 : :
485 [ - + ]: 16 : if (estate->es_instrument & INSTRUMENT_IO)
28 tomas.vondra@postgre 486 :UNC 0 : flags |= SO_SCAN_INSTRUMENT;
487 : :
159 drowley@postgresql.o 488 :GNC 16 : pscan = shm_toc_allocate(pcxt->toc, node->trss_pscanlen);
489 : 16 : table_parallelscan_initialize(node->ss.ss_currentRelation,
490 : : pscan,
491 : : estate->es_snapshot);
492 : 16 : shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
493 : 16 : node->ss.ss_currentScanDesc =
494 : 16 : table_beginscan_parallel_tidrange(node->ss.ss_currentRelation,
495 : : pscan, flags);
496 : 16 : }
497 : :
498 : : /* ----------------------------------------------------------------
499 : : * ExecTidRangeScanReInitializeDSM
500 : : *
501 : : * Reset shared state before beginning a fresh scan.
502 : : * ----------------------------------------------------------------
503 : : */
504 : : void
159 drowley@postgresql.o 505 :UNC 0 : ExecTidRangeScanReInitializeDSM(TidRangeScanState *node,
506 : : ParallelContext *pcxt)
507 : : {
508 : : ParallelTableScanDesc pscan;
509 : :
510 : 0 : pscan = node->ss.ss_currentScanDesc->rs_parallel;
511 : 0 : table_parallelscan_reinitialize(node->ss.ss_currentRelation, pscan);
512 : 0 : }
513 : :
514 : : /* ----------------------------------------------------------------
515 : : * ExecTidRangeScanInitializeWorker
516 : : *
517 : : * Copy relevant information from TOC into planstate.
518 : : * ----------------------------------------------------------------
519 : : */
520 : : void
159 drowley@postgresql.o 521 :GNC 64 : ExecTidRangeScanInitializeWorker(TidRangeScanState *node,
522 : : ParallelWorkerContext *pwcxt)
523 : : {
524 : : ParallelTableScanDesc pscan;
28 tomas.vondra@postgre 525 : 64 : uint32 flags = SO_NONE;
526 : :
527 [ + - ]: 64 : if (ScanRelIsReadOnly(&node->ss))
528 : 64 : flags |= SO_HINT_REL_READ_ONLY;
529 : :
530 [ - + ]: 64 : if (node->ss.ps.state->es_instrument & INSTRUMENT_IO)
28 tomas.vondra@postgre 531 :UNC 0 : flags |= SO_SCAN_INSTRUMENT;
532 : :
159 drowley@postgresql.o 533 :GNC 64 : pscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
534 : 64 : node->ss.ss_currentScanDesc =
535 : 64 : table_beginscan_parallel_tidrange(node->ss.ss_currentRelation,
536 : : pscan, flags);
28 tomas.vondra@postgre 537 : 64 : }
538 : :
539 : : /*
540 : : * Compute the amount of space we'll need for the shared instrumentation and
541 : : * inform pcxt->estimator.
542 : : */
543 : : void
544 : 16 : ExecTidRangeScanInstrumentEstimate(TidRangeScanState *node,
545 : : ParallelContext *pcxt)
546 : : {
547 : 16 : EState *estate = node->ss.ps.state;
548 : : Size size;
549 : :
550 [ - + - - ]: 16 : if ((estate->es_instrument & INSTRUMENT_IO) == 0 || pcxt->nworkers == 0)
551 : 16 : return;
552 : :
28 tomas.vondra@postgre 553 :UNC 0 : size = add_size(offsetof(SharedTidRangeScanInstrumentation, sinstrument),
554 : 0 : mul_size(pcxt->nworkers, sizeof(TidRangeScanInstrumentation)));
555 : :
556 : 0 : shm_toc_estimate_chunk(&pcxt->estimator, size);
557 : 0 : shm_toc_estimate_keys(&pcxt->estimator, 1);
558 : : }
559 : :
560 : : /*
561 : : * Set up parallel scan instrumentation.
562 : : */
563 : : void
28 tomas.vondra@postgre 564 :GNC 16 : ExecTidRangeScanInstrumentInitDSM(TidRangeScanState *node,
565 : : ParallelContext *pcxt)
566 : : {
567 : 16 : EState *estate = node->ss.ps.state;
568 : : SharedTidRangeScanInstrumentation *sinstrument;
569 : : Size size;
570 : :
571 [ - + - - ]: 16 : if ((estate->es_instrument & INSTRUMENT_IO) == 0 || pcxt->nworkers == 0)
572 : 16 : return;
573 : :
28 tomas.vondra@postgre 574 :UNC 0 : size = add_size(offsetof(SharedTidRangeScanInstrumentation, sinstrument),
575 : 0 : mul_size(pcxt->nworkers, sizeof(TidRangeScanInstrumentation)));
576 : 0 : sinstrument = shm_toc_allocate(pcxt->toc, size);
577 : 0 : memset(sinstrument, 0, size);
578 : 0 : sinstrument->num_workers = pcxt->nworkers;
579 : 0 : shm_toc_insert(pcxt->toc,
580 : 0 : node->ss.ps.plan->plan_node_id +
581 : : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
582 : : sinstrument);
583 : 0 : node->trss_sinstrument = sinstrument;
584 : : }
585 : :
586 : : /*
587 : : * Look up and save the location of the shared instrumentation.
588 : : */
589 : : void
28 tomas.vondra@postgre 590 :GNC 64 : ExecTidRangeScanInstrumentInitWorker(TidRangeScanState *node,
591 : : ParallelWorkerContext *pwcxt)
592 : : {
593 : 64 : EState *estate = node->ss.ps.state;
594 : :
595 [ + - ]: 64 : if ((estate->es_instrument & INSTRUMENT_IO) == 0)
596 : 64 : return;
597 : :
28 tomas.vondra@postgre 598 :UNC 0 : node->trss_sinstrument = shm_toc_lookup(pwcxt->toc,
599 : 0 : node->ss.ps.plan->plan_node_id +
600 : : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
601 : : false);
602 : : }
603 : :
604 : : /*
605 : : * Transfer scan instrumentation from DSM to private memory.
606 : : */
607 : : void
608 : 0 : ExecTidRangeScanRetrieveInstrumentation(TidRangeScanState *node)
609 : : {
610 : 0 : SharedTidRangeScanInstrumentation *sinstrument = node->trss_sinstrument;
611 : : Size size;
612 : :
613 [ # # ]: 0 : if (sinstrument == NULL)
614 : 0 : return;
615 : :
616 : 0 : size = offsetof(SharedTidRangeScanInstrumentation, sinstrument)
617 : 0 : + sinstrument->num_workers * sizeof(TidRangeScanInstrumentation);
618 : :
619 : 0 : node->trss_sinstrument = palloc(size);
620 : 0 : memcpy(node->trss_sinstrument, sinstrument, size);
621 : : }
|