Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * nodeIndexonlyscan.c
4 : * Routines to support index-only scans
5 : *
6 : * Portions Copyright (c) 1996-2017, 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/relscan.h"
34 : #include "access/visibilitymap.h"
35 : #include "executor/execdebug.h"
36 : #include "executor/nodeIndexonlyscan.h"
37 : #include "executor/nodeIndexscan.h"
38 : #include "miscadmin.h"
39 : #include "storage/bufmgr.h"
40 : #include "storage/predicate.h"
41 : #include "utils/memutils.h"
42 : #include "utils/rel.h"
43 :
44 :
45 : static TupleTableSlot *IndexOnlyNext(IndexOnlyScanState *node);
46 : static void StoreIndexTuple(TupleTableSlot *slot, IndexTuple itup,
47 : TupleDesc itupdesc);
48 :
49 :
50 : /* ----------------------------------------------------------------
51 : * IndexOnlyNext
52 : *
53 : * Retrieve a tuple from the IndexOnlyScan node's index.
54 : * ----------------------------------------------------------------
55 : */
56 : static TupleTableSlot *
57 589259 : IndexOnlyNext(IndexOnlyScanState *node)
58 : {
59 : EState *estate;
60 : ExprContext *econtext;
61 : ScanDirection direction;
62 : IndexScanDesc scandesc;
63 : TupleTableSlot *slot;
64 : ItemPointer tid;
65 :
66 : /*
67 : * extract necessary information from index scan node
68 : */
69 589259 : estate = node->ss.ps.state;
70 589259 : direction = estate->es_direction;
71 : /* flip direction if this is an overall backward scan */
72 589259 : if (ScanDirectionIsBackward(((IndexOnlyScan *) node->ss.ps.plan)->indexorderdir))
73 : {
74 46 : if (ScanDirectionIsForward(direction))
75 46 : direction = BackwardScanDirection;
76 0 : else if (ScanDirectionIsBackward(direction))
77 0 : direction = ForwardScanDirection;
78 : }
79 589259 : scandesc = node->ioss_ScanDesc;
80 589259 : econtext = node->ss.ps.ps_ExprContext;
81 589259 : slot = node->ss.ss_ScanTupleSlot;
82 :
83 589259 : if (scandesc == NULL)
84 : {
85 : /*
86 : * We reach here if the index only scan is not parallel, or if we're
87 : * executing a index only scan that was intended to be parallel
88 : * serially.
89 : */
90 540 : scandesc = index_beginscan(node->ss.ss_currentRelation,
91 : node->ioss_RelationDesc,
92 : estate->es_snapshot,
93 : node->ioss_NumScanKeys,
94 : node->ioss_NumOrderByKeys);
95 :
96 540 : node->ioss_ScanDesc = scandesc;
97 :
98 :
99 : /* Set it up for index-only scan */
100 540 : node->ioss_ScanDesc->xs_want_itup = true;
101 540 : node->ioss_VMBuffer = InvalidBuffer;
102 :
103 : /*
104 : * If no run-time keys to calculate or they are ready, go ahead and
105 : * pass the scankeys to the index AM.
106 : */
107 540 : if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
108 540 : index_rescan(scandesc,
109 : node->ioss_ScanKeys,
110 : node->ioss_NumScanKeys,
111 : node->ioss_OrderByKeys,
112 : node->ioss_NumOrderByKeys);
113 : }
114 :
115 : /*
116 : * OK, now that we have what we need, fetch the next tuple.
117 : */
118 1178557 : while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
119 : {
120 588320 : HeapTuple tuple = NULL;
121 :
122 588320 : CHECK_FOR_INTERRUPTS();
123 :
124 : /*
125 : * We can skip the heap fetch if the TID references a heap page on
126 : * which all tuples are known visible to everybody. In any case,
127 : * we'll use the index tuple not the heap tuple as the data source.
128 : *
129 : * Note on Memory Ordering Effects: visibilitymap_get_status does not
130 : * lock the visibility map buffer, and therefore the result we read
131 : * here could be slightly stale. However, it can't be stale enough to
132 : * matter.
133 : *
134 : * We need to detect clearing a VM bit due to an insert right away,
135 : * because the tuple is present in the index page but not visible. The
136 : * reading of the TID by this scan (using a shared lock on the index
137 : * buffer) is serialized with the insert of the TID into the index
138 : * (using an exclusive lock on the index buffer). Because the VM bit
139 : * is cleared before updating the index, and locking/unlocking of the
140 : * index page acts as a full memory barrier, we are sure to see the
141 : * cleared bit if we see a recently-inserted TID.
142 : *
143 : * Deletes do not update the index page (only VACUUM will clear out
144 : * the TID), so the clearing of the VM bit by a delete is not
145 : * serialized with this test below, and we may see a value that is
146 : * significantly stale. However, we don't care about the delete right
147 : * away, because the tuple is still visible until the deleting
148 : * transaction commits or the statement ends (if it's our
149 : * transaction). In either case, the lock on the VM buffer will have
150 : * been released (acting as a write barrier) after clearing the bit.
151 : * And for us to have a snapshot that includes the deleting
152 : * transaction (making the tuple invisible), we must have acquired
153 : * ProcArrayLock after that time, acting as a read barrier.
154 : *
155 : * It's worth going through this complexity to avoid needing to lock
156 : * the VM buffer, which could cause significant contention.
157 : */
158 588320 : if (!VM_ALL_VISIBLE(scandesc->heapRelation,
159 : ItemPointerGetBlockNumber(tid),
160 : &node->ioss_VMBuffer))
161 : {
162 : /*
163 : * Rats, we have to visit the heap to check visibility.
164 : */
165 147276 : node->ioss_HeapFetches++;
166 147276 : tuple = index_fetch_heap(scandesc);
167 147276 : if (tuple == NULL)
168 39 : continue; /* no visible tuple, try next index entry */
169 :
170 : /*
171 : * Only MVCC snapshots are supported here, so there should be no
172 : * need to keep following the HOT chain once a visible entry has
173 : * been found. If we did want to allow that, we'd need to keep
174 : * more state to remember not to call index_getnext_tid next time.
175 : */
176 147237 : if (scandesc->xs_continue_hot)
177 0 : elog(ERROR, "non-MVCC snapshots are not supported in index-only scans");
178 :
179 : /*
180 : * Note: at this point we are holding a pin on the heap page, as
181 : * recorded in scandesc->xs_cbuf. We could release that pin now,
182 : * but it's not clear whether it's a win to do so. The next index
183 : * entry might require a visit to the same heap page.
184 : */
185 : }
186 :
187 : /*
188 : * Fill the scan tuple slot with data from the index. This might be
189 : * provided in either HeapTuple or IndexTuple format. Conceivably an
190 : * index AM might fill both fields, in which case we prefer the heap
191 : * format, since it's probably a bit cheaper to fill a slot from.
192 : */
193 588281 : if (scandesc->xs_hitup)
194 : {
195 : /*
196 : * We don't take the trouble to verify that the provided tuple has
197 : * exactly the slot's format, but it seems worth doing a quick
198 : * check on the number of fields.
199 : */
200 142209 : Assert(slot->tts_tupleDescriptor->natts ==
201 : scandesc->xs_hitupdesc->natts);
202 142209 : ExecStoreTuple(scandesc->xs_hitup, slot, InvalidBuffer, false);
203 : }
204 446072 : else if (scandesc->xs_itup)
205 446072 : StoreIndexTuple(slot, scandesc->xs_itup, scandesc->xs_itupdesc);
206 : else
207 0 : elog(ERROR, "no data returned for index-only scan");
208 :
209 : /*
210 : * If the index was lossy, we have to recheck the index quals.
211 : * (Currently, this can never happen, but we should support the case
212 : * for possible future use, eg with GiST indexes.)
213 : */
214 588281 : if (scandesc->xs_recheck)
215 : {
216 0 : econtext->ecxt_scantuple = slot;
217 0 : ResetExprContext(econtext);
218 0 : if (!ExecQual(node->indexqual, econtext))
219 : {
220 : /* Fails recheck, so drop it and loop back for another */
221 0 : InstrCountFiltered2(node, 1);
222 0 : continue;
223 : }
224 : }
225 :
226 : /*
227 : * We don't currently support rechecking ORDER BY distances. (In
228 : * principle, if the index can support retrieval of the originally
229 : * indexed value, it should be able to produce an exact distance
230 : * calculation too. So it's not clear that adding code here for
231 : * recheck/re-sort would be worth the trouble. But we should at least
232 : * throw an error if someone tries it.)
233 : */
234 588281 : if (scandesc->numberOfOrderBys > 0 && scandesc->xs_recheckorderby)
235 0 : ereport(ERROR,
236 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
237 : errmsg("lossy distance functions are not supported in index-only scans")));
238 :
239 : /*
240 : * Predicate locks for index-only scans must be acquired at the page
241 : * level when the heap is not accessed, since tuple-level predicate
242 : * locks need the tuple's xmin value. If we had to visit the tuple
243 : * anyway, then we already have the tuple-level lock and can skip the
244 : * page lock.
245 : */
246 588281 : if (tuple == NULL)
247 882088 : PredicateLockPage(scandesc->heapRelation,
248 441044 : ItemPointerGetBlockNumber(tid),
249 : estate->es_snapshot);
250 :
251 588281 : return slot;
252 : }
253 :
254 : /*
255 : * if we get here it means the index scan failed so we are at the end of
256 : * the scan..
257 : */
258 978 : return ExecClearTuple(slot);
259 : }
260 :
261 : /*
262 : * StoreIndexTuple
263 : * Fill the slot with data from the index tuple.
264 : *
265 : * At some point this might be generally-useful functionality, but
266 : * right now we don't need it elsewhere.
267 : */
268 : static void
269 446072 : StoreIndexTuple(TupleTableSlot *slot, IndexTuple itup, TupleDesc itupdesc)
270 : {
271 446072 : int nindexatts = itupdesc->natts;
272 446072 : Datum *values = slot->tts_values;
273 446072 : bool *isnull = slot->tts_isnull;
274 : int i;
275 :
276 : /*
277 : * Note: we must use the tupdesc supplied by the AM in index_getattr, not
278 : * the slot's tupdesc, in case the latter has different datatypes (this
279 : * happens for btree name_ops in particular). They'd better have the same
280 : * number of columns though, as well as being datatype-compatible which is
281 : * something we can't so easily check.
282 : */
283 446072 : Assert(slot->tts_tupleDescriptor->natts == nindexatts);
284 :
285 446072 : ExecClearTuple(slot);
286 1042610 : for (i = 0; i < nindexatts; i++)
287 596538 : values[i] = index_getattr(itup, i + 1, itupdesc, &isnull[i]);
288 446072 : ExecStoreVirtualTuple(slot);
289 446072 : }
290 :
291 : /*
292 : * IndexOnlyRecheck -- access method routine to recheck a tuple in EvalPlanQual
293 : *
294 : * This can't really happen, since an index can't supply CTID which would
295 : * be necessary data for any potential EvalPlanQual target relation. If it
296 : * did happen, the EPQ code would pass us the wrong data, namely a heap
297 : * tuple not an index tuple. So throw an error.
298 : */
299 : static bool
300 0 : IndexOnlyRecheck(IndexOnlyScanState *node, TupleTableSlot *slot)
301 : {
302 0 : elog(ERROR, "EvalPlanQual recheck is not supported in index-only scans");
303 : return false; /* keep compiler quiet */
304 : }
305 :
306 : /* ----------------------------------------------------------------
307 : * ExecIndexOnlyScan(node)
308 : * ----------------------------------------------------------------
309 : */
310 : static TupleTableSlot *
311 585629 : ExecIndexOnlyScan(PlanState *pstate)
312 : {
313 585629 : IndexOnlyScanState *node = castNode(IndexOnlyScanState, pstate);
314 :
315 : /*
316 : * If we have runtime keys and they've not already been set up, do it now.
317 : */
318 585629 : if (node->ioss_NumRuntimeKeys != 0 && !node->ioss_RuntimeKeysReady)
319 97 : ExecReScan((PlanState *) node);
320 :
321 585629 : return ExecScan(&node->ss,
322 : (ExecScanAccessMtd) IndexOnlyNext,
323 : (ExecScanRecheckMtd) IndexOnlyRecheck);
324 : }
325 :
326 : /* ----------------------------------------------------------------
327 : * ExecReScanIndexOnlyScan(node)
328 : *
329 : * Recalculates the values of any scan keys whose value depends on
330 : * information known at runtime, then rescans the indexed relation.
331 : *
332 : * Updating the scan key was formerly done separately in
333 : * ExecUpdateIndexScanKeys. Integrating it into ReScan makes
334 : * rescans of indices and relations/general streams more uniform.
335 : * ----------------------------------------------------------------
336 : */
337 : void
338 2060 : ExecReScanIndexOnlyScan(IndexOnlyScanState *node)
339 : {
340 : /*
341 : * If we are doing runtime key calculations (ie, any of the index key
342 : * values weren't simple Consts), compute the new key values. But first,
343 : * reset the context so we don't leak memory as each outer tuple is
344 : * scanned. Note this assumes that we will recalculate *all* runtime keys
345 : * on each call.
346 : */
347 2060 : if (node->ioss_NumRuntimeKeys != 0)
348 : {
349 2041 : ExprContext *econtext = node->ioss_RuntimeContext;
350 :
351 2041 : ResetExprContext(econtext);
352 2041 : ExecIndexEvalRuntimeKeys(econtext,
353 : node->ioss_RuntimeKeys,
354 : node->ioss_NumRuntimeKeys);
355 : }
356 2060 : node->ioss_RuntimeKeysReady = true;
357 :
358 : /* reset index scan */
359 2060 : if (node->ioss_ScanDesc)
360 1850 : index_rescan(node->ioss_ScanDesc,
361 : node->ioss_ScanKeys, node->ioss_NumScanKeys,
362 : node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
363 :
364 2060 : ExecScanReScan(&node->ss);
365 2060 : }
366 :
367 :
368 : /* ----------------------------------------------------------------
369 : * ExecEndIndexOnlyScan
370 : * ----------------------------------------------------------------
371 : */
372 : void
373 777 : ExecEndIndexOnlyScan(IndexOnlyScanState *node)
374 : {
375 : Relation indexRelationDesc;
376 : IndexScanDesc indexScanDesc;
377 : Relation relation;
378 :
379 : /*
380 : * extract information from the node
381 : */
382 777 : indexRelationDesc = node->ioss_RelationDesc;
383 777 : indexScanDesc = node->ioss_ScanDesc;
384 777 : relation = node->ss.ss_currentRelation;
385 :
386 : /* Release VM buffer pin, if any. */
387 777 : if (node->ioss_VMBuffer != InvalidBuffer)
388 : {
389 228 : ReleaseBuffer(node->ioss_VMBuffer);
390 228 : node->ioss_VMBuffer = InvalidBuffer;
391 : }
392 :
393 : /*
394 : * Free the exprcontext(s) ... now dead code, see ExecFreeExprContext
395 : */
396 : #ifdef NOT_USED
397 : ExecFreeExprContext(&node->ss.ps);
398 : if (node->ioss_RuntimeContext)
399 : FreeExprContext(node->ioss_RuntimeContext, true);
400 : #endif
401 :
402 : /*
403 : * clear out tuple table slots
404 : */
405 777 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
406 777 : ExecClearTuple(node->ss.ss_ScanTupleSlot);
407 :
408 : /*
409 : * close the index relation (no-op if we didn't open it)
410 : */
411 777 : if (indexScanDesc)
412 556 : index_endscan(indexScanDesc);
413 777 : if (indexRelationDesc)
414 617 : index_close(indexRelationDesc, NoLock);
415 :
416 : /*
417 : * close the heap relation.
418 : */
419 777 : ExecCloseScanRelation(relation);
420 777 : }
421 :
422 : /* ----------------------------------------------------------------
423 : * ExecIndexOnlyMarkPos
424 : * ----------------------------------------------------------------
425 : */
426 : void
427 20000 : ExecIndexOnlyMarkPos(IndexOnlyScanState *node)
428 : {
429 20000 : index_markpos(node->ioss_ScanDesc);
430 20000 : }
431 :
432 : /* ----------------------------------------------------------------
433 : * ExecIndexOnlyRestrPos
434 : * ----------------------------------------------------------------
435 : */
436 : void
437 0 : ExecIndexOnlyRestrPos(IndexOnlyScanState *node)
438 : {
439 0 : index_restrpos(node->ioss_ScanDesc);
440 0 : }
441 :
442 : /* ----------------------------------------------------------------
443 : * ExecInitIndexOnlyScan
444 : *
445 : * Initializes the index scan's state information, creates
446 : * scan keys, and opens the base and index relations.
447 : *
448 : * Note: index scans have 2 sets of state information because
449 : * we have to keep track of the base relation and the
450 : * index relation.
451 : * ----------------------------------------------------------------
452 : */
453 : IndexOnlyScanState *
454 785 : ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
455 : {
456 : IndexOnlyScanState *indexstate;
457 : Relation currentRelation;
458 : bool relistarget;
459 : TupleDesc tupDesc;
460 :
461 : /*
462 : * create state structure
463 : */
464 785 : indexstate = makeNode(IndexOnlyScanState);
465 785 : indexstate->ss.ps.plan = (Plan *) node;
466 785 : indexstate->ss.ps.state = estate;
467 785 : indexstate->ss.ps.ExecProcNode = ExecIndexOnlyScan;
468 785 : indexstate->ioss_HeapFetches = 0;
469 :
470 : /*
471 : * Miscellaneous initialization
472 : *
473 : * create expression context for node
474 : */
475 785 : ExecAssignExprContext(estate, &indexstate->ss.ps);
476 :
477 : /*
478 : * initialize child expressions
479 : *
480 : * Note: we don't initialize all of the indexorderby expression, only the
481 : * sub-parts corresponding to runtime keys (see below).
482 : */
483 785 : indexstate->ss.ps.qual =
484 785 : ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
485 785 : indexstate->indexqual =
486 785 : ExecInitQual(node->indexqual, (PlanState *) indexstate);
487 :
488 : /*
489 : * tuple table initialization
490 : */
491 785 : ExecInitResultTupleSlot(estate, &indexstate->ss.ps);
492 785 : ExecInitScanTupleSlot(estate, &indexstate->ss);
493 :
494 : /*
495 : * open the base relation and acquire appropriate lock on it.
496 : */
497 785 : currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
498 :
499 785 : indexstate->ss.ss_currentRelation = currentRelation;
500 785 : indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
501 :
502 : /*
503 : * Build the scan tuple type using the indextlist generated by the
504 : * planner. We use this, rather than the index's physical tuple
505 : * descriptor, because the latter contains storage column types not the
506 : * types of the original datums. (It's the AM's responsibility to return
507 : * suitable data anyway.)
508 : */
509 785 : tupDesc = ExecTypeFromTL(node->indextlist, false);
510 785 : ExecAssignScanType(&indexstate->ss, tupDesc);
511 :
512 : /*
513 : * Initialize result tuple type and projection info. The node's
514 : * targetlist will contain Vars with varno = INDEX_VAR, referencing the
515 : * scan tuple.
516 : */
517 785 : ExecAssignResultTypeFromTL(&indexstate->ss.ps);
518 785 : ExecAssignScanProjectionInfoWithVarno(&indexstate->ss, INDEX_VAR);
519 :
520 : /*
521 : * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
522 : * here. This allows an index-advisor plugin to EXPLAIN a plan containing
523 : * references to nonexistent indexes.
524 : */
525 785 : if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
526 160 : return indexstate;
527 :
528 : /*
529 : * Open the index relation.
530 : *
531 : * If the parent table is one of the target relations of the query, then
532 : * InitPlan already opened and write-locked the index, so we can avoid
533 : * taking another lock here. Otherwise we need a normal reader's lock.
534 : */
535 625 : relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid);
536 625 : indexstate->ioss_RelationDesc = index_open(node->indexid,
537 : relistarget ? NoLock : AccessShareLock);
538 :
539 : /*
540 : * Initialize index-specific scan state
541 : */
542 625 : indexstate->ioss_RuntimeKeysReady = false;
543 625 : indexstate->ioss_RuntimeKeys = NULL;
544 625 : indexstate->ioss_NumRuntimeKeys = 0;
545 :
546 : /*
547 : * build the index scan keys from the index qualification
548 : */
549 625 : ExecIndexBuildScanKeys((PlanState *) indexstate,
550 : indexstate->ioss_RelationDesc,
551 : node->indexqual,
552 : false,
553 : &indexstate->ioss_ScanKeys,
554 : &indexstate->ioss_NumScanKeys,
555 : &indexstate->ioss_RuntimeKeys,
556 : &indexstate->ioss_NumRuntimeKeys,
557 : NULL, /* no ArrayKeys */
558 : NULL);
559 :
560 : /*
561 : * any ORDER BY exprs have to be turned into scankeys in the same way
562 : */
563 625 : ExecIndexBuildScanKeys((PlanState *) indexstate,
564 : indexstate->ioss_RelationDesc,
565 : node->indexorderby,
566 : true,
567 : &indexstate->ioss_OrderByKeys,
568 : &indexstate->ioss_NumOrderByKeys,
569 : &indexstate->ioss_RuntimeKeys,
570 : &indexstate->ioss_NumRuntimeKeys,
571 : NULL, /* no ArrayKeys */
572 : NULL);
573 :
574 : /*
575 : * If we have runtime keys, we need an ExprContext to evaluate them. The
576 : * node's standard context won't do because we want to reset that context
577 : * for every tuple. So, build another context just like the other one...
578 : * -tgl 7/11/00
579 : */
580 625 : if (indexstate->ioss_NumRuntimeKeys != 0)
581 : {
582 249 : ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
583 :
584 249 : ExecAssignExprContext(estate, &indexstate->ss.ps);
585 249 : indexstate->ioss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
586 249 : indexstate->ss.ps.ps_ExprContext = stdecontext;
587 : }
588 : else
589 : {
590 376 : indexstate->ioss_RuntimeContext = NULL;
591 : }
592 :
593 : /*
594 : * all done.
595 : */
596 625 : return indexstate;
597 : }
598 :
599 : /* ----------------------------------------------------------------
600 : * Parallel Index-only Scan Support
601 : * ----------------------------------------------------------------
602 : */
603 :
604 : /* ----------------------------------------------------------------
605 : * ExecIndexOnlyScanEstimate
606 : *
607 : * estimates the space required to serialize index-only scan node.
608 : * ----------------------------------------------------------------
609 : */
610 : void
611 3 : ExecIndexOnlyScanEstimate(IndexOnlyScanState *node,
612 : ParallelContext *pcxt)
613 : {
614 3 : EState *estate = node->ss.ps.state;
615 :
616 3 : node->ioss_PscanLen = index_parallelscan_estimate(node->ioss_RelationDesc,
617 : estate->es_snapshot);
618 3 : shm_toc_estimate_chunk(&pcxt->estimator, node->ioss_PscanLen);
619 3 : shm_toc_estimate_keys(&pcxt->estimator, 1);
620 3 : }
621 :
622 : /* ----------------------------------------------------------------
623 : * ExecIndexOnlyScanInitializeDSM
624 : *
625 : * Set up a parallel index-only scan descriptor.
626 : * ----------------------------------------------------------------
627 : */
628 : void
629 3 : ExecIndexOnlyScanInitializeDSM(IndexOnlyScanState *node,
630 : ParallelContext *pcxt)
631 : {
632 3 : EState *estate = node->ss.ps.state;
633 : ParallelIndexScanDesc piscan;
634 :
635 3 : piscan = shm_toc_allocate(pcxt->toc, node->ioss_PscanLen);
636 3 : index_parallelscan_initialize(node->ss.ss_currentRelation,
637 : node->ioss_RelationDesc,
638 : estate->es_snapshot,
639 : piscan);
640 3 : shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
641 3 : node->ioss_ScanDesc =
642 3 : index_beginscan_parallel(node->ss.ss_currentRelation,
643 : node->ioss_RelationDesc,
644 : node->ioss_NumScanKeys,
645 : node->ioss_NumOrderByKeys,
646 : piscan);
647 3 : node->ioss_ScanDesc->xs_want_itup = true;
648 3 : node->ioss_VMBuffer = InvalidBuffer;
649 :
650 : /*
651 : * If no run-time keys to calculate or they are ready, go ahead and pass
652 : * the scankeys to the index AM.
653 : */
654 3 : if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
655 3 : index_rescan(node->ioss_ScanDesc,
656 : node->ioss_ScanKeys, node->ioss_NumScanKeys,
657 : node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
658 3 : }
659 :
660 : /* ----------------------------------------------------------------
661 : * ExecIndexOnlyScanReInitializeDSM
662 : *
663 : * Reset shared state before beginning a fresh scan.
664 : * ----------------------------------------------------------------
665 : */
666 : void
667 2 : ExecIndexOnlyScanReInitializeDSM(IndexOnlyScanState *node,
668 : ParallelContext *pcxt)
669 : {
670 2 : index_parallelrescan(node->ioss_ScanDesc);
671 2 : }
672 :
673 : /* ----------------------------------------------------------------
674 : * ExecIndexOnlyScanInitializeWorker
675 : *
676 : * Copy relevant information from TOC into planstate.
677 : * ----------------------------------------------------------------
678 : */
679 : void
680 20 : ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node, shm_toc *toc)
681 : {
682 : ParallelIndexScanDesc piscan;
683 :
684 20 : piscan = shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id, false);
685 20 : node->ioss_ScanDesc =
686 20 : index_beginscan_parallel(node->ss.ss_currentRelation,
687 : node->ioss_RelationDesc,
688 : node->ioss_NumScanKeys,
689 : node->ioss_NumOrderByKeys,
690 : piscan);
691 20 : node->ioss_ScanDesc->xs_want_itup = true;
692 :
693 : /*
694 : * If no run-time keys to calculate or they are ready, go ahead and pass
695 : * the scankeys to the index AM.
696 : */
697 20 : if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
698 20 : index_rescan(node->ioss_ScanDesc,
699 : node->ioss_ScanKeys, node->ioss_NumScanKeys,
700 : node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
701 20 : }
|