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