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