Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/executor/nodeIndexscan.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * nodeIndexscan.c
4
 *    Routines to support indexed scans of relations
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/nodeIndexscan.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
/*
16
 * INTERFACE ROUTINES
17
 *    ExecIndexScan     scans a relation using an index
18
 *    IndexNext       retrieve next tuple using index
19
 *    IndexNextWithReorder  same, but recheck ORDER BY expressions
20
 *    ExecInitIndexScan   creates and initializes state info.
21
 *    ExecReScanIndexScan   rescans the indexed relation.
22
 *    ExecEndIndexScan    releases all storage.
23
 *    ExecIndexMarkPos    marks scan position.
24
 *    ExecIndexRestrPos   restores scan position.
25
 *    ExecIndexScanEstimate estimates DSM space needed for parallel index scan
26
 *    ExecIndexScanInitializeDSM initialize DSM for parallel indexscan
27
 *    ExecIndexScanReInitializeDSM reinitialize DSM for fresh scan
28
 *    ExecIndexScanInitializeWorker attach to DSM info in parallel worker
29
 */
30
#include "postgres.h"
31
32
#include "access/nbtree.h"
33
#include "access/relscan.h"
34
#include "access/tableam.h"
35
#include "catalog/pg_am.h"
36
#include "executor/executor.h"
37
#include "executor/instrument.h"
38
#include "executor/nodeIndexscan.h"
39
#include "lib/pairingheap.h"
40
#include "miscadmin.h"
41
#include "nodes/nodeFuncs.h"
42
#include "utils/array.h"
43
#include "utils/datum.h"
44
#include "utils/lsyscache.h"
45
#include "utils/rel.h"
46
#include "utils/sortsupport.h"
47
48
/*
49
 * When an ordering operator is used, tuples fetched from the index that
50
 * need to be reordered are queued in a pairing heap, as ReorderTuples.
51
 */
52
typedef struct
53
{
54
  pairingheap_node ph_node;
55
  HeapTuple htup;
56
  Datum    *orderbyvals;
57
  bool     *orderbynulls;
58
} ReorderTuple;
59
60
static TupleTableSlot *IndexNext(IndexScanState *node);
61
static TupleTableSlot *IndexNextWithReorder(IndexScanState *node);
62
static void EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext);
63
static bool IndexRecheck(IndexScanState *node, TupleTableSlot *slot);
64
static int  cmp_orderbyvals(const Datum *adist, const bool *anulls,
65
              const Datum *bdist, const bool *bnulls,
66
              IndexScanState *node);
67
static int  reorderqueue_cmp(const pairingheap_node *a,
68
               const pairingheap_node *b, void *arg);
69
static void reorderqueue_push(IndexScanState *node, TupleTableSlot *slot,
70
                const Datum *orderbyvals, const bool *orderbynulls);
71
static HeapTuple reorderqueue_pop(IndexScanState *node);
72
73
74
/* ----------------------------------------------------------------
75
 *    IndexNext
76
 *
77
 *    Retrieve a tuple from the IndexScan node's currentRelation
78
 *    using the index specified in the IndexScanState information.
79
 * ----------------------------------------------------------------
80
 */
81
static TupleTableSlot *
82
IndexNext(IndexScanState *node)
83
0
{
84
0
  EState     *estate;
85
0
  ExprContext *econtext;
86
0
  ScanDirection direction;
87
0
  IndexScanDesc scandesc;
88
0
  TupleTableSlot *slot;
89
90
  /*
91
   * extract necessary information from index scan node
92
   */
93
0
  estate = node->ss.ps.state;
94
95
  /*
96
   * Determine which direction to scan the index in based on the plan's scan
97
   * direction and the current direction of execution.
98
   */
99
0
  direction = ScanDirectionCombine(estate->es_direction,
100
0
                   ((IndexScan *) node->ss.ps.plan)->indexorderdir);
101
0
  scandesc = node->iss_ScanDesc;
102
0
  econtext = node->ss.ps.ps_ExprContext;
103
0
  slot = node->ss.ss_ScanTupleSlot;
104
105
0
  if (scandesc == NULL)
106
0
  {
107
    /*
108
     * We reach here if the index scan is not parallel, or if we're
109
     * serially executing an index scan that was planned to be parallel.
110
     */
111
0
    scandesc = index_beginscan(node->ss.ss_currentRelation,
112
0
                   node->iss_RelationDesc,
113
0
                   false,
114
0
                   estate->es_snapshot,
115
0
                   node->iss_Instrument,
116
0
                   node->iss_NumScanKeys,
117
0
                   node->iss_NumOrderByKeys,
118
0
                   ScanRelIsReadOnly(&node->ss) ?
119
0
                   SO_HINT_REL_READ_ONLY : SO_NONE);
120
121
0
    node->iss_ScanDesc = scandesc;
122
123
    /*
124
     * If no run-time keys to calculate or they are ready, go ahead and
125
     * pass the scankeys to the index AM.
126
     */
127
0
    if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
128
0
      index_rescan(scandesc,
129
0
             node->iss_ScanKeys, node->iss_NumScanKeys,
130
0
             node->iss_OrderByKeys, node->iss_NumOrderByKeys);
131
0
  }
132
133
  /*
134
   * ok, now that we have what we need, fetch the next tuple.
135
   */
136
0
  while (table_index_getnext_slot(scandesc, direction, slot))
137
0
  {
138
0
    CHECK_FOR_INTERRUPTS();
139
140
    /*
141
     * If the index was lossy, we have to recheck the index quals using
142
     * the fetched tuple.
143
     */
144
0
    if (scandesc->xs_recheck)
145
0
    {
146
0
      econtext->ecxt_scantuple = slot;
147
0
      if (!ExecQualAndReset(node->indexqualorig, econtext))
148
0
      {
149
        /* Fails recheck, so drop it and loop back for another */
150
0
        InstrCountFiltered2(node, 1);
151
0
        continue;
152
0
      }
153
0
    }
154
155
0
    return slot;
156
0
  }
157
158
  /*
159
   * if we get here it means the index scan failed so we are at the end of
160
   * the scan..
161
   */
162
0
  node->iss_ReachedEnd = true;
163
0
  return ExecClearTuple(slot);
164
0
}
165
166
/* ----------------------------------------------------------------
167
 *    IndexNextWithReorder
168
 *
169
 *    Like IndexNext, but this version can also re-check ORDER BY
170
 *    expressions, and reorder the tuples as necessary.
171
 * ----------------------------------------------------------------
172
 */
173
static TupleTableSlot *
174
IndexNextWithReorder(IndexScanState *node)
175
0
{
176
0
  EState     *estate;
177
0
  ExprContext *econtext;
178
0
  IndexScanDesc scandesc;
179
0
  TupleTableSlot *slot;
180
0
  ReorderTuple *topmost = NULL;
181
0
  bool    was_exact;
182
0
  Datum    *lastfetched_vals;
183
0
  bool     *lastfetched_nulls;
184
0
  int     cmp;
185
186
0
  estate = node->ss.ps.state;
187
188
  /*
189
   * Only forward scan is supported with reordering.  Note: we can get away
190
   * with just Asserting here because the system will not try to run the
191
   * plan backwards if ExecSupportsBackwardScan() says it won't work.
192
   * Currently, that is guaranteed because no index AMs support both
193
   * amcanorderbyop and amcanbackward; if any ever do,
194
   * ExecSupportsBackwardScan() will need to consider indexorderbys
195
   * explicitly.
196
   */
197
0
  Assert(!ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indexorderdir));
198
0
  Assert(ScanDirectionIsForward(estate->es_direction));
199
200
0
  scandesc = node->iss_ScanDesc;
201
0
  econtext = node->ss.ps.ps_ExprContext;
202
0
  slot = node->ss.ss_ScanTupleSlot;
203
204
0
  if (scandesc == NULL)
205
0
  {
206
    /*
207
     * We reach here if the index scan is not parallel, or if we're
208
     * serially executing an index scan that was planned to be parallel.
209
     */
210
0
    scandesc = index_beginscan(node->ss.ss_currentRelation,
211
0
                   node->iss_RelationDesc,
212
0
                   false,
213
0
                   estate->es_snapshot,
214
0
                   node->iss_Instrument,
215
0
                   node->iss_NumScanKeys,
216
0
                   node->iss_NumOrderByKeys,
217
0
                   ScanRelIsReadOnly(&node->ss) ?
218
0
                   SO_HINT_REL_READ_ONLY : SO_NONE);
219
220
0
    node->iss_ScanDesc = scandesc;
221
222
    /*
223
     * If no run-time keys to calculate or they are ready, go ahead and
224
     * pass the scankeys to the index AM.
225
     */
226
0
    if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
227
0
      index_rescan(scandesc,
228
0
             node->iss_ScanKeys, node->iss_NumScanKeys,
229
0
             node->iss_OrderByKeys, node->iss_NumOrderByKeys);
230
0
  }
231
232
0
  for (;;)
