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