PostgreSQL Source Code git master
Loading...
Searching...
No Matches
nodeIndexscan.c
Go to the documentation of this file.
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"
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 */
59
62static void EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext);
63static bool IndexRecheck(IndexScanState *node, TupleTableSlot *slot);
64static int cmp_orderbyvals(const Datum *adist, const bool *anulls,
65 const Datum *bdist, const bool *bnulls,
66 IndexScanState *node);
67static int reorderqueue_cmp(const pairingheap_node *a,
68 const pairingheap_node *b, void *arg);
69static void reorderqueue_push(IndexScanState *node, TupleTableSlot *slot,
70 const Datum *orderbyvals, const bool *orderbynulls);
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 */
81static TupleTableSlot *
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 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 direction = ScanDirectionCombine(estate->es_direction,
100 ((IndexScan *) node->ss.ps.plan)->indexorderdir);
101 scandesc = node->iss_ScanDesc;
102 econtext = node->ss.ps.ps_ExprContext;
103 slot = node->ss.ss_ScanTupleSlot;
104
105 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 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 ScanRelIsReadOnly(&node->ss) ?
119
120 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 if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
127 index_rescan(scandesc,
128 node->iss_ScanKeys, node->iss_NumScanKeys,
130 }
131
132 /*
133 * ok, now that we have what we need, fetch the next tuple.
134 */
135 while (index_getnext_slot(scandesc, direction, slot))
136 {
138
139 /*
140 * If the index was lossy, we have to recheck the index quals using
141 * the fetched tuple.
142 */
143 if (scandesc->xs_recheck)
144 {
145 econtext->ecxt_scantuple = slot;
146 if (!ExecQualAndReset(node->indexqualorig, econtext))
147 {
148 /* Fails recheck, so drop it and loop back for another */
149 InstrCountFiltered2(node, 1);
150 continue;
151 }
152 }
153
154 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 node->iss_ReachedEnd = true;
162 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 */
172static TupleTableSlot *
174{
175 EState *estate;
176 ExprContext *econtext;
177 IndexScanDesc scandesc;
178 TupleTableSlot *slot;
180 bool was_exact;
182 bool *lastfetched_nulls;
183 int cmp;
184
185 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));
198
199 scandesc = node->iss_ScanDesc;
200 econtext = node->ss.ps.ps_ExprContext;
201 slot = node->ss.ss_ScanTupleSlot;
202
203 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 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 ScanRelIsReadOnly(&node->ss) ?
217
218 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 if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
225 index_rescan(scandesc,
226 node->iss_ScanKeys, node->iss_NumScanKeys,
228 }
229
230 for (;;)
231 {
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 */
240 {
242
243 if (node->iss_ReachedEnd ||
244 cmp_orderbyvals(topmost->orderbyvals,
245 topmost->orderbynulls,
246 scandesc->xs_orderbyvals,
247 scandesc->xs_orderbynulls,
248 node) <= 0)
249 {
250 HeapTuple tuple;
251
252 tuple = reorderqueue_pop(node);
253
254 /* Pass 'true', as the tuple in the queue is a palloc'd copy */
255 ExecForceStoreHeapTuple(tuple, slot, true);
256 return slot;
257 }
258 }
259 else if (node->iss_ReachedEnd)
260 {
261 /* Queue is empty, and no more tuples from index. We're done. */
262 return ExecClearTuple(slot);
263 }
264
265 /*
266 * Fetch next tuple from the index.
267 */
269 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 node->iss_ReachedEnd = true;
276 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 if (scandesc->xs_recheck)
284 {
285 econtext->ecxt_scantuple = slot;
286 if (!ExecQualAndReset(node->indexqualorig, econtext))
287 {
288 /* Fails recheck, so drop it and loop back for another */
289 InstrCountFiltered2(node, 1);
290 /* allow this loop to be cancellable */
292 goto next_indextuple;
293 }
294 }
295
296 if (scandesc->xs_recheckorderby)
297 {
298 econtext->ecxt_scantuple = slot;
299 ResetExprContext(econtext);
300 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 */
312 node->iss_OrderByNulls,
313 scandesc->xs_orderbyvals,
314 scandesc->xs_orderbynulls,
315 node);
316 if (cmp < 0)
317 elog(ERROR, "index returned tuples in wrong order");
318 else if (cmp == 0)
319 was_exact = true;
320 else
321 was_exact = false;
324 }
325 else
326 {
327 was_exact = true;
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 */
342 topmost->orderbyvals,
343 topmost->orderbynulls,
344 node) > 0))
345 {
346 /* Put this tuple to the queue */
348 continue;
349 }
350 else
351 {
352 /* Can return this tuple immediately. */
353 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 */
367static void
369{
370 int i;
371 ListCell *l;
373
375
376 i = 0;
377 foreach(l, node->indexorderbyorig)
378 {
380
382 econtext,
383 &node->iss_OrderByNulls[i]);
384 i++;
385 }
386
388}
389
390/*
391 * IndexRecheck -- access method routine to recheck a tuple in EvalPlanQual
392 */
393static bool
395{
396 ExprContext *econtext;
397
398 /*
399 * extract necessary information from index scan node
400 */
401 econtext = node->ss.ps.ps_ExprContext;
402
403 /* Does the tuple meet the indexqual condition? */
404 econtext->ecxt_scantuple = slot;
405 return ExecQualAndReset(node->indexqualorig, econtext);
406}
407
408
409/*
410 * Compare ORDER BY expression values.
411 */
412static int
413cmp_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 for (i = 0; i < node->iss_NumOrderByKeys; i++)
421 {
422 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 if (anulls[i] && !bnulls[i])
430 return 1;
431 else if (!anulls[i] && bnulls[i])
432 return -1;
433 else if (anulls[i] && bnulls[i])
434 return 0;
435
436 result = ssup->comparator(adist[i], bdist[i], ssup);
437 if (result != 0)
438 return result;
439 }
440
441 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 */
448static int
450 void *arg)
451{
452 const ReorderTuple *rta = (const ReorderTuple *) a;
453 const ReorderTuple *rtb = (const ReorderTuple *) b;
455
456 /* exchange argument order to invert the sort order */
457 return cmp_orderbyvals(rtb->orderbyvals, rtb->orderbynulls,
458 rta->orderbyvals, rta->orderbynulls,
459 node);
460}
461
462/*
463 * Helper function to push a tuple to the reorder queue.
464 */
465static void
467 const Datum *orderbyvals, const bool *orderbynulls)
468{
469 IndexScanDesc scandesc = node->iss_ScanDesc;
470 EState *estate = node->ss.ps.state;
473 int i;
474
476 rt->htup = ExecCopySlotHeapTuple(slot);
477 rt->orderbyvals = palloc_array(Datum, scandesc->numberOfOrderBys);
478 rt->orderbynulls = palloc_array(bool, scandesc->numberOfOrderBys);
479 for (i = 0; i < node->iss_NumOrderByKeys; i++)
480 {
481 if (!orderbynulls[i])
482 rt->orderbyvals[i] = datumCopy(orderbyvals[i],
483 node->iss_OrderByTypByVals[i],
484 node->iss_OrderByTypLens[i]);
485 else
486 rt->orderbyvals[i] = (Datum) 0;
487 rt->orderbynulls[i] = orderbynulls[i];
488 }
489 pairingheap_add(node->iss_ReorderQueue, &rt->ph_node);
490
492}
493
494/*
495 * Helper function to pop the next tuple from the reorder queue.
496 */
497static HeapTuple
499{
502 int i;
503
505
506 result = topmost->htup;
507 for (i = 0; i < node->iss_NumOrderByKeys; i++)
508 {
509 if (!node->iss_OrderByTypByVals[i] && !topmost->orderbynulls[i])
510 pfree(DatumGetPointer(topmost->orderbyvals[i]));
511 }
512 pfree(topmost->orderbyvals);
513 pfree(topmost->orderbynulls);
514 pfree(topmost);
515
516 return result;
517}
518
519
520/* ----------------------------------------------------------------
521 * ExecIndexScan(node)
522 * ----------------------------------------------------------------
523 */
524static TupleTableSlot *
526{
527 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 if (node->iss_NumRuntimeKeys != 0 && !node->iss_RuntimeKeysReady)
533 ExecReScan((PlanState *) node);
534
535 if (node->iss_NumOrderByKeys > 0)
536 return ExecScan(&node->ss,
539 else
540 return ExecScan(&node->ss,
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 */
556void
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 if (node->iss_NumRuntimeKeys != 0)
567 {
568 ExprContext *econtext = node->iss_RuntimeContext;
569
570 ResetExprContext(econtext);
572 node->iss_RuntimeKeys,
573 node->iss_NumRuntimeKeys);
574 }
575 node->iss_RuntimeKeysReady = true;
576
577 /* flush the reorder queue */
578 if (node->iss_ReorderQueue)
579 {
580 HeapTuple tuple;
581
583 {
584 tuple = reorderqueue_pop(node);
585 heap_freetuple(tuple);
586 }
587 }
588
589 /* reset index scan */
590 if (node->iss_ScanDesc)
592 node->iss_ScanKeys, node->iss_NumScanKeys,
594 node->iss_ReachedEnd = false;
595
596 ExecScanReScan(&node->ss);
597}
598
599
600/*
601 * ExecIndexEvalRuntimeKeys
602 * Evaluate any runtime key values, and update the scankeys.
603 */
604void
607{
608 int j;
610
611 /* We want to keep the key values in per-tuple memory */
613
614 for (j = 0; j < numRuntimeKeys; j++)
615 {
616 ScanKey scan_key = runtimeKeys[j].scan_key;
617 ExprState *key_expr = runtimeKeys[j].key_expr;
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 scanvalue = ExecEvalExpr(key_expr,
639 econtext,
640 &isNull);
641 if (isNull)
642 {
643 scan_key->sk_argument = scanvalue;
644 scan_key->sk_flags |= SK_ISNULL;
645 }
646 else
647 {
648 if (runtimeKeys[j].key_toastable)
650 scan_key->sk_argument = scanvalue;
651 scan_key->sk_flags &= ~SK_ISNULL;
652 }
653 }
654
656}
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 */
666bool
668 IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
669{
670 bool result = true;
671 int j;
673
674 /* We want to keep the arrays in per-tuple memory */
676
677 for (j = 0; j < numArrayKeys; j++)
678 {
679 ScanKey scan_key = arrayKeys[j].scan_key;
680 ExprState *array_expr = arrayKeys[j].array_expr;
682 bool isNull;
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 arraydatum = ExecEvalExpr(array_expr,
696 econtext,
697 &isNull);
698 if (isNull)
699 {
700 result = false;
701 break; /* no point in evaluating more */
702 }
704 /* We could cache this data, but not clear it's worth it */
706 &elmlen, &elmbyval, &elmalign);
709 elmlen, elmbyval, elmalign,
710 &elem_values, &elem_nulls, &num_elems);
711 if (num_elems <= 0)
712 {
713 result = false;
714 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 arrayKeys[j].elem_values = elem_values;
723 arrayKeys[j].elem_nulls = elem_nulls;
724 arrayKeys[j].num_elems = num_elems;
725 scan_key->sk_argument = elem_values[0];
726 if (elem_nulls[0])
727 scan_key->sk_flags |= SK_ISNULL;
728 else
729 scan_key->sk_flags &= ~SK_ISNULL;
730 arrayKeys[j].next_elem = 1;
731 }
732
734
735 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 */
745bool
746ExecIndexAdvanceArrayKeys(IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
747{
748 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 for (j = numArrayKeys - 1; j >= 0; j--)
758 {
759 ScanKey scan_key = arrayKeys[j].scan_key;
760 int next_elem = arrayKeys[j].next_elem;
761 int num_elems = arrayKeys[j].num_elems;
762 Datum *elem_values = arrayKeys[j].elem_values;
763 bool *elem_nulls = arrayKeys[j].elem_nulls;
764
765 if (next_elem >= num_elems)
766 {
767 next_elem = 0;
768 found = false; /* need to advance next array key */
769 }
770 else
771 found = true;
772 scan_key->sk_argument = elem_values[next_elem];
773 if (elem_nulls[next_elem])
774 scan_key->sk_flags |= SK_ISNULL;
775 else
776 scan_key->sk_flags &= ~SK_ISNULL;
777 arrayKeys[j].next_elem = next_elem + 1;
778 if (found)
779 break;
780 }
781
782 return found;
783}
784
785
786/* ----------------------------------------------------------------
787 * ExecEndIndexScan
788 * ----------------------------------------------------------------
789 */
790void
792{
795
796 /*
797 * extract information from the node
798 */
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 if (node->iss_SharedInfo != NULL && IsParallelWorker())
808 {
809 IndexScanInstrumentation *winstrument;
810
811 Assert(ParallelWorkerNumber < node->iss_SharedInfo->num_workers);
812 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 winstrument->nsearches += node->iss_Instrument->nsearches;
821 }
822
823 /*
824 * close the index relation (no-op if we didn't open it)
825 */
826 if (indexScanDesc)
830}
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 */
839void
841{
842 EState *estate = node->ss.ps.state;
843 EPQState *epqstate = estate->es_epq_active;
844
845 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 Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
857
858 Assert(scanrelid > 0);
859 if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
860 epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
861 {
862 /* Verify the claim above */
863 if (!epqstate->relsubs_done[scanrelid - 1])
864 elog(ERROR, "unexpected ExecIndexMarkPos call in EPQ recheck");
865 return;
866 }
867 }
868
870}
871
872/* ----------------------------------------------------------------
873 * ExecIndexRestrPos
874 * ----------------------------------------------------------------
875 */
876void
878{
879 EState *estate = node->ss.ps.state;
880 EPQState *epqstate = estate->es_epq_active;
881
882 if (estate->es_epq_active != NULL)
883 {
884 /* See comments in ExecIndexMarkPos */
885 Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
886
887 Assert(scanrelid > 0);
888 if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
889 epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
890 {
891 /* Verify the claim above */
892 if (!epqstate->relsubs_done[scanrelid - 1])
893 elog(ERROR, "unexpected ExecIndexRestrPos call in EPQ recheck");
894 return;
895 }
896 }
897
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 */
913ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
914{
917 LOCKMODE lockmode;
918
919 /*
920 * create state structure
921 */
923 indexstate->ss.ps.plan = (Plan *) node;
924 indexstate->ss.ps.state = estate;
925 indexstate->ss.ps.ExecProcNode = ExecIndexScan;
926
927 /*
928 * Miscellaneous initialization
929 *
930 * create expression context for node
931 */
932 ExecAssignExprContext(estate, &indexstate->ss.ps);
933
934 /*
935 * open the scan relation
936 */
937 currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
938
939 indexstate->ss.ss_currentRelation = currentRelation;
940 indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
941
942 /*
943 * get the scan type from the relation descriptor.
944 */
949
950 /*
951 * Initialize result type and projection.
952 */
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 indexstate->ss.ps.qual =
967 ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
968 indexstate->indexqualorig =
970 indexstate->indexorderbyorig =
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 if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
979 return indexstate;
980
981 /* Set up instrumentation of index scans if requested */
982 if (estate->es_instrument)
984
985 /* Open the index relation. */
986 lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
987 indexstate->iss_RelationDesc = index_open(node->indexid, lockmode);
988
989 /*
990 * Initialize index-specific scan state
991 */
992 indexstate->iss_RuntimeKeysReady = false;
993 indexstate->iss_RuntimeKeys = NULL;
994 indexstate->iss_NumRuntimeKeys = 0;
995
996 /*
997 * build the index scan keys from the index qualification
998 */
1000 indexstate->iss_RelationDesc,
1001 node->indexqual,
1002 false,
1003 &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 */
1014 indexstate->iss_RelationDesc,
1015 node->indexorderby,
1016 true,
1017 &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 if (indexstate->iss_NumOrderByKeys > 0)
1026 {
1027 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 */
1038 indexstate->iss_SortSupport = (SortSupportData *)
1040 indexstate->iss_OrderByTypByVals = (bool *)
1041 palloc(numOrderByKeys * sizeof(bool));
1042 indexstate->iss_OrderByTypLens = (int16 *)
1043 palloc(numOrderByKeys * sizeof(int16));
1044 i = 0;
1046 {
1048 Node *orderbyexpr = (Node *) lfirst(lcx);
1051 SortSupport orderbysort = &indexstate->iss_SortSupport[i];
1052
1053 /* Initialize sort support */
1055 orderbysort->ssup_collation = orderbyColl;
1056 /* See cmp_orderbyvals() comments on NULLS LAST */
1057 orderbysort->ssup_nulls_first = false;
1058 /* ssup_attno is unused here and elsewhere */
1059 orderbysort->ssup_attno = 0;
1060 /* No abbreviation */
1061 orderbysort->abbreviate = false;
1063
1065 &indexstate->iss_OrderByTypLens[i],
1066 &indexstate->iss_OrderByTypByVals[i]);
1067 i++;
1068 }
1069
1070 /* allocate arrays to hold the re-calculated distances */
1071 indexstate->iss_OrderByValues = (Datum *)
1072 palloc(numOrderByKeys * sizeof(Datum));
1073 indexstate->iss_OrderByNulls = (bool *)
1074 palloc(numOrderByKeys * sizeof(bool));
1075
1076 /* and initialize the reorder queue */
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 if (indexstate->iss_NumRuntimeKeys != 0)
1088 {
1089 ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
1090
1091 ExecAssignExprContext(estate, &indexstate->ss.ps);
1092 indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
1093 indexstate->ss.ps.ps_ExprContext = stdecontext;
1094 }
1095 else
1096 {
1097 indexstate->iss_RuntimeContext = NULL;
1098 }
1099
1100 /*
1101 * all done.
1102 */
1103 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 */
1166void
1168 List *quals, bool isorderby,
1171 IndexArrayKeyInfo **arrayKeys, int *numArrayKeys)
1172{
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 n_scan_keys = list_length(quals);
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 */
1196
1197 /* Allocate array_keys as large as it could possibly need to be */
1200 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 j = 0;
1207 foreach(qual_cell, quals)
1208 {
1209 Expr *clause = (Expr *) lfirst(qual_cell);
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 */
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
1223 if (IsA(clause, OpExpr))
1224 {
1225 /* indexkey op const or indexkey op expression */
1226 int flags = 0;
1228
1229 opno = ((OpExpr *) clause)->opno;
1230 opfuncid = ((OpExpr *) clause)->opfuncid;
1231
1232 /*
1233 * leftop should be the index key Var, possibly relabeled
1234 */
1235 leftop = (Expr *) get_leftop(clause);
1236
1237 if (leftop && IsA(leftop, RelabelType))
1238 leftop = ((RelabelType *) leftop)->arg;
1239
1240 Assert(leftop != NULL);
1241
1242 if (!(IsA(leftop, Var) &&
1243 ((Var *) leftop)->varno == INDEX_VAR))
1244 elog(ERROR, "indexqual doesn't have key on left side");
1245
1246 varattno = ((Var *) leftop)->varattno;
1248 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 opfamily = index->rd_opfamily[varattno - 1];
1255
1256 get_op_opfamily_properties(opno, opfamily, isorderby,
1257 &op_strategy,
1258 &op_lefttype,
1259 &op_righttype);
1260
1261 if (isorderby)
1262 flags |= SK_ORDER_BY;
1263
1264 /*
1265 * rightop is the constant or variable comparison value
1266 */
1267 rightop = (Expr *) get_rightop(clause);
1268
1269 if (rightop && IsA(rightop, RelabelType))
1270 rightop = ((RelabelType *) rightop)->arg;
1271
1272 Assert(rightop != NULL);
1273
1274 if (IsA(rightop, Const))
1275 {
1276 /* OK, simple constant comparison value */
1277 scanvalue = ((Const *) rightop)->constvalue;
1278 if (((Const *) rightop)->constisnull)
1279 flags |= SK_ISNULL;
1280 }
1281 else
1282 {
1283 /* Need to treat this one as a runtime key */
1285 {
1286 if (max_runtime_keys == 0)
1287 {
1288 max_runtime_keys = 8;
1291 }
1292 else
1293 {
1294 max_runtime_keys *= 2;
1297 }
1298 }
1300 runtime_keys[n_runtime_keys].key_expr =
1301 ExecInitExpr(rightop, planstate);
1302 runtime_keys[n_runtime_keys].key_toastable =
1305 scanvalue = (Datum) 0;
1306 }
1307
1308 /*
1309 * initialize the scan key's fields appropriately
1310 */
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 else if (IsA(clause, RowCompareExpr))
1321 {
1322 /* (indexkey, indexkey, ...) op (expression, expression, ...) */
1323 RowCompareExpr *rc = (RowCompareExpr *) clause;
1325 int n_sub_key;
1330
1331 Assert(!isorderby);
1332
1334 palloc(list_length(rc->opnos) * sizeof(ScanKeyData));
1335 n_sub_key = 0;
1336
1337 /* Scan RowCompare columns and generate subsidiary ScanKey items */
1339 opnos_cell, rc->opnos, collids_cell, rc->inputcollids)
1340 {
1342 int flags = SK_ROW_MEMBER;
1345
1348 opno = lfirst_oid(opnos_cell);
1350
1351 /*
1352 * leftop should be the index key Var, possibly relabeled
1353 */
1354 if (leftop && IsA(leftop, RelabelType))
1355 leftop = ((RelabelType *) leftop)->arg;
1356
1357 Assert(leftop != NULL);
1358
1359 if (!(IsA(leftop, Var) &&
1360 ((Var *) leftop)->varno == INDEX_VAR))
1361 elog(ERROR, "indexqual doesn't have key on left side");
1362
1363 varattno = ((Var *) leftop)->varattno;
1364
1365 /*
1366 * We have to look up the operator's associated support
1367 * function
1368 */
1369 if (!index->rd_indam->amcanorder ||
1371 elog(ERROR, "bogus RowCompare index qualification");
1372 opfamily = index->rd_opfamily[varattno - 1];
1373
1374 get_op_opfamily_properties(opno, opfamily, isorderby,
1375 &op_strategy,
1376 &op_lefttype,
1377 &op_righttype);
1378
1379 if (op_strategy != rc->cmptype)
1380 elog(ERROR, "RowCompare index qualification contains wrong operator");
1381
1382 opfuncid = get_opfamily_proc(opfamily,
1385 BTORDER_PROC);
1387 elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
1389
1390 /*
1391 * rightop is the constant or variable comparison value
1392 */
1393 if (rightop && IsA(rightop, RelabelType))
1394 rightop = ((RelabelType *) rightop)->arg;
1395
1396 Assert(rightop != NULL);
1397
1398 if (IsA(rightop, Const))
1399 {
1400 /* OK, simple constant comparison value */
1401 scanvalue = ((Const *) rightop)->constvalue;
1402 if (((Const *) rightop)->constisnull)
1403 flags |= SK_ISNULL;
1404 }
1405 else
1406 {
1407 /* Need to treat this one as a runtime key */
1409 {
1410 if (max_runtime_keys == 0)
1411 {
1412 max_runtime_keys = 8;
1415 }
1416 else
1417 {
1418 max_runtime_keys *= 2;
1421 }
1422 }
1424 runtime_keys[n_runtime_keys].key_expr =
1425 ExecInitExpr(rightop, planstate);
1426 runtime_keys[n_runtime_keys].key_toastable =
1429 scanvalue = (Datum) 0;
1430 }
1431
1432 /*
1433 * initialize the subsidiary scan key's fields appropriately
1434 */
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 n_sub_key++;
1444 }
1445
1446 /* Mark the last subsidiary scankey correctly */
1447 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 MemSet(this_scan_key, 0, sizeof(ScanKeyData));
1454 this_scan_key->sk_flags = SK_ROW_HEADER;
1455 this_scan_key->sk_attno = first_sub_key->sk_attno;
1456 this_scan_key->sk_strategy = rc->cmptype;
1457 /* sk_subtype, sk_collation, sk_func not used in a header */
1459 }
1460 else if (IsA(clause, ScalarArrayOpExpr))
1461 {
1462 /* indexkey op ANY (array-expression) */
1463 ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
1464 int flags = 0;
1466
1467 Assert(!isorderby);
1468
1469 Assert(saop->useOr);
1470 opno = saop->opno;
1471 opfuncid = saop->opfuncid;
1472
1473 /*
1474 * leftop should be the index key Var, possibly relabeled
1475 */
1476 leftop = (Expr *) linitial(saop->args);
1477
1478 if (leftop && IsA(leftop, RelabelType))
1479 leftop = ((RelabelType *) leftop)->arg;
1480
1481 Assert(leftop != NULL);
1482
1483 if (!(IsA(leftop, Var) &&
1484 ((Var *) leftop)->varno == INDEX_VAR))
1485 elog(ERROR, "indexqual doesn't have key on left side");
1486
1487 varattno = ((Var *) leftop)->varattno;
1489 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 opfamily = index->rd_opfamily[varattno - 1];
1496
1497 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 rightop = (Expr *) lsecond(saop->args);
1506
1507 if (rightop && IsA(rightop, RelabelType))
1508 rightop = ((RelabelType *) rightop)->arg;
1509
1510 Assert(rightop != NULL);
1511
1512 if (index->rd_indam->amsearcharray)
1513 {
1514 /* Index AM will handle this like a simple operator */
1515 flags |= SK_SEARCHARRAY;
1516 if (IsA(rightop, Const))
1517 {
1518 /* OK, simple constant comparison value */
1519 scanvalue = ((Const *) rightop)->constvalue;
1520 if (((Const *) rightop)->constisnull)
1521 flags |= SK_ISNULL;
1522 }
1523 else
1524 {
1525 /* Need to treat this one as a runtime key */
1527 {
1528 if (max_runtime_keys == 0)
1529 {
1530 max_runtime_keys = 8;
1533 }
1534 else
1535 {
1536 max_runtime_keys *= 2;
1539 }
1540 }
1542 runtime_keys[n_runtime_keys].key_expr =
1543 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 runtime_keys[n_runtime_keys].key_toastable = true;
1553 scanvalue = (Datum) 0;
1554 }
1555 }
1556 else
1557 {
1558 /* Executor has to expand the array value */
1560 array_keys[n_array_keys].array_expr =
1561 ExecInitExpr(rightop, planstate);
1562 /* the remaining fields were zeroed by palloc0 */
1563 n_array_keys++;
1564 scanvalue = (Datum) 0;
1565 }
1566
1567 /*
1568 * initialize the scan key's fields appropriately
1569 */
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 else if (IsA(clause, NullTest))
1580 {
1581 /* indexkey IS NULL or indexkey IS NOT NULL */
1582 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 leftop = ntest->arg;
1591
1592 if (leftop && IsA(leftop, RelabelType))
1593 leftop = ((RelabelType *) leftop)->arg;
1594
1595 Assert(leftop != NULL);
1596
1597 if (!(IsA(leftop, Var) &&
1598 ((Var *) leftop)->varno == INDEX_VAR))
1599 elog(ERROR, "NullTest indexqual has wrong key");
1600
1601 varattno = ((Var *) leftop)->varattno;
1602
1603 /*
1604 * initialize the scan key's fields appropriately
1605 */
1606 switch (ntest->nulltesttype)
1607 {
1608 case IS_NULL:
1609 flags = SK_ISNULL | SK_SEARCHNULL;
1610 break;
1611 case IS_NOT_NULL:
1612 flags = SK_ISNULL | SK_SEARCHNOTNULL;
1613 break;
1614 default:
1615 elog(ERROR, "unrecognized nulltesttype: %d",
1616 (int) ntest->nulltesttype);
1617 flags = 0; /* keep compiler quiet */
1618 break;
1619 }
1620
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 elog(ERROR, "unsupported indexqual type: %d",
1632 (int) nodeTag(clause));
1633 }
1634
1636
1637 /* Get rid of any unused arrays */
1638 if (n_array_keys == 0)
1639 {
1641 array_keys = NULL;
1642 }
1643
1644 /*
1645 * Return info to our caller.
1646 */
1651 if (arrayKeys)
1652 {
1653 *arrayKeys = array_keys;
1654 *numArrayKeys = n_array_keys;
1655 }
1656 else if (n_array_keys != 0)
1657 elog(ERROR, "ScalarArrayOpExpr index qual found where not allowed");
1658}
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 */
1672void
1674 ParallelContext *pcxt)
1675{
1676 EState *estate = node->ss.ps.state;
1677
1679 node->iss_NumScanKeys,
1680 node->iss_NumOrderByKeys,
1681 estate->es_snapshot);
1684}
1685
1686/* ----------------------------------------------------------------
1687 * ExecIndexScanInitializeDSM
1688 *
1689 * Set up a parallel index scan descriptor.
1690 * ----------------------------------------------------------------
1691 */
1692void
1694 ParallelContext *pcxt)
1695{
1696 EState *estate = node->ss.ps.state;
1698
1699 piscan = shm_toc_allocate(pcxt->toc, node->iss_PscanLen);
1701 node->iss_RelationDesc,
1702 estate->es_snapshot,
1703 piscan);
1704 shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
1705
1706 node->iss_ScanDesc =
1708 node->iss_RelationDesc,
1709 node->iss_Instrument,
1710 node->iss_NumScanKeys,
1711 node->iss_NumOrderByKeys,
1712 piscan,
1713 ScanRelIsReadOnly(&node->ss) ?
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 if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
1722 node->iss_ScanKeys, node->iss_NumScanKeys,
1723 node->iss_OrderByKeys, node->iss_NumOrderByKeys);
1724}
1725
1726/* ----------------------------------------------------------------
1727 * ExecIndexScanReInitializeDSM
1728 *
1729 * Reset shared state before beginning a fresh scan.
1730 * ----------------------------------------------------------------
1731 */
1732void
1739
1740/* ----------------------------------------------------------------
1741 * ExecIndexScanInitializeWorker
1742 *
1743 * Copy relevant information from TOC into planstate.
1744 * ----------------------------------------------------------------
1745 */
1746void
1749{
1751
1752 piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
1753
1754 node->iss_ScanDesc =
1756 node->iss_RelationDesc,
1757 node->iss_Instrument,
1758 node->iss_NumScanKeys,
1759 node->iss_NumOrderByKeys,
1760 piscan,
1761 ScanRelIsReadOnly(&node->ss) ?
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 if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
1770 node->iss_ScanKeys, node->iss_NumScanKeys,
1771 node->iss_OrderByKeys, node->iss_NumOrderByKeys);
1772}
1773
1774/*
1775 * Compute the amount of space we'll need for the shared instrumentation and
1776 * inform pcxt->estimator.
1777 */
1778void
1780 ParallelContext *pcxt)
1781{
1782 Size size;
1783
1784 if (!node->ss.ps.instrument || pcxt->nworkers == 0)
1785 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 */
1794 shm_toc_estimate_chunk(&pcxt->estimator, size);
1796}
1797
1798/*
1799 * Set up parallel index scan instrumentation.
1800 */
1801void
1803 ParallelContext *pcxt)
1804{
1805 Size size;
1806
1807 if (!node->ss.ps.instrument || pcxt->nworkers == 0)
1808 return;
1809
1812 node->iss_SharedInfo =
1814
1815 /* Each per-worker area must start out as zeroes */
1816 memset(node->iss_SharedInfo, 0, size);
1817 node->iss_SharedInfo->num_workers = pcxt->nworkers;
1818 shm_toc_insert(pcxt->toc,
1819 node->ss.ps.plan->plan_node_id +
1821 node->iss_SharedInfo);
1822}
1823
1824/*
1825 * Look up and save the location of the shared instrumentation.
1826 */
1827void
1840
1841/* ----------------------------------------------------------------
1842 * ExecIndexScanRetrieveInstrumentation
1843 *
1844 * Transfer index scan statistics from DSM to private memory.
1845 * ----------------------------------------------------------------
1846 */
1847void
1849{
1851 size_t size;
1852
1853 if (SharedInfo == NULL)
1854 return;
1855
1856 /* Create a copy of SharedInfo in backend-local memory */
1857 size = offsetof(SharedIndexScanInstrumentation, winstrument) +
1859 node->iss_SharedInfo = palloc(size);
1860 memcpy(node->iss_SharedInfo, SharedInfo, size);
1861}
#define DatumGetArrayTypeP(X)
Definition array.h:261
#define ARR_ELEMTYPE(a)
Definition array.h:292
void deconstruct_array(const ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
int16 AttrNumber
Definition attnum.h:21
int ParallelWorkerNumber
Definition parallel.c:117
#define RegProcedureIsValid(p)
Definition c.h:862
#define Assert(condition)
Definition c.h:943
int16_t int16
Definition c.h:619
regproc RegProcedure
Definition c.h:734
unsigned int Index
Definition c.h:698
#define MemSet(start, val, len)
Definition c.h:1107
size_t Size
Definition c.h:689
uint32 result
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition datum.c:132
Datum arg
Definition elog.c:1322
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
void ExecReScan(PlanState *node)
Definition execAmi.c:78
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition execExpr.c:143
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition execExpr.c:250
List * ExecInitExprList(List *nodes, PlanState *parent)
Definition execExpr.c:356
TupleTableSlot * ExecScan(ScanState *node, ExecScanAccessMtd accessMtd, ExecScanRecheckMtd recheckMtd)
Definition execScan.c:47
void ExecAssignScanProjectionInfo(ScanState *node)
Definition execScan.c:81
void ExecScanReScan(ScanState *node)
Definition execScan.c:108
void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops, uint16 flags)
void ExecInitResultTypeTL(PlanState *planstate)
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
bool ScanRelIsReadOnly(ScanState *ss)
Definition execUtils.c:751
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition execUtils.c:490
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
Definition execUtils.c:768
#define InstrCountFiltered2(node, delta)
Definition execnodes.h:1312
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition executor.h:710
#define ResetExprContext(econtext)
Definition executor.h:661
bool(* ExecScanRecheckMtd)(ScanState *node, TupleTableSlot *slot)
Definition executor.h:590
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition executor.h:556
TupleTableSlot *(* ExecScanAccessMtd)(ScanState *node)
Definition executor.h:589
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:403
#define EXEC_FLAG_EXPLAIN_ONLY
Definition executor.h:67
#define palloc_object(type)
Definition fe_memutils.h:74
#define palloc_array(type, count)
Definition fe_memutils.h:76
#define palloc0_object(type)
Definition fe_memutils.h:75
#define PG_DETOAST_DATUM(datum)
Definition fmgr.h:240
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
#define IsParallelWorker()
Definition parallel.h:62
bool index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *slot)
Definition indexam.c:698
void index_restrpos(IndexScanDesc scan)
Definition indexam.c:448
IndexScanDesc index_beginscan(Relation heapRelation, Relation indexRelation, Snapshot snapshot, IndexScanInstrumentation *instrument, int nkeys, int norderbys, uint32 flags)
Definition indexam.c:257
IndexScanDesc index_beginscan_parallel(Relation heaprel, Relation indexrel, IndexScanInstrumentation *instrument, int nkeys, int norderbys, ParallelIndexScanDesc pscan, uint32 flags)
Definition indexam.c:560
void index_close(Relation relation, LOCKMODE lockmode)
Definition indexam.c:178
void index_markpos(IndexScanDesc scan)
Definition indexam.c:424
void index_endscan(IndexScanDesc scan)
Definition indexam.c:394
Size index_parallelscan_estimate(Relation indexRelation, int nkeys, int norderbys, Snapshot snapshot)
Definition indexam.c:470
void index_parallelscan_initialize(Relation heapRelation, Relation indexRelation, Snapshot snapshot, ParallelIndexScanDesc target)
Definition indexam.c:505
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition indexam.c:134
void index_parallelrescan(IndexScanDesc scan)
Definition indexam.c:538
void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys)
Definition indexam.c:368
#define PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET
int b
Definition isn.c:74
int a
Definition isn.c:73
int j
Definition isn.c:78
int i
Definition isn.c:77
int LOCKMODE
Definition lockdefs.h:26
#define NoLock
Definition lockdefs.h:34
void get_op_opfamily_properties(Oid opno, Oid opfamily, bool ordering_op, int *strategy, Oid *lefttype, Oid *righttype)
Definition lsyscache.c:140
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition lsyscache.c:2491
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition lsyscache.c:915
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition lsyscache.c:2471
#define TypeIsToastable(typid)
Definition lsyscache.h:224
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1632
void pfree(void *pointer)
Definition mcxt.c:1616
void * palloc0(Size size)
Definition mcxt.c:1417
void * palloc(Size size)
Definition mcxt.c:1387
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
#define BTORDER_PROC
Definition nbtree.h:717
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
Oid exprCollation(const Node *expr)
Definition nodeFuncs.c:826
static Node * get_rightop(const void *clause)
Definition nodeFuncs.h:95
static Node * get_leftop(const void *clause)
Definition nodeFuncs.h:83
void ExecIndexBuildScanKeys(PlanState *planstate, Relation index, List *quals, bool isorderby, ScanKey *scanKeys, int *numScanKeys, IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys, IndexArrayKeyInfo **arrayKeys, int *numArrayKeys)
void ExecIndexScanRetrieveInstrumentation(IndexScanState *node)
static void reorderqueue_push(IndexScanState *node, TupleTableSlot *slot, const Datum *orderbyvals, const bool *orderbynulls)
void ExecIndexScanEstimate(IndexScanState *node, ParallelContext *pcxt)
static void EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext)
bool ExecIndexEvalArrayKeys(ExprContext *econtext, IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
void ExecIndexEvalRuntimeKeys(ExprContext *econtext, IndexRuntimeKeyInfo *runtimeKeys, int numRuntimeKeys)
void ExecIndexScanReInitializeDSM(IndexScanState *node, ParallelContext *pcxt)
void ExecIndexScanInstrumentEstimate(IndexScanState *node, ParallelContext *pcxt)
static int cmp_orderbyvals(const Datum *adist, const bool *anulls, const Datum *bdist, const bool *bnulls, IndexScanState *node)
void ExecReScanIndexScan(IndexScanState *node)
void ExecIndexScanInitializeDSM(IndexScanState *node, ParallelContext *pcxt)
void ExecIndexScanInstrumentInitWorker(IndexScanState *node, ParallelWorkerContext *pwcxt)
void ExecIndexScanInstrumentInitDSM(IndexScanState *node, ParallelContext *pcxt)
IndexScanState * ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
static TupleTableSlot * IndexNextWithReorder(IndexScanState *node)
void ExecIndexScanInitializeWorker(IndexScanState *node, ParallelWorkerContext *pwcxt)
void ExecEndIndexScan(IndexScanState *node)
static bool IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
void ExecIndexRestrPos(IndexScanState *node)
static TupleTableSlot * ExecIndexScan(PlanState *pstate)
static int reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg)
void ExecIndexMarkPos(IndexScanState *node)
static HeapTuple reorderqueue_pop(IndexScanState *node)
bool ExecIndexAdvanceArrayKeys(IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
static TupleTableSlot * IndexNext(IndexScanState *node)
#define IsA(nodeptr, _type_)
Definition nodes.h:164
#define nodeTag(nodeptr)
Definition nodes.h:139
#define makeNode(_type_)
Definition nodes.h:161
#define castNode(_type_, nodeptr)
Definition nodes.h:182
void pairingheap_add(pairingheap *heap, pairingheap_node *node)
pairingheap * pairingheap_allocate(pairingheap_comparator compare, void *arg)
Definition pairingheap.c:42
pairingheap_node * pairingheap_remove_first(pairingheap *heap)
pairingheap_node * pairingheap_first(pairingheap *heap)
#define pairingheap_is_empty(h)
Definition pairingheap.h:99
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
#define lfirst(lc)
Definition pg_list.h:172
static int list_length(const List *l)
Definition pg_list.h:152
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h:550
#define linitial(l)
Definition pg_list.h:178
#define lsecond(l)
Definition pg_list.h:183
#define forfour(cell1, list1, cell2, list2, cell3, list3, cell4, list4)
Definition pg_list.h:607
#define lfirst_oid(lc)
Definition pg_list.h:174
static Datum PointerGetDatum(const void *X)
Definition postgres.h:342
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
#define InvalidOid
unsigned int Oid
static int fb(int x)
@ IS_NULL
Definition primnodes.h:1980
@ IS_NOT_NULL
Definition primnodes.h:1980
#define INDEX_VAR
Definition primnodes.h:245
static int cmp(const chr *x, const chr *y, size_t len)
#define RelationGetDescr(relation)
Definition rel.h:542
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition rel.h:535
void ScanKeyEntryInitialize(ScanKey entry, int flags, AttrNumber attributeNumber, StrategyNumber strategy, Oid subtype, Oid collation, RegProcedure procedure, Datum argument)
Definition scankey.c:32
#define ScanDirectionIsForward(direction)
Definition sdir.h:64
#define ScanDirectionCombine(a, b)
Definition sdir.h:36
#define ScanDirectionIsBackward(direction)
Definition sdir.h:50
ScanDirection
Definition sdir.h:25
@ ForwardScanDirection
Definition sdir.h:28
void * shm_toc_allocate(shm_toc *toc, Size nbytes)
Definition shm_toc.c:88
void shm_toc_insert(shm_toc *toc, uint64 key, void *address)
Definition shm_toc.c:171
void * shm_toc_lookup(shm_toc *toc, uint64 key, bool noError)
Definition shm_toc.c:239
#define shm_toc_estimate_chunk(e, sz)
Definition shm_toc.h:51
#define shm_toc_estimate_keys(e, cnt)
Definition shm_toc.h:53
Size add_size(Size s1, Size s2)
Definition shmem.c:1048
Size mul_size(Size s1, Size s2)
Definition shmem.c:1063
#define SK_ORDER_BY
Definition skey.h:123
#define SK_ROW_HEADER
Definition skey.h:117
#define SK_SEARCHARRAY
Definition skey.h:120
#define SK_ROW_MEMBER
Definition skey.h:118
#define SK_SEARCHNOTNULL
Definition skey.h:122
#define SK_SEARCHNULL
Definition skey.h:121
#define SK_ROW_END
Definition skey.h:119
ScanKeyData * ScanKey
Definition skey.h:75
#define SK_ISNULL
Definition skey.h:115
void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup)
#define InvalidStrategy
Definition stratnum.h:24
ExecAuxRowMark ** relsubs_rowmark
Definition execnodes.h:1378
TupleTableSlot ** relsubs_slot
Definition execnodes.h:1350
bool * relsubs_done
Definition execnodes.h:1385
int es_instrument
Definition execnodes.h:756
MemoryContext es_query_cxt
Definition execnodes.h:746
ScanDirection es_direction
Definition execnodes.h:695
struct EPQState * es_epq_active
Definition execnodes.h:778
Snapshot es_snapshot
Definition execnodes.h:696
MemoryContext ecxt_per_tuple_memory
Definition execnodes.h:295
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:287
ScanKeyData * scan_key
Definition execnodes.h:1711
ExprState * array_expr
Definition execnodes.h:1712
bool * xs_orderbynulls
Definition relscan.h:200
bool xs_recheckorderby
Definition relscan.h:201
Datum * xs_orderbyvals
Definition relscan.h:199
List * indexorderbyorig
Definition execnodes.h:1751
bool * iss_OrderByTypByVals
Definition execnodes.h:1771
struct IndexScanDescData * iss_ScanDesc
Definition execnodes.h:1761
ExprState * indexqualorig
Definition execnodes.h:1750
Relation iss_RelationDesc
Definition execnodes.h:1760
pairingheap * iss_ReorderQueue
Definition execnodes.h:1766
ScanState ss
Definition execnodes.h:1749
bool * iss_OrderByNulls
Definition execnodes.h:1769
bool iss_RuntimeKeysReady
Definition execnodes.h:1758
ScanKeyData * iss_ScanKeys
Definition execnodes.h:1752
ScanKeyData * iss_OrderByKeys
Definition execnodes.h:1754
SortSupport iss_SortSupport
Definition execnodes.h:1770
SharedIndexScanInstrumentation * iss_SharedInfo
Definition execnodes.h:1763
ExprContext * iss_RuntimeContext
Definition execnodes.h:1759
Datum * iss_OrderByValues
Definition execnodes.h:1768
int16 * iss_OrderByTypLens
Definition execnodes.h:1772
IndexRuntimeKeyInfo * iss_RuntimeKeys
Definition execnodes.h:1756
IndexScanInstrumentation * iss_Instrument
Definition execnodes.h:1762
List * indexorderby
Definition plannodes.h:614
List * indexorderbyops
Definition plannodes.h:618
Scan scan
Definition plannodes.h:606
List * indexqualorig
Definition plannodes.h:612
Oid indexid
Definition plannodes.h:608
List * indexqual
Definition plannodes.h:610
List * indexorderbyorig
Definition plannodes.h:616
Definition pg_list.h:54
Definition nodes.h:135
shm_toc_estimator estimator
Definition parallel.h:43
shm_toc * toc
Definition parallel.h:46
Plan * plan
Definition execnodes.h:1201
EState * state
Definition execnodes.h:1203
NodeInstrumentation * instrument
Definition execnodes.h:1211
ExprContext * ps_ExprContext
Definition execnodes.h:1242
bool parallel_aware
Definition plannodes.h:219
int plan_node_id
Definition plannodes.h:233
Datum * orderbyvals
bool * orderbynulls
pairingheap_node ph_node
HeapTuple htup
CompareType cmptype
Definition primnodes.h:1495
int sk_flags
Definition skey.h:66
Datum sk_argument
Definition skey.h:72
Relation ss_currentRelation
Definition execnodes.h:1660
TupleTableSlot * ss_ScanTupleSlot
Definition execnodes.h:1662
PlanState ps
Definition execnodes.h:1659
Index scanrelid
Definition plannodes.h:544
IndexScanInstrumentation winstrument[FLEXIBLE_ARRAY_MEMBER]
int(* comparator)(Datum x, Datum y, SortSupport ssup)
MemoryContext ssup_cxt
Definition sortsupport.h:66
Definition type.h:96
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition tableam.c:59
@ SO_HINT_REL_READ_ONLY
Definition tableam.h:71
@ SO_NONE
Definition tableam.h:49
static HeapTuple ExecCopySlotHeapTuple(TupleTableSlot *slot)
Definition tuptable.h:503
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
#define TTS_FLAG_OBEYS_NOT_NULL_CONSTRAINTS
Definition tuptable.h:102