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

Generated by: LCOV version 2.0-1