233
0
  {
234
0
    CHECK_FOR_INTERRUPTS();
235
236
    /*
237
     * Check the reorder queue first.  If the topmost tuple in the queue
238
     * has an ORDER BY value smaller than (or equal to) the value last
239
     * returned by the index, we can return it now.
240
     */
241
0
    if (!pairingheap_is_empty(node->iss_ReorderQueue))
242
0
    {
243
0
      topmost = (ReorderTuple *) pairingheap_first(node->iss_ReorderQueue);
244
245
0
      if (node->iss_ReachedEnd ||
246
0
        cmp_orderbyvals(topmost->orderbyvals,
247
0
                topmost->orderbynulls,
248
0
                scandesc->xs_orderbyvals,
249
0
                scandesc->xs_orderbynulls,
250
0
                node) <= 0)
251
0
      {
252
0
        HeapTuple tuple;
253
254
0
        tuple = reorderqueue_pop(node);
255
256
        /* Pass 'true', as the tuple in the queue is a palloc'd copy */
257
0
        ExecForceStoreHeapTuple(tuple, slot, true);
258
0
        return slot;
259
0
      }
260
0
    }
261
0
    else if (node->iss_ReachedEnd)
262
0
    {
263
      /* Queue is empty, and no more tuples from index.  We're done. */
264
0
      return ExecClearTuple(slot);
265
0
    }
266
267
    /*
268
     * Fetch next tuple from the index.
269
     */
270
0
next_indextuple:
271
0
    if (!table_index_getnext_slot(scandesc, ForwardScanDirection, slot))
272
0
    {
273
      /*
274
       * No more tuples from the index.  But we still need to drain any
275
       * remaining tuples from the queue before we're done.
276
       */
277
0
      node->iss_ReachedEnd = true;
278
0
      continue;
279
0
    }
280
281
    /*
282
     * If the index was lossy, we have to recheck the index quals and
283
     * ORDER BY expressions using the fetched tuple.
284
     */
285
0
    if (scandesc->xs_recheck)
286
0
    {
287
0
      econtext->ecxt_scantuple = slot;
288
0
      if (!ExecQualAndReset(node->indexqualorig, econtext))
289
0
      {
290
        /* Fails recheck, so drop it and loop back for another */
291
0
        InstrCountFiltered2(node, 1);
292
        /* allow this loop to be cancellable */
293
0
        CHECK_FOR_INTERRUPTS();
294
0
        goto next_indextuple;
295
0
      }
296
0
    }
297
298
0
    if (scandesc->xs_recheckorderby)
299
0
    {
300
0
      econtext->ecxt_scantuple = slot;
301
0
      ResetExprContext(econtext);
302
0
      EvalOrderByExpressions(node, econtext);
303
304
      /*
305
       * Was the ORDER BY value returned by the index accurate?  The
306
       * recheck flag means that the index can return inaccurate values,
307
       * but then again, the value returned for any particular tuple
308
       * could also be exactly correct.  Compare the value returned by
309
       * the index with the recalculated value.  (If the value returned
310
       * by the index happened to be exact right, we can often avoid
311
       * pushing the tuple to the queue, just to pop it back out again.)
312
       */
313
0
      cmp = cmp_orderbyvals(node->iss_OrderByValues,
314
0
                  node->iss_OrderByNulls,
315
0
                  scandesc->xs_orderbyvals,
316
0
                  scandesc->xs_orderbynulls,
317
0
                  node);
318
0
      if (cmp < 0)
319
0
        elog(ERROR, "index returned tuples in wrong order");
320
0
      else if (cmp == 0)
321
0
        was_exact = true;
322
0
      else
323
0
        was_exact = false;
324
0
      lastfetched_vals = node->iss_OrderByValues;
325
0
      lastfetched_nulls = node->iss_OrderByNulls;
326
0
    }
327
0
    else
328
0
    {
329
0
      was_exact = true;
330
0
      lastfetched_vals = scandesc->xs_orderbyvals;
331
0
      lastfetched_nulls = scandesc->xs_orderbynulls;
332
0
    }
333
334
    /*
335
     * Can we return this tuple immediately, or does it need to be pushed
336
     * to the reorder queue?  If the ORDER BY expression values returned
337
     * by the index were inaccurate, we can't return it yet, because the
338
     * next tuple from the index might need to come before this one. Also,
339
     * we can't return it yet if there are any smaller tuples in the queue
340
     * already.
341
     */
342
0
    if (!was_exact || (topmost && cmp_orderbyvals(lastfetched_vals,
343
0
                            lastfetched_nulls,
344
0
                            topmost->orderbyvals,
345
0
                            topmost->orderbynulls,
346
0
                            node) > 0))
347
0
    {
348
      /* Put this tuple to the queue */
349
0
      reorderqueue_push(node, slot, lastfetched_vals, lastfetched_nulls);
350
0
      continue;
351
0
    }
352
0
    else
353
0
    {
354
      /* Can return this tuple immediately. */
355
0
      return slot;
356
0
    }
357
0
  }
358
359
  /*
360
   * if we get here it means the index scan failed so we are at the end of
361
   * the scan..
362
   */
363
0
  return ExecClearTuple(slot);
364
0
}
365
366
/*
367
 * Calculate the expressions in the ORDER BY clause, based on the heap tuple.
368
 */
369
static void
370
EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext)
371
0
{
372
0
  int     i;
373
0
  ListCell   *l;
374
0
  MemoryContext oldContext;
375
376
0
  oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
377
378
0
  i = 0;
379
0
  foreach(l, node->indexorderbyorig)
380
0
  {
381
0
    ExprState  *orderby = (ExprState *) lfirst(l);
382
383
0
    node->iss_OrderByValues[i] = ExecEvalExpr(orderby,
384
0
                          econtext,
385
0
                          &node->iss_OrderByNulls[i]);
386
0
    i++;
387
0
  }
388
389
0
  MemoryContextSwitchTo(oldContext);
390
0
}
391
392
/*
393
 * IndexRecheck -- access method routine to recheck a tuple in EvalPlanQual
394
 */
395
static bool
396
IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
397
0
{
398
0
  ExprContext *econtext;
399
400
  /*
401
   * extract necessary information from index scan node
402
   */
403
0
  econtext = node->ss.ps.ps_ExprContext;
404
405
  /* Does the tuple meet the indexqual condition? */
406
0
  econtext->ecxt_scantuple = slot;
407
0
  return ExecQualAndReset(node->indexqualorig, econtext);
408
0
}
409
410
411
/*
412
 * Compare ORDER BY expression values.
413
 */
414
static int
415
cmp_orderbyvals(const Datum *adist, const bool *anulls,
416
        const Datum *bdist, const bool *bnulls,
417
        IndexScanState *node)
418
0
{
419
0
  int     i;
420
0
  int     result;
421
422
0
  for (i = 0; i < node->iss_NumOrderByKeys; i++)
423
0
  {
424
0
    SortSupport ssup = &node->iss_SortSupport[i];
425
426
    /*
427
     * Handle nulls.  We only need to support NULLS LAST ordering, because
428
     * match_pathkeys_to_index() doesn't consider indexorderby
429
     * implementation otherwise.
430
     */
431
0
    if (anulls[i] && !bnulls[i])
432
0
      return 1;
433
0
    else if (!anulls[i] && bnulls[i])
434
0
      return -1;
435
0
    else if (anulls[i] && bnulls[i])
436
0
      return 0;
437
438
0
    result = ssup->comparator(adist[i], bdist[i], ssup);
439
0
    if (result != 0)
440
0
      return result;
441
0
  }
442
443
0
  return 0;
444
0
}
445
446
/*
447
 * Pairing heap provides getting topmost (greatest) element while KNN provides
448
 * ascending sort.  That's why we invert the sort order.
449
 */
450
static int
451
reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
452
         void *arg)
453
0
{
454
0
  const ReorderTuple *rta = (const ReorderTuple *) a;
455
0
  const ReorderTuple *rtb = (const ReorderTuple *) b;
456
0
  IndexScanState *node = (IndexScanState *) arg;
457
458
  /* exchange argument order to invert the sort order */
459
0
  return cmp_orderbyvals(rtb->orderbyvals, rtb->orderbynulls,
460
0
               rta->orderbyvals, rta->orderbynulls,
461
0
               node);
462
0
}
463
464
/*
465
 * Helper function to push a tuple to the reorder queue.
466
 */
467
static void
468
reorderqueue_push(IndexScanState *node, TupleTableSlot *slot,
469
          const Datum *orderbyvals, const bool *orderbynulls)
470
0
{
471
0
  IndexScanDesc scandesc = node->iss_ScanDesc;
472
0
  EState     *estate = node->ss.ps.state;
473
0
  MemoryContext oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
474
0
  ReorderTuple *rt;
475
0
  int     i;
476
477
0
  rt = palloc_object(ReorderTuple);
478
0
  rt->htup = ExecCopySlotHeapTuple(slot);
479
0
  rt->orderbyvals = palloc_array(Datum, scandesc->numberOfOrderBys);
480
0
  rt->orderbynulls = palloc_array(bool, scandesc->numberOfOrderBys);
481
0
  for (i = 0; i < node->iss_NumOrderByKeys; i++)
482
0
  {
483
0
    if (!orderbynulls[i])
484
0
      rt->orderbyvals[i] = datumCopy(orderbyvals[i],
485
0
                       node->iss_OrderByTypByVals[i],
486
0
                       node->iss_OrderByTypLens[i]);
487
0
    else
488
0
      rt->orderbyvals[i] = (Datum) 0;
489
0
    rt->orderbynulls[i] = orderbynulls[i];
490
0
  }
491
0
  pairingheap_add(node->iss_ReorderQueue, &rt->ph_node);
492
493
0
  MemoryContextSwitchTo(oldContext);
494
0
}
495
496
/*
497
 * Helper function to pop the next tuple from the reorder queue.
498
 */
