LCOV - code coverage report
Current view: top level - src/backend/executor - nodeIndexscan.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 90.1 % 567 511
Test Date: 2026-07-26 02:15:44 Functions: 100.0 % 26 26
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 65.9 % 375 247

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

Generated by: LCOV version 2.0-1