PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/executor/nodeIndexscan.c
12 *
13 *-------------------------------------------------------------------------
14 */
15/*
16 * INTERFACE ROUTINES
17 * ExecIndexScan scans a relation using an index
18 * IndexNext retrieve next tuple using index
19 * IndexNextWithReorder same, but recheck ORDER BY expressions
20 * ExecInitIndexScan creates and initializes state info.
21 * ExecReScanIndexScan rescans the indexed relation.
22 * ExecEndIndexScan releases all storage.
23 * ExecIndexMarkPos marks scan position.
24 * ExecIndexRestrPos restores scan position.
25 * ExecIndexScanEstimate estimates DSM space needed for parallel index scan
26 * ExecIndexScanInitializeDSM initialize DSM for parallel indexscan
27 * ExecIndexScanReInitializeDSM reinitialize DSM for fresh scan
28 * ExecIndexScanInitializeWorker attach to DSM info in parallel worker
29 */
30#include "postgres.h"
31
32#include "access/nbtree.h"
33#include "access/relscan.h"
34#include "access/tableam.h"
35#include "catalog/pg_am.h"
36#include "executor/executor.h"
38#include "lib/pairingheap.h"
39#include "miscadmin.h"
40#include "nodes/nodeFuncs.h"
41#include "utils/array.h"
42#include "utils/datum.h"
43#include "utils/lsyscache.h"
44#include "utils/rel.h"
45
46/*
47 * When an ordering operator is used, tuples fetched from the index that
48 * need to be reordered are queued in a pairing heap, as ReorderTuples.
49 */
50typedef struct
51{
57
60static void EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext);
61static bool IndexRecheck(IndexScanState *node, TupleTableSlot *slot);
62static int cmp_orderbyvals(const Datum *adist, const bool *anulls,
63 const Datum *bdist, const bool *bnulls,
64 IndexScanState *node);
65static int reorderqueue_cmp(const pairingheap_node *a,
66 const pairingheap_node *b, void *arg);
67static void reorderqueue_push(IndexScanState *node, TupleTableSlot *slot,
68 Datum *orderbyvals, bool *orderbynulls);
70
71
72/* ----------------------------------------------------------------
73 * IndexNext
74 *
75 * Retrieve a tuple from the IndexScan node's currentRelation
76 * using the index specified in the IndexScanState information.
77 * ----------------------------------------------------------------
78 */
79static TupleTableSlot *
81{
82 EState *estate;
83 ExprContext *econtext;
84 ScanDirection direction;
85 IndexScanDesc scandesc;
86 TupleTableSlot *slot;
87
88 /*
89 * extract necessary information from index scan node
90 */
91 estate = node->ss.ps.state;
92
93 /*
94 * Determine which direction to scan the index in based on the plan's scan
95 * direction and the current direction of execution.
96 */
97 direction = ScanDirectionCombine(estate->es_direction,
98 ((IndexScan *) node->ss.ps.plan)->indexorderdir);
99 scandesc = node->iss_ScanDesc;
100 econtext = node->ss.ps.ps_ExprContext;
101 slot = node->ss.ss_ScanTupleSlot;
102
103 if (scandesc == NULL)
104 {
105 /*
106 * We reach here if the index scan is not parallel, or if we're
107 * serially executing an index scan that was planned to be parallel.
108 */
109 scandesc = index_beginscan(node->ss.ss_currentRelation,
110 node->iss_RelationDesc,
111 estate->es_snapshot,
112 &node->iss_Instrument,
113 node->iss_NumScanKeys,
114 node->iss_NumOrderByKeys);
115
116 node->iss_ScanDesc = scandesc;
117
118 /*
119 * If no run-time keys to calculate or they are ready, go ahead and
120 * pass the scankeys to the index AM.
121 */
122 if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
123 index_rescan(scandesc,
124 node->iss_ScanKeys, node->iss_NumScanKeys,
126 }
127
128 /*
129 * ok, now that we have what we need, fetch the next tuple.
130 */
131 while (index_getnext_slot(scandesc, direction, slot))
132 {
134
135 /*
136 * If the index was lossy, we have to recheck the index quals using
137 * the fetched tuple.
138 */
139 if (scandesc->xs_recheck)
140 {
141 econtext->ecxt_scantuple = slot;
142 if (!ExecQualAndReset(node->indexqualorig, econtext))
143 {
144 /* Fails recheck, so drop it and loop back for another */
145 InstrCountFiltered2(node, 1);
146 continue;
147 }
148 }
149
150 return slot;
151 }
152
153 /*
154 * if we get here it means the index scan failed so we are at the end of
155 * the scan..
156 */
157 node->iss_ReachedEnd = true;
158 return ExecClearTuple(slot);
159}
160
161/* ----------------------------------------------------------------
162 * IndexNextWithReorder
163 *
164 * Like IndexNext, but this version can also re-check ORDER BY
165 * expressions, and reorder the tuples as necessary.
166 * ----------------------------------------------------------------
167 */
168static TupleTableSlot *
170{
171 EState *estate;
172 ExprContext *econtext;
173 IndexScanDesc scandesc;
174 TupleTableSlot *slot;
175 ReorderTuple *topmost = NULL;
176 bool was_exact;
177 Datum *lastfetched_vals;
178 bool *lastfetched_nulls;
179 int cmp;
180
181 estate = node->ss.ps.state;
182
183 /*
184 * Only forward scan is supported with reordering. Note: we can get away
185 * with just Asserting here because the system will not try to run the
186 * plan backwards if ExecSupportsBackwardScan() says it won't work.
187 * Currently, that is guaranteed because no index AMs support both
188 * amcanorderbyop and amcanbackward; if any ever do,
189 * ExecSupportsBackwardScan() will need to consider indexorderbys
190 * explicitly.
191 */
192 Assert(!ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indexorderdir));
194
195 scandesc = node->iss_ScanDesc;
196 econtext = node->ss.ps.ps_ExprContext;
197 slot = node->ss.ss_ScanTupleSlot;
198
199 if (scandesc == NULL)
200 {
201 /*
202 * We reach here if the index scan is not parallel, or if we're
203 * serially executing an index scan that was planned to be parallel.
204 */
205 scandesc = index_beginscan(node->ss.ss_currentRelation,
206 node->iss_RelationDesc,
207 estate->es_snapshot,
208 &node->iss_Instrument,
209 node->iss_NumScanKeys,
210 node->iss_NumOrderByKeys);
211
212 node->iss_ScanDesc = scandesc;
213
214 /*
215 * If no run-time keys to calculate or they are ready, go ahead and
216 * pass the scankeys to the index AM.
217 */
218 if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
219 index_rescan(scandesc,
220 node->iss_ScanKeys, node->iss_NumScanKeys,
222 }
223
224 for (;;)
225 {
227
228 /*
229 * Check the reorder queue first. If the topmost tuple in the queue
230 * has an ORDER BY value smaller than (or equal to) the value last
231 * returned by the index, we can return it now.
232 */
234 {
236
237 if (node->iss_ReachedEnd ||
239 topmost->orderbynulls,
240 scandesc->xs_orderbyvals,
241 scandesc->xs_orderbynulls,
242 node) <= 0)
243 {
244 HeapTuple tuple;
245
246 tuple = reorderqueue_pop(node);
247
248 /* Pass 'true', as the tuple in the queue is a palloc'd copy */
249 ExecForceStoreHeapTuple(tuple, slot, true);
250 return slot;
251 }
252 }
253 else if (node->iss_ReachedEnd)
254 {
255 /* Queue is empty, and no more tuples from index. We're done. */
256 return ExecClearTuple(slot);
257 }
258
259 /*
260 * Fetch next tuple from the index.
261 */
262next_indextuple:
263 if (!index_getnext_slot(scandesc, ForwardScanDirection, slot))
264 {
265 /*
266 * No more tuples from the index. But we still need to drain any
267 * remaining tuples from the queue before we're done.
268 */
269 node->iss_ReachedEnd = true;
270 continue;
271 }
272
273 /*
274 * If the index was lossy, we have to recheck the index quals and
275 * ORDER BY expressions using the fetched tuple.
276 */
277 if (scandesc->xs_recheck)
278 {
279 econtext->ecxt_scantuple = slot;
280 if (!ExecQualAndReset(node->indexqualorig, econtext))
281 {
282 /* Fails recheck, so drop it and loop back for another */
283 InstrCountFiltered2(node, 1);
284 /* allow this loop to be cancellable */
286 goto next_indextuple;
287 }
288 }
289
290 if (scandesc->xs_recheckorderby)
291 {
292 econtext->ecxt_scantuple = slot;
293 ResetExprContext(econtext);
294 EvalOrderByExpressions(node, econtext);
295
296 /*
297 * Was the ORDER BY value returned by the index accurate? The
298 * recheck flag means that the index can return inaccurate values,
299 * but then again, the value returned for any particular tuple
300 * could also be exactly correct. Compare the value returned by
301 * the index with the recalculated value. (If the value returned
302 * by the index happened to be exact right, we can often avoid
303 * pushing the tuple to the queue, just to pop it back out again.)
304 */
306 node->iss_OrderByNulls,
307 scandesc->xs_orderbyvals,
308 scandesc->xs_orderbynulls,
309 node);
310 if (cmp < 0)
311 elog(ERROR, "index returned tuples in wrong order");
312 else if (cmp == 0)
313 was_exact = true;
314 else
315 was_exact = false;
316 lastfetched_vals = node->iss_OrderByValues;
317 lastfetched_nulls = node->iss_OrderByNulls;
318 }
319 else
320 {
321 was_exact = true;
322 lastfetched_vals = scandesc->xs_orderbyvals;
323 lastfetched_nulls = scandesc->xs_orderbynulls;
324 }
325
326 /*
327 * Can we return this tuple immediately, or does it need to be pushed
328 * to the reorder queue? If the ORDER BY expression values returned
329 * by the index were inaccurate, we can't return it yet, because the
330 * next tuple from the index might need to come before this one. Also,
331 * we can't return it yet if there are any smaller tuples in the queue
332 * already.
333 */
334 if (!was_exact || (topmost && cmp_orderbyvals(lastfetched_vals,
335 lastfetched_nulls,
336 topmost->orderbyvals,
337 topmost->orderbynulls,
338 node) > 0))
339 {
340 /* Put this tuple to the queue */
341 reorderqueue_push(node, slot, lastfetched_vals, lastfetched_nulls);
342 continue;
343 }
344 else
345 {
346 /* Can return this tuple immediately. */
347 return slot;
348 }
349 }
350
351 /*
352 * if we get here it means the index scan failed so we are at the end of
353 * the scan..
354 */
355 return ExecClearTuple(slot);
356}
357
358/*
359 * Calculate the expressions in the ORDER BY clause, based on the heap tuple.
360 */
361static void
363{
364 int i;
365 ListCell *l;
366 MemoryContext oldContext;
367
368 oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
369
370 i = 0;
371 foreach(l, node->indexorderbyorig)
372 {
373 ExprState *orderby = (ExprState *) lfirst(l);
374
375 node->iss_OrderByValues[i] = ExecEvalExpr(orderby,
376 econtext,
377 &node->iss_OrderByNulls[i]);
378 i++;
379 }
380
381 MemoryContextSwitchTo(oldContext);
382}
383
384/*
385 * IndexRecheck -- access method routine to recheck a tuple in EvalPlanQual
386 */
387static bool
389{
390 ExprContext *econtext;
391
392 /*
393 * extract necessary information from index scan node
394 */
395 econtext = node->ss.ps.ps_ExprContext;
396
397 /* Does the tuple meet the indexqual condition? */
398 econtext->ecxt_scantuple = slot;
399 return ExecQualAndReset(node->indexqualorig, econtext);
400}
401
402
403/*
404 * Compare ORDER BY expression values.
405 */
406static int
407cmp_orderbyvals(const Datum *adist, const bool *anulls,
408 const Datum *bdist, const bool *bnulls,
409 IndexScanState *node)
410{
411 int i;
412 int result;
413
414 for (i = 0; i < node->iss_NumOrderByKeys; i++)
415 {
416 SortSupport ssup = &node->iss_SortSupport[i];
417
418 /*
419 * Handle nulls. We only need to support NULLS LAST ordering, because
420 * match_pathkeys_to_index() doesn't consider indexorderby
421 * implementation otherwise.
422 */
423 if (anulls[i] && !bnulls[i])
424 return 1;
425 else if (!anulls[i] && bnulls[i])
426 return -1;
427 else if (anulls[i] && bnulls[i])
428 return 0;
429
430 result = ssup->comparator(adist[i], bdist[i], ssup);
431 if (result != 0)
432 return result;
433 }
434
435 return 0;
436}
437
438/*
439 * Pairing heap provides getting topmost (greatest) element while KNN provides
440 * ascending sort. That's why we invert the sort order.
441 */
442static int
444 void *arg)
445{
446 ReorderTuple *rta = (ReorderTuple *) a;
447 ReorderTuple *rtb = (ReorderTuple *) b;
449
450 /* exchange argument order to invert the sort order */
451 return cmp_orderbyvals(rtb->orderbyvals, rtb->orderbynulls,
452 rta->orderbyvals, rta->orderbynulls,
453 node);
454}
455
456/*
457 * Helper function to push a tuple to the reorder queue.
458 */
459static void
461 Datum *orderbyvals, bool *orderbynulls)
462{
463 IndexScanDesc scandesc = node->iss_ScanDesc;
464 EState *estate = node->ss.ps.state;
466 ReorderTuple *rt;
467 int i;
468
469 rt = (ReorderTuple *) palloc(sizeof(ReorderTuple));
470 rt->htup = ExecCopySlotHeapTuple(slot);
471 rt->orderbyvals =
472 (Datum *) palloc(sizeof(Datum) * scandesc->numberOfOrderBys);
473 rt->orderbynulls =
474 (bool *) palloc(sizeof(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 }
486
487 MemoryContextSwitchTo(oldContext);
488}
489
490/*
491 * Helper function to pop the next tuple from the reorder queue.
492 */
493static HeapTuple
495{
496 HeapTuple result;
497 ReorderTuple *topmost;
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])
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
602 IndexRuntimeKeyInfo *runtimeKeys, int numRuntimeKeys)
603{
604 int j;
605 MemoryContext oldContext;
606
607 /* We want to keep the key values in per-tuple memory */
608 oldContext = MemoryContextSwitchTo(econtext->ecxt_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;
614 Datum scanvalue;
615 bool isNull;
616
617 /*
618 * For each run-time key, extract the run-time expression and evaluate
619 * it with respect to the current context. We then stick the result
620 * into the proper scan key.
621 *
622 * Note: the result of the eval could be a pass-by-ref value that's
623 * stored in some outer scan's tuple, not in
624 * econtext->ecxt_per_tuple_memory. We assume that the outer tuple
625 * will stay put throughout our scan. If this is wrong, we could copy
626 * the result into our context explicitly, but I think that's not
627 * necessary.
628 *
629 * It's also entirely possible that the result of the eval is a
630 * toasted value. In this case we should forcibly detoast it, to
631 * avoid repeat detoastings each time the value is examined by an
632 * index support function.
633 */
634 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)
645 scanvalue = PointerGetDatum(PG_DETOAST_DATUM(scanvalue));
646 scan_key->sk_argument = scanvalue;
647 scan_key->sk_flags &= ~SK_ISNULL;
648 }
649 }
650
651 MemoryContextSwitchTo(oldContext);
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;
668 MemoryContext oldContext;
669
670 /* We want to keep the arrays in per-tuple memory */
671 oldContext = MemoryContextSwitchTo(econtext->ecxt_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;
677 Datum arraydatum;
678 bool isNull;
679 ArrayType *arrayval;
680 int16 elmlen;
681 bool elmbyval;
682 char elmalign;
683 int num_elems;
684 Datum *elem_values;
685 bool *elem_nulls;
686
687 /*
688 * Compute and deconstruct the array expression. (Notes in
689 * ExecIndexEvalRuntimeKeys() apply here too.)
690 */
691 arraydatum = ExecEvalExpr(array_expr,
692 econtext,
693 &isNull);
694 if (isNull)
695 {
696 result = false;
697 break; /* no point in evaluating more */
698 }
699 arrayval = DatumGetArrayTypeP(arraydatum);
700 /* We could cache this data, but not clear it's worth it */
702 &elmlen, &elmbyval, &elmalign);
703 deconstruct_array(arrayval,
704 ARR_ELEMTYPE(arrayval),
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
729 MemoryContextSwitchTo(oldContext);
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{
789 Relation indexRelationDesc;
790 IndexScanDesc indexScanDesc;
791
792 /*
793 * extract information from the node
794 */
795 indexRelationDesc = node->iss_RelationDesc;
796 indexScanDesc = node->iss_ScanDesc;
797
798 /*
799 * When ending a parallel worker, copy the statistics gathered by the
800 * worker back into shared memory so that it can be picked up by the main
801 * process to report in EXPLAIN ANALYZE
802 */
803 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)
823 index_endscan(indexScanDesc);
824 if (indexRelationDesc)
825 index_close(indexRelationDesc, NoLock);
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{
911 IndexScanState *indexstate;
912 Relation currentRelation;
913 LOCKMODE lockmode;
914
915 /*
916 * create state structure
917 */
918 indexstate = makeNode(IndexScanState);
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 */
941 ExecInitScanTupleSlot(estate, &indexstate->ss,
942 RelationGetDescr(currentRelation),
943 table_slot_callbacks(currentRelation));
944
945 /*
946 * Initialize result type and projection.
947 */
948 ExecInitResultTypeTL(&indexstate->ss.ps);
949 ExecAssignScanProjectionInfo(&indexstate->ss);
950
951 /*
952 * initialize child expressions
953 *
954 * Note: we don't initialize all of the indexqual expression, only the
955 * sub-parts corresponding to runtime keys (see below). Likewise for
956 * indexorderby, if any. But the indexqualorig expression is always
957 * initialized even though it will only be used in some uncommon cases ---
958 * would be nice to improve that. (Problem is that any SubPlans present
959 * in the expression must be found now...)
960 */
961 indexstate->ss.ps.qual =
962 ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
963 indexstate->indexqualorig =
964 ExecInitQual(node->indexqualorig, (PlanState *) indexstate);
965 indexstate->indexorderbyorig =
966 ExecInitExprList(node->indexorderbyorig, (PlanState *) indexstate);
967
968 /*
969 * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
970 * here. This allows an index-advisor plugin to EXPLAIN a plan containing
971 * references to nonexistent indexes.
972 */
973 if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
974 return indexstate;
975
976 /* Open the index relation. */
977 lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
978 indexstate->iss_RelationDesc = index_open(node->indexid, lockmode);
979
980 /*
981 * Initialize index-specific scan state
982 */
983 indexstate->iss_RuntimeKeysReady = false;
984 indexstate->iss_RuntimeKeys = NULL;
985 indexstate->iss_NumRuntimeKeys = 0;
986
987 /*
988 * build the index scan keys from the index qualification
989 */
990 ExecIndexBuildScanKeys((PlanState *) indexstate,
991 indexstate->iss_RelationDesc,
992 node->indexqual,
993 false,
994 &indexstate->iss_ScanKeys,
995 &indexstate->iss_NumScanKeys,
996 &indexstate->iss_RuntimeKeys,
997 &indexstate->iss_NumRuntimeKeys,
998 NULL, /* no ArrayKeys */
999 NULL);
1000
1001 /*
1002 * any ORDER BY exprs have to be turned into scankeys in the same way
1003 */
1004 ExecIndexBuildScanKeys((PlanState *) indexstate,
1005 indexstate->iss_RelationDesc,
1006 node->indexorderby,
1007 true,
1008 &indexstate->iss_OrderByKeys,
1009 &indexstate->iss_NumOrderByKeys,
1010 &indexstate->iss_RuntimeKeys,
1011 &indexstate->iss_NumRuntimeKeys,
1012 NULL, /* no ArrayKeys */
1013 NULL);
1014
1015 /* Initialize sort support, if we need to re-check ORDER BY exprs */
1016 if (indexstate->iss_NumOrderByKeys > 0)
1017 {
1018 int numOrderByKeys = indexstate->iss_NumOrderByKeys;
1019 int i;
1020 ListCell *lco;
1021 ListCell *lcx;
1022
1023 /*
1024 * Prepare sort support, and look up the data type for each ORDER BY
1025 * expression.
1026 */
1027 Assert(numOrderByKeys == list_length(node->indexorderbyops));
1028 Assert(numOrderByKeys == list_length(node->indexorderbyorig));
1029 indexstate->iss_SortSupport = (SortSupportData *)
1030 palloc0(numOrderByKeys * sizeof(SortSupportData));
1031 indexstate->iss_OrderByTypByVals = (bool *)
1032 palloc(numOrderByKeys * sizeof(bool));
1033 indexstate->iss_OrderByTypLens = (int16 *)
1034 palloc(numOrderByKeys * sizeof(int16));
1035 i = 0;
1036 forboth(lco, node->indexorderbyops, lcx, node->indexorderbyorig)
1037 {
1038 Oid orderbyop = lfirst_oid(lco);
1039 Node *orderbyexpr = (Node *) lfirst(lcx);
1040 Oid orderbyType = exprType(orderbyexpr);
1041 Oid orderbyColl = exprCollation(orderbyexpr);
1042 SortSupport orderbysort = &indexstate->iss_SortSupport[i];
1043
1044 /* Initialize sort support */
1045 orderbysort->ssup_cxt = CurrentMemoryContext;
1046 orderbysort->ssup_collation = orderbyColl;
1047 /* See cmp_orderbyvals() comments on NULLS LAST */
1048 orderbysort->ssup_nulls_first = false;
1049 /* ssup_attno is unused here and elsewhere */
1050 orderbysort->ssup_attno = 0;
1051 /* No abbreviation */
1052 orderbysort->abbreviate = false;
1053 PrepareSortSupportFromOrderingOp(orderbyop, orderbysort);
1054
1055 get_typlenbyval(orderbyType,
1056 &indexstate->iss_OrderByTypLens[i],
1057 &indexstate->iss_OrderByTypByVals[i]);
1058 i++;
1059 }
1060
1061 /* allocate arrays to hold the re-calculated distances */
1062 indexstate->iss_OrderByValues = (Datum *)
1063 palloc(numOrderByKeys * sizeof(Datum));
1064 indexstate->iss_OrderByNulls = (bool *)
1065 palloc(numOrderByKeys * sizeof(bool));
1066
1067 /* and initialize the reorder queue */
1069 indexstate);
1070 }
1071
1072 /*
1073 * If we have runtime keys, we need an ExprContext to evaluate them. The
1074 * node's standard context won't do because we want to reset that context
1075 * for every tuple. So, build another context just like the other one...
1076 * -tgl 7/11/00
1077 */
1078 if (indexstate->iss_NumRuntimeKeys != 0)
1079 {
1080 ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
1081
1082 ExecAssignExprContext(estate, &indexstate->ss.ps);
1083 indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
1084 indexstate->ss.ps.ps_ExprContext = stdecontext;
1085 }
1086 else
1087 {
1088 indexstate->iss_RuntimeContext = NULL;
1089 }
1090
1091 /*
1092 * all done.
1093 */
1094 return indexstate;
1095}
1096
1097
1098/*
1099 * ExecIndexBuildScanKeys
1100 * Build the index scan keys from the index qualification expressions
1101 *
1102 * The index quals are passed to the index AM in the form of a ScanKey array.
1103 * This routine sets up the ScanKeys, fills in all constant fields of the
1104 * ScanKeys, and prepares information about the keys that have non-constant
1105 * comparison values. We divide index qual expressions into five types:
1106 *
1107 * 1. Simple operator with constant comparison value ("indexkey op constant").
1108 * For these, we just fill in a ScanKey containing the constant value.
1109 *
1110 * 2. Simple operator with non-constant value ("indexkey op expression").
1111 * For these, we create a ScanKey with everything filled in except the
1112 * expression value, and set up an IndexRuntimeKeyInfo struct to drive
1113 * evaluation of the expression at the right times.
1114 *
1115 * 3. RowCompareExpr ("(indexkey, indexkey, ...) op (expr, expr, ...)").
1116 * For these, we create a header ScanKey plus a subsidiary ScanKey array,
1117 * as specified in access/skey.h. The elements of the row comparison
1118 * can have either constant or non-constant comparison values.
1119 *
1120 * 4. ScalarArrayOpExpr ("indexkey op ANY (array-expression)"). If the index
1121 * supports amsearcharray, we handle these the same as simple operators,
1122 * setting the SK_SEARCHARRAY flag to tell the AM to handle them. Otherwise,
1123 * we create a ScanKey with everything filled in except the comparison value,
1124 * and set up an IndexArrayKeyInfo struct to drive processing of the qual.
1125 * (Note that if we use an IndexArrayKeyInfo struct, the array expression is
1126 * always treated as requiring runtime evaluation, even if it's a constant.)
1127 *
1128 * 5. NullTest ("indexkey IS NULL/IS NOT NULL"). We just fill in the
1129 * ScanKey properly.
1130 *
1131 * This code is also used to prepare ORDER BY expressions for amcanorderbyop
1132 * indexes. The behavior is exactly the same, except that we have to look up
1133 * the operator differently. Note that only cases 1 and 2 are currently
1134 * possible for ORDER BY.
1135 *
1136 * Input params are:
1137 *
1138 * planstate: executor state node we are working for
1139 * index: the index we are building scan keys for
1140 * quals: indexquals (or indexorderbys) expressions
1141 * isorderby: true if processing ORDER BY exprs, false if processing quals
1142 * *runtimeKeys: ptr to pre-existing IndexRuntimeKeyInfos, or NULL if none
1143 * *numRuntimeKeys: number of pre-existing runtime keys
1144 *
1145 * Output params are:
1146 *
1147 * *scanKeys: receives ptr to array of ScanKeys
1148 * *numScanKeys: receives number of scankeys
1149 * *runtimeKeys: receives ptr to array of IndexRuntimeKeyInfos, or NULL if none
1150 * *numRuntimeKeys: receives number of runtime keys
1151 * *arrayKeys: receives ptr to array of IndexArrayKeyInfos, or NULL if none
1152 * *numArrayKeys: receives number of array keys
1153 *
1154 * Caller may pass NULL for arrayKeys and numArrayKeys to indicate that
1155 * IndexArrayKeyInfos are not supported.
1156 */
1157void
1159 List *quals, bool isorderby,
1160 ScanKey *scanKeys, int *numScanKeys,
1161 IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
1162 IndexArrayKeyInfo **arrayKeys, int *numArrayKeys)
1163{
1164 ListCell *qual_cell;
1165 ScanKey scan_keys;
1166 IndexRuntimeKeyInfo *runtime_keys;
1167 IndexArrayKeyInfo *array_keys;
1168 int n_scan_keys;
1169 int n_runtime_keys;
1170 int max_runtime_keys;
1171 int n_array_keys;
1172 int j;
1173
1174 /* Allocate array for ScanKey structs: one per qual */
1175 n_scan_keys = list_length(quals);
1176 scan_keys = (ScanKey) palloc(n_scan_keys * sizeof(ScanKeyData));
1177
1178 /*
1179 * runtime_keys array is dynamically resized as needed. We handle it this
1180 * way so that the same runtime keys array can be shared between
1181 * indexquals and indexorderbys, which will be processed in separate calls
1182 * of this function. Caller must be sure to pass in NULL/0 for first
1183 * call.
1184 */
1185 runtime_keys = *runtimeKeys;
1186 n_runtime_keys = max_runtime_keys = *numRuntimeKeys;
1187
1188 /* Allocate array_keys as large as it could possibly need to be */
1189 array_keys = (IndexArrayKeyInfo *)
1190 palloc0(n_scan_keys * sizeof(IndexArrayKeyInfo));
1191 n_array_keys = 0;
1192
1193 /*
1194 * for each opclause in the given qual, convert the opclause into a single
1195 * scan key
1196 */
1197 j = 0;
1198 foreach(qual_cell, quals)
1199 {
1200 Expr *clause = (Expr *) lfirst(qual_cell);
1201 ScanKey this_scan_key = &scan_keys[j++];
1202 Oid opno; /* operator's OID */
1203 RegProcedure opfuncid; /* operator proc id used in scan */
1204 Oid opfamily; /* opfamily of index column */
1205 int op_strategy; /* operator's strategy number */
1206 Oid op_lefttype; /* operator's declared input types */
1207 Oid op_righttype;
1208 Expr *leftop; /* expr on lhs of operator */
1209 Expr *rightop; /* expr on rhs ... */
1210 AttrNumber varattno; /* att number used in scan */
1211 int indnkeyatts;
1212
1214 if (IsA(clause, OpExpr))
1215 {
1216 /* indexkey op const or indexkey op expression */
1217 int flags = 0;
1218 Datum scanvalue;
1219
1220 opno = ((OpExpr *) clause)->opno;
1221 opfuncid = ((OpExpr *) clause)->opfuncid;
1222
1223 /*
1224 * leftop should be the index key Var, possibly relabeled
1225 */
1226 leftop = (Expr *) get_leftop(clause);
1227
1228 if (leftop && IsA(leftop, RelabelType))
1229 leftop = ((RelabelType *) leftop)->arg;
1230
1231 Assert(leftop != NULL);
1232
1233 if (!(IsA(leftop, Var) &&
1234 ((Var *) leftop)->varno == INDEX_VAR))
1235 elog(ERROR, "indexqual doesn't have key on left side");
1236
1237 varattno = ((Var *) leftop)->varattno;
1238 if (varattno < 1 || varattno > indnkeyatts)
1239 elog(ERROR, "bogus index qualification");
1240
1241 /*
1242 * We have to look up the operator's strategy number. This
1243 * provides a cross-check that the operator does match the index.
1244 */
1245 opfamily = index->rd_opfamily[varattno - 1];
1246
1247 get_op_opfamily_properties(opno, opfamily, isorderby,
1248 &op_strategy,
1249 &op_lefttype,
1250 &op_righttype);
1251
1252 if (isorderby)
1253 flags |= SK_ORDER_BY;
1254
1255 /*
1256 * rightop is the constant or variable comparison value
1257 */
1258 rightop = (Expr *) get_rightop(clause);
1259
1260 if (rightop && IsA(rightop, RelabelType))
1261 rightop = ((RelabelType *) rightop)->arg;
1262
1263 Assert(rightop != NULL);
1264
1265 if (IsA(rightop, Const))
1266 {
1267 /* OK, simple constant comparison value */
1268 scanvalue = ((Const *) rightop)->constvalue;
1269 if (((Const *) rightop)->constisnull)
1270 flags |= SK_ISNULL;
1271 }
1272 else
1273 {
1274 /* Need to treat this one as a runtime key */
1275 if (n_runtime_keys >= max_runtime_keys)
1276 {
1277 if (max_runtime_keys == 0)
1278 {
1279 max_runtime_keys = 8;
1280 runtime_keys = (IndexRuntimeKeyInfo *)
1281 palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
1282 }
1283 else
1284 {
1285 max_runtime_keys *= 2;
1286 runtime_keys = (IndexRuntimeKeyInfo *)
1287 repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
1288 }
1289 }
1290 runtime_keys[n_runtime_keys].scan_key = this_scan_key;
1291 runtime_keys[n_runtime_keys].key_expr =
1292 ExecInitExpr(rightop, planstate);
1293 runtime_keys[n_runtime_keys].key_toastable =
1294 TypeIsToastable(op_righttype);
1295 n_runtime_keys++;
1296 scanvalue = (Datum) 0;
1297 }
1298
1299 /*
1300 * initialize the scan key's fields appropriately
1301 */
1302 ScanKeyEntryInitialize(this_scan_key,
1303 flags,
1304 varattno, /* attribute number to scan */
1305 op_strategy, /* op's strategy */
1306 op_righttype, /* strategy subtype */
1307 ((OpExpr *) clause)->inputcollid, /* collation */
1308 opfuncid, /* reg proc to use */
1309 scanvalue); /* constant */
1310 }
1311 else if (IsA(clause, RowCompareExpr))
1312 {
1313 /* (indexkey, indexkey, ...) op (expression, expression, ...) */
1314 RowCompareExpr *rc = (RowCompareExpr *) clause;
1315 ScanKey first_sub_key;
1316 int n_sub_key;
1317 ListCell *largs_cell;
1318 ListCell *rargs_cell;
1319 ListCell *opnos_cell;
1320 ListCell *collids_cell;
1321
1322 Assert(!isorderby);
1323
1324 first_sub_key = (ScanKey)
1325 palloc(list_length(rc->opnos) * sizeof(ScanKeyData));
1326 n_sub_key = 0;
1327
1328 /* Scan RowCompare columns and generate subsidiary ScanKey items */
1329 forfour(largs_cell, rc->largs, rargs_cell, rc->rargs,
1330 opnos_cell, rc->opnos, collids_cell, rc->inputcollids)
1331 {
1332 ScanKey this_sub_key = &first_sub_key[n_sub_key];
1333 int flags = SK_ROW_MEMBER;
1334 Datum scanvalue;
1335 Oid inputcollation;
1336
1337 leftop = (Expr *) lfirst(largs_cell);
1338 rightop = (Expr *) lfirst(rargs_cell);
1339 opno = lfirst_oid(opnos_cell);
1340 inputcollation = lfirst_oid(collids_cell);
1341
1342 /*
1343 * leftop should be the index key Var, possibly relabeled
1344 */
1345 if (leftop && IsA(leftop, RelabelType))
1346 leftop = ((RelabelType *) leftop)->arg;
1347
1348 Assert(leftop != NULL);
1349
1350 if (!(IsA(leftop, Var) &&
1351 ((Var *) leftop)->varno == INDEX_VAR))
1352 elog(ERROR, "indexqual doesn't have key on left side");
1353
1354 varattno = ((Var *) leftop)->varattno;
1355
1356 /*
1357 * We have to look up the operator's associated support
1358 * function
1359 */
1360 if (!index->rd_indam->amcanorder ||
1361 varattno < 1 || varattno > indnkeyatts)
1362 elog(ERROR, "bogus RowCompare index qualification");
1363 opfamily = index->rd_opfamily[varattno - 1];
1364
1365 get_op_opfamily_properties(opno, opfamily, isorderby,
1366 &op_strategy,
1367 &op_lefttype,
1368 &op_righttype);
1369
1370 if (op_strategy != rc->cmptype)
1371 elog(ERROR, "RowCompare index qualification contains wrong operator");
1372
1373 opfuncid = get_opfamily_proc(opfamily,
1374 op_lefttype,
1375 op_righttype,
1376 BTORDER_PROC);
1377 if (!RegProcedureIsValid(opfuncid))
1378 elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
1379 BTORDER_PROC, op_lefttype, op_righttype, opfamily);
1380
1381 /*
1382 * rightop is the constant or variable comparison value
1383 */
1384 if (rightop && IsA(rightop, RelabelType))
1385 rightop = ((RelabelType *) rightop)->arg;
1386
1387 Assert(rightop != NULL);
1388
1389 if (IsA(rightop, Const))
1390 {
1391 /* OK, simple constant comparison value */
1392 scanvalue = ((Const *) rightop)->constvalue;
1393 if (((Const *) rightop)->constisnull)
1394 flags |= SK_ISNULL;
1395 }
1396 else
1397 {
1398 /* Need to treat this one as a runtime key */
1399 if (n_runtime_keys >= max_runtime_keys)
1400 {
1401 if (max_runtime_keys == 0)
1402 {
1403 max_runtime_keys = 8;
1404 runtime_keys = (IndexRuntimeKeyInfo *)
1405 palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
1406 }
1407 else
1408 {
1409 max_runtime_keys *= 2;
1410 runtime_keys = (IndexRuntimeKeyInfo *)
1411 repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
1412 }
1413 }
1414 runtime_keys[n_runtime_keys].scan_key = this_sub_key;
1415 runtime_keys[n_runtime_keys].key_expr =
1416 ExecInitExpr(rightop, planstate);
1417 runtime_keys[n_runtime_keys].key_toastable =
1418 TypeIsToastable(op_righttype);
1419 n_runtime_keys++;
1420 scanvalue = (Datum) 0;
1421 }
1422
1423 /*
1424 * initialize the subsidiary scan key's fields appropriately
1425 */
1426 ScanKeyEntryInitialize(this_sub_key,
1427 flags,
1428 varattno, /* attribute number */
1429 op_strategy, /* op's strategy */
1430 op_righttype, /* strategy subtype */
1431 inputcollation, /* collation */
1432 opfuncid, /* reg proc to use */
1433 scanvalue); /* constant */
1434 n_sub_key++;
1435 }
1436
1437 /* Mark the last subsidiary scankey correctly */
1438 first_sub_key[n_sub_key - 1].sk_flags |= SK_ROW_END;
1439
1440 /*
1441 * We don't use ScanKeyEntryInitialize for the header because it
1442 * isn't going to contain a valid sk_func pointer.
1443 */
1444 MemSet(this_scan_key, 0, sizeof(ScanKeyData));
1445 this_scan_key->sk_flags = SK_ROW_HEADER;
1446 this_scan_key->sk_attno = first_sub_key->sk_attno;
1447 this_scan_key->sk_strategy = rc->cmptype;
1448 /* sk_subtype, sk_collation, sk_func not used in a header */
1449 this_scan_key->sk_argument = PointerGetDatum(first_sub_key);
1450 }
1451 else if (IsA(clause, ScalarArrayOpExpr))
1452 {
1453 /* indexkey op ANY (array-expression) */
1454 ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
1455 int flags = 0;
1456 Datum scanvalue;
1457
1458 Assert(!isorderby);
1459
1460 Assert(saop->useOr);
1461 opno = saop->opno;
1462 opfuncid = saop->opfuncid;
1463
1464 /*
1465 * leftop should be the index key Var, possibly relabeled
1466 */
1467 leftop = (Expr *) linitial(saop->args);
1468
1469 if (leftop && IsA(leftop, RelabelType))
1470 leftop = ((RelabelType *) leftop)->arg;
1471
1472 Assert(leftop != NULL);
1473
1474 if (!(IsA(leftop, Var) &&
1475 ((Var *) leftop)->varno == INDEX_VAR))
1476 elog(ERROR, "indexqual doesn't have key on left side");
1477
1478 varattno = ((Var *) leftop)->varattno;
1479 if (varattno < 1 || varattno > indnkeyatts)
1480 elog(ERROR, "bogus index qualification");
1481
1482 /*
1483 * We have to look up the operator's strategy number. This
1484 * provides a cross-check that the operator does match the index.
1485 */
1486 opfamily = index->rd_opfamily[varattno - 1];
1487
1488 get_op_opfamily_properties(opno, opfamily, isorderby,
1489 &op_strategy,
1490 &op_lefttype,
1491 &op_righttype);
1492
1493 /*
1494 * rightop is the constant or variable array value
1495 */
1496 rightop = (Expr *) lsecond(saop->args);
1497
1498 if (rightop && IsA(rightop, RelabelType))
1499 rightop = ((RelabelType *) rightop)->arg;
1500
1501 Assert(rightop != NULL);
1502
1503 if (index->rd_indam->amsearcharray)
1504 {
1505 /* Index AM will handle this like a simple operator */
1506 flags |= SK_SEARCHARRAY;
1507 if (IsA(rightop, Const))
1508 {
1509 /* OK, simple constant comparison value */
1510 scanvalue = ((Const *) rightop)->constvalue;
1511 if (((Const *) rightop)->constisnull)
1512 flags |= SK_ISNULL;
1513 }
1514 else
1515 {
1516 /* Need to treat this one as a runtime key */
1517 if (n_runtime_keys >= max_runtime_keys)
1518 {
1519 if (max_runtime_keys == 0)
1520 {
1521 max_runtime_keys = 8;
1522 runtime_keys = (IndexRuntimeKeyInfo *)
1523 palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
1524 }
1525 else
1526 {
1527 max_runtime_keys *= 2;
1528 runtime_keys = (IndexRuntimeKeyInfo *)
1529 repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
1530 }
1531 }
1532 runtime_keys[n_runtime_keys].scan_key = this_scan_key;
1533 runtime_keys[n_runtime_keys].key_expr =
1534 ExecInitExpr(rightop, planstate);
1535
1536 /*
1537 * Careful here: the runtime expression is not of
1538 * op_righttype, but rather is an array of same; so
1539 * TypeIsToastable() isn't helpful. However, we can
1540 * assume that all array types are toastable.
1541 */
1542 runtime_keys[n_runtime_keys].key_toastable = true;
1543 n_runtime_keys++;
1544 scanvalue = (Datum) 0;
1545 }
1546 }
1547 else
1548 {
1549 /* Executor has to expand the array value */
1550 array_keys[n_array_keys].scan_key = this_scan_key;
1551 array_keys[n_array_keys].array_expr =
1552 ExecInitExpr(rightop, planstate);
1553 /* the remaining fields were zeroed by palloc0 */
1554 n_array_keys++;
1555 scanvalue = (Datum) 0;
1556 }
1557
1558 /*
1559 * initialize the scan key's fields appropriately
1560 */
1561 ScanKeyEntryInitialize(this_scan_key,
1562 flags,
1563 varattno, /* attribute number to scan */
1564 op_strategy, /* op's strategy */
1565 op_righttype, /* strategy subtype */
1566 saop->inputcollid, /* collation */
1567 opfuncid, /* reg proc to use */
1568 scanvalue); /* constant */
1569 }
1570 else if (IsA(clause, NullTest))
1571 {
1572 /* indexkey IS NULL or indexkey IS NOT NULL */
1573 NullTest *ntest = (NullTest *) clause;
1574 int flags;
1575
1576 Assert(!isorderby);
1577
1578 /*
1579 * argument should be the index key Var, possibly relabeled
1580 */
1581 leftop = ntest->arg;
1582
1583 if (leftop && IsA(leftop, RelabelType))
1584 leftop = ((RelabelType *) leftop)->arg;
1585
1586 Assert(leftop != NULL);
1587
1588 if (!(IsA(leftop, Var) &&
1589 ((Var *) leftop)->varno == INDEX_VAR))
1590 elog(ERROR, "NullTest indexqual has wrong key");
1591
1592 varattno = ((Var *) leftop)->varattno;
1593
1594 /*
1595 * initialize the scan key's fields appropriately
1596 */
1597 switch (ntest->nulltesttype)
1598 {
1599 case IS_NULL:
1600 flags = SK_ISNULL | SK_SEARCHNULL;
1601 break;
1602 case IS_NOT_NULL:
1603 flags = SK_ISNULL | SK_SEARCHNOTNULL;
1604 break;
1605 default:
1606 elog(ERROR, "unrecognized nulltesttype: %d",
1607 (int) ntest->nulltesttype);
1608 flags = 0; /* keep compiler quiet */
1609 break;
1610 }
1611
1612 ScanKeyEntryInitialize(this_scan_key,
1613 flags,
1614 varattno, /* attribute number to scan */
1615 InvalidStrategy, /* no strategy */
1616 InvalidOid, /* no strategy subtype */
1617 InvalidOid, /* no collation */
1618 InvalidOid, /* no reg proc for this */
1619 (Datum) 0); /* constant */
1620 }
1621 else
1622 elog(ERROR, "unsupported indexqual type: %d",
1623 (int) nodeTag(clause));
1624 }
1625
1626 Assert(n_runtime_keys <= max_runtime_keys);
1627
1628 /* Get rid of any unused arrays */
1629 if (n_array_keys == 0)
1630 {
1631 pfree(array_keys);
1632 array_keys = NULL;
1633 }
1634
1635 /*
1636 * Return info to our caller.
1637 */
1638 *scanKeys = scan_keys;
1639 *numScanKeys = n_scan_keys;
1640 *runtimeKeys = runtime_keys;
1641 *numRuntimeKeys = n_runtime_keys;
1642 if (arrayKeys)
1643 {
1644 *arrayKeys = array_keys;
1645 *numArrayKeys = n_array_keys;
1646 }
1647 else if (n_array_keys != 0)
1648 elog(ERROR, "ScalarArrayOpExpr index qual found where not allowed");
1649}
1650
1651/* ----------------------------------------------------------------
1652 * Parallel Scan Support
1653 * ----------------------------------------------------------------
1654 */
1655
1656/* ----------------------------------------------------------------
1657 * ExecIndexScanEstimate
1658 *
1659 * Compute the amount of space we'll need in the parallel
1660 * query DSM, and inform pcxt->estimator about our needs.
1661 * ----------------------------------------------------------------
1662 */
1663void
1665 ParallelContext *pcxt)
1666{
1667 EState *estate = node->ss.ps.state;
1668 bool instrument = node->ss.ps.instrument != NULL;
1669 bool parallel_aware = node->ss.ps.plan->parallel_aware;
1670
1671 if (!instrument && !parallel_aware)
1672 {
1673 /* No DSM required by the scan */
1674 return;
1675 }
1676
1678 node->iss_NumScanKeys,
1679 node->iss_NumOrderByKeys,
1680 estate->es_snapshot,
1681 instrument, parallel_aware,
1682 pcxt->nworkers);
1685}
1686
1687/* ----------------------------------------------------------------
1688 * ExecIndexScanInitializeDSM
1689 *
1690 * Set up a parallel index scan descriptor.
1691 * ----------------------------------------------------------------
1692 */
1693void
1695 ParallelContext *pcxt)
1696{
1697 EState *estate = node->ss.ps.state;
1698 ParallelIndexScanDesc piscan;
1699 bool instrument = node->ss.ps.instrument != NULL;
1700 bool parallel_aware = node->ss.ps.plan->parallel_aware;
1701
1702 if (!instrument && !parallel_aware)
1703 {
1704 /* No DSM required by the scan */
1705 return;
1706 }
1707
1708 piscan = shm_toc_allocate(pcxt->toc, node->iss_PscanLen);
1710 node->iss_RelationDesc,
1711 estate->es_snapshot,
1712 instrument, parallel_aware, pcxt->nworkers,
1713 &node->iss_SharedInfo, piscan);
1714 shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
1715
1716 if (!parallel_aware)
1717 {
1718 /* Only here to initialize SharedInfo in DSM */
1719 return;
1720 }
1721
1722 node->iss_ScanDesc =
1724 node->iss_RelationDesc,
1725 &node->iss_Instrument,
1726 node->iss_NumScanKeys,
1727 node->iss_NumOrderByKeys,
1728 piscan);
1729
1730 /*
1731 * If no run-time keys to calculate or they are ready, go ahead and pass
1732 * the scankeys to the index AM.
1733 */
1734 if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
1736 node->iss_ScanKeys, node->iss_NumScanKeys,
1737 node->iss_OrderByKeys, node->iss_NumOrderByKeys);
1738}
1739
1740/* ----------------------------------------------------------------
1741 * ExecIndexScanReInitializeDSM
1742 *
1743 * Reset shared state before beginning a fresh scan.
1744 * ----------------------------------------------------------------
1745 */
1746void
1748 ParallelContext *pcxt)
1749{
1750 Assert(node->ss.ps.plan->parallel_aware);
1752}
1753
1754/* ----------------------------------------------------------------
1755 * ExecIndexScanInitializeWorker
1756 *
1757 * Copy relevant information from TOC into planstate.
1758 * ----------------------------------------------------------------
1759 */
1760void
1762 ParallelWorkerContext *pwcxt)
1763{
1764 ParallelIndexScanDesc piscan;
1765 bool instrument = node->ss.ps.instrument != NULL;
1766 bool parallel_aware = node->ss.ps.plan->parallel_aware;
1767
1768 if (!instrument && !parallel_aware)
1769 {
1770 /* No DSM required by the scan */
1771 return;
1772 }
1773
1774 piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
1775
1776 if (instrument)
1778 OffsetToPointer(piscan, piscan->ps_offset_ins);
1779
1780 if (!parallel_aware)
1781 {
1782 /* Only here to set up worker node's SharedInfo */
1783 return;
1784 }
1785
1786 node->iss_ScanDesc =
1788 node->iss_RelationDesc,
1789 &node->iss_Instrument,
1790 node->iss_NumScanKeys,
1791 node->iss_NumOrderByKeys,
1792 piscan);
1793
1794 /*
1795 * If no run-time keys to calculate or they are ready, go ahead and pass
1796 * the scankeys to the index AM.
1797 */
1798 if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
1800 node->iss_ScanKeys, node->iss_NumScanKeys,
1801 node->iss_OrderByKeys, node->iss_NumOrderByKeys);
1802}
1803
1804/* ----------------------------------------------------------------
1805 * ExecIndexScanRetrieveInstrumentation
1806 *
1807 * Transfer index scan statistics from DSM to private memory.
1808 * ----------------------------------------------------------------
1809 */
1810void
1812{
1814 size_t size;
1815
1816 if (SharedInfo == NULL)
1817 return;
1818
1819 /* Create a copy of SharedInfo in backend-local memory */
1820 size = offsetof(SharedIndexScanInstrumentation, winstrument) +
1821 SharedInfo->num_workers * sizeof(IndexScanInstrumentation);
1822 node->iss_SharedInfo = palloc(size);
1823 memcpy(node->iss_SharedInfo, SharedInfo, size);
1824}
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_ELEMTYPE(a)
Definition: array.h:292
void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3631
int16 AttrNumber
Definition: attnum.h:21
int ParallelWorkerNumber
Definition: parallel.c:115
#define OffsetToPointer(base, offset)
Definition: c.h:743
#define RegProcedureIsValid(p)
Definition: c.h:748
int16_t int16
Definition: c.h:497
regproc RegProcedure
Definition: c.h:621
unsigned int Index
Definition: c.h:585
#define MemSet(start, val, len)
Definition: c.h:991
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
void ExecReScan(PlanState *node)
Definition: execAmi.c:77
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:143
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:229
List * ExecInitExprList(List *nodes, PlanState *parent)
Definition: execExpr.c:335
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)
Definition: execTuples.c:2000
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1944
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1658
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:486
Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
Definition: execUtils.c:743
#define InstrCountFiltered2(node, delta)
Definition: execnodes.h:1268
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:719
#define ResetExprContext(econtext)
Definition: executor.h:672
bool(* ExecScanRecheckMtd)(ScanState *node, TupleTableSlot *slot)
Definition: executor.h:602
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:568
TupleTableSlot *(* ExecScanAccessMtd)(ScanState *node)
Definition: executor.h:601
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:415
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:66
#define PG_DETOAST_DATUM(datum)
Definition: fmgr.h:240
struct IndexScanInstrumentation IndexScanInstrumentation
Assert(PointerIsAligned(start, uint64))
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
#define IsParallelWorker()
Definition: parallel.h:60
void index_parallelscan_initialize(Relation heapRelation, Relation indexRelation, Snapshot snapshot, bool instrument, bool parallel_aware, int nworkers, SharedIndexScanInstrumentation **sharedinfo, ParallelIndexScanDesc target)
Definition: indexam.c:510
bool index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *slot)
Definition: indexam.c:720
IndexScanDesc index_beginscan_parallel(Relation heaprel, Relation indexrel, IndexScanInstrumentation *instrument, int nkeys, int norderbys, ParallelIndexScanDesc pscan)
Definition: indexam.c:583
void index_restrpos(IndexScanDesc scan)
Definition: indexam.c:436
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:412
void index_endscan(IndexScanDesc scan)
Definition: indexam.c:382
Size index_parallelscan_estimate(Relation indexRelation, int nkeys, int norderbys, Snapshot snapshot, bool instrument, bool parallel_aware, int nworkers)
Definition: indexam.c:461
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
void index_parallelrescan(IndexScanDesc scan)
Definition: indexam.c:565
void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys)
Definition: indexam.c:356
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:137
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition: lsyscache.c:2411
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition: lsyscache.c:888
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2391
#define TypeIsToastable(typid)
Definition: lsyscache.h:218
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:2167
void pfree(void *pointer)
Definition: mcxt.c:2147
void * palloc0(Size size)
Definition: mcxt.c:1970
void * palloc(Size size)
Definition: mcxt.c:1940
MemoryContext CurrentMemoryContext
Definition: mcxt.c:159
#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:821
static Node * get_rightop(const void *clause)
Definition: nodeFuncs.h:95
static Node * get_leftop(const void *clause)
Definition: nodeFuncs.h:83
static void reorderqueue_push(IndexScanState *node, TupleTableSlot *slot, Datum *orderbyvals, bool *orderbynulls)
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)
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)
Definition: nodeIndexscan.c:80
#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)
Definition: pairingheap.c:112
pairingheap * pairingheap_allocate(pairingheap_comparator compare, void *arg)
Definition: pairingheap.c:42
pairingheap_node * pairingheap_remove_first(pairingheap *heap)
Definition: pairingheap.c:145
pairingheap_node * pairingheap_first(pairingheap *heap)
Definition: pairingheap.c:130
#define pairingheap_is_empty(h)
Definition: pairingheap.h:96
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
void * arg
#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:327
uintptr_t Datum
Definition: postgres.h:69
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
#define InvalidOid
Definition: postgres_ext.h:35
unsigned int Oid
Definition: postgres_ext.h:30
@ IS_NULL
Definition: primnodes.h:1957
@ IS_NOT_NULL
Definition: primnodes.h:1957
#define INDEX_VAR
Definition: primnodes.h:244
static int cmp(const chr *x, const chr *y, size_t len)
Definition: regc_locale.c:743
#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: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
struct ScanKeyData ScanKeyData
#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)
Definition: sortsupport.c:134
#define InvalidStrategy
Definition: stratnum.h:24
ExecAuxRowMark ** relsubs_rowmark
Definition: execnodes.h:1334
TupleTableSlot ** relsubs_slot
Definition: execnodes.h:1306
bool * relsubs_done
Definition: execnodes.h:1341
MemoryContext es_query_cxt
Definition: execnodes.h:708
ScanDirection es_direction
Definition: execnodes.h:656
struct EPQState * es_epq_active
Definition: execnodes.h:741
Snapshot es_snapshot
Definition: execnodes.h:657
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:276
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:268
Datum * elem_values
Definition: execnodes.h:1670
ExprState * array_expr
Definition: execnodes.h:1667
struct ScanKeyData * scan_key
Definition: execnodes.h:1666
ExprState * key_expr
Definition: execnodes.h:1660
struct ScanKeyData * scan_key
Definition: execnodes.h:1659
bool * xs_orderbynulls
Definition: relscan.h:187
int numberOfOrderBys
Definition: relscan.h:140
bool xs_recheckorderby
Definition: relscan.h:188
Datum * xs_orderbyvals
Definition: relscan.h:186
bool iss_ReachedEnd
Definition: execnodes.h:1722
List * indexorderbyorig
Definition: execnodes.h:1706
bool * iss_OrderByTypByVals
Definition: execnodes.h:1726
int iss_NumRuntimeKeys
Definition: execnodes.h:1712
struct IndexScanDescData * iss_ScanDesc
Definition: execnodes.h:1716
IndexScanInstrumentation iss_Instrument
Definition: execnodes.h:1717
ExprState * indexqualorig
Definition: execnodes.h:1705
Relation iss_RelationDesc
Definition: execnodes.h:1715
pairingheap * iss_ReorderQueue
Definition: execnodes.h:1721
ScanState ss
Definition: execnodes.h:1704
bool * iss_OrderByNulls
Definition: execnodes.h:1724
bool iss_RuntimeKeysReady
Definition: execnodes.h:1713
SortSupport iss_SortSupport
Definition: execnodes.h:1725
struct ScanKeyData * iss_ScanKeys
Definition: execnodes.h:1707
int iss_NumOrderByKeys
Definition: execnodes.h:1710
SharedIndexScanInstrumentation * iss_SharedInfo
Definition: execnodes.h:1718
ExprContext * iss_RuntimeContext
Definition: execnodes.h:1714
struct ScanKeyData * iss_OrderByKeys
Definition: execnodes.h:1709
Datum * iss_OrderByValues
Definition: execnodes.h:1723
int16 * iss_OrderByTypLens
Definition: execnodes.h:1727
IndexRuntimeKeyInfo * iss_RuntimeKeys
Definition: execnodes.h:1711
List * indexorderby
Definition: plannodes.h:553
List * indexorderbyops
Definition: plannodes.h:557
Scan scan
Definition: plannodes.h:545
List * indexqualorig
Definition: plannodes.h:551
Oid indexid
Definition: plannodes.h:547
List * indexqual
Definition: plannodes.h:549
List * indexorderbyorig
Definition: plannodes.h:555
Definition: pg_list.h:54
Definition: nodes.h:135
NullTestType nulltesttype
Definition: primnodes.h:1964
Expr * arg
Definition: primnodes.h:1963
shm_toc_estimator estimator
Definition: parallel.h:41
shm_toc * toc
Definition: parallel.h:44
Instrumentation * instrument
Definition: execnodes.h:1169
ExprState * qual
Definition: execnodes.h:1180
Plan * plan
Definition: execnodes.h:1159
EState * state
Definition: execnodes.h:1161
ExprContext * ps_ExprContext
Definition: execnodes.h:1198
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1165
bool parallel_aware
Definition: plannodes.h:193
int plan_node_id
Definition: plannodes.h:207
Datum * orderbyvals
Definition: nodeIndexscan.c:54
bool * orderbynulls
Definition: nodeIndexscan.c:55
pairingheap_node ph_node
Definition: nodeIndexscan.c:52
HeapTuple htup
Definition: nodeIndexscan.c:53
CompareType cmptype
Definition: primnodes.h:1473
int sk_flags
Definition: skey.h:66
Datum sk_argument
Definition: skey.h:72
StrategyNumber sk_strategy
Definition: skey.h:68
AttrNumber sk_attno
Definition: skey.h:67
Relation ss_currentRelation
Definition: execnodes.h:1616
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1618
PlanState ps
Definition: execnodes.h:1615
struct TableScanDescData * ss_currentScanDesc
Definition: execnodes.h:1617
Index scanrelid
Definition: plannodes.h:483
IndexScanInstrumentation winstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: genam.h:47
AttrNumber ssup_attno
Definition: sortsupport.h:81
int(* comparator)(Datum x, Datum y, SortSupport ssup)
Definition: sortsupport.h:106
bool ssup_nulls_first
Definition: sortsupport.h:75
MemoryContext ssup_cxt
Definition: sortsupport.h:66
Definition: primnodes.h:262
Definition: type.h:96
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:59
static HeapTuple ExecCopySlotHeapTuple(TupleTableSlot *slot)
Definition: tuptable.h:485
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:458