499
static HeapTuple
500
reorderqueue_pop(IndexScanState *node)
501
0
{
502
0
  HeapTuple result;
503
0
  ReorderTuple *topmost;
504
0
  int     i;
505
506
0
  topmost = (ReorderTuple *) pairingheap_remove_first(node->iss_ReorderQueue);
507
508
0
  result = topmost->htup;
509
0
  for (i = 0; i < node->iss_NumOrderByKeys; i++)
510
0
  {
511
0
    if (!node->iss_OrderByTypByVals[i] && !topmost->orderbynulls[i])
512
0
      pfree(DatumGetPointer(topmost->orderbyvals[i]));
513
0
  }
514
0
  pfree(topmost->orderbyvals);
515
0
  pfree(topmost->orderbynulls);
516
0
  pfree(topmost);
517
518
0
  return result;
519
0
}
520
521
522
/* ----------------------------------------------------------------
523
 *    ExecIndexScan(node)
524
 * ----------------------------------------------------------------
525
 */
526
static TupleTableSlot *
527
ExecIndexScan(PlanState *pstate)
528
0
{
529
0
  IndexScanState *node = castNode(IndexScanState, pstate);
530
531
  /*
532
   * If we have runtime keys and they've not already been set up, do it now.
533
   */
534
0
  if (node->iss_NumRuntimeKeys != 0 && !node->iss_RuntimeKeysReady)
535
0
    ExecReScan((PlanState *) node);
536
537
0
  if (node->iss_NumOrderByKeys > 0)
538
0
    return ExecScan(&node->ss,
539
0
            (ExecScanAccessMtd) IndexNextWithReorder,
540
0
            (ExecScanRecheckMtd) IndexRecheck);
541
0
  else
542
0
    return ExecScan(&node->ss,
543
0
            (ExecScanAccessMtd) IndexNext,
544
0
            (ExecScanRecheckMtd) IndexRecheck);
545
0
}
546
547
/* ----------------------------------------------------------------
548
 *    ExecReScanIndexScan(node)
549
 *
550
 *    Recalculates the values of any scan keys whose value depends on
551
 *    information known at runtime, then rescans the indexed relation.
552
 *
553
 *    Updating the scan key was formerly done separately in
554
 *    ExecUpdateIndexScanKeys. Integrating it into ReScan makes
555
 *    rescans of indices and relations/general streams more uniform.
556
 * ----------------------------------------------------------------
557
 */
558
void
559
ExecReScanIndexScan(IndexScanState *node)
560
0
{
561
  /*
562
   * If we are doing runtime key calculations (ie, any of the index key
563
   * values weren't simple Consts), compute the new key values.  But first,
564
   * reset the context so we don't leak memory as each outer tuple is
565
   * scanned.  Note this assumes that we will recalculate *all* runtime keys
566
   * on each call.
567
   */
568
0
  if (node->iss_NumRuntimeKeys != 0)
569
0
  {
570
0
    ExprContext *econtext = node->iss_RuntimeContext;
571
572
0
    ResetExprContext(econtext);
573
0
    ExecIndexEvalRuntimeKeys(econtext,
574
0
                 node->iss_RuntimeKeys,
575
0
                 node->iss_NumRuntimeKeys);
576
0
  }
577
0
  node->iss_RuntimeKeysReady = true;
578
579
  /* flush the reorder queue */
580
0
  if (node->iss_ReorderQueue)
581
0
  {
582
0
    HeapTuple tuple;
583
584
0
    while (!pairingheap_is_empty(node->iss_ReorderQueue))
585
0
    {
586
0
      tuple = reorderqueue_pop(node);
587
0
      heap_freetuple(tuple);
588
0
    }
589
0
  }
590
591
  /* reset index scan */
592
0
  if (node->iss_ScanDesc)
593
0
    index_rescan(node->iss_ScanDesc,
594
0
           node->iss_ScanKeys, node->iss_NumScanKeys,
595
0
           node->iss_OrderByKeys, node->iss_NumOrderByKeys);
596
0
  node->iss_ReachedEnd = false;
597
598
0
  ExecScanReScan(&node->ss);
599
0
}
600
601
602
/*
603
 * ExecIndexEvalRuntimeKeys
604
 *    Evaluate any runtime key values, and update the scankeys.
605
 */
606
void
607
ExecIndexEvalRuntimeKeys(ExprContext *econtext,
608
             IndexRuntimeKeyInfo *runtimeKeys, int numRuntimeKeys)
609
0
{
610
0
  int     j;
611
0
  MemoryContext oldContext;
612
613
  /* We want to keep the key values in per-tuple memory */
614
0
  oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
615
616
0
  for (j = 0; j < numRuntimeKeys; j++)
617
0
  {
618
0
    ScanKey   scan_key = runtimeKeys[j].scan_key;
619
0
    ExprState  *key_expr = runtimeKeys[j].key_expr;
620
0
    Datum   scanvalue;
621
0
    bool    isNull;
622
623
    /*
624
     * For each run-time key, extract the run-time expression and evaluate
625
     * it with respect to the current context.  We then stick the result
626
     * into the proper scan key.
627
     *
628
     * Note: the result of the eval could be a pass-by-ref value that's
629
     * stored in some outer scan's tuple, not in
630
     * econtext->ecxt_per_tuple_memory.  We assume that the outer tuple
631
     * will stay put throughout our scan.  If this is wrong, we could copy
632
     * the result into our context explicitly, but I think that's not
633
     * necessary.
634
     *
635
     * It's also entirely possible that the result of the eval is a
636
     * toasted value.  In this case we should forcibly detoast it, to
637
     * avoid repeat detoastings each time the value is examined by an
638
     * index support function.
639
     */
640
0
    scanvalue = ExecEvalExpr(key_expr,
641
0
                 econtext,
642
0
                 &isNull);
643
0
    if (isNull)
644
0
    {
645
0
      scan_key->sk_argument = scanvalue;
646
0
      scan_key->sk_flags |= SK_ISNULL;
647
0
    }
648
0
    else
649
0
    {
650
0
      if (runtimeKeys[j].key_toastable)
651
0
        scanvalue = PointerGetDatum(PG_DETOAST_DATUM(scanvalue));
652
0
      scan_key->sk_argument = scanvalue;
653
0
      scan_key->sk_flags &= ~SK_ISNULL;
654
0
    }
655
0
  }
656
657
0
  MemoryContextSwitchTo(oldContext);
658
0
}
659
660
/*
661
 * ExecIndexEvalArrayKeys
662
 *    Evaluate any array key values, and set up to iterate through arrays.
663
 *
664
 * Returns true if there are array elements to consider; false means there
665
 * is at least one null or empty array, so no match is possible.  On true
666
 * result, the scankeys are initialized with the first elements of the arrays.
667
 */
668
bool
669
ExecIndexEvalArrayKeys(ExprContext *econtext,
670
             IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
671
0
{
672
0
  bool    result = true;
673
0
  int     j;
674
0
  MemoryContext oldContext;
675
676
  /* We want to keep the arrays in per-tuple memory */
677
0
  oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
678
679
0
  for (j = 0; j < numArrayKeys; j++)
680
0
  {
681
0
    ScanKey   scan_key = arrayKeys[j].scan_key;
682
0
    ExprState  *array_expr = arrayKeys[j].array_expr;
683
0
    Datum   arraydatum;
684
0
    bool    isNull;
685
0
    ArrayType  *arrayval;
686
0
    int16   elmlen;
687
0
    bool    elmbyval;
688
0
    char    elmalign;
689
0
    int     num_elems;
690
0
    Datum    *elem_values;
691
0
    bool     *elem_nulls;
692
693
    /*
694
     * Compute and deconstruct the array expression. (Notes in
695
     * ExecIndexEvalRuntimeKeys() apply here too.)
696
     */
697
0
    arraydatum = ExecEvalExpr(array_expr,
698
0
                  econtext,
699
0
                  &isNull);
700
0
    if (isNull)
701
0
    {
702
0
      result = false;
703
0
      break;        /* no point in evaluating more */
704
0
    }
705
0
    arrayval = DatumGetArrayTypeP(arraydatum);
706
    /* We could cache this data, but not clear it's worth it */
707
0
    get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
708
0
               &elmlen, &elmbyval, &elmalign);
709
0
    deconstruct_array(arrayval,
710
0
              ARR_ELEMTYPE(arrayval),
711
0
              elmlen, elmbyval, elmalign,
712
0
              &elem_values, &elem_nulls, &num_elems);
713
0
    if (num_elems <= 0)
714
0
    {
715
0
      result = false;
716
0
      break;        /* no point in evaluating more */
717
0
    }
718
719
    /*
720
     * Note: we expect the previous array data, if any, to be
721
     * automatically freed by resetting the per-tuple context; hence no
722
     * pfree's here.
723
     */
724
0
    arrayKeys[j].elem_values = elem_values;
725
0
    arrayKeys[j].elem_nulls = elem_nulls;
726
0
    arrayKeys[j].num_elems = num_elems;
727
0
    scan_key->sk_argument = elem_values[0];
728
0
    if (elem_nulls[0])
729
0
      scan_key->sk_flags |= SK_ISNULL;
730
0
    else
731
0
      scan_key->sk_flags &= ~SK_ISNULL;
732
0
    arrayKeys[j].next_elem = 1;
733
0
  }
734
735
0
  MemoryContextSwitchTo(oldContext);
736
737
0
  return result;
738
0
}
739
740
/*
741
 * ExecIndexAdvanceArrayKeys
742
 *    Advance to the next set of array key values, if any.
743
 *
744
 * Returns true if there is another set of values to consider, false if not.
745
 * On true result, the scankeys are initialized with the next set of values.
746
 */
