LCOV - code coverage report
Current view: top level - src/backend/executor - nodeIndexscan.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 507 564 89.9 %
Date: 2025-04-01 14:15:22 Functions: 23 23 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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"
      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             : {
      52             :     pairingheap_node ph_node;
      53             :     HeapTuple   htup;
      54             :     Datum      *orderbyvals;
      55             :     bool       *orderbynulls;
      56             : } ReorderTuple;
      57             : 
      58             : static TupleTableSlot *IndexNext(IndexScanState *node);
      59             : static TupleTableSlot *IndexNextWithReorder(IndexScanState *node);
      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);
      69             : static HeapTuple reorderqueue_pop(IndexScanState *node);
      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 *
      80     2231870 : IndexNext(IndexScanState *node)
      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     2231870 :     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     2231870 :     direction = ScanDirectionCombine(estate->es_direction,
      98             :                                      ((IndexScan *) node->ss.ps.plan)->indexorderdir);
      99     2231870 :     scandesc = node->iss_ScanDesc;
     100     2231870 :     econtext = node->ss.ps.ps_ExprContext;
     101     2231870 :     slot = node->ss.ss_ScanTupleSlot;
     102             : 
     103     2231870 :     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      152558 :         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      152558 :         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      152558 :         if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
     123      152558 :             index_rescan(scandesc,
     124      152558 :                          node->iss_ScanKeys, node->iss_NumScanKeys,
     125      152558 :                          node->iss_OrderByKeys, node->iss_NumOrderByKeys);
     126             :     }
     127             : 
     128             :     /*
     129             :      * ok, now that we have what we need, fetch the next tuple.
     130             :      */
     131     2234652 :     while (index_getnext_slot(scandesc, direction, slot))
     132             :     {
     133     1717220 :         CHECK_FOR_INTERRUPTS();
     134             : 
     135             :         /*
     136             :          * If the index was lossy, we have to recheck the index quals using
     137             :          * the fetched tuple.
     138             :          */
     139     1717220 :         if (scandesc->xs_recheck)
     140             :         {
     141      325960 :             econtext->ecxt_scantuple = slot;
     142      325960 :             if (!ExecQualAndReset(node->indexqualorig, econtext))
     143             :             {
     144             :                 /* Fails recheck, so drop it and loop back for another */
     145        2782 :                 InstrCountFiltered2(node, 1);
     146        2782 :                 continue;
     147             :             }
     148             :         }
     149             : 
     150     1714438 :         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      517428 :     node->iss_ReachedEnd = true;
     158      517428 :     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             :  */
     168             : static TupleTableSlot *
     169       82662 : IndexNextWithReorder(IndexScanState *node)
     170             : {
     171             :     EState     *estate;
     172             :     ExprContext *econtext;
     173             :     IndexScanDesc scandesc;
     174             :     TupleTableSlot *slot;
     175       82662 :     ReorderTuple *topmost = NULL;
     176             :     bool        was_exact;
     177             :     Datum      *lastfetched_vals;
     178             :     bool       *lastfetched_nulls;
     179             :     int         cmp;
     180             : 
     181       82662 :     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));
     193             :     Assert(ScanDirectionIsForward(estate->es_direction));
     194             : 
     195       82662 :     scandesc = node->iss_ScanDesc;
     196       82662 :     econtext = node->ss.ps.ps_ExprContext;
     197       82662 :     slot = node->ss.ss_ScanTupleSlot;
     198             : 
     199       82662 :     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          46 :         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          46 :         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          46 :         if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
     219          46 :             index_rescan(scandesc,
     220          46 :                          node->iss_ScanKeys, node->iss_NumScanKeys,
     221          46 :                          node->iss_OrderByKeys, node->iss_NumOrderByKeys);
     222             :     }
     223             : 
     224             :     for (;;)
     225             :     {
     226       87856 :         CHECK_FOR_INTERRUPTS();
     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             :          */
     233       87856 :         if (!pairingheap_is_empty(node->iss_ReorderQueue))
     234             :         {
     235       10252 :             topmost = (ReorderTuple *) pairingheap_first(node->iss_ReorderQueue);
     236             : 
     237       20498 :             if (node->iss_ReachedEnd ||
     238       10246 :                 cmp_orderbyvals(topmost->orderbyvals,
     239       10246 :                                 topmost->orderbynulls,
     240       10246 :                                 scandesc->xs_orderbyvals,
     241       10246 :                                 scandesc->xs_orderbynulls,
     242             :                                 node) <= 0)
     243             :             {
     244             :                 HeapTuple   tuple;
     245             : 
     246        5076 :                 tuple = reorderqueue_pop(node);
     247             : 
     248             :                 /* Pass 'true', as the tuple in the queue is a palloc'd copy */
     249        5076 :                 ExecForceStoreHeapTuple(tuple, slot, true);
     250        5076 :                 return slot;
     251             :             }
     252             :         }
     253       77604 :         else if (node->iss_ReachedEnd)
     254             :         {
     255             :             /* Queue is empty, and no more tuples from index.  We're done. */
     256          18 :             return ExecClearTuple(slot);
     257             :         }
     258             : 
     259             :         /*
     260             :          * Fetch next tuple from the index.
     261             :          */
     262       82762 : next_indextuple:
     263       86902 :         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          18 :             node->iss_ReachedEnd = true;
     270          18 :             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       86884 :         if (scandesc->xs_recheck)
     278             :         {
     279        9126 :             econtext->ecxt_scantuple = slot;
     280        9126 :             if (!ExecQualAndReset(node->indexqualorig, econtext))
     281             :             {
     282             :                 /* Fails recheck, so drop it and loop back for another */
     283        4140 :                 InstrCountFiltered2(node, 1);
     284             :                 /* allow this loop to be cancellable */
     285        4140 :                 CHECK_FOR_INTERRUPTS();
     286        4140 :                 goto next_indextuple;
     287             :             }
     288             :         }
     289             : 
     290       82744 :         if (scandesc->xs_recheckorderby)
     291             :         {
     292        5300 :             econtext->ecxt_scantuple = slot;
     293        5300 :             ResetExprContext(econtext);
     294        5300 :             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             :              */
     305        5300 :             cmp = cmp_orderbyvals(node->iss_OrderByValues,
     306        5300 :                                   node->iss_OrderByNulls,
     307        5300 :                                   scandesc->xs_orderbyvals,
     308        5300 :                                   scandesc->xs_orderbynulls,
     309             :                                   node);
     310        5300 :             if (cmp < 0)
     311           0 :                 elog(ERROR, "index returned tuples in wrong order");
     312        5300 :             else if (cmp == 0)
     313         130 :                 was_exact = true;
     314             :             else
     315        5170 :                 was_exact = false;
     316        5300 :             lastfetched_vals = node->iss_OrderByValues;
     317        5300 :             lastfetched_nulls = node->iss_OrderByNulls;
     318             :         }
     319             :         else
     320             :         {
     321       77444 :             was_exact = true;
     322       77444 :             lastfetched_vals = scandesc->xs_orderbyvals;
     323       77444 :             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       82832 :         if (!was_exact || (topmost && cmp_orderbyvals(lastfetched_vals,
     335             :                                                       lastfetched_nulls,
     336          88 :                                                       topmost->orderbyvals,
     337          88 :                                                       topmost->orderbynulls,
     338             :                                                       node) > 0))
     339             :         {
     340             :             /* Put this tuple to the queue */
     341        5176 :             reorderqueue_push(node, slot, lastfetched_vals, lastfetched_nulls);
     342        5176 :             continue;
     343             :         }
     344             :         else
     345             :         {
     346             :             /* Can return this tuple immediately. */
     347       77568 :             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             :  */
     361             : static void
     362        5300 : EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext)
     363             : {
     364             :     int         i;
     365             :     ListCell   *l;
     366             :     MemoryContext oldContext;
     367             : 
     368        5300 :     oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
     369             : 
     370        5300 :     i = 0;
     371       10600 :     foreach(l, node->indexorderbyorig)
     372             :     {
     373        5300 :         ExprState  *orderby = (ExprState *) lfirst(l);
     374             : 
     375       10600 :         node->iss_OrderByValues[i] = ExecEvalExpr(orderby,
     376             :                                                   econtext,
     377        5300 :                                                   &node->iss_OrderByNulls[i]);
     378        5300 :         i++;
     379             :     }
     380             : 
     381        5300 :     MemoryContextSwitchTo(oldContext);
     382        5300 : }
     383             : 
     384             : /*
     385             :  * IndexRecheck -- access method routine to recheck a tuple in EvalPlanQual
     386             :  */
     387             : static bool
     388         104 : IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
     389             : {
     390             :     ExprContext *econtext;
     391             : 
     392             :     /*
     393             :      * extract necessary information from index scan node
     394             :      */
     395         104 :     econtext = node->ss.ps.ps_ExprContext;
     396             : 
     397             :     /* Does the tuple meet the indexqual condition? */
     398         104 :     econtext->ecxt_scantuple = slot;
     399         104 :     return ExecQualAndReset(node->indexqualorig, econtext);
     400             : }
     401             : 
     402             : 
     403             : /*
     404             :  * Compare ORDER BY expression values.
     405             :  */
     406             : static int
     407       29228 : cmp_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       29416 :     for (i = 0; i < node->iss_NumOrderByKeys; i++)
     415             :     {
     416       29228 :         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       29228 :         if (anulls[i] && !bnulls[i])
     424           0 :             return 1;
     425       29228 :         else if (!anulls[i] && bnulls[i])
     426           0 :             return -1;
     427       29228 :         else if (anulls[i] && bnulls[i])
     428           0 :             return 0;
     429             : 
     430       29228 :         result = ssup->comparator(adist[i], bdist[i], ssup);
     431       29228 :         if (result != 0)
     432       29040 :             return result;
     433             :     }
     434             : 
     435         188 :     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             :  */
     442             : static int
     443       13594 : reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
     444             :                  void *arg)
     445             : {
     446       13594 :     ReorderTuple *rta = (ReorderTuple *) a;
     447       13594 :     ReorderTuple *rtb = (ReorderTuple *) b;
     448       13594 :     IndexScanState *node = (IndexScanState *) arg;
     449             : 
     450             :     /* exchange argument order to invert the sort order */
     451       27188 :     return cmp_orderbyvals(rtb->orderbyvals, rtb->orderbynulls,
     452       13594 :                            rta->orderbyvals, rta->orderbynulls,
     453             :                            node);
     454             : }
     455             : 
     456             : /*
     457             :  * Helper function to push a tuple to the reorder queue.
     458             :  */
     459             : static void
     460        5176 : reorderqueue_push(IndexScanState *node, TupleTableSlot *slot,
     461             :                   Datum *orderbyvals, bool *orderbynulls)
     462             : {
     463        5176 :     IndexScanDesc scandesc = node->iss_ScanDesc;
     464        5176 :     EState     *estate = node->ss.ps.state;
     465        5176 :     MemoryContext oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
     466             :     ReorderTuple *rt;
     467             :     int         i;
     468             : 
     469        5176 :     rt = (ReorderTuple *) palloc(sizeof(ReorderTuple));
     470        5176 :     rt->htup = ExecCopySlotHeapTuple(slot);
     471        5176 :     rt->orderbyvals =
     472        5176 :         (Datum *) palloc(sizeof(Datum) * scandesc->numberOfOrderBys);
     473        5176 :     rt->orderbynulls =
     474        5176 :         (bool *) palloc(sizeof(bool) * scandesc->numberOfOrderBys);
     475       10352 :     for (i = 0; i < node->iss_NumOrderByKeys; i++)
     476             :     {
     477        5176 :         if (!orderbynulls[i])
     478        5176 :             rt->orderbyvals[i] = datumCopy(orderbyvals[i],
     479        5176 :                                            node->iss_OrderByTypByVals[i],
     480        5176 :                                            node->iss_OrderByTypLens[i]);
     481             :         else
     482           0 :             rt->orderbyvals[i] = (Datum) 0;
     483        5176 :         rt->orderbynulls[i] = orderbynulls[i];
     484             :     }
     485        5176 :     pairingheap_add(node->iss_ReorderQueue, &rt->ph_node);
     486             : 
     487        5176 :     MemoryContextSwitchTo(oldContext);
     488        5176 : }
     489             : 
     490             : /*
     491             :  * Helper function to pop the next tuple from the reorder queue.
     492             :  */
     493             : static HeapTuple
     494        5136 : reorderqueue_pop(IndexScanState *node)
     495             : {
     496             :     HeapTuple   result;
     497             :     ReorderTuple *topmost;
     498             :     int         i;
     499             : 
     500        5136 :     topmost = (ReorderTuple *) pairingheap_remove_first(node->iss_ReorderQueue);
     501             : 
     502        5136 :     result = topmost->htup;
     503       10272 :     for (i = 0; i < node->iss_NumOrderByKeys; i++)
     504             :     {
     505        5136 :         if (!node->iss_OrderByTypByVals[i] && !topmost->orderbynulls[i])
     506           0 :             pfree(DatumGetPointer(topmost->orderbyvals[i]));
     507             :     }
     508        5136 :     pfree(topmost->orderbyvals);
     509        5136 :     pfree(topmost->orderbynulls);
     510        5136 :     pfree(topmost);
     511             : 
     512        5136 :     return result;
     513             : }
     514             : 
     515             : 
     516             : /* ----------------------------------------------------------------
     517             :  *      ExecIndexScan(node)
     518             :  * ----------------------------------------------------------------
     519             :  */
     520             : static TupleTableSlot *
     521     2069272 : ExecIndexScan(PlanState *pstate)
     522             : {
     523     2069272 :     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     2069272 :     if (node->iss_NumRuntimeKeys != 0 && !node->iss_RuntimeKeysReady)
     529       37996 :         ExecReScan((PlanState *) node);
     530             : 
     531     2069266 :     if (node->iss_NumOrderByKeys > 0)
     532       82662 :         return ExecScan(&node->ss,
     533             :                         (ExecScanAccessMtd) IndexNextWithReorder,
     534             :                         (ExecScanRecheckMtd) IndexRecheck);
     535             :     else
     536     1986604 :         return ExecScan(&node->ss,
     537             :                         (ExecScanAccessMtd) IndexNext,
     538             :                         (ExecScanRecheckMtd) IndexRecheck);
     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             :  */
     552             : void
     553      526200 : ExecReScanIndexScan(IndexScanState *node)
     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      526200 :     if (node->iss_NumRuntimeKeys != 0)
     563             :     {
     564      514272 :         ExprContext *econtext = node->iss_RuntimeContext;
     565             : 
     566      514272 :         ResetExprContext(econtext);
     567      514272 :         ExecIndexEvalRuntimeKeys(econtext,
     568             :                                  node->iss_RuntimeKeys,
     569             :                                  node->iss_NumRuntimeKeys);
     570             :     }
     571      526194 :     node->iss_RuntimeKeysReady = true;
     572             : 
     573             :     /* flush the reorder queue */
     574      526194 :     if (node->iss_ReorderQueue)
     575             :     {
     576             :         HeapTuple   tuple;
     577             : 
     578         126 :         while (!pairingheap_is_empty(node->iss_ReorderQueue))
     579             :         {
     580          60 :             tuple = reorderqueue_pop(node);
     581          60 :             heap_freetuple(tuple);
     582             :         }
     583             :     }
     584             : 
     585             :     /* reset index scan */
     586      526194 :     if (node->iss_ScanDesc)
     587      443538 :         index_rescan(node->iss_ScanDesc,
     588      443538 :                      node->iss_ScanKeys, node->iss_NumScanKeys,
     589      443538 :                      node->iss_OrderByKeys, node->iss_NumOrderByKeys);
     590      526194 :     node->iss_ReachedEnd = false;
     591             : 
     592      526194 :     ExecScanReScan(&node->ss);
     593      526194 : }
     594             : 
     595             : 
     596             : /*
     597             :  * ExecIndexEvalRuntimeKeys
     598             :  *      Evaluate any runtime key values, and update the scankeys.
     599             :  */
     600             : void
     601      740154 : ExecIndexEvalRuntimeKeys(ExprContext *econtext,
     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      740154 :     oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
     609             : 
     610     1559360 :     for (j = 0; j < numRuntimeKeys; j++)
     611             :     {
     612      819212 :         ScanKey     scan_key = runtimeKeys[j].scan_key;
     613      819212 :         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      819212 :         scanvalue = ExecEvalExpr(key_expr,
     635             :                                  econtext,
     636             :                                  &isNull);
     637      819206 :         if (isNull)
     638             :         {
     639        1072 :             scan_key->sk_argument = scanvalue;
     640        1072 :             scan_key->sk_flags |= SK_ISNULL;
     641             :         }
     642             :         else
     643             :         {
     644      818134 :             if (runtimeKeys[j].key_toastable)
     645       69772 :                 scanvalue = PointerGetDatum(PG_DETOAST_DATUM(scanvalue));
     646      818134 :             scan_key->sk_argument = scanvalue;
     647      818134 :             scan_key->sk_flags &= ~SK_ISNULL;
     648             :         }
     649             :     }
     650             : 
     651      740148 :     MemoryContextSwitchTo(oldContext);
     652      740148 : }
     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             :  */
     662             : bool
     663          26 : ExecIndexEvalArrayKeys(ExprContext *econtext,
     664             :                        IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
     665             : {
     666          26 :     bool        result = true;
     667             :     int         j;
     668             :     MemoryContext oldContext;
     669             : 
     670             :     /* We want to keep the arrays in per-tuple memory */
     671          26 :     oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
     672             : 
     673          52 :     for (j = 0; j < numArrayKeys; j++)
     674             :     {
     675          26 :         ScanKey     scan_key = arrayKeys[j].scan_key;
     676          26 :         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          26 :         arraydatum = ExecEvalExpr(array_expr,
     692             :                                   econtext,
     693             :                                   &isNull);
     694          26 :         if (isNull)
     695             :         {
     696           0 :             result = false;
     697           0 :             break;              /* no point in evaluating more */
     698             :         }
     699          26 :         arrayval = DatumGetArrayTypeP(arraydatum);
     700             :         /* We could cache this data, but not clear it's worth it */
     701          26 :         get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
     702             :                              &elmlen, &elmbyval, &elmalign);
     703          26 :         deconstruct_array(arrayval,
     704             :                           ARR_ELEMTYPE(arrayval),
     705             :                           elmlen, elmbyval, elmalign,
     706             :                           &elem_values, &elem_nulls, &num_elems);
     707          26 :         if (num_elems <= 0)
     708             :         {
     709           0 :             result = false;
     710           0 :             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          26 :         arrayKeys[j].elem_values = elem_values;
     719          26 :         arrayKeys[j].elem_nulls = elem_nulls;
     720          26 :         arrayKeys[j].num_elems = num_elems;
     721          26 :         scan_key->sk_argument = elem_values[0];
     722          26 :         if (elem_nulls[0])
     723           0 :             scan_key->sk_flags |= SK_ISNULL;
     724             :         else
     725          26 :             scan_key->sk_flags &= ~SK_ISNULL;
     726          26 :         arrayKeys[j].next_elem = 1;
     727             :     }
     728             : 
     729          26 :     MemoryContextSwitchTo(oldContext);
     730             : 
     731          26 :     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             :  */
     741             : bool
     742       25428 : ExecIndexAdvanceArrayKeys(IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
     743             : {
     744       25428 :     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       25454 :     for (j = numArrayKeys - 1; j >= 0; j--)
     754             :     {
     755          52 :         ScanKey     scan_key = arrayKeys[j].scan_key;
     756          52 :         int         next_elem = arrayKeys[j].next_elem;
     757          52 :         int         num_elems = arrayKeys[j].num_elems;
     758          52 :         Datum      *elem_values = arrayKeys[j].elem_values;
     759          52 :         bool       *elem_nulls = arrayKeys[j].elem_nulls;
     760             : 
     761          52 :         if (next_elem >= num_elems)
     762             :         {
     763          26 :             next_elem = 0;
     764          26 :             found = false;      /* need to advance next array key */
     765             :         }
     766             :         else
     767          26 :             found = true;
     768          52 :         scan_key->sk_argument = elem_values[next_elem];
     769          52 :         if (elem_nulls[next_elem])
     770           0 :             scan_key->sk_flags |= SK_ISNULL;
     771             :         else
     772          52 :             scan_key->sk_flags &= ~SK_ISNULL;
     773          52 :         arrayKeys[j].next_elem = next_elem + 1;
     774          52 :         if (found)
     775          26 :             break;
     776             :     }
     777             : 
     778       25428 :     return found;
     779             : }
     780             : 
     781             : 
     782             : /* ----------------------------------------------------------------
     783             :  *      ExecEndIndexScan
     784             :  * ----------------------------------------------------------------
     785             :  */
     786             : void
     787      189764 : ExecEndIndexScan(IndexScanState *node)
     788             : {
     789             :     Relation    indexRelationDesc;
     790             :     IndexScanDesc indexScanDesc;
     791             : 
     792             :     /*
     793             :      * extract information from the node
     794             :      */
     795      189764 :     indexRelationDesc = node->iss_RelationDesc;
     796      189764 :     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      189764 :     if (node->iss_SharedInfo != NULL && IsParallelWorker())
     804             :     {
     805             :         IndexScanInstrumentation *winstrument;
     806             : 
     807             :         Assert(ParallelWorkerNumber <= node->iss_SharedInfo->num_workers);
     808         270 :         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         270 :         winstrument->nsearches += node->iss_Instrument.nsearches;
     817             :     }
     818             : 
     819             :     /*
     820             :      * close the index relation (no-op if we didn't open it)
     821             :      */
     822      189764 :     if (indexScanDesc)
     823      151978 :         index_endscan(indexScanDesc);
     824      189764 :     if (indexRelationDesc)
     825      186492 :         index_close(indexRelationDesc, NoLock);
     826      189764 : }
     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             :  */
     835             : void
     836        6040 : ExecIndexMarkPos(IndexScanState *node)
     837             : {
     838        6040 :     EState     *estate = node->ss.ps.state;
     839        6040 :     EPQState   *epqstate = estate->es_epq_active;
     840             : 
     841        6040 :     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           2 :         Index       scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
     853             : 
     854             :         Assert(scanrelid > 0);
     855           2 :         if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
     856           0 :             epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
     857             :         {
     858             :             /* Verify the claim above */
     859           2 :             if (!epqstate->relsubs_done[scanrelid - 1])
     860           0 :                 elog(ERROR, "unexpected ExecIndexMarkPos call in EPQ recheck");
     861           2 :             return;
     862             :         }
     863             :     }
     864             : 
     865        6038 :     index_markpos(node->iss_ScanDesc);
     866             : }
     867             : 
     868             : /* ----------------------------------------------------------------
     869             :  *      ExecIndexRestrPos
     870             :  * ----------------------------------------------------------------
     871             :  */
     872             : void
     873       54018 : ExecIndexRestrPos(IndexScanState *node)
     874             : {
     875       54018 :     EState     *estate = node->ss.ps.state;
     876       54018 :     EPQState   *epqstate = estate->es_epq_active;
     877             : 
     878       54018 :     if (estate->es_epq_active != NULL)
     879             :     {
     880             :         /* See comments in ExecIndexMarkPos */
     881           0 :         Index       scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
     882             : 
     883             :         Assert(scanrelid > 0);
     884           0 :         if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
     885           0 :             epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
     886             :         {
     887             :             /* Verify the claim above */
     888           0 :             if (!epqstate->relsubs_done[scanrelid - 1])
     889           0 :                 elog(ERROR, "unexpected ExecIndexRestrPos call in EPQ recheck");
     890           0 :             return;
     891             :         }
     892             :     }
     893             : 
     894       54018 :     index_restrpos(node->iss_ScanDesc);
     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             :  */
     908             : IndexScanState *
     909      190568 : ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
     910             : {
     911             :     IndexScanState *indexstate;
     912             :     Relation    currentRelation;
     913             :     LOCKMODE    lockmode;
     914             : 
     915             :     /*
     916             :      * create state structure
     917             :      */
     918      190568 :     indexstate = makeNode(IndexScanState);
     919      190568 :     indexstate->ss.ps.plan = (Plan *) node;
     920      190568 :     indexstate->ss.ps.state = estate;
     921      190568 :     indexstate->ss.ps.ExecProcNode = ExecIndexScan;
     922             : 
     923             :     /*
     924             :      * Miscellaneous initialization
     925             :      *
     926             :      * create expression context for node
     927             :      */
     928      190568 :     ExecAssignExprContext(estate, &indexstate->ss.ps);
     929             : 
     930             :     /*
     931             :      * open the scan relation
     932             :      */
     933      190568 :     currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
     934             : 
     935      190568 :     indexstate->ss.ss_currentRelation = currentRelation;
     936      190568 :     indexstate->ss.ss_currentScanDesc = NULL;    /* no heap scan here */
     937             : 
     938             :     /*
     939             :      * get the scan type from the relation descriptor.
     940             :      */
     941      190568 :     ExecInitScanTupleSlot(estate, &indexstate->ss,
     942             :                           RelationGetDescr(currentRelation),
     943             :                           table_slot_callbacks(currentRelation));
     944             : 
     945             :     /*
     946             :      * Initialize result type and projection.
     947             :      */
     948      190568 :     ExecInitResultTypeTL(&indexstate->ss.ps);
     949      190568 :     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      190568 :     indexstate->ss.ps.qual =
     962      190568 :         ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
     963      190568 :     indexstate->indexqualorig =
     964      190568 :         ExecInitQual(node->indexqualorig, (PlanState *) indexstate);
     965      190568 :     indexstate->indexorderbyorig =
     966      190568 :         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      190568 :     if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
     974        3272 :         return indexstate;
     975             : 
     976             :     /* Open the index relation. */
     977      187296 :     lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
     978      187296 :     indexstate->iss_RelationDesc = index_open(node->indexid, lockmode);
     979             : 
     980             :     /*
     981             :      * Initialize index-specific scan state
     982             :      */
     983      187296 :     indexstate->iss_RuntimeKeysReady = false;
     984      187296 :     indexstate->iss_RuntimeKeys = NULL;
     985      187296 :     indexstate->iss_NumRuntimeKeys = 0;
     986             : 
     987             :     /*
     988             :      * build the index scan keys from the index qualification
     989             :      */
     990      187296 :     ExecIndexBuildScanKeys((PlanState *) indexstate,
     991             :                            indexstate->iss_RelationDesc,
     992             :                            node->indexqual,
     993             :                            false,
     994      187296 :                            &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      187296 :     ExecIndexBuildScanKeys((PlanState *) indexstate,
    1005             :                            indexstate->iss_RelationDesc,
    1006             :                            node->indexorderby,
    1007             :                            true,
    1008      187296 :                            &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      187296 :     if (indexstate->iss_NumOrderByKeys > 0)
    1017             :     {
    1018          46 :         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          46 :         indexstate->iss_SortSupport = (SortSupportData *)
    1030          46 :             palloc0(numOrderByKeys * sizeof(SortSupportData));
    1031          46 :         indexstate->iss_OrderByTypByVals = (bool *)
    1032          46 :             palloc(numOrderByKeys * sizeof(bool));
    1033          46 :         indexstate->iss_OrderByTypLens = (int16 *)
    1034          46 :             palloc(numOrderByKeys * sizeof(int16));
    1035          46 :         i = 0;
    1036          92 :         forboth(lco, node->indexorderbyops, lcx, node->indexorderbyorig)
    1037             :         {
    1038          46 :             Oid         orderbyop = lfirst_oid(lco);
    1039          46 :             Node       *orderbyexpr = (Node *) lfirst(lcx);
    1040          46 :             Oid         orderbyType = exprType(orderbyexpr);
    1041          46 :             Oid         orderbyColl = exprCollation(orderbyexpr);
    1042          46 :             SortSupport orderbysort = &indexstate->iss_SortSupport[i];
    1043             : 
    1044             :             /* Initialize sort support */
    1045          46 :             orderbysort->ssup_cxt = CurrentMemoryContext;
    1046          46 :             orderbysort->ssup_collation = orderbyColl;
    1047             :             /* See cmp_orderbyvals() comments on NULLS LAST */
    1048          46 :             orderbysort->ssup_nulls_first = false;
    1049             :             /* ssup_attno is unused here and elsewhere */
    1050          46 :             orderbysort->ssup_attno = 0;
    1051             :             /* No abbreviation */
    1052          46 :             orderbysort->abbreviate = false;
    1053          46 :             PrepareSortSupportFromOrderingOp(orderbyop, orderbysort);
    1054             : 
    1055          46 :             get_typlenbyval(orderbyType,
    1056          46 :                             &indexstate->iss_OrderByTypLens[i],
    1057          46 :                             &indexstate->iss_OrderByTypByVals[i]);
    1058          46 :             i++;
    1059             :         }
    1060             : 
    1061             :         /* allocate arrays to hold the re-calculated distances */
    1062          46 :         indexstate->iss_OrderByValues = (Datum *)
    1063          46 :             palloc(numOrderByKeys * sizeof(Datum));
    1064          46 :         indexstate->iss_OrderByNulls = (bool *)
    1065          46 :             palloc(numOrderByKeys * sizeof(bool));
    1066             : 
    1067             :         /* and initialize the reorder queue */
    1068          46 :         indexstate->iss_ReorderQueue = pairingheap_allocate(reorderqueue_cmp,
    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      187296 :     if (indexstate->iss_NumRuntimeKeys != 0)
    1079             :     {
    1080      104694 :         ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
    1081             : 
    1082      104694 :         ExecAssignExprContext(estate, &indexstate->ss.ps);
    1083      104694 :         indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
    1084      104694 :         indexstate->ss.ps.ps_ExprContext = stdecontext;
    1085             :     }
    1086             :     else
    1087             :     {
    1088       82602 :         indexstate->iss_RuntimeContext = NULL;
    1089             :     }
    1090             : 
    1091             :     /*
    1092             :      * all done.
    1093             :      */
    1094      187296 :     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             :  */
    1157             : void
    1158      425494 : ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
    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      425494 :     n_scan_keys = list_length(quals);
    1176      425494 :     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      425494 :     runtime_keys = *runtimeKeys;
    1186      425494 :     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      425494 :         palloc0(n_scan_keys * sizeof(IndexArrayKeyInfo));
    1191      425494 :     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      425494 :     j = 0;
    1198      686058 :     foreach(qual_cell, quals)
    1199             :     {
    1200      260564 :         Expr       *clause = (Expr *) lfirst(qual_cell);
    1201      260564 :         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             : 
    1213      260564 :         indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
    1214      260564 :         if (IsA(clause, OpExpr))
    1215             :         {
    1216             :             /* indexkey op const or indexkey op expression */
    1217      258534 :             int         flags = 0;
    1218             :             Datum       scanvalue;
    1219             : 
    1220      258534 :             opno = ((OpExpr *) clause)->opno;
    1221      258534 :             opfuncid = ((OpExpr *) clause)->opfuncid;
    1222             : 
    1223             :             /*
    1224             :              * leftop should be the index key Var, possibly relabeled
    1225             :              */
    1226      258534 :             leftop = (Expr *) get_leftop(clause);
    1227             : 
    1228      258534 :             if (leftop && IsA(leftop, RelabelType))
    1229           0 :                 leftop = ((RelabelType *) leftop)->arg;
    1230             : 
    1231             :             Assert(leftop != NULL);
    1232             : 
    1233      258534 :             if (!(IsA(leftop, Var) &&
    1234      258534 :                   ((Var *) leftop)->varno == INDEX_VAR))
    1235           0 :                 elog(ERROR, "indexqual doesn't have key on left side");
    1236             : 
    1237      258534 :             varattno = ((Var *) leftop)->varattno;
    1238      258534 :             if (varattno < 1 || varattno > indnkeyatts)
    1239           0 :                 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      258534 :             opfamily = index->rd_opfamily[varattno - 1];
    1246             : 
    1247      258534 :             get_op_opfamily_properties(opno, opfamily, isorderby,
    1248             :                                        &op_strategy,
    1249             :                                        &op_lefttype,
    1250             :                                        &op_righttype);
    1251             : 
    1252      258534 :             if (isorderby)
    1253         198 :                 flags |= SK_ORDER_BY;
    1254             : 
    1255             :             /*
    1256             :              * rightop is the constant or variable comparison value
    1257             :              */
    1258      258534 :             rightop = (Expr *) get_rightop(clause);
    1259             : 
    1260      258534 :             if (rightop && IsA(rightop, RelabelType))
    1261        4662 :                 rightop = ((RelabelType *) rightop)->arg;
    1262             : 
    1263             :             Assert(rightop != NULL);
    1264             : 
    1265      258534 :             if (IsA(rightop, Const))
    1266             :             {
    1267             :                 /* OK, simple constant comparison value */
    1268      121514 :                 scanvalue = ((Const *) rightop)->constvalue;
    1269      121514 :                 if (((Const *) rightop)->constisnull)
    1270           0 :                     flags |= SK_ISNULL;
    1271             :             }
    1272             :             else
    1273             :             {
    1274             :                 /* Need to treat this one as a runtime key */
    1275      137020 :                 if (n_runtime_keys >= max_runtime_keys)
    1276             :                 {
    1277      114412 :                     if (max_runtime_keys == 0)
    1278             :                     {
    1279      114406 :                         max_runtime_keys = 8;
    1280             :                         runtime_keys = (IndexRuntimeKeyInfo *)
    1281      114406 :                             palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
    1282             :                     }
    1283             :                     else
    1284             :                     {
    1285           6 :                         max_runtime_keys *= 2;
    1286             :                         runtime_keys = (IndexRuntimeKeyInfo *)
    1287           6 :                             repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
    1288             :                     }
    1289             :                 }
    1290      137020 :                 runtime_keys[n_runtime_keys].scan_key = this_scan_key;
    1291      274040 :                 runtime_keys[n_runtime_keys].key_expr =
    1292      137020 :                     ExecInitExpr(rightop, planstate);
    1293      137020 :                 runtime_keys[n_runtime_keys].key_toastable =
    1294      137020 :                     TypeIsToastable(op_righttype);
    1295      137020 :                 n_runtime_keys++;
    1296      137020 :                 scanvalue = (Datum) 0;
    1297             :             }
    1298             : 
    1299             :             /*
    1300             :              * initialize the scan key's fields appropriately
    1301             :              */
    1302      258534 :             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        2030 :         else if (IsA(clause, RowCompareExpr))
    1312             :         {
    1313             :             /* (indexkey, indexkey, ...) op (expression, expression, ...) */
    1314          66 :             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          66 :                 palloc(list_length(rc->opnos) * sizeof(ScanKeyData));
    1326          66 :             n_sub_key = 0;
    1327             : 
    1328             :             /* Scan RowCompare columns and generate subsidiary ScanKey items */
    1329         198 :             forfour(largs_cell, rc->largs, rargs_cell, rc->rargs,
    1330             :                     opnos_cell, rc->opnos, collids_cell, rc->inputcollids)
    1331             :             {
    1332         132 :                 ScanKey     this_sub_key = &first_sub_key[n_sub_key];
    1333         132 :                 int         flags = SK_ROW_MEMBER;
    1334             :                 Datum       scanvalue;
    1335             :                 Oid         inputcollation;
    1336             : 
    1337         132 :                 leftop = (Expr *) lfirst(largs_cell);
    1338         132 :                 rightop = (Expr *) lfirst(rargs_cell);
    1339         132 :                 opno = lfirst_oid(opnos_cell);
    1340         132 :                 inputcollation = lfirst_oid(collids_cell);
    1341             : 
    1342             :                 /*
    1343             :                  * leftop should be the index key Var, possibly relabeled
    1344             :                  */
    1345         132 :                 if (leftop && IsA(leftop, RelabelType))
    1346           0 :                     leftop = ((RelabelType *) leftop)->arg;
    1347             : 
    1348             :                 Assert(leftop != NULL);
    1349             : 
    1350         132 :                 if (!(IsA(leftop, Var) &&
    1351         132 :                       ((Var *) leftop)->varno == INDEX_VAR))
    1352           0 :                     elog(ERROR, "indexqual doesn't have key on left side");
    1353             : 
    1354         132 :                 varattno = ((Var *) leftop)->varattno;
    1355             : 
    1356             :                 /*
    1357             :                  * We have to look up the operator's associated support
    1358             :                  * function
    1359             :                  */
    1360         132 :                 if (!index->rd_indam->amcanorder ||
    1361         132 :                     varattno < 1 || varattno > indnkeyatts)
    1362           0 :                     elog(ERROR, "bogus RowCompare index qualification");
    1363         132 :                 opfamily = index->rd_opfamily[varattno - 1];
    1364             : 
    1365         132 :                 get_op_opfamily_properties(opno, opfamily, isorderby,
    1366             :                                            &op_strategy,
    1367             :                                            &op_lefttype,
    1368             :                                            &op_righttype);
    1369             : 
    1370         132 :                 if (op_strategy != rc->cmptype)
    1371           0 :                     elog(ERROR, "RowCompare index qualification contains wrong operator");
    1372             : 
    1373         132 :                 opfuncid = get_opfamily_proc(opfamily,
    1374             :                                              op_lefttype,
    1375             :                                              op_righttype,
    1376             :                                              BTORDER_PROC);
    1377         132 :                 if (!RegProcedureIsValid(opfuncid))
    1378           0 :                     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         132 :                 if (rightop && IsA(rightop, RelabelType))
    1385           0 :                     rightop = ((RelabelType *) rightop)->arg;
    1386             : 
    1387             :                 Assert(rightop != NULL);
    1388             : 
    1389         132 :                 if (IsA(rightop, Const))
    1390             :                 {
    1391             :                     /* OK, simple constant comparison value */
    1392         132 :                     scanvalue = ((Const *) rightop)->constvalue;
    1393         132 :                     if (((Const *) rightop)->constisnull)
    1394          18 :                         flags |= SK_ISNULL;
    1395             :                 }
    1396             :                 else
    1397             :                 {
    1398             :                     /* Need to treat this one as a runtime key */
    1399           0 :                     if (n_runtime_keys >= max_runtime_keys)
    1400             :                     {
    1401           0 :                         if (max_runtime_keys == 0)
    1402             :                         {
    1403           0 :                             max_runtime_keys = 8;
    1404             :                             runtime_keys = (IndexRuntimeKeyInfo *)
    1405           0 :                                 palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
    1406             :                         }
    1407             :                         else
    1408             :                         {
    1409           0 :                             max_runtime_keys *= 2;
    1410             :                             runtime_keys = (IndexRuntimeKeyInfo *)
    1411           0 :                                 repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
    1412             :                         }
    1413             :                     }
    1414           0 :                     runtime_keys[n_runtime_keys].scan_key = this_sub_key;
    1415           0 :                     runtime_keys[n_runtime_keys].key_expr =
    1416           0 :                         ExecInitExpr(rightop, planstate);
    1417           0 :                     runtime_keys[n_runtime_keys].key_toastable =
    1418           0 :                         TypeIsToastable(op_righttype);
    1419           0 :                     n_runtime_keys++;
    1420           0 :                     scanvalue = (Datum) 0;
    1421             :                 }
    1422             : 
    1423             :                 /*
    1424             :                  * initialize the subsidiary scan key's fields appropriately
    1425             :                  */
    1426         132 :                 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         132 :                 n_sub_key++;
    1435             :             }
    1436             : 
    1437             :             /* Mark the last subsidiary scankey correctly */
    1438          66 :             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         660 :             MemSet(this_scan_key, 0, sizeof(ScanKeyData));
    1445          66 :             this_scan_key->sk_flags = SK_ROW_HEADER;
    1446          66 :             this_scan_key->sk_attno = first_sub_key->sk_attno;
    1447          66 :             this_scan_key->sk_strategy = rc->cmptype;
    1448             :             /* sk_subtype, sk_collation, sk_func not used in a header */
    1449          66 :             this_scan_key->sk_argument = PointerGetDatum(first_sub_key);
    1450             :         }
    1451        1964 :         else if (IsA(clause, ScalarArrayOpExpr))
    1452             :         {
    1453             :             /* indexkey op ANY (array-expression) */
    1454        1430 :             ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
    1455        1430 :             int         flags = 0;
    1456             :             Datum       scanvalue;
    1457             : 
    1458             :             Assert(!isorderby);
    1459             : 
    1460             :             Assert(saop->useOr);
    1461        1430 :             opno = saop->opno;
    1462        1430 :             opfuncid = saop->opfuncid;
    1463             : 
    1464             :             /*
    1465             :              * leftop should be the index key Var, possibly relabeled
    1466             :              */
    1467        1430 :             leftop = (Expr *) linitial(saop->args);
    1468             : 
    1469        1430 :             if (leftop && IsA(leftop, RelabelType))
    1470           0 :                 leftop = ((RelabelType *) leftop)->arg;
    1471             : 
    1472             :             Assert(leftop != NULL);
    1473             : 
    1474        1430 :             if (!(IsA(leftop, Var) &&
    1475        1430 :                   ((Var *) leftop)->varno == INDEX_VAR))
    1476           0 :                 elog(ERROR, "indexqual doesn't have key on left side");
    1477             : 
    1478        1430 :             varattno = ((Var *) leftop)->varattno;
    1479        1430 :             if (varattno < 1 || varattno > indnkeyatts)
    1480           0 :                 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        1430 :             opfamily = index->rd_opfamily[varattno - 1];
    1487             : 
    1488        1430 :             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        1430 :             rightop = (Expr *) lsecond(saop->args);
    1497             : 
    1498        1430 :             if (rightop && IsA(rightop, RelabelType))
    1499           0 :                 rightop = ((RelabelType *) rightop)->arg;
    1500             : 
    1501             :             Assert(rightop != NULL);
    1502             : 
    1503        1430 :             if (index->rd_indam->amsearcharray)
    1504             :             {
    1505             :                 /* Index AM will handle this like a simple operator */
    1506        1404 :                 flags |= SK_SEARCHARRAY;
    1507        1404 :                 if (IsA(rightop, Const))
    1508             :                 {
    1509             :                     /* OK, simple constant comparison value */
    1510        1344 :                     scanvalue = ((Const *) rightop)->constvalue;
    1511        1344 :                     if (((Const *) rightop)->constisnull)
    1512           0 :                         flags |= SK_ISNULL;
    1513             :                 }
    1514             :                 else
    1515             :                 {
    1516             :                     /* Need to treat this one as a runtime key */
    1517          60 :                     if (n_runtime_keys >= max_runtime_keys)
    1518             :                     {
    1519          60 :                         if (max_runtime_keys == 0)
    1520             :                         {
    1521          60 :                             max_runtime_keys = 8;
    1522             :                             runtime_keys = (IndexRuntimeKeyInfo *)
    1523          60 :                                 palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
    1524             :                         }
    1525             :                         else
    1526             :                         {
    1527           0 :                             max_runtime_keys *= 2;
    1528             :                             runtime_keys = (IndexRuntimeKeyInfo *)
    1529           0 :                                 repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
    1530             :                         }
    1531             :                     }
    1532          60 :                     runtime_keys[n_runtime_keys].scan_key = this_scan_key;
    1533         120 :                     runtime_keys[n_runtime_keys].key_expr =
    1534          60 :                         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          60 :                     runtime_keys[n_runtime_keys].key_toastable = true;
    1543          60 :                     n_runtime_keys++;
    1544          60 :                     scanvalue = (Datum) 0;
    1545             :                 }
    1546             :             }
    1547             :             else
    1548             :             {
    1549             :                 /* Executor has to expand the array value */
    1550          26 :                 array_keys[n_array_keys].scan_key = this_scan_key;
    1551          52 :                 array_keys[n_array_keys].array_expr =
    1552          26 :                     ExecInitExpr(rightop, planstate);
    1553             :                 /* the remaining fields were zeroed by palloc0 */
    1554          26 :                 n_array_keys++;
    1555          26 :                 scanvalue = (Datum) 0;
    1556             :             }
    1557             : 
    1558             :             /*
    1559             :              * initialize the scan key's fields appropriately
    1560             :              */
    1561        1430 :             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         534 :         else if (IsA(clause, NullTest))
    1571             :         {
    1572             :             /* indexkey IS NULL or indexkey IS NOT NULL */
    1573         534 :             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         534 :             leftop = ntest->arg;
    1582             : 
    1583         534 :             if (leftop && IsA(leftop, RelabelType))
    1584           0 :                 leftop = ((RelabelType *) leftop)->arg;
    1585             : 
    1586             :             Assert(leftop != NULL);
    1587             : 
    1588         534 :             if (!(IsA(leftop, Var) &&
    1589         534 :                   ((Var *) leftop)->varno == INDEX_VAR))
    1590           0 :                 elog(ERROR, "NullTest indexqual has wrong key");
    1591             : 
    1592         534 :             varattno = ((Var *) leftop)->varattno;
    1593             : 
    1594             :             /*
    1595             :              * initialize the scan key's fields appropriately
    1596             :              */
    1597         534 :             switch (ntest->nulltesttype)
    1598             :             {
    1599         182 :                 case IS_NULL:
    1600         182 :                     flags = SK_ISNULL | SK_SEARCHNULL;
    1601         182 :                     break;
    1602         352 :                 case IS_NOT_NULL:
    1603         352 :                     flags = SK_ISNULL | SK_SEARCHNOTNULL;
    1604         352 :                     break;
    1605           0 :                 default:
    1606           0 :                     elog(ERROR, "unrecognized nulltesttype: %d",
    1607             :                          (int) ntest->nulltesttype);
    1608             :                     flags = 0;  /* keep compiler quiet */
    1609             :                     break;
    1610             :             }
    1611             : 
    1612         534 :             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           0 :             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      425494 :     if (n_array_keys == 0)
    1630             :     {
    1631      425468 :         pfree(array_keys);
    1632      425468 :         array_keys = NULL;
    1633             :     }
    1634             : 
    1635             :     /*
    1636             :      * Return info to our caller.
    1637             :      */
    1638      425494 :     *scanKeys = scan_keys;
    1639      425494 :     *numScanKeys = n_scan_keys;
    1640      425494 :     *runtimeKeys = runtime_keys;
    1641      425494 :     *numRuntimeKeys = n_runtime_keys;
    1642      425494 :     if (arrayKeys)
    1643             :     {
    1644       22798 :         *arrayKeys = array_keys;
    1645       22798 :         *numArrayKeys = n_array_keys;
    1646             :     }
    1647      402696 :     else if (n_array_keys != 0)
    1648           0 :         elog(ERROR, "ScalarArrayOpExpr index qual found where not allowed");
    1649      425494 : }
    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             :  */
    1663             : void
    1664         294 : ExecIndexScanEstimate(IndexScanState *node,
    1665             :                       ParallelContext *pcxt)
    1666             : {
    1667         294 :     EState     *estate = node->ss.ps.state;
    1668         294 :     bool        instrument = node->ss.ps.instrument != NULL;
    1669         294 :     bool        parallel_aware = node->ss.ps.plan->parallel_aware;
    1670             : 
    1671         294 :     if (!instrument && !parallel_aware)
    1672             :     {
    1673             :         /* No DSM required by the scan */
    1674           6 :         return;
    1675             :     }
    1676             : 
    1677         288 :     node->iss_PscanLen = index_parallelscan_estimate(node->iss_RelationDesc,
    1678             :                                                      node->iss_NumScanKeys,
    1679             :                                                      node->iss_NumOrderByKeys,
    1680             :                                                      estate->es_snapshot,
    1681             :                                                      instrument, parallel_aware,
    1682             :                                                      pcxt->nworkers);
    1683         288 :     shm_toc_estimate_chunk(&pcxt->estimator, node->iss_PscanLen);
    1684         288 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
    1685             : }
    1686             : 
    1687             : /* ----------------------------------------------------------------
    1688             :  *      ExecIndexScanInitializeDSM
    1689             :  *
    1690             :  *      Set up a parallel index scan descriptor.
    1691             :  * ----------------------------------------------------------------
    1692             :  */
    1693             : void
    1694         294 : ExecIndexScanInitializeDSM(IndexScanState *node,
    1695             :                            ParallelContext *pcxt)
    1696             : {
    1697         294 :     EState     *estate = node->ss.ps.state;
    1698             :     ParallelIndexScanDesc piscan;
    1699         294 :     bool        instrument = node->ss.ps.instrument != NULL;
    1700         294 :     bool        parallel_aware = node->ss.ps.plan->parallel_aware;
    1701             : 
    1702         294 :     if (!instrument && !parallel_aware)
    1703             :     {
    1704             :         /* No DSM required by the scan */
    1705           6 :         return;
    1706             :     }
    1707             : 
    1708         288 :     piscan = shm_toc_allocate(pcxt->toc, node->iss_PscanLen);
    1709         288 :     index_parallelscan_initialize(node->ss.ss_currentRelation,
    1710             :                                   node->iss_RelationDesc,
    1711             :                                   estate->es_snapshot,
    1712             :                                   instrument, parallel_aware, pcxt->nworkers,
    1713             :                                   &node->iss_SharedInfo, piscan);
    1714         288 :     shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
    1715             : 
    1716         288 :     if (!parallel_aware)
    1717             :     {
    1718             :         /* Only here to initialize SharedInfo in DSM */
    1719         270 :         return;
    1720             :     }
    1721             : 
    1722          18 :     node->iss_ScanDesc =
    1723          18 :         index_beginscan_parallel(node->ss.ss_currentRelation,
    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          18 :     if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
    1735          12 :         index_rescan(node->iss_ScanDesc,
    1736          12 :                      node->iss_ScanKeys, node->iss_NumScanKeys,
    1737          12 :                      node->iss_OrderByKeys, node->iss_NumOrderByKeys);
    1738             : }
    1739             : 
    1740             : /* ----------------------------------------------------------------
    1741             :  *      ExecIndexScanReInitializeDSM
    1742             :  *
    1743             :  *      Reset shared state before beginning a fresh scan.
    1744             :  * ----------------------------------------------------------------
    1745             :  */
    1746             : void
    1747          12 : ExecIndexScanReInitializeDSM(IndexScanState *node,
    1748             :                              ParallelContext *pcxt)
    1749             : {
    1750             :     Assert(node->ss.ps.plan->parallel_aware);
    1751          12 :     index_parallelrescan(node->iss_ScanDesc);
    1752          12 : }
    1753             : 
    1754             : /* ----------------------------------------------------------------
    1755             :  *      ExecIndexScanInitializeWorker
    1756             :  *
    1757             :  *      Copy relevant information from TOC into planstate.
    1758             :  * ----------------------------------------------------------------
    1759             :  */
    1760             : void
    1761         396 : ExecIndexScanInitializeWorker(IndexScanState *node,
    1762             :                               ParallelWorkerContext *pwcxt)
    1763             : {
    1764             :     ParallelIndexScanDesc piscan;
    1765         396 :     bool        instrument = node->ss.ps.instrument != NULL;
    1766         396 :     bool        parallel_aware = node->ss.ps.plan->parallel_aware;
    1767             : 
    1768         396 :     if (!instrument && !parallel_aware)
    1769             :     {
    1770             :         /* No DSM required by the scan */
    1771           6 :         return;
    1772             :     }
    1773             : 
    1774         390 :     piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
    1775             : 
    1776         390 :     if (instrument)
    1777         270 :         node->iss_SharedInfo = (SharedIndexScanInstrumentation *)
    1778         270 :             OffsetToPointer(piscan, piscan->ps_offset_ins);
    1779             : 
    1780         390 :     if (!parallel_aware)
    1781             :     {
    1782             :         /* Only here to set up worker node's SharedInfo */
    1783         270 :         return;
    1784             :     }
    1785             : 
    1786         120 :     node->iss_ScanDesc =
    1787         120 :         index_beginscan_parallel(node->ss.ss_currentRelation,
    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         120 :     if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
    1799          96 :         index_rescan(node->iss_ScanDesc,
    1800          96 :                      node->iss_ScanKeys, node->iss_NumScanKeys,
    1801          96 :                      node->iss_OrderByKeys, node->iss_NumOrderByKeys);
    1802             : }
    1803             : 
    1804             : /* ----------------------------------------------------------------
    1805             :  * ExecIndexScanRetrieveInstrumentation
    1806             :  *
    1807             :  *      Transfer index scan statistics from DSM to private memory.
    1808             :  * ----------------------------------------------------------------
    1809             :  */
    1810             : void
    1811         270 : ExecIndexScanRetrieveInstrumentation(IndexScanState *node)
    1812             : {
    1813         270 :     SharedIndexScanInstrumentation *SharedInfo = node->iss_SharedInfo;
    1814             :     size_t      size;
    1815             : 
    1816         270 :     if (SharedInfo == NULL)
    1817           0 :         return;
    1818             : 
    1819             :     /* Create a copy of SharedInfo in backend-local memory */
    1820         270 :     size = offsetof(SharedIndexScanInstrumentation, winstrument) +
    1821         270 :         SharedInfo->num_workers * sizeof(IndexScanInstrumentation);
    1822         270 :     node->iss_SharedInfo = palloc(size);
    1823         270 :     memcpy(node->iss_SharedInfo, SharedInfo, size);
    1824             : }

Generated by: LCOV version 1.14