LCOV - code coverage report
Current view: top level - src/backend/executor - nodeIndexscan.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 89.9 % 552 496
Test Date: 2026-03-21 04:16:06 Functions: 100.0 % 23 23
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-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *    src/backend/executor/nodeIndexscan.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : /*
      16              :  * INTERFACE ROUTINES
      17              :  *      ExecIndexScan           scans a relation using an index
      18              :  *      IndexNext               retrieve next tuple using index
      19              :  *      IndexNextWithReorder    same, but recheck ORDER BY expressions
      20              :  *      ExecInitIndexScan       creates and initializes state info.
      21              :  *      ExecReScanIndexScan     rescans the indexed relation.
      22              :  *      ExecEndIndexScan        releases all storage.
      23              :  *      ExecIndexMarkPos        marks scan position.
      24              :  *      ExecIndexRestrPos       restores scan position.
      25              :  *      ExecIndexScanEstimate   estimates DSM space needed for parallel index scan
      26              :  *      ExecIndexScanInitializeDSM initialize DSM for parallel indexscan
      27              :  *      ExecIndexScanReInitializeDSM reinitialize DSM for fresh scan
      28              :  *      ExecIndexScanInitializeWorker attach to DSM info in parallel worker
      29              :  */
      30              : #include "postgres.h"
      31              : 
      32              : #include "access/nbtree.h"
      33              : #include "access/relscan.h"
      34              : #include "access/tableam.h"
      35              : #include "catalog/pg_am.h"
      36              : #include "executor/executor.h"
      37              : #include "executor/instrument.h"
      38              : #include "executor/nodeIndexscan.h"
      39              : #include "lib/pairingheap.h"
      40              : #include "miscadmin.h"
      41              : #include "nodes/nodeFuncs.h"
      42              : #include "utils/array.h"
      43              : #include "utils/datum.h"
      44              : #include "utils/lsyscache.h"
      45              : #include "utils/rel.h"
      46              : #include "utils/sortsupport.h"
      47              : 
      48              : /*
      49              :  * When an ordering operator is used, tuples fetched from the index that
      50              :  * need to be reordered are queued in a pairing heap, as ReorderTuples.
      51              :  */
      52              : typedef struct
      53              : {
      54              :     pairingheap_node ph_node;
      55              :     HeapTuple   htup;
      56              :     Datum      *orderbyvals;
      57              :     bool       *orderbynulls;
      58              : } ReorderTuple;
      59              : 
      60              : static TupleTableSlot *IndexNext(IndexScanState *node);
      61              : static TupleTableSlot *IndexNextWithReorder(IndexScanState *node);
      62              : static void EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext);
      63              : static bool IndexRecheck(IndexScanState *node, TupleTableSlot *slot);
      64              : static int  cmp_orderbyvals(const Datum *adist, const bool *anulls,
      65              :                             const Datum *bdist, const bool *bnulls,
      66              :                             IndexScanState *node);
      67              : static int  reorderqueue_cmp(const pairingheap_node *a,
      68              :                              const pairingheap_node *b, void *arg);
      69              : static void reorderqueue_push(IndexScanState *node, TupleTableSlot *slot,
      70              :                               const Datum *orderbyvals, const bool *orderbynulls);
      71              : static HeapTuple reorderqueue_pop(IndexScanState *node);
      72              : 
      73              : 
      74              : /* ----------------------------------------------------------------
      75              :  *      IndexNext
      76              :  *
      77              :  *      Retrieve a tuple from the IndexScan node's currentRelation
      78              :  *      using the index specified in the IndexScanState information.
      79              :  * ----------------------------------------------------------------
      80              :  */
      81              : static TupleTableSlot *
      82      1554836 : IndexNext(IndexScanState *node)
      83              : {
      84              :     EState     *estate;
      85              :     ExprContext *econtext;
      86              :     ScanDirection direction;
      87              :     IndexScanDesc scandesc;
      88              :     TupleTableSlot *slot;
      89              : 
      90              :     /*
      91              :      * extract necessary information from index scan node
      92              :      */
      93      1554836 :     estate = node->ss.ps.state;
      94              : 
      95              :     /*
      96              :      * Determine which direction to scan the index in based on the plan's scan
      97              :      * direction and the current direction of execution.
      98              :      */
      99      1554836 :     direction = ScanDirectionCombine(estate->es_direction,
     100              :                                      ((IndexScan *) node->ss.ps.plan)->indexorderdir);
     101      1554836 :     scandesc = node->iss_ScanDesc;
     102      1554836 :     econtext = node->ss.ps.ps_ExprContext;
     103      1554836 :     slot = node->ss.ss_ScanTupleSlot;
     104              : 
     105      1554836 :     if (scandesc == NULL)
     106              :     {
     107              :         /*
     108              :          * We reach here if the index scan is not parallel, or if we're
     109              :          * serially executing an index scan that was planned to be parallel.
     110              :          */
     111       180847 :         scandesc = index_beginscan(node->ss.ss_currentRelation,
     112              :                                    node->iss_RelationDesc,
     113              :                                    estate->es_snapshot,
     114              :                                    &node->iss_Instrument,
     115              :                                    node->iss_NumScanKeys,
     116              :                                    node->iss_NumOrderByKeys);
     117              : 
     118       180847 :         node->iss_ScanDesc = scandesc;
     119              : 
     120              :         /*
     121              :          * If no run-time keys to calculate or they are ready, go ahead and
     122              :          * pass the scankeys to the index AM.
     123              :          */
     124       180847 :         if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
     125       180847 :             index_rescan(scandesc,
     126              :                          node->iss_ScanKeys, node->iss_NumScanKeys,
     127              :                          node->iss_OrderByKeys, node->iss_NumOrderByKeys);
     128              :     }
     129              : 
     130              :     /*
     131              :      * ok, now that we have what we need, fetch the next tuple.
     132              :      */
     133      1556679 :     while (index_getnext_slot(scandesc, direction, slot))
     134              :     {
     135      1246234 :         CHECK_FOR_INTERRUPTS();
     136              : 
     137              :         /*
     138              :          * If the index was lossy, we have to recheck the index quals using
     139              :          * the fetched tuple.
     140              :          */
     141      1246234 :         if (scandesc->xs_recheck)
     142              :         {
     143       217829 :             econtext->ecxt_scantuple = slot;
     144       217829 :             if (!ExecQualAndReset(node->indexqualorig, econtext))
     145              :             {
     146              :                 /* Fails recheck, so drop it and loop back for another */
     147         1843 :                 InstrCountFiltered2(node, 1);
     148         1843 :                 continue;
     149              :             }
     150              :         }
     151              : 
     152      1244391 :         return slot;
     153              :     }
     154              : 
     155              :     /*
     156              :      * if we get here it means the index scan failed so we are at the end of
     157              :      * the scan..
     158              :      */
     159       310443 :     node->iss_ReachedEnd = true;
     160       310443 :     return ExecClearTuple(slot);
     161              : }
     162              : 
     163              : /* ----------------------------------------------------------------
     164              :  *      IndexNextWithReorder
     165              :  *
     166              :  *      Like IndexNext, but this version can also re-check ORDER BY
     167              :  *      expressions, and reorder the tuples as necessary.
     168              :  * ----------------------------------------------------------------
     169              :  */
     170              : static TupleTableSlot *
     171        55101 : IndexNextWithReorder(IndexScanState *node)
     172              : {
     173              :     EState     *estate;
     174              :     ExprContext *econtext;
     175              :     IndexScanDesc scandesc;
     176              :     TupleTableSlot *slot;
     177        55101 :     ReorderTuple *topmost = NULL;
     178              :     bool        was_exact;
     179              :     Datum      *lastfetched_vals;
     180              :     bool       *lastfetched_nulls;
     181              :     int         cmp;
     182              : 
     183        55101 :     estate = node->ss.ps.state;
     184              : 
     185              :     /*
     186              :      * Only forward scan is supported with reordering.  Note: we can get away
     187              :      * with just Asserting here because the system will not try to run the
     188              :      * plan backwards if ExecSupportsBackwardScan() says it won't work.
     189              :      * Currently, that is guaranteed because no index AMs support both
     190              :      * amcanorderbyop and amcanbackward; if any ever do,
     191              :      * ExecSupportsBackwardScan() will need to consider indexorderbys
     192              :      * explicitly.
     193              :      */
     194              :     Assert(!ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indexorderdir));
     195              :     Assert(ScanDirectionIsForward(estate->es_direction));
     196              : 
     197        55101 :     scandesc = node->iss_ScanDesc;
     198        55101 :     econtext = node->ss.ps.ps_ExprContext;
     199        55101 :     slot = node->ss.ss_ScanTupleSlot;
     200              : 
     201        55101 :     if (scandesc == NULL)
     202              :     {
     203              :         /*
     204              :          * We reach here if the index scan is not parallel, or if we're
     205              :          * serially executing an index scan that was planned to be parallel.
     206              :          */
     207           29 :         scandesc = index_beginscan(node->ss.ss_currentRelation,
     208              :                                    node->iss_RelationDesc,
     209              :                                    estate->es_snapshot,
     210              :                                    &node->iss_Instrument,
     211              :                                    node->iss_NumScanKeys,
     212              :                                    node->iss_NumOrderByKeys);
     213              : 
     214           29 :         node->iss_ScanDesc = scandesc;
     215              : 
     216              :         /*
     217              :          * If no run-time keys to calculate or they are ready, go ahead and
     218              :          * pass the scankeys to the index AM.
     219              :          */
     220           29 :         if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
     221           29 :             index_rescan(scandesc,
     222              :                          node->iss_ScanKeys, node->iss_NumScanKeys,
     223              :                          node->iss_OrderByKeys, node->iss_NumOrderByKeys);
     224              :     }
     225              : 
     226              :     for (;;)
     227              :     {
     228        58556 :         CHECK_FOR_INTERRUPTS();
     229              : 
     230              :         /*
     231              :          * Check the reorder queue first.  If the topmost tuple in the queue
     232              :          * has an ORDER BY value smaller than (or equal to) the value last
     233              :          * returned by the index, we can return it now.
     234              :          */
     235        58556 :         if (!pairingheap_is_empty(node->iss_ReorderQueue))
     236              :         {
     237         6824 :             topmost = (ReorderTuple *) pairingheap_first(node->iss_ReorderQueue);
     238              : 
     239        13644 :             if (node->iss_ReachedEnd ||
     240         6820 :                 cmp_orderbyvals(topmost->orderbyvals,
     241         6820 :                                 topmost->orderbynulls,
     242         6820 :                                 scandesc->xs_orderbyvals,
     243         6820 :                                 scandesc->xs_orderbynulls,
     244              :                                 node) <= 0)
     245              :             {
     246              :                 HeapTuple   tuple;
     247              : 
     248         3381 :                 tuple = reorderqueue_pop(node);
     249              : 
     250              :                 /* Pass 'true', as the tuple in the queue is a palloc'd copy */
     251         3381 :                 ExecForceStoreHeapTuple(tuple, slot, true);
     252         3381 :                 return slot;
     253              :             }
     254              :         }
     255        51732 :         else if (node->iss_ReachedEnd)
     256              :         {
     257              :             /* Queue is empty, and no more tuples from index.  We're done. */
     258           12 :             return ExecClearTuple(slot);
     259              :         }
     260              : 
     261              :         /*
     262              :          * Fetch next tuple from the index.
     263              :          */
     264        55163 : next_indextuple:
     265        57923 :         if (!index_getnext_slot(scandesc, ForwardScanDirection, slot))
     266              :         {
     267              :             /*
     268              :              * No more tuples from the index.  But we still need to drain any
     269              :              * remaining tuples from the queue before we're done.
     270              :              */
     271           12 :             node->iss_ReachedEnd = true;
     272           12 :             continue;
     273              :         }
     274              : 
     275              :         /*
     276              :          * If the index was lossy, we have to recheck the index quals and
     277              :          * ORDER BY expressions using the fetched tuple.
     278              :          */
     279        57911 :         if (scandesc->xs_recheck)
     280              :         {
     281         6084 :             econtext->ecxt_scantuple = slot;
     282         6084 :             if (!ExecQualAndReset(node->indexqualorig, econtext))
     283              :             {
     284              :                 /* Fails recheck, so drop it and loop back for another */
     285         2760 :                 InstrCountFiltered2(node, 1);
     286              :                 /* allow this loop to be cancellable */
     287         2760 :                 CHECK_FOR_INTERRUPTS();
     288         2760 :                 goto next_indextuple;
     289              :             }
     290              :         }
     291              : 
     292        55151 :         if (scandesc->xs_recheckorderby)
     293              :         {
     294         3524 :             econtext->ecxt_scantuple = slot;
     295         3524 :             ResetExprContext(econtext);
     296         3524 :             EvalOrderByExpressions(node, econtext);
     297              : 
     298              :             /*
     299              :              * Was the ORDER BY value returned by the index accurate?  The
     300              :              * recheck flag means that the index can return inaccurate values,
     301              :              * but then again, the value returned for any particular tuple
     302              :              * could also be exactly correct.  Compare the value returned by
     303              :              * the index with the recalculated value.  (If the value returned
     304              :              * by the index happened to be exact right, we can often avoid
     305              :              * pushing the tuple to the queue, just to pop it back out again.)
     306              :              */
     307         3524 :             cmp = cmp_orderbyvals(node->iss_OrderByValues,
     308         3524 :                                   node->iss_OrderByNulls,
     309         3524 :                                   scandesc->xs_orderbyvals,
     310         3524 :                                   scandesc->xs_orderbynulls,
     311              :                                   node);
     312         3524 :             if (cmp < 0)
     313            0 :                 elog(ERROR, "index returned tuples in wrong order");
     314         3524 :             else if (cmp == 0)
     315           85 :                 was_exact = true;
     316              :             else
     317         3439 :                 was_exact = false;
     318         3524 :             lastfetched_vals = node->iss_OrderByValues;
     319         3524 :             lastfetched_nulls = node->iss_OrderByNulls;
     320              :         }
     321              :         else
     322              :         {
     323        51627 :             was_exact = true;
     324        51627 :             lastfetched_vals = scandesc->xs_orderbyvals;
     325        51627 :             lastfetched_nulls = scandesc->xs_orderbynulls;
     326              :         }
     327              : 
     328              :         /*
     329              :          * Can we return this tuple immediately, or does it need to be pushed
     330              :          * to the reorder queue?  If the ORDER BY expression values returned
     331              :          * by the index were inaccurate, we can't return it yet, because the
     332              :          * next tuple from the index might need to come before this one. Also,
     333              :          * we can't return it yet if there are any smaller tuples in the queue
     334              :          * already.
     335              :          */
     336        55209 :         if (!was_exact || (topmost && cmp_orderbyvals(lastfetched_vals,
     337              :                                                       lastfetched_nulls,
     338           58 :                                                       topmost->orderbyvals,
     339           58 :                                                       topmost->orderbynulls,
     340              :                                                       node) > 0))
     341              :         {
     342              :             /* Put this tuple to the queue */
     343         3443 :             reorderqueue_push(node, slot, lastfetched_vals, lastfetched_nulls);
     344         3443 :             continue;
     345              :         }
     346              :         else
     347              :         {
     348              :             /* Can return this tuple immediately. */
     349        51708 :             return slot;
     350              :         }
     351              :     }
     352              : 
     353              :     /*
     354              :      * if we get here it means the index scan failed so we are at the end of
     355              :      * the scan..
     356              :      */
     357              :     return ExecClearTuple(slot);
     358              : }
     359              : 
     360              : /*
     361              :  * Calculate the expressions in the ORDER BY clause, based on the heap tuple.
     362              :  */
     363              : static void
     364         3524 : EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext)
     365              : {
     366              :     int         i;
     367              :     ListCell   *l;
     368              :     MemoryContext oldContext;
     369              : 
     370         3524 :     oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
     371              : 
     372         3524 :     i = 0;
     373         7048 :     foreach(l, node->indexorderbyorig)
     374              :     {
     375         3524 :         ExprState  *orderby = (ExprState *) lfirst(l);
     376              : 
     377         7048 :         node->iss_OrderByValues[i] = ExecEvalExpr(orderby,
     378              :                                                   econtext,
     379         3524 :                                                   &node->iss_OrderByNulls[i]);
     380         3524 :         i++;
     381              :     }
     382              : 
     383         3524 :     MemoryContextSwitchTo(oldContext);
     384         3524 : }
     385              : 
     386              : /*
     387              :  * IndexRecheck -- access method routine to recheck a tuple in EvalPlanQual
     388              :  */
     389              : static bool
     390           58 : IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
     391              : {
     392              :     ExprContext *econtext;
     393              : 
     394              :     /*
     395              :      * extract necessary information from index scan node
     396              :      */
     397           58 :     econtext = node->ss.ps.ps_ExprContext;
     398              : 
     399              :     /* Does the tuple meet the indexqual condition? */
     400           58 :     econtext->ecxt_scantuple = slot;
     401           58 :     return ExecQualAndReset(node->indexqualorig, econtext);
     402              : }
     403              : 
     404              : 
     405              : /*
     406              :  * Compare ORDER BY expression values.
     407              :  */
     408              : static int
     409        19446 : cmp_orderbyvals(const Datum *adist, const bool *anulls,
     410              :                 const Datum *bdist, const bool *bnulls,
     411              :                 IndexScanState *node)
     412              : {
     413              :     int         i;
     414              :     int         result;
     415              : 
     416        19581 :     for (i = 0; i < node->iss_NumOrderByKeys; i++)
     417              :     {
     418        19446 :         SortSupport ssup = &node->iss_SortSupport[i];
     419              : 
     420              :         /*
     421              :          * Handle nulls.  We only need to support NULLS LAST ordering, because
     422              :          * match_pathkeys_to_index() doesn't consider indexorderby
     423              :          * implementation otherwise.
     424              :          */
     425        19446 :         if (anulls[i] && !bnulls[i])
     426            0 :             return 1;
     427        19446 :         else if (!anulls[i] && bnulls[i])
     428            0 :             return -1;
     429        19446 :         else if (anulls[i] && bnulls[i])
     430            0 :             return 0;
     431              : 
     432        19446 :         result = ssup->comparator(adist[i], bdist[i], ssup);
     433        19446 :         if (result != 0)
     434        19311 :             return result;
     435              :     }
     436              : 
     437          135 :     return 0;
     438              : }
     439              : 
     440              : /*
     441              :  * Pairing heap provides getting topmost (greatest) element while KNN provides
     442              :  * ascending sort.  That's why we invert the sort order.
     443              :  */
     444              : static int
     445         9044 : reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
     446              :                  void *arg)
     447              : {
     448         9044 :     const ReorderTuple *rta = (const ReorderTuple *) a;
     449         9044 :     const ReorderTuple *rtb = (const ReorderTuple *) b;
     450         9044 :     IndexScanState *node = (IndexScanState *) arg;
     451              : 
     452              :     /* exchange argument order to invert the sort order */
     453        18088 :     return cmp_orderbyvals(rtb->orderbyvals, rtb->orderbynulls,
     454         9044 :                            rta->orderbyvals, rta->orderbynulls,
     455              :                            node);
     456              : }
     457              : 
     458              : /*
     459              :  * Helper function to push a tuple to the reorder queue.
     460              :  */
     461              : static void
     462         3443 : reorderqueue_push(IndexScanState *node, TupleTableSlot *slot,
     463              :                   const Datum *orderbyvals, const bool *orderbynulls)
     464              : {
     465         3443 :     IndexScanDesc scandesc = node->iss_ScanDesc;
     466         3443 :     EState     *estate = node->ss.ps.state;
     467         3443 :     MemoryContext oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
     468              :     ReorderTuple *rt;
     469              :     int         i;
     470              : 
     471         3443 :     rt = palloc_object(ReorderTuple);
     472         3443 :     rt->htup = ExecCopySlotHeapTuple(slot);
     473         3443 :     rt->orderbyvals = palloc_array(Datum, scandesc->numberOfOrderBys);
     474         3443 :     rt->orderbynulls = palloc_array(bool, scandesc->numberOfOrderBys);
     475         6886 :     for (i = 0; i < node->iss_NumOrderByKeys; i++)
     476              :     {
     477         3443 :         if (!orderbynulls[i])
     478         3443 :             rt->orderbyvals[i] = datumCopy(orderbyvals[i],
     479         3443 :                                            node->iss_OrderByTypByVals[i],
     480         3443 :                                            node->iss_OrderByTypLens[i]);
     481              :         else
     482            0 :             rt->orderbyvals[i] = (Datum) 0;
     483         3443 :         rt->orderbynulls[i] = orderbynulls[i];
     484              :     }
     485         3443 :     pairingheap_add(node->iss_ReorderQueue, &rt->ph_node);
     486              : 
     487         3443 :     MemoryContextSwitchTo(oldContext);
     488         3443 : }
     489              : 
     490              : /*
     491              :  * Helper function to pop the next tuple from the reorder queue.
     492              :  */
     493              : static HeapTuple
     494         3421 : reorderqueue_pop(IndexScanState *node)
     495              : {
     496              :     HeapTuple   result;
     497              :     ReorderTuple *topmost;
     498              :     int         i;
     499              : 
     500         3421 :     topmost = (ReorderTuple *) pairingheap_remove_first(node->iss_ReorderQueue);
     501              : 
     502         3421 :     result = topmost->htup;
     503         6842 :     for (i = 0; i < node->iss_NumOrderByKeys; i++)
     504              :     {
     505         3421 :         if (!node->iss_OrderByTypByVals[i] && !topmost->orderbynulls[i])
     506            0 :             pfree(DatumGetPointer(topmost->orderbyvals[i]));
     507              :     }
     508         3421 :     pfree(topmost->orderbyvals);
     509         3421 :     pfree(topmost->orderbynulls);
     510         3421 :     pfree(topmost);
     511              : 
     512         3421 :     return result;
     513              : }
     514              : 
     515              : 
     516              : /* ----------------------------------------------------------------
     517              :  *      ExecIndexScan(node)
     518              :  * ----------------------------------------------------------------
     519              :  */
     520              : static TupleTableSlot *
     521      1457742 : ExecIndexScan(PlanState *pstate)
     522              : {
     523      1457742 :     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      1457742 :     if (node->iss_NumRuntimeKeys != 0 && !node->iss_RuntimeKeysReady)
     529       115468 :         ExecReScan((PlanState *) node);
     530              : 
     531      1457742 :     if (node->iss_NumOrderByKeys > 0)
     532        55101 :         return ExecScan(&node->ss,
     533              :                         (ExecScanAccessMtd) IndexNextWithReorder,
     534              :                         (ExecScanRecheckMtd) IndexRecheck);
     535              :     else
     536      1402641 :         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       447898 : 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       447898 :     if (node->iss_NumRuntimeKeys != 0)
     563              :     {
     564       441153 :         ExprContext *econtext = node->iss_RuntimeContext;
     565              : 
     566       441153 :         ResetExprContext(econtext);
     567       441153 :         ExecIndexEvalRuntimeKeys(econtext,
     568              :                                  node->iss_RuntimeKeys,
     569              :                                  node->iss_NumRuntimeKeys);
     570              :     }
     571       447898 :     node->iss_RuntimeKeysReady = true;
     572              : 
     573              :     /* flush the reorder queue */
     574       447898 :     if (node->iss_ReorderQueue)
     575              :     {
     576              :         HeapTuple   tuple;
     577              : 
     578           84 :         while (!pairingheap_is_empty(node->iss_ReorderQueue))
     579              :         {
     580           40 :             tuple = reorderqueue_pop(node);
     581           40 :             heap_freetuple(tuple);
     582              :         }
     583              :     }
     584              : 
     585              :     /* reset index scan */
     586       447898 :     if (node->iss_ScanDesc)
     587       311491 :         index_rescan(node->iss_ScanDesc,
     588              :                      node->iss_ScanKeys, node->iss_NumScanKeys,
     589              :                      node->iss_OrderByKeys, node->iss_NumOrderByKeys);
     590       447898 :     node->iss_ReachedEnd = false;
     591              : 
     592       447898 :     ExecScanReScan(&node->ss);
     593       447898 : }
     594              : 
     595              : 
     596              : /*
     597              :  * ExecIndexEvalRuntimeKeys
     598              :  *      Evaluate any runtime key values, and update the scankeys.
     599              :  */
     600              : void
     601       596098 : 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       596098 :     oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
     609              : 
     610      1210662 :     for (j = 0; j < numRuntimeKeys; j++)
     611              :     {
     612       614564 :         ScanKey     scan_key = runtimeKeys[j].scan_key;
     613       614564 :         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       614564 :         scanvalue = ExecEvalExpr(key_expr,
     635              :                                  econtext,
     636              :                                  &isNull);
     637       614564 :         if (isNull)
     638              :         {
     639         1768 :             scan_key->sk_argument = scanvalue;
     640         1768 :             scan_key->sk_flags |= SK_ISNULL;
     641              :         }
     642              :         else
     643              :         {
     644       612796 :             if (runtimeKeys[j].key_toastable)
     645        46545 :                 scanvalue = PointerGetDatum(PG_DETOAST_DATUM(scanvalue));
     646       612796 :             scan_key->sk_argument = scanvalue;
     647       612796 :             scan_key->sk_flags &= ~SK_ISNULL;
     648              :         }
     649              :     }
     650              : 
     651       596098 :     MemoryContextSwitchTo(oldContext);
     652       596098 : }
     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           17 : ExecIndexEvalArrayKeys(ExprContext *econtext,
     664              :                        IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
     665              : {
     666           17 :     bool        result = true;
     667              :     int         j;
     668              :     MemoryContext oldContext;
     669              : 
     670              :     /* We want to keep the arrays in per-tuple memory */
     671           17 :     oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
     672              : 
     673           34 :     for (j = 0; j < numArrayKeys; j++)
     674              :     {
     675           17 :         ScanKey     scan_key = arrayKeys[j].scan_key;
     676           17 :         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           17 :         arraydatum = ExecEvalExpr(array_expr,
     692              :                                   econtext,
     693              :                                   &isNull);
     694           17 :         if (isNull)
     695              :         {
     696            0 :             result = false;
     697            0 :             break;              /* no point in evaluating more */
     698              :         }
     699           17 :         arrayval = DatumGetArrayTypeP(arraydatum);
     700              :         /* We could cache this data, but not clear it's worth it */
     701           17 :         get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
     702              :                              &elmlen, &elmbyval, &elmalign);
     703           17 :         deconstruct_array(arrayval,
     704              :                           ARR_ELEMTYPE(arrayval),
     705              :                           elmlen, elmbyval, elmalign,
     706              :                           &elem_values, &elem_nulls, &num_elems);
     707           17 :         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           17 :         arrayKeys[j].elem_values = elem_values;
     719           17 :         arrayKeys[j].elem_nulls = elem_nulls;
     720           17 :         arrayKeys[j].num_elems = num_elems;
     721           17 :         scan_key->sk_argument = elem_values[0];
     722           17 :         if (elem_nulls[0])
     723            0 :             scan_key->sk_flags |= SK_ISNULL;
     724              :         else
     725           17 :             scan_key->sk_flags &= ~SK_ISNULL;
     726           17 :         arrayKeys[j].next_elem = 1;
     727              :     }
     728              : 
     729           17 :     MemoryContextSwitchTo(oldContext);
     730              : 
     731           17 :     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        15783 : ExecIndexAdvanceArrayKeys(IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
     743              : {
     744        15783 :     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        15800 :     for (j = numArrayKeys - 1; j >= 0; j--)
     754              :     {
     755           34 :         ScanKey     scan_key = arrayKeys[j].scan_key;
     756           34 :         int         next_elem = arrayKeys[j].next_elem;
     757           34 :         int         num_elems = arrayKeys[j].num_elems;
     758           34 :         Datum      *elem_values = arrayKeys[j].elem_values;
     759           34 :         bool       *elem_nulls = arrayKeys[j].elem_nulls;
     760              : 
     761           34 :         if (next_elem >= num_elems)
     762              :         {
     763           17 :             next_elem = 0;
     764           17 :             found = false;      /* need to advance next array key */
     765              :         }
     766              :         else
     767           17 :             found = true;
     768           34 :         scan_key->sk_argument = elem_values[next_elem];
     769           34 :         if (elem_nulls[next_elem])
     770            0 :             scan_key->sk_flags |= SK_ISNULL;
     771              :         else
     772           34 :             scan_key->sk_flags &= ~SK_ISNULL;
     773           34 :         arrayKeys[j].next_elem = next_elem + 1;
     774           34 :         if (found)
     775           17 :             break;
     776              :     }
     777              : 
     778        15783 :     return found;
     779              : }
     780              : 
     781              : 
     782              : /* ----------------------------------------------------------------
     783              :  *      ExecEndIndexScan
     784              :  * ----------------------------------------------------------------
     785              :  */
     786              : void
     787       203586 : ExecEndIndexScan(IndexScanState *node)
     788              : {
     789              :     Relation    indexRelationDesc;
     790              :     IndexScanDesc indexScanDesc;
     791              : 
     792              :     /*
     793              :      * extract information from the node
     794              :      */
     795       203586 :     indexRelationDesc = node->iss_RelationDesc;
     796       203586 :     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       203586 :     if (node->iss_SharedInfo != NULL && IsParallelWorker())
     804              :     {
     805              :         IndexScanInstrumentation *winstrument;
     806              : 
     807              :         Assert(ParallelWorkerNumber < node->iss_SharedInfo->num_workers);
     808          180 :         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          180 :         winstrument->nsearches += node->iss_Instrument.nsearches;
     817              :     }
     818              : 
     819              :     /*
     820              :      * close the index relation (no-op if we didn't open it)
     821              :      */
     822       203586 :     if (indexScanDesc)
     823       180488 :         index_endscan(indexScanDesc);
     824       203586 :     if (indexRelationDesc)
     825       201370 :         index_close(indexRelationDesc, NoLock);
     826       203586 : }
     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         4040 : ExecIndexMarkPos(IndexScanState *node)
     837              : {
     838         4040 :     EState     *estate = node->ss.ps.state;
     839         4040 :     EPQState   *epqstate = estate->es_epq_active;
     840              : 
     841         4040 :     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            1 :         Index       scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
     853              : 
     854              :         Assert(scanrelid > 0);
     855            1 :         if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
     856            0 :             epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
     857              :         {
     858              :             /* Verify the claim above */
     859            1 :             if (!epqstate->relsubs_done[scanrelid - 1])
     860            0 :                 elog(ERROR, "unexpected ExecIndexMarkPos call in EPQ recheck");
     861            1 :             return;
     862              :         }
     863              :     }
     864              : 
     865         4039 :     index_markpos(node->iss_ScanDesc);
     866              : }
     867              : 
     868              : /* ----------------------------------------------------------------
     869              :  *      ExecIndexRestrPos
     870              :  * ----------------------------------------------------------------
     871              :  */
     872              : void
     873        36012 : ExecIndexRestrPos(IndexScanState *node)
     874              : {
     875        36012 :     EState     *estate = node->ss.ps.state;
     876        36012 :     EPQState   *epqstate = estate->es_epq_active;
     877              : 
     878        36012 :     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        36012 :     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       204087 : 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       204087 :     indexstate = makeNode(IndexScanState);
     919       204087 :     indexstate->ss.ps.plan = (Plan *) node;
     920       204087 :     indexstate->ss.ps.state = estate;
     921       204087 :     indexstate->ss.ps.ExecProcNode = ExecIndexScan;
     922              : 
     923              :     /*
     924              :      * Miscellaneous initialization
     925              :      *
     926              :      * create expression context for node
     927              :      */
     928       204087 :     ExecAssignExprContext(estate, &indexstate->ss.ps);
     929              : 
     930              :     /*
     931              :      * open the scan relation
     932              :      */
     933       204087 :     currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
     934              : 
     935       204087 :     indexstate->ss.ss_currentRelation = currentRelation;
     936       204087 :     indexstate->ss.ss_currentScanDesc = NULL;    /* no heap scan here */
     937              : 
     938              :     /*
     939              :      * get the scan type from the relation descriptor.
     940              :      */
     941       204087 :     ExecInitScanTupleSlot(estate, &indexstate->ss,
     942              :                           RelationGetDescr(currentRelation),
     943              :                           table_slot_callbacks(currentRelation),
     944              :                           TTS_FLAG_OBEYS_NOT_NULL_CONSTRAINTS);
     945              : 
     946              :     /*
     947              :      * Initialize result type and projection.
     948              :      */
     949       204087 :     ExecInitResultTypeTL(&indexstate->ss.ps);
     950       204087 :     ExecAssignScanProjectionInfo(&indexstate->ss);
     951              : 
     952              :     /*
     953              :      * initialize child expressions
     954              :      *
     955              :      * Note: we don't initialize all of the indexqual expression, only the
     956              :      * sub-parts corresponding to runtime keys (see below).  Likewise for
     957              :      * indexorderby, if any.  But the indexqualorig expression is always
     958              :      * initialized even though it will only be used in some uncommon cases ---
     959              :      * would be nice to improve that.  (Problem is that any SubPlans present
     960              :      * in the expression must be found now...)
     961              :      */
     962       204087 :     indexstate->ss.ps.qual =
     963       204087 :         ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
     964       204087 :     indexstate->indexqualorig =
     965       204087 :         ExecInitQual(node->indexqualorig, (PlanState *) indexstate);
     966       204087 :     indexstate->indexorderbyorig =
     967       204087 :         ExecInitExprList(node->indexorderbyorig, (PlanState *) indexstate);
     968              : 
     969              :     /*
     970              :      * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
     971              :      * here.  This allows an index-advisor plugin to EXPLAIN a plan containing
     972              :      * references to nonexistent indexes.
     973              :      */
     974       204087 :     if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
     975         2216 :         return indexstate;
     976              : 
     977              :     /* Open the index relation. */
     978       201871 :     lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
     979       201871 :     indexstate->iss_RelationDesc = index_open(node->indexid, lockmode);
     980              : 
     981              :     /*
     982              :      * Initialize index-specific scan state
     983              :      */
     984       201871 :     indexstate->iss_RuntimeKeysReady = false;
     985       201871 :     indexstate->iss_RuntimeKeys = NULL;
     986       201871 :     indexstate->iss_NumRuntimeKeys = 0;
     987              : 
     988              :     /*
     989              :      * build the index scan keys from the index qualification
     990              :      */
     991       201871 :     ExecIndexBuildScanKeys((PlanState *) indexstate,
     992              :                            indexstate->iss_RelationDesc,
     993              :                            node->indexqual,
     994              :                            false,
     995       201871 :                            &indexstate->iss_ScanKeys,
     996              :                            &indexstate->iss_NumScanKeys,
     997              :                            &indexstate->iss_RuntimeKeys,
     998              :                            &indexstate->iss_NumRuntimeKeys,
     999              :                            NULL,    /* no ArrayKeys */
    1000              :                            NULL);
    1001              : 
    1002              :     /*
    1003              :      * any ORDER BY exprs have to be turned into scankeys in the same way
    1004              :      */
    1005       201871 :     ExecIndexBuildScanKeys((PlanState *) indexstate,
    1006              :                            indexstate->iss_RelationDesc,
    1007              :                            node->indexorderby,
    1008              :                            true,
    1009       201871 :                            &indexstate->iss_OrderByKeys,
    1010              :                            &indexstate->iss_NumOrderByKeys,
    1011              :                            &indexstate->iss_RuntimeKeys,
    1012              :                            &indexstate->iss_NumRuntimeKeys,
    1013              :                            NULL,    /* no ArrayKeys */
    1014              :                            NULL);
    1015              : 
    1016              :     /* Initialize sort support, if we need to re-check ORDER BY exprs */
    1017       201871 :     if (indexstate->iss_NumOrderByKeys > 0)
    1018              :     {
    1019           29 :         int         numOrderByKeys = indexstate->iss_NumOrderByKeys;
    1020              :         int         i;
    1021              :         ListCell   *lco;
    1022              :         ListCell   *lcx;
    1023              : 
    1024              :         /*
    1025              :          * Prepare sort support, and look up the data type for each ORDER BY
    1026              :          * expression.
    1027              :          */
    1028              :         Assert(numOrderByKeys == list_length(node->indexorderbyops));
    1029              :         Assert(numOrderByKeys == list_length(node->indexorderbyorig));
    1030           29 :         indexstate->iss_SortSupport = (SortSupportData *)
    1031           29 :             palloc0(numOrderByKeys * sizeof(SortSupportData));
    1032           29 :         indexstate->iss_OrderByTypByVals = (bool *)
    1033           29 :             palloc(numOrderByKeys * sizeof(bool));
    1034           29 :         indexstate->iss_OrderByTypLens = (int16 *)
    1035           29 :             palloc(numOrderByKeys * sizeof(int16));
    1036           29 :         i = 0;
    1037           58 :         forboth(lco, node->indexorderbyops, lcx, node->indexorderbyorig)
    1038              :         {
    1039           29 :             Oid         orderbyop = lfirst_oid(lco);
    1040           29 :             Node       *orderbyexpr = (Node *) lfirst(lcx);
    1041           29 :             Oid         orderbyType = exprType(orderbyexpr);
    1042           29 :             Oid         orderbyColl = exprCollation(orderbyexpr);
    1043           29 :             SortSupport orderbysort = &indexstate->iss_SortSupport[i];
    1044              : 
    1045              :             /* Initialize sort support */
    1046           29 :             orderbysort->ssup_cxt = CurrentMemoryContext;
    1047           29 :             orderbysort->ssup_collation = orderbyColl;
    1048              :             /* See cmp_orderbyvals() comments on NULLS LAST */
    1049           29 :             orderbysort->ssup_nulls_first = false;
    1050              :             /* ssup_attno is unused here and elsewhere */
    1051           29 :             orderbysort->ssup_attno = 0;
    1052              :             /* No abbreviation */
    1053           29 :             orderbysort->abbreviate = false;
    1054           29 :             PrepareSortSupportFromOrderingOp(orderbyop, orderbysort);
    1055              : 
    1056           29 :             get_typlenbyval(orderbyType,
    1057           29 :                             &indexstate->iss_OrderByTypLens[i],
    1058           29 :                             &indexstate->iss_OrderByTypByVals[i]);
    1059           29 :             i++;
    1060              :         }
    1061              : 
    1062              :         /* allocate arrays to hold the re-calculated distances */
    1063           29 :         indexstate->iss_OrderByValues = (Datum *)
    1064           29 :             palloc(numOrderByKeys * sizeof(Datum));
    1065           29 :         indexstate->iss_OrderByNulls = (bool *)
    1066           29 :             palloc(numOrderByKeys * sizeof(bool));
    1067              : 
    1068              :         /* and initialize the reorder queue */
    1069           29 :         indexstate->iss_ReorderQueue = pairingheap_allocate(reorderqueue_cmp,
    1070              :                                                             indexstate);
    1071              :     }
    1072              : 
    1073              :     /*
    1074              :      * If we have runtime keys, we need an ExprContext to evaluate them. The
    1075              :      * node's standard context won't do because we want to reset that context
    1076              :      * for every tuple.  So, build another context just like the other one...
    1077              :      * -tgl 7/11/00
    1078              :      */
    1079       201871 :     if (indexstate->iss_NumRuntimeKeys != 0)
    1080              :     {
    1081       150033 :         ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
    1082              : 
    1083       150033 :         ExecAssignExprContext(estate, &indexstate->ss.ps);
    1084       150033 :         indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
    1085       150033 :         indexstate->ss.ps.ps_ExprContext = stdecontext;
    1086              :     }
    1087              :     else
    1088              :     {
    1089        51838 :         indexstate->iss_RuntimeContext = NULL;
    1090              :     }
    1091              : 
    1092              :     /*
    1093              :      * all done.
    1094              :      */
    1095       201871 :     return indexstate;
    1096              : }
    1097              : 
    1098              : 
    1099              : /*
    1100              :  * ExecIndexBuildScanKeys
    1101              :  *      Build the index scan keys from the index qualification expressions
    1102              :  *
    1103              :  * The index quals are passed to the index AM in the form of a ScanKey array.
    1104              :  * This routine sets up the ScanKeys, fills in all constant fields of the
    1105              :  * ScanKeys, and prepares information about the keys that have non-constant
    1106              :  * comparison values.  We divide index qual expressions into five types:
    1107              :  *
    1108              :  * 1. Simple operator with constant comparison value ("indexkey op constant").
    1109              :  * For these, we just fill in a ScanKey containing the constant value.
    1110              :  *
    1111              :  * 2. Simple operator with non-constant value ("indexkey op expression").
    1112              :  * For these, we create a ScanKey with everything filled in except the
    1113              :  * expression value, and set up an IndexRuntimeKeyInfo struct to drive
    1114              :  * evaluation of the expression at the right times.
    1115              :  *
    1116              :  * 3. RowCompareExpr ("(indexkey, indexkey, ...) op (expr, expr, ...)").
    1117              :  * For these, we create a header ScanKey plus a subsidiary ScanKey array,
    1118              :  * as specified in access/skey.h.  The elements of the row comparison
    1119              :  * can have either constant or non-constant comparison values.
    1120              :  *
    1121              :  * 4. ScalarArrayOpExpr ("indexkey op ANY (array-expression)").  If the index
    1122              :  * supports amsearcharray, we handle these the same as simple operators,
    1123              :  * setting the SK_SEARCHARRAY flag to tell the AM to handle them.  Otherwise,
    1124              :  * we create a ScanKey with everything filled in except the comparison value,
    1125              :  * and set up an IndexArrayKeyInfo struct to drive processing of the qual.
    1126              :  * (Note that if we use an IndexArrayKeyInfo struct, the array expression is
    1127              :  * always treated as requiring runtime evaluation, even if it's a constant.)
    1128              :  *
    1129              :  * 5. NullTest ("indexkey IS NULL/IS NOT NULL").  We just fill in the
    1130              :  * ScanKey properly.
    1131              :  *
    1132              :  * This code is also used to prepare ORDER BY expressions for amcanorderbyop
    1133              :  * indexes.  The behavior is exactly the same, except that we have to look up
    1134              :  * the operator differently.  Note that only cases 1 and 2 are currently
    1135              :  * possible for ORDER BY.
    1136              :  *
    1137              :  * Input params are:
    1138              :  *
    1139              :  * planstate: executor state node we are working for
    1140              :  * index: the index we are building scan keys for
    1141              :  * quals: indexquals (or indexorderbys) expressions
    1142              :  * isorderby: true if processing ORDER BY exprs, false if processing quals
    1143              :  * *runtimeKeys: ptr to pre-existing IndexRuntimeKeyInfos, or NULL if none
    1144              :  * *numRuntimeKeys: number of pre-existing runtime keys
    1145              :  *
    1146              :  * Output params are:
    1147              :  *
    1148              :  * *scanKeys: receives ptr to array of ScanKeys
    1149              :  * *numScanKeys: receives number of scankeys
    1150              :  * *runtimeKeys: receives ptr to array of IndexRuntimeKeyInfos, or NULL if none
    1151              :  * *numRuntimeKeys: receives number of runtime keys
    1152              :  * *arrayKeys: receives ptr to array of IndexArrayKeyInfos, or NULL if none
    1153              :  * *numArrayKeys: receives number of array keys
    1154              :  *
    1155              :  * Caller may pass NULL for arrayKeys and numArrayKeys to indicate that
    1156              :  * IndexArrayKeyInfos are not supported.
    1157              :  */
    1158              : void
    1159       438954 : ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
    1160              :                        List *quals, bool isorderby,
    1161              :                        ScanKey *scanKeys, int *numScanKeys,
    1162              :                        IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
    1163              :                        IndexArrayKeyInfo **arrayKeys, int *numArrayKeys)
    1164              : {
    1165              :     ListCell   *qual_cell;
    1166              :     ScanKey     scan_keys;
    1167              :     IndexRuntimeKeyInfo *runtime_keys;
    1168              :     IndexArrayKeyInfo *array_keys;
    1169              :     int         n_scan_keys;
    1170              :     int         n_runtime_keys;
    1171              :     int         max_runtime_keys;
    1172              :     int         n_array_keys;
    1173              :     int         j;
    1174              : 
    1175              :     /* Allocate array for ScanKey structs: one per qual */
    1176       438954 :     n_scan_keys = list_length(quals);
    1177       438954 :     scan_keys = (ScanKey) palloc(n_scan_keys * sizeof(ScanKeyData));
    1178              : 
    1179              :     /*
    1180              :      * runtime_keys array is dynamically resized as needed.  We handle it this
    1181              :      * way so that the same runtime keys array can be shared between
    1182              :      * indexquals and indexorderbys, which will be processed in separate calls
    1183              :      * of this function.  Caller must be sure to pass in NULL/0 for first
    1184              :      * call.
    1185              :      */
    1186       438954 :     runtime_keys = *runtimeKeys;
    1187       438954 :     n_runtime_keys = max_runtime_keys = *numRuntimeKeys;
    1188              : 
    1189              :     /* Allocate array_keys as large as it could possibly need to be */
    1190              :     array_keys = (IndexArrayKeyInfo *)
    1191       438954 :         palloc0(n_scan_keys * sizeof(IndexArrayKeyInfo));
    1192       438954 :     n_array_keys = 0;
    1193              : 
    1194              :     /*
    1195              :      * for each opclause in the given qual, convert the opclause into a single
    1196              :      * scan key
    1197              :      */
    1198       438954 :     j = 0;
    1199       686068 :     foreach(qual_cell, quals)
    1200              :     {
    1201       247114 :         Expr       *clause = (Expr *) lfirst(qual_cell);
    1202       247114 :         ScanKey     this_scan_key = &scan_keys[j++];
    1203              :         Oid         opno;       /* operator's OID */
    1204              :         RegProcedure opfuncid;  /* operator proc id used in scan */
    1205              :         Oid         opfamily;   /* opfamily of index column */
    1206              :         int         op_strategy;    /* operator's strategy number */
    1207              :         Oid         op_lefttype;    /* operator's declared input types */
    1208              :         Oid         op_righttype;
    1209              :         Expr       *leftop;     /* expr on lhs of operator */
    1210              :         Expr       *rightop;    /* expr on rhs ... */
    1211              :         AttrNumber  varattno;   /* att number used in scan */
    1212              :         int         indnkeyatts;
    1213              : 
    1214       247114 :         indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
    1215       247114 :         if (IsA(clause, OpExpr))
    1216              :         {
    1217              :             /* indexkey op const or indexkey op expression */
    1218       245534 :             int         flags = 0;
    1219              :             Datum       scanvalue;
    1220              : 
    1221       245534 :             opno = ((OpExpr *) clause)->opno;
    1222       245534 :             opfuncid = ((OpExpr *) clause)->opfuncid;
    1223              : 
    1224              :             /*
    1225              :              * leftop should be the index key Var, possibly relabeled
    1226              :              */
    1227       245534 :             leftop = (Expr *) get_leftop(clause);
    1228              : 
    1229       245534 :             if (leftop && IsA(leftop, RelabelType))
    1230            0 :                 leftop = ((RelabelType *) leftop)->arg;
    1231              : 
    1232              :             Assert(leftop != NULL);
    1233              : 
    1234       245534 :             if (!(IsA(leftop, Var) &&
    1235       245534 :                   ((Var *) leftop)->varno == INDEX_VAR))
    1236            0 :                 elog(ERROR, "indexqual doesn't have key on left side");
    1237              : 
    1238       245534 :             varattno = ((Var *) leftop)->varattno;
    1239       245534 :             if (varattno < 1 || varattno > indnkeyatts)
    1240            0 :                 elog(ERROR, "bogus index qualification");
    1241              : 
    1242              :             /*
    1243              :              * We have to look up the operator's strategy number.  This
    1244              :              * provides a cross-check that the operator does match the index.
    1245              :              */
    1246       245534 :             opfamily = index->rd_opfamily[varattno - 1];
    1247              : 
    1248       245534 :             get_op_opfamily_properties(opno, opfamily, isorderby,
    1249              :                                        &op_strategy,
    1250              :                                        &op_lefttype,
    1251              :                                        &op_righttype);
    1252              : 
    1253       245534 :             if (isorderby)
    1254          123 :                 flags |= SK_ORDER_BY;
    1255              : 
    1256              :             /*
    1257              :              * rightop is the constant or variable comparison value
    1258              :              */
    1259       245534 :             rightop = (Expr *) get_rightop(clause);
    1260              : 
    1261       245534 :             if (rightop && IsA(rightop, RelabelType))
    1262         2465 :                 rightop = ((RelabelType *) rightop)->arg;
    1263              : 
    1264              :             Assert(rightop != NULL);
    1265              : 
    1266       245534 :             if (IsA(rightop, Const))
    1267              :             {
    1268              :                 /* OK, simple constant comparison value */
    1269        84228 :                 scanvalue = ((Const *) rightop)->constvalue;
    1270        84228 :                 if (((Const *) rightop)->constisnull)
    1271            0 :                     flags |= SK_ISNULL;
    1272              :             }
    1273              :             else
    1274              :             {
    1275              :                 /* Need to treat this one as a runtime key */
    1276       161306 :                 if (n_runtime_keys >= max_runtime_keys)
    1277              :                 {
    1278       154238 :                     if (max_runtime_keys == 0)
    1279              :                     {
    1280       154234 :                         max_runtime_keys = 8;
    1281              :                         runtime_keys = (IndexRuntimeKeyInfo *)
    1282       154234 :                             palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
    1283              :                     }
    1284              :                     else
    1285              :                     {
    1286            4 :                         max_runtime_keys *= 2;
    1287              :                         runtime_keys = (IndexRuntimeKeyInfo *)
    1288            4 :                             repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
    1289              :                     }
    1290              :                 }
    1291       161306 :                 runtime_keys[n_runtime_keys].scan_key = this_scan_key;
    1292       322612 :                 runtime_keys[n_runtime_keys].key_expr =
    1293       161306 :                     ExecInitExpr(rightop, planstate);
    1294       161306 :                 runtime_keys[n_runtime_keys].key_toastable =
    1295       161306 :                     TypeIsToastable(op_righttype);
    1296       161306 :                 n_runtime_keys++;
    1297       161306 :                 scanvalue = (Datum) 0;
    1298              :             }
    1299              : 
    1300              :             /*
    1301              :              * initialize the scan key's fields appropriately
    1302              :              */
    1303       245534 :             ScanKeyEntryInitialize(this_scan_key,
    1304              :                                    flags,
    1305              :                                    varattno,    /* attribute number to scan */
    1306              :                                    op_strategy, /* op's strategy */
    1307              :                                    op_righttype,    /* strategy subtype */
    1308              :                                    ((OpExpr *) clause)->inputcollid, /* collation */
    1309              :                                    opfuncid,    /* reg proc to use */
    1310              :                                    scanvalue);  /* constant */
    1311              :         }
    1312         1580 :         else if (IsA(clause, RowCompareExpr))
    1313              :         {
    1314              :             /* (indexkey, indexkey, ...) op (expression, expression, ...) */
    1315           56 :             RowCompareExpr *rc = (RowCompareExpr *) clause;
    1316              :             ScanKey     first_sub_key;
    1317              :             int         n_sub_key;
    1318              :             ListCell   *largs_cell;
    1319              :             ListCell   *rargs_cell;
    1320              :             ListCell   *opnos_cell;
    1321              :             ListCell   *collids_cell;
    1322              : 
    1323              :             Assert(!isorderby);
    1324              : 
    1325              :             first_sub_key = (ScanKey)
    1326           56 :                 palloc(list_length(rc->opnos) * sizeof(ScanKeyData));
    1327           56 :             n_sub_key = 0;
    1328              : 
    1329              :             /* Scan RowCompare columns and generate subsidiary ScanKey items */
    1330          168 :             forfour(largs_cell, rc->largs, rargs_cell, rc->rargs,
    1331              :                     opnos_cell, rc->opnos, collids_cell, rc->inputcollids)
    1332              :             {
    1333          112 :                 ScanKey     this_sub_key = &first_sub_key[n_sub_key];
    1334          112 :                 int         flags = SK_ROW_MEMBER;
    1335              :                 Datum       scanvalue;
    1336              :                 Oid         inputcollation;
    1337              : 
    1338          112 :                 leftop = (Expr *) lfirst(largs_cell);
    1339          112 :                 rightop = (Expr *) lfirst(rargs_cell);
    1340          112 :                 opno = lfirst_oid(opnos_cell);
    1341          112 :                 inputcollation = lfirst_oid(collids_cell);
    1342              : 
    1343              :                 /*
    1344              :                  * leftop should be the index key Var, possibly relabeled
    1345              :                  */
    1346          112 :                 if (leftop && IsA(leftop, RelabelType))
    1347            0 :                     leftop = ((RelabelType *) leftop)->arg;
    1348              : 
    1349              :                 Assert(leftop != NULL);
    1350              : 
    1351          112 :                 if (!(IsA(leftop, Var) &&
    1352          112 :                       ((Var *) leftop)->varno == INDEX_VAR))
    1353            0 :                     elog(ERROR, "indexqual doesn't have key on left side");
    1354              : 
    1355          112 :                 varattno = ((Var *) leftop)->varattno;
    1356              : 
    1357              :                 /*
    1358              :                  * We have to look up the operator's associated support
    1359              :                  * function
    1360              :                  */
    1361          112 :                 if (!index->rd_indam->amcanorder ||
    1362          112 :                     varattno < 1 || varattno > indnkeyatts)
    1363            0 :                     elog(ERROR, "bogus RowCompare index qualification");
    1364          112 :                 opfamily = index->rd_opfamily[varattno - 1];
    1365              : 
    1366          112 :                 get_op_opfamily_properties(opno, opfamily, isorderby,
    1367              :                                            &op_strategy,
    1368              :                                            &op_lefttype,
    1369              :                                            &op_righttype);
    1370              : 
    1371          112 :                 if (op_strategy != rc->cmptype)
    1372            0 :                     elog(ERROR, "RowCompare index qualification contains wrong operator");
    1373              : 
    1374          112 :                 opfuncid = get_opfamily_proc(opfamily,
    1375              :                                              op_lefttype,
    1376              :                                              op_righttype,
    1377              :                                              BTORDER_PROC);
    1378          112 :                 if (!RegProcedureIsValid(opfuncid))
    1379            0 :                     elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
    1380              :                          BTORDER_PROC, op_lefttype, op_righttype, opfamily);
    1381              : 
    1382              :                 /*
    1383              :                  * rightop is the constant or variable comparison value
    1384              :                  */
    1385          112 :                 if (rightop && IsA(rightop, RelabelType))
    1386            0 :                     rightop = ((RelabelType *) rightop)->arg;
    1387              : 
    1388              :                 Assert(rightop != NULL);
    1389              : 
    1390          112 :                 if (IsA(rightop, Const))
    1391              :                 {
    1392              :                     /* OK, simple constant comparison value */
    1393          112 :                     scanvalue = ((Const *) rightop)->constvalue;
    1394          112 :                     if (((Const *) rightop)->constisnull)
    1395           24 :                         flags |= SK_ISNULL;
    1396              :                 }
    1397              :                 else
    1398              :                 {
    1399              :                     /* Need to treat this one as a runtime key */
    1400            0 :                     if (n_runtime_keys >= max_runtime_keys)
    1401              :                     {
    1402            0 :                         if (max_runtime_keys == 0)
    1403              :                         {
    1404            0 :                             max_runtime_keys = 8;
    1405              :                             runtime_keys = (IndexRuntimeKeyInfo *)
    1406            0 :                                 palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
    1407              :                         }
    1408              :                         else
    1409              :                         {
    1410            0 :                             max_runtime_keys *= 2;
    1411              :                             runtime_keys = (IndexRuntimeKeyInfo *)
    1412            0 :                                 repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
    1413              :                         }
    1414              :                     }
    1415            0 :                     runtime_keys[n_runtime_keys].scan_key = this_sub_key;
    1416            0 :                     runtime_keys[n_runtime_keys].key_expr =
    1417            0 :                         ExecInitExpr(rightop, planstate);
    1418            0 :                     runtime_keys[n_runtime_keys].key_toastable =
    1419            0 :                         TypeIsToastable(op_righttype);
    1420            0 :                     n_runtime_keys++;
    1421            0 :                     scanvalue = (Datum) 0;
    1422              :                 }
    1423              : 
    1424              :                 /*
    1425              :                  * initialize the subsidiary scan key's fields appropriately
    1426              :                  */
    1427          112 :                 ScanKeyEntryInitialize(this_sub_key,
    1428              :                                        flags,
    1429              :                                        varattno,    /* attribute number */
    1430              :                                        op_strategy, /* op's strategy */
    1431              :                                        op_righttype,    /* strategy subtype */
    1432              :                                        inputcollation,  /* collation */
    1433              :                                        opfuncid,    /* reg proc to use */
    1434              :                                        scanvalue);  /* constant */
    1435          112 :                 n_sub_key++;
    1436              :             }
    1437              : 
    1438              :             /* Mark the last subsidiary scankey correctly */
    1439           56 :             first_sub_key[n_sub_key - 1].sk_flags |= SK_ROW_END;
    1440              : 
    1441              :             /*
    1442              :              * We don't use ScanKeyEntryInitialize for the header because it
    1443              :              * isn't going to contain a valid sk_func pointer.
    1444              :              */
    1445          560 :             MemSet(this_scan_key, 0, sizeof(ScanKeyData));
    1446           56 :             this_scan_key->sk_flags = SK_ROW_HEADER;
    1447           56 :             this_scan_key->sk_attno = first_sub_key->sk_attno;
    1448           56 :             this_scan_key->sk_strategy = rc->cmptype;
    1449              :             /* sk_subtype, sk_collation, sk_func not used in a header */
    1450           56 :             this_scan_key->sk_argument = PointerGetDatum(first_sub_key);
    1451              :         }
    1452         1524 :         else if (IsA(clause, ScalarArrayOpExpr))
    1453              :         {
    1454              :             /* indexkey op ANY (array-expression) */
    1455         1159 :             ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
    1456         1159 :             int         flags = 0;
    1457              :             Datum       scanvalue;
    1458              : 
    1459              :             Assert(!isorderby);
    1460              : 
    1461              :             Assert(saop->useOr);
    1462         1159 :             opno = saop->opno;
    1463         1159 :             opfuncid = saop->opfuncid;
    1464              : 
    1465              :             /*
    1466              :              * leftop should be the index key Var, possibly relabeled
    1467              :              */
    1468         1159 :             leftop = (Expr *) linitial(saop->args);
    1469              : 
    1470         1159 :             if (leftop && IsA(leftop, RelabelType))
    1471            0 :                 leftop = ((RelabelType *) leftop)->arg;
    1472              : 
    1473              :             Assert(leftop != NULL);
    1474              : 
    1475         1159 :             if (!(IsA(leftop, Var) &&
    1476         1159 :                   ((Var *) leftop)->varno == INDEX_VAR))
    1477            0 :                 elog(ERROR, "indexqual doesn't have key on left side");
    1478              : 
    1479         1159 :             varattno = ((Var *) leftop)->varattno;
    1480         1159 :             if (varattno < 1 || varattno > indnkeyatts)
    1481            0 :                 elog(ERROR, "bogus index qualification");
    1482              : 
    1483              :             /*
    1484              :              * We have to look up the operator's strategy number.  This
    1485              :              * provides a cross-check that the operator does match the index.
    1486              :              */
    1487         1159 :             opfamily = index->rd_opfamily[varattno - 1];
    1488              : 
    1489         1159 :             get_op_opfamily_properties(opno, opfamily, isorderby,
    1490              :                                        &op_strategy,
    1491              :                                        &op_lefttype,
    1492              :                                        &op_righttype);
    1493              : 
    1494              :             /*
    1495              :              * rightop is the constant or variable array value
    1496              :              */
    1497         1159 :             rightop = (Expr *) lsecond(saop->args);
    1498              : 
    1499         1159 :             if (rightop && IsA(rightop, RelabelType))
    1500            0 :                 rightop = ((RelabelType *) rightop)->arg;
    1501              : 
    1502              :             Assert(rightop != NULL);
    1503              : 
    1504         1159 :             if (index->rd_indam->amsearcharray)
    1505              :             {
    1506              :                 /* Index AM will handle this like a simple operator */
    1507         1142 :                 flags |= SK_SEARCHARRAY;
    1508         1142 :                 if (IsA(rightop, Const))
    1509              :                 {
    1510              :                     /* OK, simple constant comparison value */
    1511         1102 :                     scanvalue = ((Const *) rightop)->constvalue;
    1512         1102 :                     if (((Const *) rightop)->constisnull)
    1513            4 :                         flags |= SK_ISNULL;
    1514              :                 }
    1515              :                 else
    1516              :                 {
    1517              :                     /* Need to treat this one as a runtime key */
    1518           40 :                     if (n_runtime_keys >= max_runtime_keys)
    1519              :                     {
    1520           40 :                         if (max_runtime_keys == 0)
    1521              :                         {
    1522           40 :                             max_runtime_keys = 8;
    1523              :                             runtime_keys = (IndexRuntimeKeyInfo *)
    1524           40 :                                 palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
    1525              :                         }
    1526              :                         else
    1527              :                         {
    1528            0 :                             max_runtime_keys *= 2;
    1529              :                             runtime_keys = (IndexRuntimeKeyInfo *)
    1530            0 :                                 repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo));
    1531              :                         }
    1532              :                     }
    1533           40 :                     runtime_keys[n_runtime_keys].scan_key = this_scan_key;
    1534           80 :                     runtime_keys[n_runtime_keys].key_expr =
    1535           40 :                         ExecInitExpr(rightop, planstate);
    1536              : 
    1537              :                     /*
    1538              :                      * Careful here: the runtime expression is not of
    1539              :                      * op_righttype, but rather is an array of same; so
    1540              :                      * TypeIsToastable() isn't helpful.  However, we can
    1541              :                      * assume that all array types are toastable.
    1542              :                      */
    1543           40 :                     runtime_keys[n_runtime_keys].key_toastable = true;
    1544           40 :                     n_runtime_keys++;
    1545           40 :                     scanvalue = (Datum) 0;
    1546              :                 }
    1547              :             }
    1548              :             else
    1549              :             {
    1550              :                 /* Executor has to expand the array value */
    1551           17 :                 array_keys[n_array_keys].scan_key = this_scan_key;
    1552           34 :                 array_keys[n_array_keys].array_expr =
    1553           17 :                     ExecInitExpr(rightop, planstate);
    1554              :                 /* the remaining fields were zeroed by palloc0 */
    1555           17 :                 n_array_keys++;
    1556           17 :                 scanvalue = (Datum) 0;
    1557              :             }
    1558              : 
    1559              :             /*
    1560              :              * initialize the scan key's fields appropriately
    1561              :              */
    1562         1159 :             ScanKeyEntryInitialize(this_scan_key,
    1563              :                                    flags,
    1564              :                                    varattno,    /* attribute number to scan */
    1565              :                                    op_strategy, /* op's strategy */
    1566              :                                    op_righttype,    /* strategy subtype */
    1567              :                                    saop->inputcollid,    /* collation */
    1568              :                                    opfuncid,    /* reg proc to use */
    1569              :                                    scanvalue);  /* constant */
    1570              :         }
    1571          365 :         else if (IsA(clause, NullTest))
    1572              :         {
    1573              :             /* indexkey IS NULL or indexkey IS NOT NULL */
    1574          365 :             NullTest   *ntest = (NullTest *) clause;
    1575              :             int         flags;
    1576              : 
    1577              :             Assert(!isorderby);
    1578              : 
    1579              :             /*
    1580              :              * argument should be the index key Var, possibly relabeled
    1581              :              */
    1582          365 :             leftop = ntest->arg;
    1583              : 
    1584          365 :             if (leftop && IsA(leftop, RelabelType))
    1585            0 :                 leftop = ((RelabelType *) leftop)->arg;
    1586              : 
    1587              :             Assert(leftop != NULL);
    1588              : 
    1589          365 :             if (!(IsA(leftop, Var) &&
    1590          365 :                   ((Var *) leftop)->varno == INDEX_VAR))
    1591            0 :                 elog(ERROR, "NullTest indexqual has wrong key");
    1592              : 
    1593          365 :             varattno = ((Var *) leftop)->varattno;
    1594              : 
    1595              :             /*
    1596              :              * initialize the scan key's fields appropriately
    1597              :              */
    1598          365 :             switch (ntest->nulltesttype)
    1599              :             {
    1600          131 :                 case IS_NULL:
    1601          131 :                     flags = SK_ISNULL | SK_SEARCHNULL;
    1602          131 :                     break;
    1603          234 :                 case IS_NOT_NULL:
    1604          234 :                     flags = SK_ISNULL | SK_SEARCHNOTNULL;
    1605          234 :                     break;
    1606            0 :                 default:
    1607            0 :                     elog(ERROR, "unrecognized nulltesttype: %d",
    1608              :                          (int) ntest->nulltesttype);
    1609              :                     flags = 0;  /* keep compiler quiet */
    1610              :                     break;
    1611              :             }
    1612              : 
    1613          365 :             ScanKeyEntryInitialize(this_scan_key,
    1614              :                                    flags,
    1615              :                                    varattno,    /* attribute number to scan */
    1616              :                                    InvalidStrategy, /* no strategy */
    1617              :                                    InvalidOid,  /* no strategy subtype */
    1618              :                                    InvalidOid,  /* no collation */
    1619              :                                    InvalidOid,  /* no reg proc for this */
    1620              :                                    (Datum) 0);  /* constant */
    1621              :         }
    1622              :         else
    1623            0 :             elog(ERROR, "unsupported indexqual type: %d",
    1624              :                  (int) nodeTag(clause));
    1625              :     }
    1626              : 
    1627              :     Assert(n_runtime_keys <= max_runtime_keys);
    1628              : 
    1629              :     /* Get rid of any unused arrays */
    1630       438954 :     if (n_array_keys == 0)
    1631              :     {
    1632       438937 :         pfree(array_keys);
    1633       438937 :         array_keys = NULL;
    1634              :     }
    1635              : 
    1636              :     /*
    1637              :      * Return info to our caller.
    1638              :      */
    1639       438954 :     *scanKeys = scan_keys;
    1640       438954 :     *numScanKeys = n_scan_keys;
    1641       438954 :     *runtimeKeys = runtime_keys;
    1642       438954 :     *numRuntimeKeys = n_runtime_keys;
    1643       438954 :     if (arrayKeys)
    1644              :     {
    1645        14240 :         *arrayKeys = array_keys;
    1646        14240 :         *numArrayKeys = n_array_keys;
    1647              :     }
    1648       424714 :     else if (n_array_keys != 0)
    1649            0 :         elog(ERROR, "ScalarArrayOpExpr index qual found where not allowed");
    1650       438954 : }
    1651              : 
    1652              : /* ----------------------------------------------------------------
    1653              :  *                      Parallel Scan Support
    1654              :  * ----------------------------------------------------------------
    1655              :  */
    1656              : 
    1657              : /* ----------------------------------------------------------------
    1658              :  *      ExecIndexScanEstimate
    1659              :  *
    1660              :  *      Compute the amount of space we'll need in the parallel
    1661              :  *      query DSM, and inform pcxt->estimator about our needs.
    1662              :  * ----------------------------------------------------------------
    1663              :  */
    1664              : void
    1665          276 : ExecIndexScanEstimate(IndexScanState *node,
    1666              :                       ParallelContext *pcxt)
    1667              : {
    1668          276 :     EState     *estate = node->ss.ps.state;
    1669          276 :     bool        instrument = node->ss.ps.instrument != NULL;
    1670          276 :     bool        parallel_aware = node->ss.ps.plan->parallel_aware;
    1671              : 
    1672          276 :     if (!instrument && !parallel_aware)
    1673              :     {
    1674              :         /* No DSM required by the scan */
    1675           84 :         return;
    1676              :     }
    1677              : 
    1678          192 :     node->iss_PscanLen = index_parallelscan_estimate(node->iss_RelationDesc,
    1679              :                                                      node->iss_NumScanKeys,
    1680              :                                                      node->iss_NumOrderByKeys,
    1681              :                                                      estate->es_snapshot,
    1682              :                                                      instrument, parallel_aware,
    1683              :                                                      pcxt->nworkers);
    1684          192 :     shm_toc_estimate_chunk(&pcxt->estimator, node->iss_PscanLen);
    1685          192 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
    1686              : }
    1687              : 
    1688              : /* ----------------------------------------------------------------
    1689              :  *      ExecIndexScanInitializeDSM
    1690              :  *
    1691              :  *      Set up a parallel index scan descriptor.
    1692              :  * ----------------------------------------------------------------
    1693              :  */
    1694              : void
    1695          276 : ExecIndexScanInitializeDSM(IndexScanState *node,
    1696              :                            ParallelContext *pcxt)
    1697              : {
    1698          276 :     EState     *estate = node->ss.ps.state;
    1699              :     ParallelIndexScanDesc piscan;
    1700          276 :     bool        instrument = node->ss.ps.instrument != NULL;
    1701          276 :     bool        parallel_aware = node->ss.ps.plan->parallel_aware;
    1702              : 
    1703          276 :     if (!instrument && !parallel_aware)
    1704              :     {
    1705              :         /* No DSM required by the scan */
    1706           84 :         return;
    1707              :     }
    1708              : 
    1709          192 :     piscan = shm_toc_allocate(pcxt->toc, node->iss_PscanLen);
    1710          192 :     index_parallelscan_initialize(node->ss.ss_currentRelation,
    1711              :                                   node->iss_RelationDesc,
    1712              :                                   estate->es_snapshot,
    1713              :                                   instrument, parallel_aware, pcxt->nworkers,
    1714              :                                   &node->iss_SharedInfo, piscan);
    1715          192 :     shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
    1716              : 
    1717          192 :     if (!parallel_aware)
    1718              :     {
    1719              :         /* Only here to initialize SharedInfo in DSM */
    1720          180 :         return;
    1721              :     }
    1722              : 
    1723           12 :     node->iss_ScanDesc =
    1724           12 :         index_beginscan_parallel(node->ss.ss_currentRelation,
    1725              :                                  node->iss_RelationDesc,
    1726              :                                  &node->iss_Instrument,
    1727              :                                  node->iss_NumScanKeys,
    1728              :                                  node->iss_NumOrderByKeys,
    1729              :                                  piscan);
    1730              : 
    1731              :     /*
    1732              :      * If no run-time keys to calculate or they are ready, go ahead and pass
    1733              :      * the scankeys to the index AM.
    1734              :      */
    1735           12 :     if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
    1736            8 :         index_rescan(node->iss_ScanDesc,
    1737              :                      node->iss_ScanKeys, node->iss_NumScanKeys,
    1738              :                      node->iss_OrderByKeys, node->iss_NumOrderByKeys);
    1739              : }
    1740              : 
    1741              : /* ----------------------------------------------------------------
    1742              :  *      ExecIndexScanReInitializeDSM
    1743              :  *
    1744              :  *      Reset shared state before beginning a fresh scan.
    1745              :  * ----------------------------------------------------------------
    1746              :  */
    1747              : void
    1748            8 : ExecIndexScanReInitializeDSM(IndexScanState *node,
    1749              :                              ParallelContext *pcxt)
    1750              : {
    1751              :     Assert(node->ss.ps.plan->parallel_aware);
    1752            8 :     index_parallelrescan(node->iss_ScanDesc);
    1753            8 : }
    1754              : 
    1755              : /* ----------------------------------------------------------------
    1756              :  *      ExecIndexScanInitializeWorker
    1757              :  *
    1758              :  *      Copy relevant information from TOC into planstate.
    1759              :  * ----------------------------------------------------------------
    1760              :  */
    1761              : void
    1762          424 : ExecIndexScanInitializeWorker(IndexScanState *node,
    1763              :                               ParallelWorkerContext *pwcxt)
    1764              : {
    1765              :     ParallelIndexScanDesc piscan;
    1766          424 :     bool        instrument = node->ss.ps.instrument != NULL;
    1767          424 :     bool        parallel_aware = node->ss.ps.plan->parallel_aware;
    1768              : 
    1769          424 :     if (!instrument && !parallel_aware)
    1770              :     {
    1771              :         /* No DSM required by the scan */
    1772          164 :         return;
    1773              :     }
    1774              : 
    1775          260 :     piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
    1776              : 
    1777          260 :     if (instrument)
    1778          180 :         node->iss_SharedInfo = (SharedIndexScanInstrumentation *)
    1779          180 :             OffsetToPointer(piscan, piscan->ps_offset_ins);
    1780              : 
    1781          260 :     if (!parallel_aware)
    1782              :     {
    1783              :         /* Only here to set up worker node's SharedInfo */
    1784          180 :         return;
    1785              :     }
    1786              : 
    1787           80 :     node->iss_ScanDesc =
    1788           80 :         index_beginscan_parallel(node->ss.ss_currentRelation,
    1789              :                                  node->iss_RelationDesc,
    1790              :                                  &node->iss_Instrument,
    1791              :                                  node->iss_NumScanKeys,
    1792              :                                  node->iss_NumOrderByKeys,
    1793              :                                  piscan);
    1794              : 
    1795              :     /*
    1796              :      * If no run-time keys to calculate or they are ready, go ahead and pass
    1797              :      * the scankeys to the index AM.
    1798              :      */
    1799           80 :     if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
    1800           64 :         index_rescan(node->iss_ScanDesc,
    1801              :                      node->iss_ScanKeys, node->iss_NumScanKeys,
    1802              :                      node->iss_OrderByKeys, node->iss_NumOrderByKeys);
    1803              : }
    1804              : 
    1805              : /* ----------------------------------------------------------------
    1806              :  * ExecIndexScanRetrieveInstrumentation
    1807              :  *
    1808              :  *      Transfer index scan statistics from DSM to private memory.
    1809              :  * ----------------------------------------------------------------
    1810              :  */
    1811              : void
    1812          180 : ExecIndexScanRetrieveInstrumentation(IndexScanState *node)
    1813              : {
    1814          180 :     SharedIndexScanInstrumentation *SharedInfo = node->iss_SharedInfo;
    1815              :     size_t      size;
    1816              : 
    1817          180 :     if (SharedInfo == NULL)
    1818            0 :         return;
    1819              : 
    1820              :     /* Create a copy of SharedInfo in backend-local memory */
    1821          180 :     size = offsetof(SharedIndexScanInstrumentation, winstrument) +
    1822          180 :         SharedInfo->num_workers * sizeof(IndexScanInstrumentation);
    1823          180 :     node->iss_SharedInfo = palloc(size);
    1824          180 :     memcpy(node->iss_SharedInfo, SharedInfo, size);
    1825              : }
        

Generated by: LCOV version 2.0-1