747
bool
748
ExecIndexAdvanceArrayKeys(IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
749
0
{
750
0
  bool    found = false;
751
0
  int     j;
752
753
  /*
754
   * Note we advance the rightmost array key most quickly, since it will
755
   * correspond to the lowest-order index column among the available
756
   * qualifications.  This is hypothesized to result in better locality of
757
   * access in the index.
758
   */
759
0
  for (j = numArrayKeys - 1; j >= 0; j--)
760
0
  {
761
0
    ScanKey   scan_key = arrayKeys[j].scan_key;
762
0
    int     next_elem = arrayKeys[j].next_elem;
763
0
    int     num_elems = arrayKeys[j].num_elems;
764
0
    Datum    *elem_values = arrayKeys[j].elem_values;
765
0
    bool     *elem_nulls = arrayKeys[j].elem_nulls;
766
767
0
    if (next_elem >= num_elems)
768
0
    {
769
0
      next_elem = 0;
770
0
      found = false;    /* need to advance next array key */
771
0
    }
772
0
    else
773
0
      found = true;
774
0
    scan_key->sk_argument = elem_values[next_elem];
775
0
    if (elem_nulls[next_elem])
776
0
      scan_key->sk_flags |= SK_ISNULL;
777
0
    else
778
0
      scan_key->sk_flags &= ~SK_ISNULL;
779
0
    arrayKeys[j].next_elem = next_elem + 1;
780
0
    if (found)
781
0
      break;
782
0
  }
783
784
0
  return found;
785
0
}
786
787
788
/* ----------------------------------------------------------------
789
 *    ExecEndIndexScan
790
 * ----------------------------------------------------------------
791
 */
792
void
793
ExecEndIndexScan(IndexScanState *node)
794
0
{
795
0
  Relation  indexRelationDesc;
796
0
  IndexScanDesc indexScanDesc;
797
798
  /*
799
   * extract information from the node
800
   */
801
0
  indexRelationDesc = node->iss_RelationDesc;
802
0
  indexScanDesc = node->iss_ScanDesc;
803
804
  /*
805
   * When ending a parallel worker, copy the statistics gathered by the
806
   * worker back into shared memory so that it can be picked up by the main
807
   * process to report in EXPLAIN ANALYZE
808
   */
809
0
  if (node->iss_SharedInfo != NULL && IsParallelWorker())
810
0
  {
811
0
    IndexScanInstrumentation *winstrument;
812
813
0
    Assert(ParallelWorkerNumber < node->iss_SharedInfo->num_workers);
814
0
    winstrument = &node->iss_SharedInfo->winstrument[ParallelWorkerNumber];
815
816
    /*
817
     * We have to accumulate the stats rather than performing a memcpy.
818
     * When a Gather/GatherMerge node finishes it will perform planner
819
     * shutdown on the workers.  On rescan it will spin up new workers
820
     * which will have a new IndexOnlyScanState and zeroed stats.
821
     */
822
0
    winstrument->nsearches += node->iss_Instrument->nsearches;
823
0
    Assert(node->iss_Instrument->ntabletuplefetches == 0);
824
0
  }
825
826
  /*
827
   * close the index relation (no-op if we didn't open it)
828
   */
829
0
  if (indexScanDesc)
830
0
    index_endscan(indexScanDesc);
831
0
  if (indexRelationDesc)
832
0
    index_close(indexRelationDesc, NoLock);
833
0
}
834
835
/* ----------------------------------------------------------------
836
 *    ExecIndexMarkPos
837
 *
838
 * Note: we assume that no caller attempts to set a mark before having read
839
 * at least one tuple.  Otherwise, iss_ScanDesc might still be NULL.
840
 * ----------------------------------------------------------------
841
 */
842
void
843
ExecIndexMarkPos(IndexScanState *node)
844
0
{
845
0
  EState     *estate = node->ss.ps.state;
846
0
  EPQState   *epqstate = estate->es_epq_active;
847
848
0
  if (epqstate != NULL)
849
0
  {
850
    /*
851
     * We are inside an EvalPlanQual recheck.  If a test tuple exists for
852
     * this relation, then we shouldn't access the index at all.  We would
853
     * instead need to save, and later restore, the state of the
854
     * relsubs_done flag, so that re-fetching the test tuple is possible.
855
     * However, given the assumption that no caller sets a mark at the
856
     * start of the scan, we can only get here with relsubs_done[i]
857
     * already set, and so no state need be saved.
858
     */
859
0
    Index   scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
860
861
0
    Assert(scanrelid > 0);
862
0
    if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
863
0
      epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
864
0
    {
865
      /* Verify the claim above */
866
0
      if (!epqstate->relsubs_done[scanrelid - 1])
867
0
        elog(ERROR, "unexpected ExecIndexMarkPos call in EPQ recheck");
868
0
      return;
869
0
    }
870
0
  }
871
872
0
  index_markpos(node->iss_ScanDesc);
873
0
}
874
875
/* ----------------------------------------------------------------
876
 *    ExecIndexRestrPos
877
 * ----------------------------------------------------------------
878
 */
879
void
880
ExecIndexRestrPos(IndexScanState *node)
881
0
{
882
0
  EState     *estate = node->ss.ps.state;
883
0
  EPQState   *epqstate = estate->es_epq_active;
884
885
0
  if (estate->es_epq_active != NULL)
886
0
  {
887
    /* See comments in ExecIndexMarkPos */
888
0
    Index   scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
889
890
0
    Assert(scanrelid > 0);
891
0
    if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
892
0
      epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
893
0
    {
894
      /* Verify the claim above */
895
0
      if (!epqstate->relsubs_done[scanrelid - 1])
896
0
        elog(ERROR, "unexpected ExecIndexRestrPos call in EPQ recheck");
897
0
      return;
898
0
    }
899
0
  }
900
901
0
  index_restrpos(node->iss_ScanDesc);
902
0
}
903
904
/* ----------------------------------------------------------------
905
 *    ExecInitIndexScan
906
 *
907
 *    Initializes the index scan's state information, creates
908
 *    scan keys, and opens the base and index relations.
909
 *
910
 *    Note: index scans have 2 sets of state information because
911
 *        we have to keep track of the base relation and the
912
 *        index relation.
913
 * ----------------------------------------------------------------
914
 */
915
IndexScanState *
916
ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
917
0
{
918
0
  IndexScanState *indexstate;
919
0
  Relation  currentRelation;
920
0
  LOCKMODE  lockmode;
921
922
  /*
923
   * create state structure
924
   */
925
0
  indexstate = makeNode(IndexScanState);
926
0
  indexstate->ss.ps.plan = (Plan *) node;
927
0
  indexstate->ss.ps.state = estate;
928
0
  indexstate->ss.ps.ExecProcNode = ExecIndexScan;
929
930
  /*
931
   * Miscellaneous initialization
932
   *
933
   * create expression context for node
934
   */
935
0
  ExecAssignExprContext(estate, &indexstate->ss.ps);
936
937
  /*
938
   * open the scan relation
939
   */
940
0
  currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
941
942
0
  indexstate->ss.ss_currentRelation = currentRelation;
943
0
  indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
944
945
  /*
946
   * get the scan type from the relation descriptor.
947
   */
948
0
  ExecInitScanTupleSlot(estate, &indexstate->ss,
949
0
              RelationGetDescr(currentRelation),
950
0
              table_slot_callbacks(currentRelation),
951
0
              TTS_FLAG_OBEYS_NOT_NULL_CONSTRAINTS);
952
953
  /*
954
   * Initialize result type and projection.
955
   */
956
0
  ExecInitResultTypeTL(&indexstate->ss.ps);
957
0
  ExecAssignScanProjectionInfo(&indexstate->ss);
958
959
  /*
960
   * initialize child expressions
961
   *
962
   * Note: we don't initialize all of the indexqual expression, only the
963
   * sub-parts corresponding to runtime keys (see below).  Likewise for
964
   * indexorderby, if any.  But the indexqualorig expression is always
965
   * initialized even though it will only be used in some uncommon cases ---
966
   * would be nice to improve that.  (Problem is that any SubPlans present
967
   * in the expression must be found now...)
968
   */
969
0
  indexstate->ss.ps.qual =
970
0
    ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
971
0
  indexstate->indexqualorig =
972
0
    ExecInitQual(node->indexqualorig, (PlanState *) indexstate);
973
0
  indexstate->indexorderbyorig =
974
0
    ExecInitExprList(node->indexorderbyorig, (PlanState *) indexstate);
975
976
  /*
977
   * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
978
   * here.  This allows an index-advisor plugin to EXPLAIN a plan containing
979
   * references to nonexistent indexes.
980
   */
981
0
  if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
982
0
    return indexstate;
983
984
  /* Set up instrumentation of index scans if requested */
985
0
  if (estate->es_instrument)
986
0
    indexstate->iss_Instrument = palloc0_object(IndexScanInstrumentation);
987
988
  /* Open the index relation. */
989
0
  lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
990
0
  indexstate->iss_RelationDesc = index_open(node->indexid, lockmode);
991
992
  /*
993
   * Initialize index-specific scan state
994
   */
995
0
  indexstate->iss_RuntimeKeysReady = false;
996
0
  indexstate->iss_RuntimeKeys = NULL;
997
0
  indexstate->iss_NumRuntimeKeys = 0;
998
999
  /*
1000
   * build the index scan keys from the index qualification
1001
   */
1002
0
  ExecIndexBuildScanKeys((PlanState *) indexstate,
1003
0
               indexstate->iss_RelationDesc,
1004
0
               node->indexqual,
1005
0
               false,
1006
0
               &indexstate->iss_ScanKeys,
1007
0
               &indexstate->iss_NumScanKeys,
1008
0
               &indexstate->iss_RuntimeKeys,
1009
0
               &indexstate->iss_NumRuntimeKeys,
1010
0
               NULL, /* no ArrayKeys */
1011
0
               NULL);
1012
1013
  /*
1014
   * any ORDER BY exprs have to be turned into scankeys in the same way
1015
   */
1016
0
  ExecIndexBuildScanKeys((PlanState *) indexstate,
1017
0
               indexstate->iss_RelationDesc,
1018
0
               node->indexorderby,
1019
0
               true,
1020
0
               &indexstate->iss_OrderByKeys,
1021
0
               &indexstate->iss_NumOrderByKeys,
1022
0
               &indexstate->iss_RuntimeKeys,
1023
0
               &indexstate->iss_NumRuntimeKeys,
1024
0
               NULL, /* no ArrayKeys */
1025
0
               NULL);
1026
1027
  /* Initialize sort support, if we need to re-check ORDER BY exprs */
1028
0
  if (indexstate->iss_NumOrderByKeys > 0)
1029
0
  {
1030
0
    int     numOrderByKeys = indexstate->iss_NumOrderByKeys;
1031
0
    int     i;
1032
0
    ListCell   *lco;
1033
0
    ListCell   *lcx;
1034
1035
    /*
1036
     * Prepare sort support, and look up the data type for each ORDER BY
1037
     * expression.
1038
     */
1039
0
    Assert(numOrderByKeys == list_length(node->indexorderbyops));
1040
0
    Assert(numOrderByKeys == list_length(node->indexorderbyorig));
1041
0
    indexstate->iss_SortSupport = palloc0_array(SortSupportData, numOrderByKeys);
1042
0
    indexstate->iss_OrderByTypByVals = palloc_array(bool, numOrderByKeys);
1043
0
    indexstate->iss_OrderByTypLens = palloc_array(int16, numOrderByKeys);
1044
0
    i = 0;
1045
0
    forboth(lco, node->indexorderbyops, lcx, node->indexorderbyorig)
1046
0
    {
1047
0
      Oid     orderbyop = lfirst_oid(lco);
1048
0
      Node     *orderbyexpr = (Node *) lfirst(lcx);
1049
0
      Oid     orderbyType = exprType(orderbyexpr);
1050
0
      Oid     orderbyColl = exprCollation(orderbyexpr);
1051
0
      SortSupport orderbysort = &indexstate->iss_SortSupport[i];
1052
1053
      /* Initialize sort support */
1054
0
      orderbysort->ssup_cxt = CurrentMemoryContext;
1055
0
      orderbysort->ssup_collation = orderbyColl;
1056
      /* See cmp_orderbyvals() comments on NULLS LAST */
1057
0
      orderbysort->ssup_nulls_first = false;
1058
      /* ssup_attno is unused here and elsewhere */
1059
0
      orderbysort->ssup_attno = 0;
1060
      /* No abbreviation */
1061
0
      orderbysort->abbreviate = false;
1062
0
      PrepareSortSupportFromOrderingOp(orderbyop, orderbysort);
1063
1064
0
      get_typlenbyval(orderbyType,
1065
0
              &indexstate->iss_OrderByTypLens[i],
1066
0
              &indexstate->iss_OrderByTypByVals[i]);
1067
0
      i++;
1068
0
    }
1069
1070
    /* allocate arrays to hold the re-calculated distances */
1071
0
    indexstate->iss_OrderByValues = palloc_array(Datum, numOrderByKeys);
1072
0
    indexstate->iss_OrderByNulls = palloc_array(bool, numOrderByKeys);
1073
1074
    /* and initialize the reorder queue */
1075
0
    indexstate->iss_ReorderQueue = pairingheap_allocate(reorderqueue_cmp,
1076
0
                              indexstate);
1077
0
  }
1078
1079
  /*
1080
   * If we have runtime keys, we need an ExprContext to evaluate them. The
1081
   * node's standard context won't do because we want to reset that context
1082
   * for every tuple.  So, build another context just like the other one...
1083
   * -tgl 7/11/00
1084
   */
1085
0
  if (indexstate->iss_NumRuntimeKeys != 0)
1086
0
  {
1087
0
    ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
1088
1089
0
    ExecAssignExprContext(estate, &indexstate->ss.ps);
1090
0
    indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
1091
0
    indexstate->ss.ps.ps_ExprContext = stdecontext;
1092
0
  }
1093
0
  else
1094
0
  {
1095
0
    indexstate->iss_RuntimeContext = NULL;
1096
0
  }
1097
1098
  /*
1099
   * all done.
1100
   */
1101
0
  return indexstate;
1102
0
}
1103
1104
1105
/*
1106
 * ExecIndexBuildScanKeys
1107
 *    Build the index scan keys from the index qualification expressions
1108
 *
1109
 * The index quals are passed to the index AM in the form of a ScanKey array.
1110
 * This routine sets up the ScanKeys, fills in all constant fields of the
1111
 * ScanKeys, and prepares information about the keys that have non-constant
1112
 * comparison values.  We divide index qual expressions into five types:
1113
 *
1114
 * 1. Simple operator with constant comparison value ("indexkey op constant").
1115
 * For these, we just fill in a ScanKey containing the constant value.
1116
 *
1117
 * 2. Simple operator with non-constant value ("indexkey op expression").
1118
 * For these, we create a ScanKey with everything filled in except the
1119
 * expression value, and set up an IndexRuntimeKeyInfo struct to drive
1120
 * evaluation of the expression at the right times.
1121
 *
1122
 * 3. RowCompareExpr ("(indexkey, indexkey, ...) op (expr, expr, ...)").
1123
 * For these, we create a header ScanKey plus a subsidiary ScanKey array,
1124
 * as specified in access/skey.h.  The elements of the row comparison
1125
 * can have either constant or non-constant comparison values.
1126
 *
1127
 * 4. ScalarArrayOpExpr ("indexkey op ANY (array-expression)").  If the index
1128
 * supports amsearcharray, we handle these the same as simple operators,
1129
 * setting the SK_SEARCHARRAY flag to tell the AM to handle them.  Otherwise,
1130
 * we create a ScanKey with everything filled in except the comparison value,
1131
 * and set up an IndexArrayKeyInfo struct to drive processing of the qual.
1132
 * (Note that if we use an IndexArrayKeyInfo struct, the array expression is
1133
 * always treated as requiring runtime evaluation, even if it's a constant.)
1134
 *
1135
 * 5. NullTest ("indexkey IS NULL/IS NOT NULL").  We just fill in the
1136
 * ScanKey properly.
1137
 *
1138
 * This code is also used to prepare ORDER BY expressions for amcanorderbyop
1139
 * indexes.  The behavior is exactly the same, except that we have to look up
1140
 * the operator differently.  Note that only cases 1 and 2 are currently
1141
 * possible for ORDER BY.
1142
 *
1143
 * Input params are:
1144
 *
1145
 * planstate: executor state node we are working for
1146
 * index: the index we are building scan keys for
1147
 * quals: indexquals (or indexorderbys) expressions
1148
 * isorderby: true if processing ORDER BY exprs, false if processing quals
1149
 * *runtimeKeys: ptr to pre-existing IndexRuntimeKeyInfos, or NULL if none
1150
 * *numRuntimeKeys: number of pre-existing runtime keys
1151
 *
1152
 * Output params are:
1153
 *
1154
 * *scanKeys: receives ptr to array of ScanKeys
1155
 * *numScanKeys: receives number of scankeys
1156
 * *runtimeKeys: receives ptr to array of IndexRuntimeKeyInfos, or NULL if none
1157
 * *numRuntimeKeys: receives number of runtime keys
1158
 * *arrayKeys: receives ptr to array of IndexArrayKeyInfos, or NULL if none
1159
 * *numArrayKeys: receives number of array keys
1160
 *
1161
 * Caller may pass NULL for arrayKeys and numArrayKeys to indicate that
1162
 * IndexArrayKeyInfos are not supported.
1163
 */
1164
void
1165
ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
1166
             List *quals, bool isorderby,
1167
             ScanKey *scanKeys, int *numScanKeys,
1168
             IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
1169
             IndexArrayKeyInfo **arrayKeys, int *numArrayKeys)
