LCOV - code coverage report
Current view: top level - src/backend/executor - nodeIndexscan.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 89.9 % 552 496
Test Date: 2026-02-28 23:15:01 Functions: 100.0 % 23 23
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1