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