1170
0
{
1171
0
  ListCell   *qual_cell;
1172
0
  ScanKey   scan_keys;
1173
0
  IndexRuntimeKeyInfo *runtime_keys;
1174
0
  IndexArrayKeyInfo *array_keys;
1175
0
  int     n_scan_keys;
1176
0
  int     n_runtime_keys;
1177
0
  int     max_runtime_keys;
1178
0
  int     n_array_keys;
1179
0
  int     j;
1180
1181
  /* Allocate array for ScanKey structs: one per qual */
1182
0
  n_scan_keys = list_length(quals);
1183
0
  scan_keys = palloc_array(ScanKeyData, n_scan_keys);
1184
1185
  /*
1186
   * runtime_keys array is dynamically resized as needed.  We handle it this
1187
   * way so that the same runtime keys array can be shared between
1188
   * indexquals and indexorderbys, which will be processed in separate calls
1189
   * of this function.  Caller must be sure to pass in NULL/0 for first
1190
   * call.
1191
   */
1192
0
  runtime_keys = *runtimeKeys;
1193
0
  n_runtime_keys = max_runtime_keys = *numRuntimeKeys;
1194
1195
  /* Allocate array_keys as large as it could possibly need to be */
1196
0
  array_keys = palloc0_array(IndexArrayKeyInfo, n_scan_keys);
1197
0
  n_array_keys = 0;
1198
1199
  /*
1200
   * for each opclause in the given qual, convert the opclause into a single
1201
   * scan key
1202
   */
1203
0
  j = 0;
1204
0
  foreach(qual_cell, quals)
1205
0
  {
1206
0
    Expr     *clause = (Expr *) lfirst(qual_cell);
1207
0
    ScanKey   this_scan_key = &scan_keys[j++];
1208
0
    Oid     opno;   /* operator's OID */
1209
0
    RegProcedure opfuncid;  /* operator proc id used in scan */
1210
0
    Oid     opfamily; /* opfamily of index column */
1211
0
    int     op_strategy;  /* operator's strategy number */
1212
0
    Oid     op_lefttype;  /* operator's declared input types */
1213
0
    Oid     op_righttype;
1214
0
    Expr     *leftop;   /* expr on lhs of operator */
1215
0
    Expr     *rightop;  /* expr on rhs ... */
1216
0
    AttrNumber  varattno; /* att number used in scan */
1217
0
    int     indnkeyatts;
1218
1219
0
    indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
1220
0
    if (IsA(clause, OpExpr))
1221
0
    {
1222
      /* indexkey op const or indexkey op expression */
1223
0
      int     flags = 0;
1224
0
      Datum   scanvalue;
1225
1226
0
      opno = ((OpExpr *) clause)->opno;
1227
0
      opfuncid = ((OpExpr *) clause)->opfuncid;
1228
1229
      /*
1230
       * leftop should be the index key Var, possibly relabeled
1231
       */
1232
0
      leftop = (Expr *) get_leftop(clause);
1233
1234
0
      if (leftop && IsA(leftop, RelabelType))
1235
0
        leftop = ((RelabelType *) leftop)->arg;
1236
1237
0
      Assert(leftop != NULL);
1238
1239
0
      if (!(IsA(leftop, Var) &&
1240
0
          ((Var *) leftop)->varno == INDEX_VAR))
1241
0
        elog(ERROR, "indexqual doesn't have key on left side");
1242
1243
0
      varattno = ((Var *) leftop)->varattno;
1244
0
      if (varattno < 1 || varattno > indnkeyatts)
1245
0
        elog(ERROR, "bogus index qualification");
1246
1247
      /*
1248
       * We have to look up the operator's strategy number.  This
1249
       * provides a cross-check that the operator does match the index.
1250
       */
1251
0
      opfamily = index->rd_opfamily[varattno - 1];
1252
1253
0
      get_op_opfamily_properties(opno, opfamily, isorderby,
1254
0
                     &op_strategy,
1255
0
                     &op_lefttype,
1256
0
                     &op_righttype);
1257
1258
0
      if (isorderby)
1259
0
        flags |= SK_ORDER_BY;
1260
1261
      /*
1262
       * rightop is the constant or variable comparison value
1263
       */
1264
0
      rightop = (Expr *) get_rightop(clause);
1265
1266
0
      if (rightop && IsA(rightop, RelabelType))
1267
0
        rightop = ((RelabelType *) rightop)->arg;
1268
1269
0
      Assert(rightop != NULL);
1270
1271
0
      if (IsA(rightop, Const))
1272
0
      {
1273
        /* OK, simple constant comparison value */
1274
0
        scanvalue = ((Const *) rightop)->constvalue;
1275
0
        if (((Const *) rightop)->constisnull)
1276
0
          flags |= SK_ISNULL;
1277
0
      }
1278
0
      else
1279
0
      {
1280
        /* Need to treat this one as a runtime key */
1281
0
        if (n_runtime_keys >= max_runtime_keys)
1282
0
        {
1283
0
          if (max_runtime_keys == 0)
1284
0
          {
1285
0
            max_runtime_keys = 8;
1286
0
            runtime_keys = palloc_array(IndexRuntimeKeyInfo, max_runtime_keys);
1287
0
          }
1288
0
          else
1289
0
          {
1290
0
            max_runtime_keys *= 2;
1291
0
            runtime_keys = repalloc_array(runtime_keys,
1292
0
                            IndexRuntimeKeyInfo, max_runtime_keys);
1293
0
          }
1294
0
        }
1295
0
        runtime_keys[n_runtime_keys].scan_key = this_scan_key;
1296
0
        runtime_keys[n_runtime_keys].key_expr =
1297
0
          ExecInitExpr(rightop, planstate);
1298
0
        runtime_keys[n_runtime_keys].key_toastable =
1299
0
          TypeIsToastable(op_righttype);
1300
0
        n_runtime_keys++;
1301
0
        scanvalue = (Datum) 0;
1302
0
      }
1303
1304
      /*
1305
       * initialize the scan key's fields appropriately
1306
       */
1307
0
      ScanKeyEntryInitialize(this_scan_key,
1308
0
                   flags,
1309
0
                   varattno,  /* attribute number to scan */
1310
0
                   op_strategy, /* op's strategy */
1311
0
                   op_righttype,  /* strategy subtype */
1312
0
                   ((OpExpr *) clause)->inputcollid,  /* collation */
1313
0
                   opfuncid,  /* reg proc to use */
1314
0
                   scanvalue);  /* constant */
1315
0
    }
1316
0
    else if (IsA(clause, RowCompareExpr))
1317
0
    {
1318
      /* (indexkey, indexkey, ...) op (expression, expression, ...) */
1319
0
      RowCompareExpr *rc = (RowCompareExpr *) clause;
1320
0
      ScanKey   first_sub_key;
1321
0
      int     n_sub_key;
1322
0
      ListCell   *largs_cell;
1323
0
      ListCell   *rargs_cell;
1324
0
      ListCell   *opnos_cell;
1325
0
      ListCell   *collids_cell;
1326
1327
0
      Assert(!isorderby);
1328
1329
0
      first_sub_key = palloc_array(ScanKeyData, list_length(rc->opnos));
1330
0
      n_sub_key = 0;
1331
1332
      /* Scan RowCompare columns and generate subsidiary ScanKey items */
1333
0
      forfour(largs_cell, rc->largs, rargs_cell, rc->rargs,
1334
0
          opnos_cell, rc->opnos, collids_cell, rc->inputcollids)
1335
0
      {
1336
0
        ScanKey   this_sub_key = &first_sub_key[n_sub_key];
1337
0
        int     flags = SK_ROW_MEMBER;
1338
0
        Datum   scanvalue;
1339
0
        Oid     inputcollation;
1340
1341
0
        leftop = (Expr *) lfirst(largs_cell);
1342
0
        rightop = (Expr *) lfirst(rargs_cell);
1343
0
        opno = lfirst_oid(opnos_cell);
1344
0
        inputcollation = lfirst_oid(collids_cell);
1345
1346
        /*
1347
         * leftop should be the index key Var, possibly relabeled
1348
         */
1349
0
        if (leftop && IsA(leftop, RelabelType))
1350
0
          leftop = ((RelabelType *) leftop)->arg;
1351
1352
0
        Assert(leftop != NULL);
1353
1354
0
        if (!(IsA(leftop, Var) &&
1355
0
            ((Var *) leftop)->varno == INDEX_VAR))
1356
0
          elog(ERROR, "indexqual doesn't have key on left side");
1357
1358
0
        varattno = ((Var *) leftop)->varattno;
1359
1360
        /*
1361
         * We have to look up the operator's associated support
1362
         * function
1363
         */
1364
0
        if (!index->rd_indam->amcanorder ||
1365
0
          varattno < 1 || varattno > indnkeyatts)
1366
0
          elog(ERROR, "bogus RowCompare index qualification");
1367
0
        opfamily = index->rd_opfamily[varattno - 1];
1368
1369
0
        get_op_opfamily_properties(opno, opfamily, isorderby,
1370
0
                       &op_strategy,
1371
0
                       &op_lefttype,
1372
0
                       &op_righttype);
1373
1374
0
        if (op_strategy != rc->cmptype)
1375
0
          elog(ERROR, "RowCompare index qualification contains wrong operator");
1376
1377
0
        opfuncid = get_opfamily_proc(opfamily,
1378
0
                       op_lefttype,
1379
0
                       op_righttype,
1380
0
                       BTORDER_PROC);
1381
0
        if (!RegProcedureIsValid(opfuncid))
1382
0
          elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
1383
0
             BTORDER_PROC, op_lefttype, op_righttype, opfamily);
1384
1385
        /*
1386
         * rightop is the constant or variable comparison value
1387
         */
1388
0
        if (rightop && IsA(rightop, RelabelType))
1389
0
          rightop = ((RelabelType *) rightop)->arg;
1390
1391
0
        Assert(rightop != NULL);
1392
1393
0
        if (IsA(rightop, Const))
1394
0
        {
1395
          /* OK, simple constant comparison value */
1396
0
          scanvalue = ((Const *) rightop)->constvalue;
1397
0
          if (((Const *) rightop)->constisnull)
1398
0
            flags |= SK_ISNULL;
1399
0
        }
1400
0
        else
1401
0
        {
1402
          /* Need to treat this one as a runtime key */
1403
0
          if (n_runtime_keys >= max_runtime_keys)
1404
0
          {
1405
0
            if (max_runtime_keys == 0)
1406
0
            {
1407
0
              max_runtime_keys = 8;
1408
0
              runtime_keys = palloc_array(IndexRuntimeKeyInfo, max_runtime_keys);
1409
0
            }
1410
0
            else
1411
0
            {
1412
0
              max_runtime_keys *= 2;
1413
0
              runtime_keys = repalloc_array(runtime_keys,
1414
0
                              IndexRuntimeKeyInfo, max_runtime_keys);
1415
0
            }
1416
0
          }
1417
0
          runtime_keys[n_runtime_keys].scan_key = this_sub_key;
1418
0
          runtime_keys[n_runtime_keys].key_expr =
1419
0
            ExecInitExpr(rightop, planstate);
1420
0
          runtime_keys[n_runtime_keys].key_toastable =
1421
0
            TypeIsToastable(op_righttype);
1422
0
          n_runtime_keys++;
1423
0
          scanvalue = (Datum) 0;
1424
0
        }
1425
1426
        /*
1427
         * initialize the subsidiary scan key's fields appropriately
1428
         */
1429
0
        ScanKeyEntryInitialize(this_sub_key,
1430
0
                     flags,
1431
0
                     varattno,  /* attribute number */
1432
0
                     op_strategy, /* op's strategy */
1433
0
                     op_righttype,  /* strategy subtype */
1434
0
                     inputcollation,  /* collation */
1435
0
                     opfuncid,  /* reg proc to use */
1436
0
                     scanvalue);  /* constant */
1437
0
        n_sub_key++;
1438
0
      }
1439
1440
      /* Mark the last subsidiary scankey correctly */
1441
0
      first_sub_key[n_sub_key - 1].sk_flags |= SK_ROW_END;
1442
1443
      /*
1444
       * We don't use ScanKeyEntryInitialize for the header because it
1445
       * isn't going to contain a valid sk_func pointer.
1446
       */
1447
0
      MemSet(this_scan_key, 0, sizeof(ScanKeyData));
1448
0
      this_scan_key->sk_flags = SK_ROW_HEADER;
1449
0
      this_scan_key->sk_attno = first_sub_key->sk_attno;
1450
0
      this_scan_key->sk_strategy = rc->cmptype;
1451
      /* sk_subtype, sk_collation, sk_func not used in a header */
1452
0
      this_scan_key->sk_argument = PointerGetDatum(first_sub_key);
1453
0
    }
1454
0
    else if (IsA(clause, ScalarArrayOpExpr))
1455
0
    {
1456
      /* indexkey op ANY (array-expression) */
1457
0
      ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
1458
0
      int     flags = 0;
1459
0
      Datum   scanvalue;
1460
1461
0
      Assert(!isorderby);
1462
1463
0
      Assert(saop->useOr);
1464
0
      opno = saop->opno;
1465
0
      opfuncid = saop->opfuncid;
1466
1467
      /*
1468
       * leftop should be the index key Var, possibly relabeled
1469
       */
1470
0
      leftop = (Expr *) linitial(saop->args);
1471
1472
0
      if (leftop && IsA(leftop, RelabelType))
1473
0
        leftop = ((RelabelType *) leftop)->arg;
1474
1475
0
      Assert(leftop != NULL);
1476
1477
0
      if (!(IsA(leftop, Var) &&
1478
0
          ((Var *) leftop)->varno == INDEX_VAR))
1479
0
        elog(ERROR, "indexqual doesn't have key on left side");
1480
1481
0
      varattno = ((Var *) leftop)->varattno;
1482
0
      if (varattno < 1 || varattno > indnkeyatts)
1483
0
        elog(ERROR, "bogus index qualification");
1484
1485
      /*
1486
       * We have to look up the operator's strategy number.  This
1487
       * provides a cross-check that the operator does match the index.
1488
       */
1489
0
      opfamily = index->rd_opfamily[varattno - 1];
1490
1491
0
      get_op_opfamily_properties(opno, opfamily, isorderby,
1492
0
                     &op_strategy,
1493
0
                     &op_lefttype,
1494
0
                     &op_righttype);
1495
1496
      /*
1497
       * rightop is the constant or variable array value
1498
       */
1499
0
      rightop = (Expr *) lsecond(saop->args);
1500
1501
0
      if (rightop && IsA(rightop, RelabelType))
1502
0
        rightop = ((RelabelType *) rightop)->arg;
1503
1504
0
      Assert(rightop != NULL);
1505
1506
0
      if (index->rd_indam->amsearcharray)
1507
0
      {
1508
        /* Index AM will handle this like a simple operator */
1509
0
        flags |= SK_SEARCHARRAY;
1510
0
        if (IsA(rightop, Const))
1511
0
        {
1512
          /* OK, simple constant comparison value */
1513
0
          scanvalue = ((Const *) rightop)->constvalue;
1514
0
          if (((Const *) rightop)->constisnull)
1515
0
            flags |= SK_ISNULL;
1516
0
        }
1517
0
        else
1518
0
        {
1519
          /* Need to treat this one as a runtime key */
1520
0
          if (n_runtime_keys >= max_runtime_keys)
1521
0
          {
1522
0
            if (max_runtime_keys == 0)
1523
0
            {
1524
0
              max_runtime_keys = 8;
1525
0
              runtime_keys = palloc_array(IndexRuntimeKeyInfo, max_runtime_keys);
1526
0
            }
1527
0
            else
1528
0
            {
1529
0
              max_runtime_keys *= 2;
1530
0
              runtime_keys = repalloc_array(runtime_keys,
1531
0
                              IndexRuntimeKeyInfo, max_runtime_keys);
1532
0
            }
1533
0
          }
1534
0
          runtime_keys[n_runtime_keys].scan_key = this_scan_key;
1535
0
          runtime_keys[n_runtime_keys].key_expr =
1536
0
            ExecInitExpr(rightop, planstate);
1537
1538
          /*
1539
           * Careful here: the runtime expression is not of
1540
           * op_righttype, but rather is an array of same; so
1541
           * TypeIsToastable() isn't helpful.  However, we can
1542
           * assume that all array types are toastable.
1543
           */
1544
0
          runtime_keys[n_runtime_keys].key_toastable = true;
1545
0
          n_runtime_keys++;
1546
0
          scanvalue = (Datum) 0;
1547
0
        }
1548
0
      }
1549
0
      else
1550
0
      {
1551
        /* Executor has to expand the array value */
1552
0
        array_keys[n_array_keys].scan_key = this_scan_key;
1553
0
        array_keys[n_array_keys].array_expr =
1554
0
          ExecInitExpr(rightop, planstate);
1555
        /* the remaining fields were zeroed by palloc0 */
1556
0
        n_array_keys++;
1557
0
        scanvalue = (Datum) 0;
1558
0
      }
1559
1560
      /*
1561
       * initialize the scan key's fields appropriately
1562
       */
1563
0
      ScanKeyEntryInitialize(this_scan_key,
1564
0
                   flags,
1565
0
                   varattno,  /* attribute number to scan */
1566
0
                   op_strategy, /* op's strategy */
1567
0
                   op_righttype,  /* strategy subtype */
1568
0
                   saop->inputcollid, /* collation */
1569
0
                   opfuncid,  /* reg proc to use */
1570
0
                   scanvalue);  /* constant */
1571
0
    }
1572
0
    else if (IsA(clause, NullTest))
1573
0
    {
1574
      /* indexkey IS NULL or indexkey IS NOT NULL */
1575
0
      NullTest   *ntest = (NullTest *) clause;
1576
0
      int     flags;
1577
1578
0
      Assert(!isorderby);
1579
1580
      /*
1581
       * argument should be the index key Var, possibly relabeled
1582
       */
1583
0
      leftop = ntest->arg;
1584
1585
0
      if (leftop && IsA(leftop, RelabelType))
1586
0
        leftop = ((RelabelType *) leftop)->arg;
1587
1588
0
      Assert(leftop != NULL);
1589
1590
0
      if (!(IsA(leftop, Var) &&
1591
0
          ((Var *) leftop)->varno == INDEX_VAR))
1592
0
        elog(ERROR, "NullTest indexqual has wrong key");
1593
1594
0
      varattno = ((Var *) leftop)->varattno;
1595
1596
      /*
1597
       * initialize the scan key's fields appropriately
1598
       */
1599
0
      switch (ntest->nulltesttype)
1600
0
      {
1601
0
        case IS_NULL:
1602
0
          flags = SK_ISNULL | SK_SEARCHNULL;
1603
0
          break;
1604
0
        case IS_NOT_NULL:
1605
0
          flags = SK_ISNULL | SK_SEARCHNOTNULL;
1606
0
          break;
1607
0
        default:
1608
0
          elog(ERROR, "unrecognized nulltesttype: %d",
1609
0
             (int) ntest->nulltesttype);
1610
0
          flags = 0;  /* keep compiler quiet */
1611
0
          break;
1612
0
      }
1613
1614
0
      ScanKeyEntryInitialize(this_scan_key,
1615
0
                   flags,
1616
0
                   varattno,  /* attribute number to scan */
1617
0
                   InvalidStrategy, /* no strategy */
1618
0
                   InvalidOid, /* no strategy subtype */
1619
0
                   InvalidOid, /* no collation */
1620
0
                   InvalidOid, /* no reg proc for this */
1621
0
                   (Datum) 0);  /* constant */
1622
0
    }
1623
0
    else
1624
0
      elog(ERROR, "unsupported indexqual type: %d",
1625
0
         (int) nodeTag(clause));
1626
0
  }
