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