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

Generated by: LCOV version 1.11