1627
1628
0
  Assert(n_runtime_keys <= max_runtime_keys);
1629
1630
  /* Get rid of any unused arrays */
1631
0
  if (n_array_keys == 0)
1632
0
  {
1633
0
    pfree(array_keys);
1634
0
    array_keys = NULL;
1635
0
  }
1636
1637
  /*
1638
   * Return info to our caller.
1639
   */
1640
0
  *scanKeys = scan_keys;
1641
0
  *numScanKeys = n_scan_keys;
1642
0
  *runtimeKeys = runtime_keys;
1643
0
  *numRuntimeKeys = n_runtime_keys;
1644
0
  if (arrayKeys)
1645
0
  {
1646
0
    *arrayKeys = array_keys;
1647
0
    *numArrayKeys = n_array_keys;
1648
0
  }
1649
0
  else if (n_array_keys != 0)
1650
0
    elog(ERROR, "ScalarArrayOpExpr index qual found where not allowed");
1651
0
}
1652
1653
/* ----------------------------------------------------------------
1654
 *            Parallel Scan Support
1655
 * ----------------------------------------------------------------
1656
 */
1657
1658
/* ----------------------------------------------------------------
1659
 *    ExecIndexScanEstimate
1660
 *
1661
 *    Compute the amount of space we'll need in the parallel
1662
 *    query DSM, and inform pcxt->estimator about our needs.
1663
 * ----------------------------------------------------------------
1664
 */
