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-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 *
4569 tgl@sss.pgh.pa.us 59 :CBC 2681040 : 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 : 2681040 : 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 : : */
438 drowley@postgresql.o 77 : 2681040 : direction = ScanDirectionCombine(estate->es_direction,
78 : : ((IndexOnlyScan *) node->ss.ps.plan)->indexorderdir);
4569 tgl@sss.pgh.pa.us 79 : 2681040 : scandesc = node->ioss_ScanDesc;
80 : 2681040 : econtext = node->ss.ps.ps_ExprContext;
81 : 2681040 : slot = node->ss.ss_ScanTupleSlot;
82 : :
2594 rhaas@postgresql.org 83 [ + + ]: 2681040 : 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 : 4089 : 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 : 4089 : node->ioss_ScanDesc = scandesc;
97 : :
98 : :
99 : : /* Set it up for index-only scan */
100 : 4089 : node->ioss_ScanDesc->xs_want_itup = true;
101 : 4089 : 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 [ + + + - ]: 4089 : if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
108 : 4089 : index_rescan(scandesc,
109 : 4089 : node->ioss_ScanKeys,
110 : : node->ioss_NumScanKeys,
111 : 4089 : node->ioss_OrderByKeys,
112 : : node->ioss_NumOrderByKeys);
113 : : }
114 : :
115 : : /*
116 : : * OK, now that we have what we need, fetch the next tuple.
117 : : */
4569 tgl@sss.pgh.pa.us 118 [ + + ]: 2741792 : while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
119 : : {
1861 andres@anarazel.de 120 : 2701373 : bool tuple_from_heap = false;
121 : :
2455 122 [ + + ]: 2701373 : 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 : : */
2966 rhaas@postgresql.org 158 [ + + ]: 2701373 : 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 : : */
2196 alvherre@alvh.no-ip. 165 [ + + ]: 1019423 : InstrCountTuples2(node, 1);
1774 heikki.linnakangas@i 166 [ + + ]: 1019423 : if (!index_fetch_heap(scandesc, node->ioss_TableSlot))
4326 bruce@momjian.us 167 : 60749 : continue; /* no visible tuple, try next index entry */
168 : :
1774 heikki.linnakangas@i 169 : 958674 : 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 : : */
1861 andres@anarazel.de 177 [ - + ]: 958674 : if (scandesc->xs_heap_continue)
4569 tgl@sss.pgh.pa.us 178 [ # # ]:UBC 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 : :
1861 andres@anarazel.de 187 :CBC 958674 : 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 : : */
2603 tgl@sss.pgh.pa.us 196 [ + + ]: 2640624 : 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 [ - + ]: 718842 : Assert(slot->tts_tupleDescriptor->natts ==
204 : : scandesc->xs_hitupdesc->natts);
1822 andres@anarazel.de 205 : 718842 : ExecForceStoreHeapTuple(scandesc->xs_hitup, slot, false);
206 : : }
2603 tgl@sss.pgh.pa.us 207 [ + - ]: 1921782 : else if (scandesc->xs_itup)
208 : 1921782 : StoreIndexTuple(slot, scandesc->xs_itup, scandesc->xs_itupdesc);
209 : : else
2603 tgl@sss.pgh.pa.us 210 [ # # ]:UBC 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 : : */
4569 tgl@sss.pgh.pa.us 215 [ + + ]:CBC 2640624 : if (scandesc->xs_recheck)
216 : : {
217 : 7 : econtext->ecxt_scantuple = slot;
832 218 [ + + ]: 7 : if (!ExecQualAndReset(node->recheckqual, econtext))
219 : : {
220 : : /* Fails recheck, so drop it and loop back for another */
4569 221 [ - + ]: 3 : InstrCountFiltered2(node, 1);
222 : 3 : 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 : : */
3249 234 [ + + + + ]: 2640621 : if (scandesc->numberOfOrderBys > 0 && scandesc->xs_recheckorderby)
235 [ + - ]: 3 : 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 : : */
1861 andres@anarazel.de 243 [ + + ]: 2640618 : if (!tuple_from_heap)
4240 kgrittn@postgresql.o 244 : 1681950 : PredicateLockPage(scandesc->heapRelation,
245 : : ItemPointerGetBlockNumber(tid),
246 : : estate->es_snapshot);
247 : :
4569 tgl@sss.pgh.pa.us 248 : 2640618 : 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 : 40419 : 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
4564 266 : 1921782 : 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 : : */
1869 275 [ - + ]: 1921782 : Assert(slot->tts_tupleDescriptor->natts == itupdesc->natts);
276 : :
4569 277 : 1921782 : ExecClearTuple(slot);
1869 278 : 1921782 : index_deform_tuple(itup, itupdesc, slot->tts_values, slot->tts_isnull);
4569 279 : 1921782 : ExecStoreVirtualTuple(slot);
280 : 1921782 : }
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
4569 tgl@sss.pgh.pa.us 291 :UBC 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 *
2463 andres@anarazel.de 302 :CBC 2680562 : ExecIndexOnlyScan(PlanState *pstate)
303 : : {
304 : 2680562 : 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 : : */
4569 tgl@sss.pgh.pa.us 309 [ + + + + ]: 2680562 : if (node->ioss_NumRuntimeKeys != 0 && !node->ioss_RuntimeKeysReady)
310 : 279 : ExecReScan((PlanState *) node);
311 : :
312 : 2680562 : 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 : 40844 : 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 [ + + ]: 40844 : if (node->ioss_NumRuntimeKeys != 0)
339 : : {
340 : 40760 : ExprContext *econtext = node->ioss_RuntimeContext;
341 : :
342 : 40760 : ResetExprContext(econtext);
343 : 40760 : ExecIndexEvalRuntimeKeys(econtext,
344 : : node->ioss_RuntimeKeys,
345 : : node->ioss_NumRuntimeKeys);
346 : : }
347 : 40844 : node->ioss_RuntimeKeysReady = true;
348 : :
349 : : /* reset index scan */
2611 rhaas@postgresql.org 350 [ + + ]: 40844 : if (node->ioss_ScanDesc)
351 : 40010 : index_rescan(node->ioss_ScanDesc,
352 : 40010 : node->ioss_ScanKeys, node->ioss_NumScanKeys,
353 : 40010 : node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
354 : :
4569 tgl@sss.pgh.pa.us 355 : 40844 : ExecScanReScan(&node->ss);
356 : 40844 : }
357 : :
358 : :
359 : : /* ----------------------------------------------------------------
360 : : * ExecEndIndexOnlyScan
361 : : * ----------------------------------------------------------------
362 : : */
363 : : void
364 : 6936 : ExecEndIndexOnlyScan(IndexOnlyScanState *node)
365 : : {
366 : : Relation indexRelationDesc;
367 : : IndexScanDesc indexScanDesc;
368 : :
369 : : /*
370 : : * extract information from the node
371 : : */
372 : 6936 : indexRelationDesc = node->ioss_RelationDesc;
373 : 6936 : indexScanDesc = node->ioss_ScanDesc;
374 : :
375 : : /* Release VM buffer pin, if any. */
376 [ + + ]: 6936 : if (node->ioss_VMBuffer != InvalidBuffer)
377 : : {
378 : 2306 : ReleaseBuffer(node->ioss_VMBuffer);
379 : 2306 : node->ioss_VMBuffer = InvalidBuffer;
380 : : }
381 : :
382 : : /*
383 : : * close the index relation (no-op if we didn't open it)
384 : : */
385 [ + + ]: 6936 : if (indexScanDesc)
386 : 4178 : index_endscan(indexScanDesc);
387 [ + + ]: 6936 : if (indexRelationDesc)
388 : 5761 : index_close(indexRelationDesc, NoLock);
389 : 6936 : }
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 : 62002 : ExecIndexOnlyMarkPos(IndexOnlyScanState *node)
400 : : {
2269 401 : 62002 : EState *estate = node->ss.ps.state;
1683 andres@anarazel.de 402 : 62002 : EPQState *epqstate = estate->es_epq_active;
403 : :
404 [ - + ]: 62002 : 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 : : */
2269 tgl@sss.pgh.pa.us 415 :UBC 0 : Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
416 : :
417 [ # # ]: 0 : Assert(scanrelid > 0);
1683 andres@anarazel.de 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])
2269 tgl@sss.pgh.pa.us 423 [ # # ]: 0 : elog(ERROR, "unexpected ExecIndexOnlyMarkPos call in EPQ recheck");
424 : 0 : return;
425 : : }
426 : : }
427 : :
4569 tgl@sss.pgh.pa.us 428 :CBC 62002 : index_markpos(node->ioss_ScanDesc);
429 : : }
430 : :
431 : : /* ----------------------------------------------------------------
432 : : * ExecIndexOnlyRestrPos
433 : : * ----------------------------------------------------------------
434 : : */
435 : : void
4569 tgl@sss.pgh.pa.us 436 :LBC (3) : ExecIndexOnlyRestrPos(IndexOnlyScanState *node)
437 : : {
2269 438 : (3) : EState *estate = node->ss.ps.state;
1683 andres@anarazel.de 439 : (3) : EPQState *epqstate = estate->es_epq_active;
440 : :
441 [ # # ]: (3) : if (estate->es_epq_active != NULL)
442 : : {
443 : : /* See comments in ExecIndexMarkPos */
2269 tgl@sss.pgh.pa.us 444 :UBC 0 : Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
445 : :
446 [ # # ]: 0 : Assert(scanrelid > 0);
1683 andres@anarazel.de 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])
2269 tgl@sss.pgh.pa.us 452 [ # # ]: 0 : elog(ERROR, "unexpected ExecIndexOnlyRestrPos call in EPQ recheck");
453 : 0 : return;
454 : : }
455 : : }
456 : :
4569 tgl@sss.pgh.pa.us 457 :LBC (3) : 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 *
4569 tgl@sss.pgh.pa.us 472 :CBC 6967 : 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 : 6967 : indexstate = makeNode(IndexOnlyScanState);
483 : 6967 : indexstate->ss.ps.plan = (Plan *) node;
484 : 6967 : indexstate->ss.ps.state = estate;
2463 andres@anarazel.de 485 : 6967 : indexstate->ss.ps.ExecProcNode = ExecIndexOnlyScan;
486 : :
487 : : /*
488 : : * Miscellaneous initialization
489 : : *
490 : : * create expression context for node
491 : : */
4569 tgl@sss.pgh.pa.us 492 : 6967 : ExecAssignExprContext(estate, &indexstate->ss.ps);
493 : :
494 : : /*
495 : : * open the scan relation
496 : : */
4005 497 : 6967 : currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
498 : :
4569 499 : 6967 : indexstate->ss.ss_currentRelation = currentRelation;
500 : 6967 : 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 : : */
1972 andres@anarazel.de 509 : 6967 : tupDesc = ExecTypeFromTL(node->indextlist);
1861 510 : 6967 : 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 : : */
1774 heikki.linnakangas@i 517 : 6967 : indexstate->ioss_TableSlot =
518 : 6967 : 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 : : */
1983 andres@anarazel.de 526 : 6967 : ExecInitResultTypeTL(&indexstate->ss.ps);
3262 tgl@sss.pgh.pa.us 527 : 6967 : 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 : : */
2249 andres@anarazel.de 535 : 6967 : indexstate->ss.ps.qual =
536 : 6967 : ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
832 tgl@sss.pgh.pa.us 537 : 6967 : indexstate->recheckqual =
538 : 6967 : 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 : : */
4569 545 [ + + ]: 6967 : if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
546 : 1175 : return indexstate;
547 : :
548 : : /* Open the index relation. */
1837 549 : 5792 : lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
550 : 5792 : indexstate->ioss_RelationDesc = index_open(node->indexid, lockmode);
551 : :
552 : : /*
553 : : * Initialize index-specific scan state
554 : : */
4569 555 : 5792 : indexstate->ioss_RuntimeKeysReady = false;
556 : 5792 : indexstate->ioss_RuntimeKeys = NULL;
557 : 5792 : indexstate->ioss_NumRuntimeKeys = 0;
558 : :
559 : : /*
560 : : * build the index scan keys from the index qualification
561 : : */
562 : 5792 : ExecIndexBuildScanKeys((PlanState *) indexstate,
563 : : indexstate->ioss_RelationDesc,
564 : : node->indexqual,
565 : : false,
566 : 5792 : &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 : 5792 : ExecIndexBuildScanKeys((PlanState *) indexstate,
577 : : indexstate->ioss_RelationDesc,
578 : : node->indexorderby,
579 : : true,
580 : 5792 : &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 [ + + ]: 5792 : if (indexstate->ioss_NumRuntimeKeys != 0)
594 : : {
595 : 1076 : ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
596 : :
597 : 1076 : ExecAssignExprContext(estate, &indexstate->ss.ps);
598 : 1076 : indexstate->ioss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
599 : 1076 : indexstate->ss.ps.ps_ExprContext = stdecontext;
600 : : }
601 : : else
602 : : {
603 : 4716 : indexstate->ioss_RuntimeContext = NULL;
604 : : }
605 : :
606 : : /*
607 : : * all done.
608 : : */
609 : 5792 : 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
2611 rhaas@postgresql.org 625 : 20 : ExecIndexOnlyScanEstimate(IndexOnlyScanState *node,
626 : : ParallelContext *pcxt)
627 : : {
628 : 20 : EState *estate = node->ss.ps.state;
629 : :
630 : 20 : node->ioss_PscanLen = index_parallelscan_estimate(node->ioss_RelationDesc,
631 : : node->ioss_NumScanKeys,
632 : : node->ioss_NumOrderByKeys,
633 : : estate->es_snapshot);
634 : 20 : shm_toc_estimate_chunk(&pcxt->estimator, node->ioss_PscanLen);
635 : 20 : shm_toc_estimate_keys(&pcxt->estimator, 1);
636 : 20 : }
637 : :
638 : : /* ----------------------------------------------------------------
639 : : * ExecIndexOnlyScanInitializeDSM
640 : : *
641 : : * Set up a parallel index-only scan descriptor.
642 : : * ----------------------------------------------------------------
643 : : */
644 : : void
645 : 20 : ExecIndexOnlyScanInitializeDSM(IndexOnlyScanState *node,
646 : : ParallelContext *pcxt)
647 : : {
648 : 20 : EState *estate = node->ss.ps.state;
649 : : ParallelIndexScanDesc piscan;
650 : :
651 : 20 : piscan = shm_toc_allocate(pcxt->toc, node->ioss_PscanLen);
652 : 20 : index_parallelscan_initialize(node->ss.ss_currentRelation,
653 : : node->ioss_RelationDesc,
654 : : estate->es_snapshot,
655 : : piscan);
656 : 20 : shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
657 : 20 : node->ioss_ScanDesc =
658 : 20 : index_beginscan_parallel(node->ss.ss_currentRelation,
659 : : node->ioss_RelationDesc,
660 : : node->ioss_NumScanKeys,
661 : : node->ioss_NumOrderByKeys,
662 : : piscan);
663 : 20 : node->ioss_ScanDesc->xs_want_itup = true;
664 : 20 : node->ioss_VMBuffer = InvalidBuffer;
665 : :
666 : : /*
667 : : * If no run-time keys to calculate or they are ready, go ahead and pass
668 : : * the scankeys to the index AM.
669 : : */
2594 670 [ - + - - ]: 20 : if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
2611 671 : 20 : index_rescan(node->ioss_ScanDesc,
672 : 20 : node->ioss_ScanKeys, node->ioss_NumScanKeys,
673 : 20 : node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
674 : 20 : }
675 : :
676 : : /* ----------------------------------------------------------------
677 : : * ExecIndexOnlyScanReInitializeDSM
678 : : *
679 : : * Reset shared state before beginning a fresh scan.
680 : : * ----------------------------------------------------------------
681 : : */
682 : : void
2419 tgl@sss.pgh.pa.us 683 : 6 : ExecIndexOnlyScanReInitializeDSM(IndexOnlyScanState *node,
684 : : ParallelContext *pcxt)
685 : : {
686 : 6 : index_parallelrescan(node->ioss_ScanDesc);
687 : 6 : }
688 : :
689 : : /* ----------------------------------------------------------------
690 : : * ExecIndexOnlyScanInitializeWorker
691 : : *
692 : : * Copy relevant information from TOC into planstate.
693 : : * ----------------------------------------------------------------
694 : : */
695 : : void
2341 andres@anarazel.de 696 : 100 : ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node,
697 : : ParallelWorkerContext *pwcxt)
698 : : {
699 : : ParallelIndexScanDesc piscan;
700 : :
701 : 100 : piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
2611 rhaas@postgresql.org 702 : 100 : node->ioss_ScanDesc =
703 : 100 : index_beginscan_parallel(node->ss.ss_currentRelation,
704 : : node->ioss_RelationDesc,
705 : : node->ioss_NumScanKeys,
706 : : node->ioss_NumOrderByKeys,
707 : : piscan);
708 : 100 : node->ioss_ScanDesc->xs_want_itup = true;
709 : :
710 : : /*
711 : : * If no run-time keys to calculate or they are ready, go ahead and pass
712 : : * the scankeys to the index AM.
713 : : */
2594 714 [ - + - - ]: 100 : if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
2611 715 : 100 : index_rescan(node->ioss_ScanDesc,
716 : 100 : node->ioss_ScanKeys, node->ioss_NumScanKeys,
717 : 100 : node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
718 : 100 : }
|