Coverage Report

Created: 2026-08-13 07:12

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