1665
void
1666
ExecIndexScanEstimate(IndexScanState *node,
1667
            ParallelContext *pcxt)
1668
0
{
1669
0
  EState     *estate = node->ss.ps.state;
1670
1671
0
  node->iss_PscanLen = index_parallelscan_estimate(node->iss_RelationDesc,
1672
0
                           node->iss_NumScanKeys,
1673
0
                           node->iss_NumOrderByKeys,
1674
0
                           estate->es_snapshot);
1675
0
  shm_toc_estimate_chunk(&pcxt->estimator, node->iss_PscanLen);
1676
0
  shm_toc_estimate_keys(&pcxt->estimator, 1);
1677
0
}
1678
1679
/* ----------------------------------------------------------------
1680
 *    ExecIndexScanInitializeDSM
1681
 *
1682
 *    Set up a parallel index scan descriptor.
1683
 * ----------------------------------------------------------------
1684
 */
1685
void
1686
ExecIndexScanInitializeDSM(IndexScanState *node,
1687
               ParallelContext *pcxt)
1688
0
{
1689
0
  EState     *estate = node->ss.ps.state;
1690
0
  ParallelIndexScanDesc piscan;
1691
1692
0
  piscan = shm_toc_allocate(pcxt->toc, node->iss_PscanLen);
1693
0
  index_parallelscan_initialize(node->ss.ss_currentRelation,
1694
0
                  node->iss_RelationDesc,
1695
0
                  estate->es_snapshot,
1696
0
                  piscan);
1697
0
  shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
1698
1699
0
  node->iss_ScanDesc =
1700
0
    index_beginscan_parallel(node->ss.ss_currentRelation,
1701
0
                 node->iss_RelationDesc,
1702
0
                 false,
1703
0
                 node->iss_Instrument,
1704
0
                 node->iss_NumScanKeys,
1705
0
                 node->iss_NumOrderByKeys,
1706
0
                 piscan,
1707
0
                 ScanRelIsReadOnly(&node->ss) ?
1708
0
                 SO_HINT_REL_READ_ONLY : SO_NONE);
1709
1710
  /*
1711
   * If no run-time keys to calculate or they are ready, go ahead and pass
1712
   * the scankeys to the index AM.
1713
   */
1714
0
  if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
1715
0
    index_rescan(node->iss_ScanDesc,
1716
0
           node->iss_ScanKeys, node->iss_NumScanKeys,
1717
0
           node->iss_OrderByKeys, node->iss_NumOrderByKeys);
1718
0
}
1719
1720
/* ----------------------------------------------------------------
1721
 *    ExecIndexScanReInitializeDSM
1722
 *
1723
 *    Reset shared state before beginning a fresh scan.
1724
 * ----------------------------------------------------------------
1725
 */
