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