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