Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * nodeIndexonlyscan.c
4 : * Routines to support index-only scans
5 : *
6 : * Portions Copyright (c) 1996-2024, 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 "access/tupdesc.h"
37 : #include "access/visibilitymap.h"
38 : #include "executor/executor.h"
39 : #include "executor/nodeIndexonlyscan.h"
40 : #include "executor/nodeIndexscan.h"
41 : #include "miscadmin.h"
42 : #include "storage/bufmgr.h"
43 : #include "storage/predicate.h"
44 : #include "utils/rel.h"
45 :
46 :
47 : static TupleTableSlot *IndexOnlyNext(IndexOnlyScanState *node);
48 : static void StoreIndexTuple(TupleTableSlot *slot, IndexTuple itup,
49 : TupleDesc itupdesc);
50 :
51 :
52 : /* ----------------------------------------------------------------
53 : * IndexOnlyNext
54 : *
55 : * Retrieve a tuple from the IndexOnlyScan node's index.
56 : * ----------------------------------------------------------------
57 : */
58 : static TupleTableSlot *
59 5110116 : IndexOnlyNext(IndexOnlyScanState *node)
60 : {
61 : EState *estate;
62 : ExprContext *econtext;
63 : ScanDirection direction;
64 : IndexScanDesc scandesc;
65 : TupleTableSlot *slot;
66 : ItemPointer tid;
67 :
68 : /*
69 : * extract necessary information from index scan node
70 : */
71 5110116 : estate = node->ss.ps.state;
72 :
73 : /*
74 : * Determine which direction to scan the index in based on the plan's scan
75 : * direction and the current direction of execution.
76 : */
77 5110116 : direction = ScanDirectionCombine(estate->es_direction,
78 : ((IndexOnlyScan *) node->ss.ps.plan)->indexorderdir);
79 5110116 : scandesc = node->ioss_ScanDesc;
80 5110116 : econtext = node->ss.ps.ps_ExprContext;
81 5110116 : slot = node->ss.ss_ScanTupleSlot;
82 :
83 5110116 : if (scandesc == NULL)
84 : {
85 : /*
86 : * We reach here if the index only scan is not parallel, or if we're
87 : * serially executing an index only scan that was planned to be
88 : * parallel.
89 : */
90 9750 : 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 9750 : node->ioss_ScanDesc = scandesc;
97 :
98 :
99 : /* Set it up for index-only scan */
100 9750 : node->ioss_ScanDesc->xs_want_itup = true;
101 9750 : 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 9750 : if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
108 9750 : index_rescan(scandesc,
109 9750 : node->ioss_ScanKeys,
110 : node->ioss_NumScanKeys,
111 9750 : 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 5235452 : while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
119 : {
120 5153182 : bool tuple_from_heap = false;
121 :
122 5153182 : 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 5153182 : 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 1866980 : InstrCountTuples2(node, 1);
166 1866980 : if (!index_fetch_heap(scandesc, node->ioss_TableSlot))
167 125330 : continue; /* no visible tuple, try next index entry */
168 :
169 1741650 : ExecClearTuple(node->ioss_TableSlot);
170 :
171 : /*
172 : * Only MVCC snapshots are supported here, so there should be no
173 : * need to keep following the HOT chain once a visible entry has
174 : * been found. If we did want to allow that, we'd need to keep
175 : * more state to remember not to call index_getnext_tid next time.
176 : */
177 1741650 : if (scandesc->xs_heap_continue)
178 0 : elog(ERROR, "non-MVCC snapshots are not supported in index-only scans");
179 :
180 : /*
181 : * Note: at this point we are holding a pin on the heap page, as
182 : * recorded in scandesc->xs_cbuf. We could release that pin now,
183 : * but it's not clear whether it's a win to do so. The next index
184 : * entry might require a visit to the same heap page.
185 : */
186 :
187 1741650 : tuple_from_heap = true;
188 : }
189 :
190 : /*
191 : * Fill the scan tuple slot with data from the index. This might be
192 : * provided in either HeapTuple or IndexTuple format. Conceivably an
193 : * index AM might fill both fields, in which case we prefer the heap
194 : * format, since it's probably a bit cheaper to fill a slot from.
195 : */
196 5027852 : if (scandesc->xs_hitup)
197 : {
198 : /*
199 : * We don't take the trouble to verify that the provided tuple has
200 : * exactly the slot's format, but it seems worth doing a quick
201 : * check on the number of fields.
202 : */
203 : Assert(slot->tts_tupleDescriptor->natts ==
204 : scandesc->xs_hitupdesc->natts);
205 1437684 : ExecForceStoreHeapTuple(scandesc->xs_hitup, slot, false);
206 : }
207 3590168 : else if (scandesc->xs_itup)
208 3590168 : StoreIndexTuple(slot, scandesc->xs_itup, scandesc->xs_itupdesc);
209 : else
210 0 : elog(ERROR, "no data returned for index-only scan");
211 :
212 : /*
213 : * If the index was lossy, we have to recheck the index quals.
214 : */
215 5027852 : if (scandesc->xs_recheck)
216 : {
217 14 : econtext->ecxt_scantuple = slot;
218 14 : if (!ExecQualAndReset(node->recheckqual, econtext))
219 : {
220 : /* Fails recheck, so drop it and loop back for another */
221 6 : InstrCountFiltered2(node, 1);
222 6 : 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 5027846 : if (scandesc->numberOfOrderBys > 0 && scandesc->xs_recheckorderby)
235 6 : ereport(ERROR,
236 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
237 : errmsg("lossy distance functions are not supported in index-only scans")));
238 :
239 : /*
240 : * If we didn't access the heap, then we'll need to take a predicate
241 : * lock explicitly, as if we had. For now we do that at page level.
242 : */
243 5027840 : if (!tuple_from_heap)
244 3286202 : PredicateLockPage(scandesc->heapRelation,
245 : ItemPointerGetBlockNumber(tid),
246 : estate->es_snapshot);
247 :
248 5027840 : return slot;
249 : }
250 :
251 : /*
252 : * if we get here it means the index scan failed so we are at the end of
253 : * the scan..
254 : */
255 82270 : return ExecClearTuple(slot);
256 : }
257 :
258 : /*
259 : * StoreIndexTuple
260 : * Fill the slot with data from the index tuple.
261 : *
262 : * At some point this might be generally-useful functionality, but
263 : * right now we don't need it elsewhere.
264 : */
265 : static void
266 3590168 : StoreIndexTuple(TupleTableSlot *slot, IndexTuple itup, TupleDesc itupdesc)
267 : {
268 : /*
269 : * Note: we must use the tupdesc supplied by the AM in index_deform_tuple,
270 : * not the slot's tupdesc, in case the latter has different datatypes
271 : * (this happens for btree name_ops in particular). They'd better have
272 : * the same number of columns though, as well as being datatype-compatible
273 : * which is something we can't so easily check.
274 : */
275 : Assert(slot->tts_tupleDescriptor->natts == itupdesc->natts);
276 :
277 3590168 : ExecClearTuple(slot);
278 3590168 : index_deform_tuple(itup, itupdesc, slot->tts_values, slot->tts_isnull);
279 3590168 : ExecStoreVirtualTuple(slot);
280 3590168 : }
281 :
282 : /*
283 : * IndexOnlyRecheck -- access method routine to recheck a tuple in EvalPlanQual
284 : *
285 : * This can't really happen, since an index can't supply CTID which would
286 : * be necessary data for any potential EvalPlanQual target relation. If it
287 : * did happen, the EPQ code would pass us the wrong data, namely a heap
288 : * tuple not an index tuple. So throw an error.
289 : */
290 : static bool
291 0 : IndexOnlyRecheck(IndexOnlyScanState *node, TupleTableSlot *slot)
292 : {
293 0 : elog(ERROR, "EvalPlanQual recheck is not supported in index-only scans");
294 : return false; /* keep compiler quiet */
295 : }
296 :
297 : /* ----------------------------------------------------------------
298 : * ExecIndexOnlyScan(node)
299 : * ----------------------------------------------------------------
300 : */
301 : static TupleTableSlot *
302 5109094 : ExecIndexOnlyScan(PlanState *pstate)
303 : {
304 5109094 : IndexOnlyScanState *node = castNode(IndexOnlyScanState, pstate);
305 :
306 : /*
307 : * If we have runtime keys and they've not already been set up, do it now.
308 : */
309 5109094 : if (node->ioss_NumRuntimeKeys != 0 && !node->ioss_RuntimeKeysReady)
310 556 : ExecReScan((PlanState *) node);
311 :
312 5109094 : return ExecScan(&node->ss,
313 : (ExecScanAccessMtd) IndexOnlyNext,
314 : (ExecScanRecheckMtd) IndexOnlyRecheck);
315 : }
316 :
317 : /* ----------------------------------------------------------------
318 : * ExecReScanIndexOnlyScan(node)
319 : *
320 : * Recalculates the values of any scan keys whose value depends on
321 : * information known at runtime, then rescans the indexed relation.
322 : *
323 : * Updating the scan key was formerly done separately in
324 : * ExecUpdateIndexScanKeys. Integrating it into ReScan makes
325 : * rescans of indices and relations/general streams more uniform.
326 : * ----------------------------------------------------------------
327 : */
328 : void
329 95520 : ExecReScanIndexOnlyScan(IndexOnlyScanState *node)
330 : {
331 : /*
332 : * If we are doing runtime key calculations (ie, any of the index key
333 : * values weren't simple Consts), compute the new key values. But first,
334 : * reset the context so we don't leak memory as each outer tuple is
335 : * scanned. Note this assumes that we will recalculate *all* runtime keys
336 : * on each call.
337 : */
338 95520 : if (node->ioss_NumRuntimeKeys != 0)
339 : {
340 95352 : ExprContext *econtext = node->ioss_RuntimeContext;
341 :
342 95352 : ResetExprContext(econtext);
343 95352 : ExecIndexEvalRuntimeKeys(econtext,
344 : node->ioss_RuntimeKeys,
345 : node->ioss_NumRuntimeKeys);
346 : }
347 95520 : node->ioss_RuntimeKeysReady = true;
348 :
349 : /* reset index scan */
350 95520 : if (node->ioss_ScanDesc)
351 93788 : index_rescan(node->ioss_ScanDesc,
352 93788 : node->ioss_ScanKeys, node->ioss_NumScanKeys,
353 93788 : node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
354 :
355 95520 : ExecScanReScan(&node->ss);
356 95520 : }
357 :
358 :
359 : /* ----------------------------------------------------------------
360 : * ExecEndIndexOnlyScan
361 : * ----------------------------------------------------------------
362 : */
363 : void
364 15298 : ExecEndIndexOnlyScan(IndexOnlyScanState *node)
365 : {
366 : Relation indexRelationDesc;
367 : IndexScanDesc indexScanDesc;
368 :
369 : /*
370 : * extract information from the node
371 : */
372 15298 : indexRelationDesc = node->ioss_RelationDesc;
373 15298 : indexScanDesc = node->ioss_ScanDesc;
374 :
375 : /* Release VM buffer pin, if any. */
376 15298 : if (node->ioss_VMBuffer != InvalidBuffer)
377 : {
378 5968 : ReleaseBuffer(node->ioss_VMBuffer);
379 5968 : node->ioss_VMBuffer = InvalidBuffer;
380 : }
381 :
382 : /*
383 : * close the index relation (no-op if we didn't open it)
384 : */
385 15298 : if (indexScanDesc)
386 9858 : index_endscan(indexScanDesc);
387 15298 : if (indexRelationDesc)
388 13026 : index_close(indexRelationDesc, NoLock);
389 15298 : }
390 :
391 : /* ----------------------------------------------------------------
392 : * ExecIndexOnlyMarkPos
393 : *
394 : * Note: we assume that no caller attempts to set a mark before having read
395 : * at least one tuple. Otherwise, ioss_ScanDesc might still be NULL.
396 : * ----------------------------------------------------------------
397 : */
398 : void
399 124010 : ExecIndexOnlyMarkPos(IndexOnlyScanState *node)
400 : {
401 124010 : EState *estate = node->ss.ps.state;
402 124010 : EPQState *epqstate = estate->es_epq_active;
403 :
404 124010 : if (epqstate != NULL)
405 : {
406 : /*
407 : * We are inside an EvalPlanQual recheck. If a test tuple exists for
408 : * this relation, then we shouldn't access the index at all. We would
409 : * instead need to save, and later restore, the state of the
410 : * relsubs_done flag, so that re-fetching the test tuple is possible.
411 : * However, given the assumption that no caller sets a mark at the
412 : * start of the scan, we can only get here with relsubs_done[i]
413 : * already set, and so no state need be saved.
414 : */
415 0 : Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
416 :
417 : Assert(scanrelid > 0);
418 0 : if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
419 0 : epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
420 : {
421 : /* Verify the claim above */
422 0 : if (!epqstate->relsubs_done[scanrelid - 1])
423 0 : elog(ERROR, "unexpected ExecIndexOnlyMarkPos call in EPQ recheck");
424 0 : return;
425 : }
426 : }
427 :
428 124010 : index_markpos(node->ioss_ScanDesc);
429 : }
430 :
431 : /* ----------------------------------------------------------------
432 : * ExecIndexOnlyRestrPos
433 : * ----------------------------------------------------------------
434 : */
435 : void
436 6 : ExecIndexOnlyRestrPos(IndexOnlyScanState *node)
437 : {
438 6 : EState *estate = node->ss.ps.state;
439 6 : EPQState *epqstate = estate->es_epq_active;
440 :
441 6 : if (estate->es_epq_active != NULL)
442 : {
443 : /* See comments in ExecIndexMarkPos */
444 0 : Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
445 :
446 : Assert(scanrelid > 0);
447 0 : if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
448 0 : epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
449 : {
450 : /* Verify the claim above */
451 0 : if (!epqstate->relsubs_done[scanrelid - 1])
452 0 : elog(ERROR, "unexpected ExecIndexOnlyRestrPos call in EPQ recheck");
453 0 : return;
454 : }
455 : }
456 :
457 6 : index_restrpos(node->ioss_ScanDesc);
458 : }
459 :
460 : /* ----------------------------------------------------------------
461 : * ExecInitIndexOnlyScan
462 : *
463 : * Initializes the index scan's state information, creates
464 : * scan keys, and opens the base and index relations.
465 : *
466 : * Note: index scans have 2 sets of state information because
467 : * we have to keep track of the base relation and the
468 : * index relation.
469 : * ----------------------------------------------------------------
470 : */
471 : IndexOnlyScanState *
472 15430 : ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
473 : {
474 : IndexOnlyScanState *indexstate;
475 : Relation currentRelation;
476 : LOCKMODE lockmode;
477 : TupleDesc tupDesc;
478 :
479 : /*
480 : * create state structure
481 : */
482 15430 : indexstate = makeNode(IndexOnlyScanState);
483 15430 : indexstate->ss.ps.plan = (Plan *) node;
484 15430 : indexstate->ss.ps.state = estate;
485 15430 : indexstate->ss.ps.ExecProcNode = ExecIndexOnlyScan;
486 :
487 : /*
488 : * Miscellaneous initialization
489 : *
490 : * create expression context for node
491 : */
492 15430 : ExecAssignExprContext(estate, &indexstate->ss.ps);
493 :
494 : /*
495 : * open the scan relation
496 : */
497 15430 : currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
498 :
499 15430 : indexstate->ss.ss_currentRelation = currentRelation;
500 15430 : 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 15430 : tupDesc = ExecTypeFromTL(node->indextlist);
510 15430 : ExecInitScanTupleSlot(estate, &indexstate->ss, tupDesc,
511 : &TTSOpsVirtual);
512 :
513 : /*
514 : * We need another slot, in a format that's suitable for the table AM, for
515 : * when we need to fetch a tuple from the table for rechecking visibility.
516 : */
517 15430 : indexstate->ioss_TableSlot =
518 15430 : ExecAllocTableSlot(&estate->es_tupleTable,
519 : RelationGetDescr(currentRelation),
520 : table_slot_callbacks(currentRelation));
521 :
522 : /*
523 : * Initialize result type and projection info. The node's targetlist will
524 : * contain Vars with varno = INDEX_VAR, referencing the scan tuple.
525 : */
526 15430 : ExecInitResultTypeTL(&indexstate->ss.ps);
527 15430 : ExecAssignScanProjectionInfoWithVarno(&indexstate->ss, INDEX_VAR);
528 :
529 : /*
530 : * initialize child expressions
531 : *
532 : * Note: we don't initialize all of the indexorderby expression, only the
533 : * sub-parts corresponding to runtime keys (see below).
534 : */
535 15430 : indexstate->ss.ps.qual =
536 15430 : ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
537 15430 : indexstate->recheckqual =
538 15430 : ExecInitQual(node->recheckqual, (PlanState *) indexstate);
539 :
540 : /*
541 : * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
542 : * here. This allows an index-advisor plugin to EXPLAIN a plan containing
543 : * references to nonexistent indexes.
544 : */
545 15430 : if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
546 2272 : return indexstate;
547 :
548 : /* Open the index relation. */
549 13158 : lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
550 13158 : indexstate->ioss_RelationDesc = index_open(node->indexid, lockmode);
551 :
552 : /*
553 : * Initialize index-specific scan state
554 : */
555 13158 : indexstate->ioss_RuntimeKeysReady = false;
556 13158 : indexstate->ioss_RuntimeKeys = NULL;
557 13158 : indexstate->ioss_NumRuntimeKeys = 0;
558 :
559 : /*
560 : * build the index scan keys from the index qualification
561 : */
562 13158 : ExecIndexBuildScanKeys((PlanState *) indexstate,
563 : indexstate->ioss_RelationDesc,
564 : node->indexqual,
565 : false,
566 13158 : &indexstate->ioss_ScanKeys,
567 : &indexstate->ioss_NumScanKeys,
568 : &indexstate->ioss_RuntimeKeys,
569 : &indexstate->ioss_NumRuntimeKeys,
570 : NULL, /* no ArrayKeys */
571 : NULL);
572 :
573 : /*
574 : * any ORDER BY exprs have to be turned into scankeys in the same way
575 : */
576 13158 : ExecIndexBuildScanKeys((PlanState *) indexstate,
577 : indexstate->ioss_RelationDesc,
578 : node->indexorderby,
579 : true,
580 13158 : &indexstate->ioss_OrderByKeys,
581 : &indexstate->ioss_NumOrderByKeys,
582 : &indexstate->ioss_RuntimeKeys,
583 : &indexstate->ioss_NumRuntimeKeys,
584 : NULL, /* no ArrayKeys */
585 : NULL);
586 :
587 : /*
588 : * If we have runtime keys, we need an ExprContext to evaluate them. The
589 : * node's standard context won't do because we want to reset that context
590 : * for every tuple. So, build another context just like the other one...
591 : * -tgl 7/11/00
592 : */
593 13158 : if (indexstate->ioss_NumRuntimeKeys != 0)
594 : {
595 2210 : ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
596 :
597 2210 : ExecAssignExprContext(estate, &indexstate->ss.ps);
598 2210 : indexstate->ioss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
599 2210 : indexstate->ss.ps.ps_ExprContext = stdecontext;
600 : }
601 : else
602 : {
603 10948 : indexstate->ioss_RuntimeContext = NULL;
604 : }
605 :
606 : /*
607 : * all done.
608 : */
609 13158 : return indexstate;
610 : }
611 :
612 : /* ----------------------------------------------------------------
613 : * Parallel Index-only Scan Support
614 : * ----------------------------------------------------------------
615 : */
616 :
617 : /* ----------------------------------------------------------------
618 : * ExecIndexOnlyScanEstimate
619 : *
620 : * Compute the amount of space we'll need in the parallel
621 : * query DSM, and inform pcxt->estimator about our needs.
622 : * ----------------------------------------------------------------
623 : */
624 : void
625 40 : ExecIndexOnlyScanEstimate(IndexOnlyScanState *node,
626 : ParallelContext *pcxt)
627 : {
628 40 : EState *estate = node->ss.ps.state;
629 :
630 40 : node->ioss_PscanLen = index_parallelscan_estimate(node->ioss_RelationDesc,
631 : estate->es_snapshot);
632 40 : shm_toc_estimate_chunk(&pcxt->estimator, node->ioss_PscanLen);
633 40 : shm_toc_estimate_keys(&pcxt->estimator, 1);
634 40 : }
635 :
636 : /* ----------------------------------------------------------------
637 : * ExecIndexOnlyScanInitializeDSM
638 : *
639 : * Set up a parallel index-only scan descriptor.
640 : * ----------------------------------------------------------------
641 : */
642 : void
643 40 : ExecIndexOnlyScanInitializeDSM(IndexOnlyScanState *node,
644 : ParallelContext *pcxt)
645 : {
646 40 : EState *estate = node->ss.ps.state;
647 : ParallelIndexScanDesc piscan;
648 :
649 40 : piscan = shm_toc_allocate(pcxt->toc, node->ioss_PscanLen);
650 40 : index_parallelscan_initialize(node->ss.ss_currentRelation,
651 : node->ioss_RelationDesc,
652 : estate->es_snapshot,
653 : piscan);
654 40 : shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
655 40 : node->ioss_ScanDesc =
656 40 : index_beginscan_parallel(node->ss.ss_currentRelation,
657 : node->ioss_RelationDesc,
658 : node->ioss_NumScanKeys,
659 : node->ioss_NumOrderByKeys,
660 : piscan);
661 40 : node->ioss_ScanDesc->xs_want_itup = true;
662 40 : node->ioss_VMBuffer = InvalidBuffer;
663 :
664 : /*
665 : * If no run-time keys to calculate or they are ready, go ahead and pass
666 : * the scankeys to the index AM.
667 : */
668 40 : if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
669 40 : index_rescan(node->ioss_ScanDesc,
670 40 : node->ioss_ScanKeys, node->ioss_NumScanKeys,
671 40 : node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
672 40 : }
673 :
674 : /* ----------------------------------------------------------------
675 : * ExecIndexOnlyScanReInitializeDSM
676 : *
677 : * Reset shared state before beginning a fresh scan.
678 : * ----------------------------------------------------------------
679 : */
680 : void
681 12 : ExecIndexOnlyScanReInitializeDSM(IndexOnlyScanState *node,
682 : ParallelContext *pcxt)
683 : {
684 12 : index_parallelrescan(node->ioss_ScanDesc);
685 12 : }
686 :
687 : /* ----------------------------------------------------------------
688 : * ExecIndexOnlyScanInitializeWorker
689 : *
690 : * Copy relevant information from TOC into planstate.
691 : * ----------------------------------------------------------------
692 : */
693 : void
694 200 : ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node,
695 : ParallelWorkerContext *pwcxt)
696 : {
697 : ParallelIndexScanDesc piscan;
698 :
699 200 : piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
700 200 : node->ioss_ScanDesc =
701 200 : index_beginscan_parallel(node->ss.ss_currentRelation,
702 : node->ioss_RelationDesc,
703 : node->ioss_NumScanKeys,
704 : node->ioss_NumOrderByKeys,
705 : piscan);
706 200 : node->ioss_ScanDesc->xs_want_itup = true;
707 :
708 : /*
709 : * If no run-time keys to calculate or they are ready, go ahead and pass
710 : * the scankeys to the index AM.
711 : */
712 200 : if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
713 200 : index_rescan(node->ioss_ScanDesc,
714 200 : node->ioss_ScanKeys, node->ioss_NumScanKeys,
715 200 : node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
716 200 : }
|