Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * nodeIndexonlyscan.c
4 : : * Routines to support index-only scans
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/nodeIndexonlyscan.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : /*
16 : : * INTERFACE ROUTINES
17 : : * ExecIndexOnlyScan scans an index
18 : : * IndexOnlyNext retrieve next tuple
19 : : * ExecInitIndexOnlyScan creates and initializes state info.
20 : : * ExecReScanIndexOnlyScan rescans the indexed relation.
21 : : * ExecEndIndexOnlyScan releases all storage.
22 : : * ExecIndexOnlyMarkPos marks scan position.
23 : : * ExecIndexOnlyRestrPos restores scan position.
24 : : * ExecIndexOnlyScanEstimate estimates DSM space needed for
25 : : * parallel index-only scan
26 : : * ExecIndexOnlyScanInitializeDSM initialize DSM for parallel
27 : : * index-only scan
28 : : * ExecIndexOnlyScanReInitializeDSM reinitialize DSM for fresh scan
29 : : * ExecIndexOnlyScanInitializeWorker attach to DSM info in parallel worker
30 : : */
31 : : #include "postgres.h"
32 : :
33 : : #include "access/genam.h"
34 : : #include "access/relscan.h"
35 : : #include "access/tableam.h"
36 : : #include "executor/executor.h"
37 : : #include "executor/instrument.h"
38 : : #include "executor/nodeIndexonlyscan.h"
39 : : #include "executor/nodeIndexscan.h"
40 : : #include "miscadmin.h"
41 : : #include "utils/rel.h"
42 : :
43 : :
44 : : static TupleTableSlot *IndexOnlyNext(IndexOnlyScanState *node);
45 : :
46 : :
47 : : /* ----------------------------------------------------------------
48 : : * IndexOnlyNext
49 : : *
50 : : * Retrieve a tuple from the IndexOnlyScan node's index.
51 : : * ----------------------------------------------------------------
52 : : */
53 : : static TupleTableSlot *
5458 tgl@sss.pgh.pa.us 54 :CBC 3866232 : IndexOnlyNext(IndexOnlyScanState *node)
55 : : {
56 : : EState *estate;
57 : : ExprContext *econtext;
58 : : ScanDirection direction;
59 : : IndexScanDesc scandesc;
60 : : TupleTableSlot *slot;
61 : :
62 : : /*
63 : : * extract necessary information from index scan node
64 : : */
65 : 3866232 : estate = node->ss.ps.state;
66 : :
67 : : /*
68 : : * Determine which direction to scan the index in based on the plan's scan
69 : : * direction and the current direction of execution.
70 : : */
1327 drowley@postgresql.o 71 : 3866232 : direction = ScanDirectionCombine(estate->es_direction,
72 : : ((IndexOnlyScan *) node->ss.ps.plan)->indexorderdir);
5458 tgl@sss.pgh.pa.us 73 : 3866232 : scandesc = node->ioss_ScanDesc;
74 : 3866232 : econtext = node->ss.ps.ps_ExprContext;
75 : 3866232 : slot = node->ss.ss_ScanTupleSlot;
76 : :
3483 rhaas@postgresql.org 77 [ + + ]: 3866232 : if (scandesc == NULL)
78 : : {
79 : : /*
80 : : * We reach here if the index only scan is not parallel, or if we're
81 : : * serially executing an index only scan that was planned to be
82 : : * parallel.
83 : : */
84 [ + - ]: 5936 : scandesc = index_beginscan(node->ss.ss_currentRelation,
85 : : node->ioss_RelationDesc,
86 : : true,
87 : : estate->es_snapshot,
88 : : node->ioss_Instrument,
89 : : node->ioss_NumScanKeys,
90 : : node->ioss_NumOrderByKeys,
174 melanieplageman@gmai 91 : 5936 : ScanRelIsReadOnly(&node->ss) ?
92 : : SO_HINT_REL_READ_ONLY : SO_NONE);
93 : :
3483 rhaas@postgresql.org 94 : 5936 : node->ioss_ScanDesc = scandesc;
5 pg@bowt.ie 95 [ - + ]:GNC 5936 : Assert(node->ioss_ScanDesc->xs_want_itup);
96 : :
97 : : /*
98 : : * If no run-time keys to calculate or they are ready, go ahead and
99 : : * pass the scankeys to the index AM.
100 : : */
3483 rhaas@postgresql.org 101 [ + + + - ]:CBC 5936 : if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
102 : 5936 : index_rescan(scandesc,
103 : : node->ioss_ScanKeys,
104 : : node->ioss_NumScanKeys,
105 : : node->ioss_OrderByKeys,
106 : : node->ioss_NumOrderByKeys);
107 : : }
108 : :
109 : : /*
110 : : * OK, now that we have what we need, fetch the next tuple.
111 : : */
5 pg@bowt.ie 112 [ + + ]:GNC 3866236 : while (table_index_getnext_slot(scandesc, direction, slot))
113 : : {
3344 andres@anarazel.de 114 [ + + ]:CBC 3736077 : CHECK_FOR_INTERRUPTS();
115 : :
116 : : /*
117 : : * If the index was lossy, we have to recheck the index quals.
118 : : */
5458 tgl@sss.pgh.pa.us 119 [ + + ]: 3736077 : if (scandesc->xs_recheck)
120 : : {
121 : 9 : econtext->ecxt_scantuple = slot;
1721 122 [ + + ]: 9 : if (!ExecQualAndReset(node->recheckqual, econtext))
123 : : {
124 : : /* Fails recheck, so drop it and loop back for another */
5458 125 [ - + ]: 4 : InstrCountFiltered2(node, 1);
126 : 4 : continue;
127 : : }
128 : : }
129 : :
130 : : /*
131 : : * We don't currently support rechecking ORDER BY distances. (In
132 : : * principle, if the index can support retrieval of the originally
133 : : * indexed value, it should be able to produce an exact distance
134 : : * calculation too. So it's not clear that adding code here for
135 : : * recheck/re-sort would be worth the trouble. But we should at least
136 : : * throw an error if someone tries it.)
137 : : */
4138 138 [ + + + + ]: 3736073 : if (scandesc->numberOfOrderBys > 0 && scandesc->xs_recheckorderby)
139 [ + - ]: 4 : ereport(ERROR,
140 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
141 : : errmsg("lossy distance functions are not supported in index-only scans")));
5458 142 : 3736069 : return slot;
143 : : }
144 : :
145 : : /*
146 : : * if we get here it means the index scan failed so we are at the end of
147 : : * the scan..
148 : : */
149 : 130159 : return ExecClearTuple(slot);
150 : : }
151 : :
152 : : /*
153 : : * IndexOnlyRecheck -- access method routine to recheck a tuple in EvalPlanQual
154 : : *
155 : : * This can't really happen, since an index can't supply CTID which would
156 : : * be necessary data for any potential EvalPlanQual target relation. If it
157 : : * did happen, the EPQ code would pass us the wrong data, namely a heap
158 : : * tuple not an index tuple. So throw an error.
159 : : */
160 : : static bool
5458 tgl@sss.pgh.pa.us 161 :UBC 0 : IndexOnlyRecheck(IndexOnlyScanState *node, TupleTableSlot *slot)
162 : : {
163 [ # # ]: 0 : elog(ERROR, "EvalPlanQual recheck is not supported in index-only scans");
164 : : return false; /* keep compiler quiet */
165 : : }
166 : :
167 : : /* ----------------------------------------------------------------
168 : : * ExecIndexOnlyScan(node)
169 : : * ----------------------------------------------------------------
170 : : */
171 : : static TupleTableSlot *
3352 andres@anarazel.de 172 :CBC 3659783 : ExecIndexOnlyScan(PlanState *pstate)
173 : : {
174 : 3659783 : IndexOnlyScanState *node = castNode(IndexOnlyScanState, pstate);
175 : :
176 : : /*
177 : : * If we have runtime keys and they've not already been set up, do it now.
178 : : */
5458 tgl@sss.pgh.pa.us 179 [ + + + + ]: 3659783 : if (node->ioss_NumRuntimeKeys != 0 && !node->ioss_RuntimeKeysReady)
180 : 371 : ExecReScan((PlanState *) node);
181 : :
182 : 3659783 : return ExecScan(&node->ss,
183 : : (ExecScanAccessMtd) IndexOnlyNext,
184 : : (ExecScanRecheckMtd) IndexOnlyRecheck);
185 : : }
186 : :
187 : : /* ----------------------------------------------------------------
188 : : * ExecReScanIndexOnlyScan(node)
189 : : *
190 : : * Recalculates the values of any scan keys whose value depends on
191 : : * information known at runtime, then rescans the indexed relation.
192 : : *
193 : : * Updating the scan key was formerly done separately in
194 : : * ExecUpdateIndexScanKeys. Integrating it into ReScan makes
195 : : * rescans of indices and relations/general streams more uniform.
196 : : * ----------------------------------------------------------------
197 : : */
198 : : void
199 : 146259 : ExecReScanIndexOnlyScan(IndexOnlyScanState *node)
200 : : {
201 : : /*
202 : : * If we are doing runtime key calculations (ie, any of the index key
203 : : * values weren't simple Consts), compute the new key values. But first,
204 : : * reset the context so we don't leak memory as each outer tuple is
205 : : * scanned. Note this assumes that we will recalculate *all* runtime keys
206 : : * on each call.
207 : : */
208 [ + + ]: 146259 : if (node->ioss_NumRuntimeKeys != 0)
209 : : {
210 : 146186 : ExprContext *econtext = node->ioss_RuntimeContext;
211 : :
212 : 146186 : ResetExprContext(econtext);
213 : 146186 : ExecIndexEvalRuntimeKeys(econtext,
214 : : node->ioss_RuntimeKeys,
215 : : node->ioss_NumRuntimeKeys);
216 : : }
217 : 146259 : node->ioss_RuntimeKeysReady = true;
218 : :
219 : : /* reset index scan */
3500 rhaas@postgresql.org 220 [ + + ]: 146259 : if (node->ioss_ScanDesc)
221 : 144590 : index_rescan(node->ioss_ScanDesc,
222 : : node->ioss_ScanKeys, node->ioss_NumScanKeys,
223 : : node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
224 : :
5458 tgl@sss.pgh.pa.us 225 : 146259 : ExecScanReScan(&node->ss);
226 : 146259 : }
227 : :
228 : :
229 : : /* ----------------------------------------------------------------
230 : : * ExecEndIndexOnlyScan
231 : : * ----------------------------------------------------------------
232 : : */
233 : : void
234 : 10218 : ExecEndIndexOnlyScan(IndexOnlyScanState *node)
235 : : {
236 : : Relation indexRelationDesc;
237 : : IndexScanDesc indexScanDesc;
238 : :
239 : : /*
240 : : * extract information from the node
241 : : */
242 : 10218 : indexRelationDesc = node->ioss_RelationDesc;
243 : 10218 : indexScanDesc = node->ioss_ScanDesc;
244 : :
245 : : /*
246 : : * When ending a parallel worker, copy the statistics gathered by the
247 : : * worker back into shared memory so that it can be picked up by the main
248 : : * process to report in EXPLAIN ANALYZE
249 : : */
558 pg@bowt.ie 250 [ - + - - ]: 10218 : if (node->ioss_SharedInfo != NULL && IsParallelWorker())
251 : : {
252 : : IndexScanInstrumentation *winstrument;
253 : :
190 tomas.vondra@postgre 254 [ # # ]:UBC 0 : Assert(ParallelWorkerNumber < node->ioss_SharedInfo->num_workers);
558 pg@bowt.ie 255 : 0 : winstrument = &node->ioss_SharedInfo->winstrument[ParallelWorkerNumber];
256 : :
257 : : /*
258 : : * We have to accumulate the stats rather than performing a memcpy.
259 : : * When a Gather/GatherMerge node finishes it will perform planner
260 : : * shutdown on the workers. On rescan it will spin up new workers
261 : : * which will have a new IndexOnlyScanState and zeroed stats.
262 : : */
182 263 : 0 : winstrument->nsearches += node->ioss_Instrument->nsearches;
5 pg@bowt.ie 264 :UNC 0 : winstrument->ntabletuplefetches += node->ioss_Instrument->ntabletuplefetches;
265 : : }
266 : :
267 : : /*
268 : : * close the index relation (no-op if we didn't open it)
269 : : */
5458 tgl@sss.pgh.pa.us 270 [ + + ]:CBC 10218 : if (indexScanDesc)
271 : 6069 : index_endscan(indexScanDesc);
272 [ + + ]: 10218 : if (indexRelationDesc)
273 : 8385 : index_close(indexRelationDesc, NoLock);
274 : 10218 : }
275 : :
276 : : /* ----------------------------------------------------------------
277 : : * ExecIndexOnlyMarkPos
278 : : *
279 : : * Note: we assume that no caller attempts to set a mark before having read
280 : : * at least one tuple. Otherwise, ioss_ScanDesc might still be NULL.
281 : : * ----------------------------------------------------------------
282 : : */
283 : : void
284 : 82019 : ExecIndexOnlyMarkPos(IndexOnlyScanState *node)
285 : : {
3158 286 : 82019 : EState *estate = node->ss.ps.state;
2572 andres@anarazel.de 287 : 82019 : EPQState *epqstate = estate->es_epq_active;
288 : :
289 [ - + ]: 82019 : if (epqstate != NULL)
290 : : {
291 : : /*
292 : : * We are inside an EvalPlanQual recheck. If a test tuple exists for
293 : : * this relation, then we shouldn't access the index at all. We would
294 : : * instead need to save, and later restore, the state of the
295 : : * relsubs_done flag, so that re-fetching the test tuple is possible.
296 : : * However, given the assumption that no caller sets a mark at the
297 : : * start of the scan, we can only get here with relsubs_done[i]
298 : : * already set, and so no state need be saved.
299 : : */
3158 tgl@sss.pgh.pa.us 300 :UBC 0 : Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
301 : :
302 [ # # ]: 0 : Assert(scanrelid > 0);
2572 andres@anarazel.de 303 [ # # ]: 0 : if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
304 [ # # ]: 0 : epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
305 : : {
306 : : /* Verify the claim above */
307 [ # # ]: 0 : if (!epqstate->relsubs_done[scanrelid - 1])
3158 tgl@sss.pgh.pa.us 308 [ # # ]: 0 : elog(ERROR, "unexpected ExecIndexOnlyMarkPos call in EPQ recheck");
309 : 0 : return;
310 : : }
311 : : }
312 : :
5458 tgl@sss.pgh.pa.us 313 :CBC 82019 : index_markpos(node->ioss_ScanDesc);
314 : : }
315 : :
316 : : /* ----------------------------------------------------------------
317 : : * ExecIndexOnlyRestrPos
318 : : * ----------------------------------------------------------------
319 : : */
320 : : void
5458 tgl@sss.pgh.pa.us 321 :UBC 0 : ExecIndexOnlyRestrPos(IndexOnlyScanState *node)
322 : : {
3158 323 : 0 : EState *estate = node->ss.ps.state;
2572 andres@anarazel.de 324 : 0 : EPQState *epqstate = estate->es_epq_active;
325 : :
326 [ # # ]: 0 : if (estate->es_epq_active != NULL)
327 : : {
328 : : /* See comments in ExecIndexMarkPos */
3158 tgl@sss.pgh.pa.us 329 : 0 : Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
330 : :
331 [ # # ]: 0 : Assert(scanrelid > 0);
2572 andres@anarazel.de 332 [ # # ]: 0 : if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
333 [ # # ]: 0 : epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
334 : : {
335 : : /* Verify the claim above */
336 [ # # ]: 0 : if (!epqstate->relsubs_done[scanrelid - 1])
3158 tgl@sss.pgh.pa.us 337 [ # # ]: 0 : elog(ERROR, "unexpected ExecIndexOnlyRestrPos call in EPQ recheck");
338 : 0 : return;
339 : : }
340 : : }
341 : :
5458 342 : 0 : index_restrpos(node->ioss_ScanDesc);
343 : : }
344 : :
345 : : /* ----------------------------------------------------------------
346 : : * ExecInitIndexOnlyScan
347 : : *
348 : : * Initializes the index scan's state information, creates
349 : : * scan keys, and opens the base and index relations.
350 : : *
351 : : * Note: index scans have 2 sets of state information because
352 : : * we have to keep track of the base relation and the
353 : : * index relation.
354 : : * ----------------------------------------------------------------
355 : : */
356 : : IndexOnlyScanState *
5458 tgl@sss.pgh.pa.us 357 :CBC 10251 : ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
358 : : {
359 : : IndexOnlyScanState *indexstate;
360 : : Relation currentRelation;
361 : : Relation indexRelation;
362 : : LOCKMODE lockmode;
363 : : TupleDesc tupDesc;
364 : :
365 : : /*
366 : : * create state structure
367 : : */
368 : 10251 : indexstate = makeNode(IndexOnlyScanState);
369 : 10251 : indexstate->ss.ps.plan = (Plan *) node;
370 : 10251 : indexstate->ss.ps.state = estate;
3352 andres@anarazel.de 371 : 10251 : indexstate->ss.ps.ExecProcNode = ExecIndexOnlyScan;
372 : :
373 : : /*
374 : : * Miscellaneous initialization
375 : : *
376 : : * create expression context for node
377 : : */
5458 tgl@sss.pgh.pa.us 378 : 10251 : ExecAssignExprContext(estate, &indexstate->ss.ps);
379 : :
380 : : /*
381 : : * open the scan relation
382 : : */
4894 383 : 10251 : currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
384 : :
5458 385 : 10251 : indexstate->ss.ss_currentRelation = currentRelation;
386 : 10251 : indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
387 : :
388 : : /*
389 : : * Build the scan tuple type using the indextlist generated by the
390 : : * planner. We use this, rather than the index's physical tuple
391 : : * descriptor, because the latter contains storage column types not the
392 : : * types of the original datums. (It's the index AM's responsibility to
393 : : * return suitable data anyway. The table AM fills our slot with that
394 : : * returned data.)
395 : : */
2861 andres@anarazel.de 396 : 10251 : tupDesc = ExecTypeFromTL(node->indextlist);
2750 397 : 10251 : ExecInitScanTupleSlot(estate, &indexstate->ss, tupDesc,
398 : : &TTSOpsVirtual,
399 : : 0);
400 : :
401 : : /*
402 : : * Initialize result type and projection info. The node's targetlist will
403 : : * contain Vars with varno = INDEX_VAR, referencing the scan tuple.
404 : : */
2872 405 : 10251 : ExecInitResultTypeTL(&indexstate->ss.ps);
4151 tgl@sss.pgh.pa.us 406 : 10251 : ExecAssignScanProjectionInfoWithVarno(&indexstate->ss, INDEX_VAR);
407 : :
408 : : /*
409 : : * initialize child expressions
410 : : *
411 : : * Note: we don't initialize all of the indexorderby expression, only the
412 : : * sub-parts corresponding to runtime keys (see below).
413 : : */
3138 andres@anarazel.de 414 : 10251 : indexstate->ss.ps.qual =
415 : 10251 : ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
1721 tgl@sss.pgh.pa.us 416 : 10251 : indexstate->recheckqual =
417 : 10251 : ExecInitQual(node->recheckqual, (PlanState *) indexstate);
418 : :
419 : : /*
420 : : * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
421 : : * here. This allows an index-advisor plugin to EXPLAIN a plan containing
422 : : * references to nonexistent indexes.
423 : : */
5458 424 [ + + ]: 10251 : if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
425 : 1833 : return indexstate;
426 : :
427 : : /* Set up instrumentation of index-only scans if requested */
182 pg@bowt.ie 428 [ + + ]: 8418 : if (estate->es_instrument)
429 : 80 : indexstate->ioss_Instrument = palloc0_object(IndexScanInstrumentation);
430 : :
431 : : /* Open the index relation. */
2726 tgl@sss.pgh.pa.us 432 : 8418 : lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
872 drowley@postgresql.o 433 : 8418 : indexRelation = index_open(node->indexid, lockmode);
434 : 8418 : indexstate->ioss_RelationDesc = indexRelation;
435 : :
436 : : /*
437 : : * Initialize index-specific scan state
438 : : */
5458 tgl@sss.pgh.pa.us 439 : 8418 : indexstate->ioss_RuntimeKeysReady = false;
440 : 8418 : indexstate->ioss_RuntimeKeys = NULL;
441 : 8418 : indexstate->ioss_NumRuntimeKeys = 0;
442 : :
443 : : /*
444 : : * build the index scan keys from the index qualification
445 : : */
446 : 8418 : ExecIndexBuildScanKeys((PlanState *) indexstate,
447 : : indexRelation,
448 : : node->indexqual,
449 : : false,
450 : 8418 : &indexstate->ioss_ScanKeys,
451 : : &indexstate->ioss_NumScanKeys,
452 : : &indexstate->ioss_RuntimeKeys,
453 : : &indexstate->ioss_NumRuntimeKeys,
454 : : NULL, /* no ArrayKeys */
455 : : NULL);
456 : :
457 : : /*
458 : : * any ORDER BY exprs have to be turned into scankeys in the same way
459 : : */
460 : 8418 : ExecIndexBuildScanKeys((PlanState *) indexstate,
461 : : indexRelation,
462 : : node->indexorderby,
463 : : true,
464 : 8418 : &indexstate->ioss_OrderByKeys,
465 : : &indexstate->ioss_NumOrderByKeys,
466 : : &indexstate->ioss_RuntimeKeys,
467 : : &indexstate->ioss_NumRuntimeKeys,
468 : : NULL, /* no ArrayKeys */
469 : : NULL);
470 : :
471 : : /*
472 : : * If we have runtime keys, we need an ExprContext to evaluate them. The
473 : : * node's standard context won't do because we want to reset that context
474 : : * for every tuple. So, build another context just like the other one...
475 : : * -tgl 7/11/00
476 : : */
477 [ + + ]: 8418 : if (indexstate->ioss_NumRuntimeKeys != 0)
478 : : {
479 : 3880 : ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
480 : :
481 : 3880 : ExecAssignExprContext(estate, &indexstate->ss.ps);
482 : 3880 : indexstate->ioss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
483 : 3880 : indexstate->ss.ps.ps_ExprContext = stdecontext;
484 : : }
485 : : else
486 : : {
487 : 4538 : indexstate->ioss_RuntimeContext = NULL;
488 : : }
489 : :
490 : : /*
491 : : * all done.
492 : : */
493 : 8418 : return indexstate;
494 : : }
495 : :
496 : : /* ----------------------------------------------------------------
497 : : * Parallel Index-only Scan Support
498 : : * ----------------------------------------------------------------
499 : : */
500 : :
501 : : /* ----------------------------------------------------------------
502 : : * ExecIndexOnlyScanEstimate
503 : : *
504 : : * Compute the amount of space we'll need in the parallel
505 : : * query DSM, and inform pcxt->estimator about our needs.
506 : : * ----------------------------------------------------------------
507 : : */
508 : : void
3500 rhaas@postgresql.org 509 : 30 : ExecIndexOnlyScanEstimate(IndexOnlyScanState *node,
510 : : ParallelContext *pcxt)
511 : : {
512 : 30 : EState *estate = node->ss.ps.state;
513 : :
514 : 30 : node->ioss_PscanLen = index_parallelscan_estimate(node->ioss_RelationDesc,
515 : : node->ioss_NumScanKeys,
516 : : node->ioss_NumOrderByKeys,
517 : : estate->es_snapshot);
518 : 30 : shm_toc_estimate_chunk(&pcxt->estimator, node->ioss_PscanLen);
519 : 30 : shm_toc_estimate_keys(&pcxt->estimator, 1);
520 : 30 : }
521 : :
522 : : /* ----------------------------------------------------------------
523 : : * ExecIndexOnlyScanInitializeDSM
524 : : *
525 : : * Set up a parallel index-only scan descriptor.
526 : : * ----------------------------------------------------------------
527 : : */
528 : : void
529 : 30 : ExecIndexOnlyScanInitializeDSM(IndexOnlyScanState *node,
530 : : ParallelContext *pcxt)
531 : : {
532 : 30 : EState *estate = node->ss.ps.state;
533 : : ParallelIndexScanDesc piscan;
534 : :
535 : 30 : piscan = shm_toc_allocate(pcxt->toc, node->ioss_PscanLen);
536 : 30 : index_parallelscan_initialize(node->ss.ss_currentRelation,
537 : : node->ioss_RelationDesc,
538 : : estate->es_snapshot,
539 : : piscan);
540 : 30 : shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
541 : :
542 : 30 : node->ioss_ScanDesc =
543 [ + - ]: 30 : index_beginscan_parallel(node->ss.ss_currentRelation,
544 : : node->ioss_RelationDesc,
545 : : true,
546 : : node->ioss_Instrument,
547 : : node->ioss_NumScanKeys,
548 : : node->ioss_NumOrderByKeys,
549 : : piscan,
174 melanieplageman@gmai 550 : 30 : ScanRelIsReadOnly(&node->ss) ?
551 : : SO_HINT_REL_READ_ONLY : SO_NONE);
5 pg@bowt.ie 552 [ - + ]:GNC 30 : Assert(node->ioss_ScanDesc->xs_want_itup);
553 : :
554 : : /*
555 : : * If no run-time keys to calculate or they are ready, go ahead and pass
556 : : * the scankeys to the index AM.
557 : : */
3483 rhaas@postgresql.org 558 [ - + - - ]:CBC 30 : if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
3500 559 : 30 : index_rescan(node->ioss_ScanDesc,
560 : : node->ioss_ScanKeys, node->ioss_NumScanKeys,
561 : : node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
562 : 30 : }
563 : :
564 : : /* ----------------------------------------------------------------
565 : : * ExecIndexOnlyScanReInitializeDSM
566 : : *
567 : : * Reset shared state before beginning a fresh scan.
568 : : * ----------------------------------------------------------------
569 : : */
570 : : void
3308 tgl@sss.pgh.pa.us 571 : 8 : ExecIndexOnlyScanReInitializeDSM(IndexOnlyScanState *node,
572 : : ParallelContext *pcxt)
573 : : {
558 pg@bowt.ie 574 [ - + ]: 8 : Assert(node->ss.ps.plan->parallel_aware);
3308 tgl@sss.pgh.pa.us 575 : 8 : index_parallelrescan(node->ioss_ScanDesc);
576 : 8 : }
577 : :
578 : : /* ----------------------------------------------------------------
579 : : * ExecIndexOnlyScanInitializeWorker
580 : : *
581 : : * Copy relevant information from TOC into planstate.
582 : : * ----------------------------------------------------------------
583 : : */
584 : : void
3230 andres@anarazel.de 585 : 136 : ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node,
586 : : ParallelWorkerContext *pwcxt)
587 : : {
588 : : ParallelIndexScanDesc piscan;
589 : :
590 : 136 : piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
591 : :
3500 rhaas@postgresql.org 592 : 136 : node->ioss_ScanDesc =
593 [ + - ]: 136 : index_beginscan_parallel(node->ss.ss_currentRelation,
594 : : node->ioss_RelationDesc,
595 : : true,
596 : : node->ioss_Instrument,
597 : : node->ioss_NumScanKeys,
598 : : node->ioss_NumOrderByKeys,
599 : : piscan,
174 melanieplageman@gmai 600 : 136 : ScanRelIsReadOnly(&node->ss) ?
601 : : SO_HINT_REL_READ_ONLY : SO_NONE);
5 pg@bowt.ie 602 [ - + ]:GNC 136 : Assert(node->ioss_ScanDesc->xs_want_itup);
603 : :
604 : : /*
605 : : * If no run-time keys to calculate or they are ready, go ahead and pass
606 : : * the scankeys to the index AM.
607 : : */
3483 rhaas@postgresql.org 608 [ - + - - ]:CBC 136 : if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
3500 609 : 136 : index_rescan(node->ioss_ScanDesc,
610 : : node->ioss_ScanKeys, node->ioss_NumScanKeys,
611 : : node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
612 : 136 : }
613 : :
614 : : /*
615 : : * Compute the amount of space we'll need for the shared instrumentation and
616 : : * inform pcxt->estimator.
617 : : */
618 : : void
167 melanieplageman@gmai 619 : 38 : ExecIndexOnlyScanInstrumentEstimate(IndexOnlyScanState *node,
620 : : ParallelContext *pcxt)
621 : : {
622 : : Size size;
623 : :
624 [ - + - - ]: 38 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
625 : 38 : return;
626 : :
627 : : /*
628 : : * This size calculation is trivial enough that we don't bother saving it
629 : : * in the IndexOnlyScanState. We'll recalculate the needed size in
630 : : * ExecIndexOnlyScanInstrumentInitDSM().
631 : : */
166 tomas.vondra@postgre 632 :UBC 0 : size = add_size(offsetof(SharedIndexScanInstrumentation, winstrument),
633 : 0 : mul_size(pcxt->nworkers, sizeof(IndexScanInstrumentation)));
167 melanieplageman@gmai 634 : 0 : shm_toc_estimate_chunk(&pcxt->estimator, size);
635 : 0 : shm_toc_estimate_keys(&pcxt->estimator, 1);
636 : : }
637 : :
638 : : /*
639 : : * Set up parallel index-only scan instrumentation.
640 : : */
641 : : void
167 melanieplageman@gmai 642 :CBC 38 : ExecIndexOnlyScanInstrumentInitDSM(IndexOnlyScanState *node,
643 : : ParallelContext *pcxt)
644 : : {
645 : : Size size;
646 : :
647 [ - + - - ]: 38 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
648 : 38 : return;
649 : :
166 tomas.vondra@postgre 650 :UBC 0 : size = add_size(offsetof(SharedIndexScanInstrumentation, winstrument),
651 : 0 : mul_size(pcxt->nworkers, sizeof(IndexScanInstrumentation)));
167 melanieplageman@gmai 652 : 0 : node->ioss_SharedInfo =
653 : 0 : (SharedIndexScanInstrumentation *) shm_toc_allocate(pcxt->toc, size);
654 : :
655 : : /* Each per-worker area must start out as zeroes */
656 : 0 : memset(node->ioss_SharedInfo, 0, size);
657 : 0 : node->ioss_SharedInfo->num_workers = pcxt->nworkers;
658 : 0 : shm_toc_insert(pcxt->toc,
659 : 0 : node->ss.ps.plan->plan_node_id +
660 : : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
661 : 0 : node->ioss_SharedInfo);
662 : : }
663 : :
664 : : /*
665 : : * Look up and save the location of the shared instrumentation.
666 : : */
667 : : void
167 melanieplageman@gmai 668 :CBC 160 : ExecIndexOnlyScanInstrumentInitWorker(IndexOnlyScanState *node,
669 : : ParallelWorkerContext *pwcxt)
670 : : {
671 [ + - ]: 160 : if (!node->ss.ps.instrument)
672 : 160 : return;
673 : :
167 melanieplageman@gmai 674 :UBC 0 : node->ioss_SharedInfo = (SharedIndexScanInstrumentation *)
675 : 0 : shm_toc_lookup(pwcxt->toc,
676 : 0 : node->ss.ps.plan->plan_node_id +
677 : : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
678 : : false);
679 : : }
680 : :
681 : : /* ----------------------------------------------------------------
682 : : * ExecIndexOnlyScanRetrieveInstrumentation
683 : : *
684 : : * Transfer index-only scan statistics from DSM to private memory.
685 : : * ----------------------------------------------------------------
686 : : */
687 : : void
558 pg@bowt.ie 688 : 0 : ExecIndexOnlyScanRetrieveInstrumentation(IndexOnlyScanState *node)
689 : : {
690 : 0 : SharedIndexScanInstrumentation *SharedInfo = node->ioss_SharedInfo;
691 : : size_t size;
692 : :
693 [ # # ]: 0 : if (SharedInfo == NULL)
694 : 0 : return;
695 : :
696 : : /* Create a copy of SharedInfo in backend-local memory */
697 : 0 : size = offsetof(SharedIndexScanInstrumentation, winstrument) +
698 : 0 : SharedInfo->num_workers * sizeof(IndexScanInstrumentation);
699 : 0 : node->ioss_SharedInfo = palloc(size);
700 : 0 : memcpy(node->ioss_SharedInfo, SharedInfo, size);
701 : : }
|