LCOV - code coverage report
Current view: top level - src/backend/executor - nodeIndexscan.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 90.6 % 810 734
Test Date: 2026-01-26 10:56:24 Functions: 95.7 % 23 22
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 59.6 % 451 269

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

Generated by: LCOV version 2.3.2-1