1726
void
1727
ExecIndexScanReInitializeDSM(IndexScanState *node,
1728
               ParallelContext *pcxt)
1729
0
{
1730
0
  Assert(node->ss.ps.plan->parallel_aware);
1731
0
  index_parallelrescan(node->iss_ScanDesc);
1732
0
}
1733
1734
/* ----------------------------------------------------------------
1735
 *    ExecIndexScanInitializeWorker
1736
 *
1737
 *    Copy relevant information from TOC into planstate.
1738
 * ----------------------------------------------------------------
1739
 */
1740
void
1741
ExecIndexScanInitializeWorker(IndexScanState *node,
1742
                ParallelWorkerContext *pwcxt)
1743
0
{
1744
0
  ParallelIndexScanDesc piscan;
1745
1746
0
  piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
1747
1748
0
  node->iss_ScanDesc =
1749
0
    index_beginscan_parallel(node->ss.ss_currentRelation,
1750
0
                 node->iss_RelationDesc,
1751
0
                 false,
1752
0
                 node->iss_Instrument,
1753
0
                 node->iss_NumScanKeys,
1754
0
                 node->iss_NumOrderByKeys,
1755
0
                 piscan,
1756
0
                 ScanRelIsReadOnly(&node->ss) ?
1757
0
                 SO_HINT_REL_READ_ONLY : SO_NONE);
1758
1759
  /*
1760
   * If no run-time keys to calculate or they are ready, go ahead and pass
1761
   * the scankeys to the index AM.
1762
   */
1763
0
  if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
1764
0
    index_rescan(node->iss_ScanDesc,
1765
0
           node->iss_ScanKeys, node->iss_NumScanKeys,
1766
0
           node->iss_OrderByKeys, node->iss_NumOrderByKeys);
1767
0
}
1768
1769
/*
1770
 * Compute the amount of space we'll need for the shared instrumentation and
1771
 * inform pcxt->estimator.
1772
 */
1773
void
1774
ExecIndexScanInstrumentEstimate(IndexScanState *node,
1775
                ParallelContext *pcxt)
1776
0
{
1777
0
  Size    size;
1778
1779
0
  if (!node->ss.ps.instrument || pcxt->nworkers == 0)
1780
0
    return;
1781
1782
  /*
1783
   * This size calculation is trivial enough that we don't bother saving it
1784
   * in the IndexScanState. We'll recalculate the needed size in
1785
   * ExecIndexScanInstrumentInitDSM().
1786
   */
1787
0
  size = add_size(offsetof(SharedIndexScanInstrumentation, winstrument),
1788
0
          mul_size(pcxt->nworkers, sizeof(IndexScanInstrumentation)));
1789
0
  shm_toc_estimate_chunk(&pcxt->estimator, size);
1790
0
  shm_toc_estimate_keys(&pcxt->estimator, 1);
1791
0
}
1792
1793
/*
1794
 * Set up parallel index scan instrumentation.
1795
 */
1796
void
1797
ExecIndexScanInstrumentInitDSM(IndexScanState *node,
1798
                 ParallelContext *pcxt)
1799
0
{
1800
0
  Size    size;
1801
1802
0
  if (!node->ss.ps.instrument || pcxt->nworkers == 0)
1803
0
    return;
1804
1805
0
  size = add_size(offsetof(SharedIndexScanInstrumentation, winstrument),
1806
0
          mul_size(pcxt->nworkers, sizeof(IndexScanInstrumentation)));
1807
0
  node->iss_SharedInfo =
1808
0
    (SharedIndexScanInstrumentation *) shm_toc_allocate(pcxt->toc, size);
1809
1810
  /* Each per-worker area must start out as zeroes */
1811
0
  memset(node->iss_SharedInfo, 0, size);
1812
0
  node->iss_SharedInfo->num_workers = pcxt->nworkers;
1813
0
  shm_toc_insert(pcxt->toc,
1814
0
           node->ss.ps.plan->plan_node_id +
1815
0
           PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
1816
0
           node->iss_SharedInfo);
1817
0
}
1818
1819
/*
1820
 * Look up and save the location of the shared instrumentation.
1821
 */
1822
void
1823
ExecIndexScanInstrumentInitWorker(IndexScanState *node,
1824
                  ParallelWorkerContext *pwcxt)
1825
0
{
1826
0
  if (!node->ss.ps.instrument)
1827
0
    return;
1828
1829
0
  node->iss_SharedInfo = (SharedIndexScanInstrumentation *)
1830
0
    shm_toc_lookup(pwcxt->toc,
1831
0
             node->ss.ps.plan->plan_node_id +
1832
0
             PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
1833
0
             false);
1834
0
}
1835
1836
/* ----------------------------------------------------------------
1837
 * ExecIndexScanRetrieveInstrumentation
1838
 *
1839
 *    Transfer index scan statistics from DSM to private memory.
1840
 * ----------------------------------------------------------------
1841
 */
1842
void
1843
ExecIndexScanRetrieveInstrumentation(IndexScanState *node)
1844
0
{
1845
0
  SharedIndexScanInstrumentation *SharedInfo = node->iss_SharedInfo;
1846
0
  size_t    size;
1847
1848
0
  if (SharedInfo == NULL)
1849
0
    return;
1850
1851
  /* Create a copy of SharedInfo in backend-local memory */
1852
0
  size = offsetof(SharedIndexScanInstrumentation, winstrument) +
1853
0
    SharedInfo->num_workers * sizeof(IndexScanInstrumentation);
1854
0
  node->iss_SharedInfo = palloc(size);
1855
0
  memcpy(node->iss_SharedInfo, SharedInfo, size);
1856
0
}