LCOV - code coverage report
Current view: top level - src/backend/access/gist - gistget.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 206 270 76.3 %
Date: 2017-09-29 15:12:54 Functions: 7 8 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * gistget.c
       4             :  *    fetch tuples from a GiST scan.
       5             :  *
       6             :  *
       7             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/access/gist/gistget.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/gist_private.h"
      18             : #include "access/relscan.h"
      19             : #include "catalog/pg_type.h"
      20             : #include "miscadmin.h"
      21             : #include "pgstat.h"
      22             : #include "lib/pairingheap.h"
      23             : #include "utils/builtins.h"
      24             : #include "utils/memutils.h"
      25             : #include "utils/rel.h"
      26             : 
      27             : /*
      28             :  * gistkillitems() -- set LP_DEAD state for items an indexscan caller has
      29             :  * told us were killed.
      30             :  *
      31             :  * We re-read page here, so it's important to check page LSN. If the page
      32             :  * has been modified since the last read (as determined by LSN), we cannot
      33             :  * flag any entries because it is possible that the old entry was vacuumed
      34             :  * away and the TID was re-used by a completely different heap tuple.
      35             :  */
      36             : static void
      37           0 : gistkillitems(IndexScanDesc scan)
      38             : {
      39           0 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
      40             :     Buffer      buffer;
      41             :     Page        page;
      42             :     OffsetNumber offnum;
      43             :     ItemId      iid;
      44             :     int         i;
      45           0 :     bool        killedsomething = false;
      46             : 
      47           0 :     Assert(so->curBlkno != InvalidBlockNumber);
      48           0 :     Assert(!XLogRecPtrIsInvalid(so->curPageLSN));
      49           0 :     Assert(so->killedItems != NULL);
      50             : 
      51           0 :     buffer = ReadBuffer(scan->indexRelation, so->curBlkno);
      52           0 :     if (!BufferIsValid(buffer))
      53           0 :         return;
      54             : 
      55           0 :     LockBuffer(buffer, GIST_SHARE);
      56           0 :     gistcheckpage(scan->indexRelation, buffer);
      57           0 :     page = BufferGetPage(buffer);
      58             : 
      59             :     /*
      60             :      * If page LSN differs it means that the page was modified since the last
      61             :      * read. killedItems could be not valid so LP_DEAD hints applying is not
      62             :      * safe.
      63             :      */
      64           0 :     if (PageGetLSN(page) != so->curPageLSN)
      65             :     {
      66           0 :         UnlockReleaseBuffer(buffer);
      67           0 :         so->numKilled = 0;       /* reset counter */
      68           0 :         return;
      69             :     }
      70             : 
      71           0 :     Assert(GistPageIsLeaf(page));
      72             : 
      73             :     /*
      74             :      * Mark all killedItems as dead. We need no additional recheck, because,
      75             :      * if page was modified, pageLSN must have changed.
      76             :      */
      77           0 :     for (i = 0; i < so->numKilled; i++)
      78             :     {
      79           0 :         offnum = so->killedItems[i];
      80           0 :         iid = PageGetItemId(page, offnum);
      81           0 :         ItemIdMarkDead(iid);
      82           0 :         killedsomething = true;
      83             :     }
      84             : 
      85           0 :     if (killedsomething)
      86             :     {
      87           0 :         GistMarkPageHasGarbage(page);
      88           0 :         MarkBufferDirtyHint(buffer, true);
      89             :     }
      90             : 
      91           0 :     UnlockReleaseBuffer(buffer);
      92             : 
      93             :     /*
      94             :      * Always reset the scan state, so we don't look for same items on other
      95             :      * pages.
      96             :      */
      97           0 :     so->numKilled = 0;
      98             : }
      99             : 
     100             : /*
     101             :  * gistindex_keytest() -- does this index tuple satisfy the scan key(s)?
     102             :  *
     103             :  * The index tuple might represent either a heap tuple or a lower index page,
     104             :  * depending on whether the containing page is a leaf page or not.
     105             :  *
     106             :  * On success return for a heap tuple, *recheck_p is set to indicate whether
     107             :  * the quals need to be rechecked.  We recheck if any of the consistent()
     108             :  * functions request it.  recheck is not interesting when examining a non-leaf
     109             :  * entry, since we must visit the lower index page if there's any doubt.
     110             :  * Similarly, *recheck_distances_p is set to indicate whether the distances
     111             :  * need to be rechecked, and it is also ignored for non-leaf entries.
     112             :  *
     113             :  * If we are doing an ordered scan, so->distances[] is filled with distance
     114             :  * data from the distance() functions before returning success.
     115             :  *
     116             :  * We must decompress the key in the IndexTuple before passing it to the
     117             :  * sk_funcs (which actually are the opclass Consistent or Distance methods).
     118             :  *
     119             :  * Note that this function is always invoked in a short-lived memory context,
     120             :  * so we don't need to worry about cleaning up allocated memory, either here
     121             :  * or in the implementation of any Consistent or Distance methods.
     122             :  */
     123             : static bool
     124       75629 : gistindex_keytest(IndexScanDesc scan,
     125             :                   IndexTuple tuple,
     126             :                   Page page,
     127             :                   OffsetNumber offset,
     128             :                   bool *recheck_p,
     129             :                   bool *recheck_distances_p)
     130             : {
     131       75629 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     132       75629 :     GISTSTATE  *giststate = so->giststate;
     133       75629 :     ScanKey     key = scan->keyData;
     134       75629 :     int         keySize = scan->numberOfKeys;
     135             :     double     *distance_p;
     136       75629 :     Relation    r = scan->indexRelation;
     137             : 
     138       75629 :     *recheck_p = false;
     139       75629 :     *recheck_distances_p = false;
     140             : 
     141             :     /*
     142             :      * If it's a leftover invalid tuple from pre-9.1, treat it as a match with
     143             :      * minimum possible distances.  This means we'll always follow it to the
     144             :      * referenced page.
     145             :      */
     146       75629 :     if (GistTupleIsInvalid(tuple))
     147             :     {
     148             :         int         i;
     149             : 
     150           0 :         if (GistPageIsLeaf(page))   /* shouldn't happen */
     151           0 :             elog(ERROR, "invalid GiST tuple found on leaf page");
     152           0 :         for (i = 0; i < scan->numberOfOrderBys; i++)
     153           0 :             so->distances[i] = -get_float8_infinity();
     154           0 :         return true;
     155             :     }
     156             : 
     157             :     /* Check whether it matches according to the Consistent functions */
     158      191202 :     while (keySize > 0)
     159             :     {
     160             :         Datum       datum;
     161             :         bool        isNull;
     162             : 
     163       75346 :         datum = index_getattr(tuple,
     164             :                               key->sk_attno,
     165             :                               giststate->tupdesc,
     166             :                               &isNull);
     167             : 
     168       75346 :         if (key->sk_flags & SK_ISNULL)
     169             :         {
     170             :             /*
     171             :              * On non-leaf page we can't conclude that child hasn't NULL
     172             :              * values because of assumption in GiST: union (VAL, NULL) is VAL.
     173             :              * But if on non-leaf page key IS NULL, then all children are
     174             :              * NULL.
     175             :              */
     176        6826 :             if (key->sk_flags & SK_SEARCHNULL)
     177             :             {
     178        6819 :                 if (GistPageIsLeaf(page) && !isNull)
     179       41608 :                     return false;
     180             :             }
     181             :             else
     182             :             {
     183           7 :                 Assert(key->sk_flags & SK_SEARCHNOTNULL);
     184           7 :                 if (isNull)
     185           1 :                     return false;
     186             :             }
     187             :         }
     188       68520 :         else if (isNull)
     189             :         {
     190          17 :             return false;
     191             :         }
     192             :         else
     193             :         {
     194             :             Datum       test;
     195             :             bool        recheck;
     196             :             GISTENTRY   de;
     197             : 
     198       68503 :             gistdentryinit(giststate, key->sk_attno - 1, &de,
     199             :                            datum, r, page, offset,
     200             :                            FALSE, isNull);
     201             : 
     202             :             /*
     203             :              * Call the Consistent function to evaluate the test.  The
     204             :              * arguments are the index datum (as a GISTENTRY*), the comparison
     205             :              * datum, the comparison operator's strategy number and subtype
     206             :              * from pg_amop, and the recheck flag.
     207             :              *
     208             :              * (Presently there's no need to pass the subtype since it'll
     209             :              * always be zero, but might as well pass it for possible future
     210             :              * use.)
     211             :              *
     212             :              * We initialize the recheck flag to true (the safest assumption)
     213             :              * in case the Consistent function forgets to set it.
     214             :              */
     215       68503 :             recheck = true;
     216             : 
     217      137006 :             test = FunctionCall5Coll(&key->sk_func,
     218             :                                      key->sk_collation,
     219             :                                      PointerGetDatum(&de),
     220             :                                      key->sk_argument,
     221       68503 :                                      Int16GetDatum(key->sk_strategy),
     222             :                                      ObjectIdGetDatum(key->sk_subtype),
     223             :                                      PointerGetDatum(&recheck));
     224             : 
     225       68503 :             if (!DatumGetBool(test))
     226       29178 :                 return false;
     227       39325 :             *recheck_p |= recheck;
     228             :         }
     229             : 
     230       39944 :         key++;
     231       39944 :         keySize--;
     232             :     }
     233             : 
     234             :     /* OK, it passes --- now let's compute the distances */
     235       40227 :     key = scan->orderByData;
     236       40227 :     distance_p = so->distances;
     237       40227 :     keySize = scan->numberOfOrderBys;
     238       80833 :     while (keySize > 0)
     239             :     {
     240             :         Datum       datum;
     241             :         bool        isNull;
     242             : 
     243         379 :         datum = index_getattr(tuple,
     244             :                               key->sk_attno,
     245             :                               giststate->tupdesc,
     246             :                               &isNull);
     247             : 
     248         379 :         if ((key->sk_flags & SK_ISNULL) || isNull)
     249             :         {
     250             :             /* Assume distance computes as null and sorts to the end */
     251           3 :             *distance_p = get_float8_infinity();
     252             :         }
     253             :         else
     254             :         {
     255             :             Datum       dist;
     256             :             bool        recheck;
     257             :             GISTENTRY   de;
     258             : 
     259         376 :             gistdentryinit(giststate, key->sk_attno - 1, &de,
     260             :                            datum, r, page, offset,
     261             :                            FALSE, isNull);
     262             : 
     263             :             /*
     264             :              * Call the Distance function to evaluate the distance.  The
     265             :              * arguments are the index datum (as a GISTENTRY*), the comparison
     266             :              * datum, the ordering operator's strategy number and subtype from
     267             :              * pg_amop, and the recheck flag.
     268             :              *
     269             :              * (Presently there's no need to pass the subtype since it'll
     270             :              * always be zero, but might as well pass it for possible future
     271             :              * use.)
     272             :              *
     273             :              * If the function sets the recheck flag, the returned distance is
     274             :              * a lower bound on the true distance and needs to be rechecked.
     275             :              * We initialize the flag to 'false'.  This flag was added in
     276             :              * version 9.5; distance functions written before that won't know
     277             :              * about the flag, but are expected to never be lossy.
     278             :              */
     279         376 :             recheck = false;
     280         752 :             dist = FunctionCall5Coll(&key->sk_func,
     281             :                                      key->sk_collation,
     282             :                                      PointerGetDatum(&de),
     283             :                                      key->sk_argument,
     284         376 :                                      Int16GetDatum(key->sk_strategy),
     285             :                                      ObjectIdGetDatum(key->sk_subtype),
     286             :                                      PointerGetDatum(&recheck));
     287         376 :             *recheck_distances_p |= recheck;
     288         376 :             *distance_p = DatumGetFloat8(dist);
     289             :         }
     290             : 
     291         379 :         key++;
     292         379 :         distance_p++;
     293         379 :         keySize--;
     294             :     }
     295             : 
     296       40227 :     return true;
     297             : }
     298             : 
     299             : /*
     300             :  * Scan all items on the GiST index page identified by *pageItem, and insert
     301             :  * them into the queue (or directly to output areas)
     302             :  *
     303             :  * scan: index scan we are executing
     304             :  * pageItem: search queue item identifying an index page to scan
     305             :  * myDistances: distances array associated with pageItem, or NULL at the root
     306             :  * tbm: if not NULL, gistgetbitmap's output bitmap
     307             :  * ntids: if not NULL, gistgetbitmap's output tuple counter
     308             :  *
     309             :  * If tbm/ntids aren't NULL, we are doing an amgetbitmap scan, and heap
     310             :  * tuples should be reported directly into the bitmap.  If they are NULL,
     311             :  * we're doing a plain or ordered indexscan.  For a plain indexscan, heap
     312             :  * tuple TIDs are returned into so->pageData[].  For an ordered indexscan,
     313             :  * heap tuple TIDs are pushed into individual search queue items.  In an
     314             :  * index-only scan, reconstructed index tuples are returned along with the
     315             :  * TIDs.
     316             :  *
     317             :  * If we detect that the index page has split since we saw its downlink
     318             :  * in the parent, we push its new right sibling onto the queue so the
     319             :  * sibling will be processed next.
     320             :  */
     321             : static void
     322         837 : gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
     323             :              TIDBitmap *tbm, int64 *ntids)
     324             : {
     325         837 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     326         837 :     GISTSTATE  *giststate = so->giststate;
     327         837 :     Relation    r = scan->indexRelation;
     328             :     Buffer      buffer;
     329             :     Page        page;
     330             :     GISTPageOpaque opaque;
     331             :     OffsetNumber maxoff;
     332             :     OffsetNumber i;
     333             :     MemoryContext oldcxt;
     334             : 
     335         837 :     Assert(!GISTSearchItemIsHeap(*pageItem));
     336             : 
     337         837 :     buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
     338         837 :     LockBuffer(buffer, GIST_SHARE);
     339         837 :     gistcheckpage(scan->indexRelation, buffer);
     340         837 :     page = BufferGetPage(buffer);
     341         837 :     TestForOldSnapshot(scan->xs_snapshot, r, page);
     342         837 :     opaque = GistPageGetOpaque(page);
     343             : 
     344             :     /*
     345             :      * Check if we need to follow the rightlink. We need to follow it if the
     346             :      * page was concurrently split since we visited the parent (in which case
     347             :      * parentlsn < nsn), or if the system crashed after a page split but
     348             :      * before the downlink was inserted into the parent.
     349             :      */
     350        1561 :     if (!XLogRecPtrIsInvalid(pageItem->data.parentlsn) &&
     351        1448 :         (GistFollowRight(page) ||
     352         724 :          pageItem->data.parentlsn < GistPageGetNSN(page)) &&
     353           0 :         opaque->rightlink != InvalidBlockNumber /* sanity check */ )
     354             :     {
     355             :         /* There was a page split, follow right link to add pages */
     356             :         GISTSearchItem *item;
     357             : 
     358             :         /* This can't happen when starting at the root */
     359           0 :         Assert(myDistances != NULL);
     360             : 
     361           0 :         oldcxt = MemoryContextSwitchTo(so->queueCxt);
     362             : 
     363             :         /* Create new GISTSearchItem for the right sibling index page */
     364           0 :         item = palloc(SizeOfGISTSearchItem(scan->numberOfOrderBys));
     365           0 :         item->blkno = opaque->rightlink;
     366           0 :         item->data.parentlsn = pageItem->data.parentlsn;
     367             : 
     368             :         /* Insert it into the queue using same distances as for this page */
     369           0 :         memcpy(item->distances, myDistances,
     370           0 :                sizeof(double) * scan->numberOfOrderBys);
     371             : 
     372           0 :         pairingheap_add(so->queue, &item->phNode);
     373             : 
     374           0 :         MemoryContextSwitchTo(oldcxt);
     375             :     }
     376             : 
     377         837 :     so->nPageData = so->curPageData = 0;
     378         837 :     scan->xs_hitup = NULL;       /* might point into pageDataCxt */
     379         837 :     if (so->pageDataCxt)
     380         409 :         MemoryContextReset(so->pageDataCxt);
     381             : 
     382             :     /*
     383             :      * We save the LSN of the page as we read it, so that we know whether it
     384             :      * safe to apply LP_DEAD hints to the page later. This allows us to drop
     385             :      * the pin for MVCC scans, which allows vacuum to avoid blocking.
     386             :      */
     387         837 :     so->curPageLSN = PageGetLSN(page);
     388             : 
     389             :     /*
     390             :      * check all tuples on page
     391             :      */
     392         837 :     maxoff = PageGetMaxOffsetNumber(page);
     393       76466 :     for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     394             :     {
     395       75629 :         ItemId      iid = PageGetItemId(page, i);
     396             :         IndexTuple  it;
     397             :         bool        match;
     398             :         bool        recheck;
     399             :         bool        recheck_distances;
     400             : 
     401             :         /*
     402             :          * If the scan specifies not to return killed tuples, then we treat a
     403             :          * killed tuple as not passing the qual.
     404             :          */
     405       75629 :         if (scan->ignore_killed_tuples && ItemIdIsDead(iid))
     406       35402 :             continue;
     407             : 
     408       75629 :         it = (IndexTuple) PageGetItem(page, iid);
     409             : 
     410             :         /*
     411             :          * Must call gistindex_keytest in tempCxt, and clean up any leftover
     412             :          * junk afterward.
     413             :          */
     414       75629 :         oldcxt = MemoryContextSwitchTo(so->giststate->tempCxt);
     415             : 
     416       75629 :         match = gistindex_keytest(scan, it, page, i,
     417             :                                   &recheck, &recheck_distances);
     418             : 
     419       75629 :         MemoryContextSwitchTo(oldcxt);
     420       75629 :         MemoryContextReset(so->giststate->tempCxt);
     421             : 
     422             :         /* Ignore tuple if it doesn't match */
     423       75629 :         if (!match)
     424       35402 :             continue;
     425             : 
     426       40227 :         if (tbm && GistPageIsLeaf(page))
     427             :         {
     428             :             /*
     429             :              * getbitmap scan, so just push heap tuple TIDs into the bitmap
     430             :              * without worrying about ordering
     431             :              */
     432        2539 :             tbm_add_tuples(tbm, &it->t_tid, 1, recheck);
     433        2539 :             (*ntids)++;
     434             :         }
     435       37688 :         else if (scan->numberOfOrderBys == 0 && GistPageIsLeaf(page))
     436             :         {
     437             :             /*
     438             :              * Non-ordered scan, so report tuples in so->pageData[]
     439             :              */
     440       36592 :             so->pageData[so->nPageData].heapPtr = it->t_tid;
     441       36592 :             so->pageData[so->nPageData].recheck = recheck;
     442       36592 :             so->pageData[so->nPageData].offnum = i;
     443             : 
     444             :             /*
     445             :              * In an index-only scan, also fetch the data from the tuple.  The
     446             :              * reconstructed tuples are stored in pageDataCxt.
     447             :              */
     448       36592 :             if (scan->xs_want_itup)
     449             :             {
     450       35089 :                 oldcxt = MemoryContextSwitchTo(so->pageDataCxt);
     451       70178 :                 so->pageData[so->nPageData].recontup =
     452       35089 :                     gistFetchTuple(giststate, r, it);
     453       35089 :                 MemoryContextSwitchTo(oldcxt);
     454             :             }
     455       36592 :             so->nPageData++;
     456             :         }
     457             :         else
     458             :         {
     459             :             /*
     460             :              * Must push item into search queue.  We get here for any lower
     461             :              * index page, and also for heap tuples if doing an ordered
     462             :              * search.
     463             :              */
     464             :             GISTSearchItem *item;
     465             : 
     466        1096 :             oldcxt = MemoryContextSwitchTo(so->queueCxt);
     467             : 
     468             :             /* Create new GISTSearchItem for this item */
     469        1096 :             item = palloc(SizeOfGISTSearchItem(scan->numberOfOrderBys));
     470             : 
     471        1096 :             if (GistPageIsLeaf(page))
     472             :             {
     473             :                 /* Creating heap-tuple GISTSearchItem */
     474         318 :                 item->blkno = InvalidBlockNumber;
     475         318 :                 item->data.heap.heapPtr = it->t_tid;
     476         318 :                 item->data.heap.recheck = recheck;
     477         318 :                 item->data.heap.recheckDistances = recheck_distances;
     478             : 
     479             :                 /*
     480             :                  * In an index-only scan, also fetch the data from the tuple.
     481             :                  */
     482         318 :                 if (scan->xs_want_itup)
     483          61 :                     item->data.heap.recontup = gistFetchTuple(giststate, r, it);
     484             :             }
     485             :             else
     486             :             {
     487             :                 /* Creating index-page GISTSearchItem */
     488         778 :                 item->blkno = ItemPointerGetBlockNumber(&it->t_tid);
     489             : 
     490             :                 /*
     491             :                  * LSN of current page is lsn of parent page for child. We
     492             :                  * only have a shared lock, so we need to get the LSN
     493             :                  * atomically.
     494             :                  */
     495         778 :                 item->data.parentlsn = BufferGetLSNAtomic(buffer);
     496             :             }
     497             : 
     498             :             /* Insert it into the queue using new distance data */
     499        1096 :             memcpy(item->distances, so->distances,
     500        1096 :                    sizeof(double) * scan->numberOfOrderBys);
     501             : 
     502        1096 :             pairingheap_add(so->queue, &item->phNode);
     503             : 
     504        1096 :             MemoryContextSwitchTo(oldcxt);
     505             :         }
     506             :     }
     507             : 
     508         837 :     UnlockReleaseBuffer(buffer);
     509         837 : }
     510             : 
     511             : /*
     512             :  * Extract next item (in order) from search queue
     513             :  *
     514             :  * Returns a GISTSearchItem or NULL.  Caller must pfree item when done with it.
     515             :  */
     516             : static GISTSearchItem *
     517         890 : getNextGISTSearchItem(GISTScanOpaque so)
     518             : {
     519             :     GISTSearchItem *item;
     520             : 
     521         890 :     if (!pairingheap_is_empty(so->queue))
     522             :     {
     523         790 :         item = (GISTSearchItem *) pairingheap_remove_first(so->queue);
     524             :     }
     525             :     else
     526             :     {
     527             :         /* Done when both heaps are empty */
     528         100 :         item = NULL;
     529             :     }
     530             : 
     531             :     /* Return item; caller is responsible to pfree it */
     532         890 :     return item;
     533             : }
     534             : 
     535             : /*
     536             :  * Fetch next heap tuple in an ordered search
     537             :  */
     538             : static bool
     539          71 : getNextNearest(IndexScanDesc scan)
     540             : {
     541          71 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     542          71 :     bool        res = false;
     543             :     int         i;
     544             : 
     545          71 :     if (scan->xs_hitup)
     546             :     {
     547             :         /* free previously returned tuple */
     548          42 :         pfree(scan->xs_hitup);
     549          42 :         scan->xs_hitup = NULL;
     550             :     }
     551             : 
     552             :     do
     553             :     {
     554          78 :         GISTSearchItem *item = getNextGISTSearchItem(so);
     555             : 
     556          78 :         if (!item)
     557           5 :             break;
     558             : 
     559          73 :         if (GISTSearchItemIsHeap(*item))
     560             :         {
     561             :             /* found a heap item at currently minimal distance */
     562          66 :             scan->xs_ctup.t_self = item->data.heap.heapPtr;
     563          66 :             scan->xs_recheck = item->data.heap.recheck;
     564          66 :             scan->xs_recheckorderby = item->data.heap.recheckDistances;
     565         132 :             for (i = 0; i < scan->numberOfOrderBys; i++)
     566             :             {
     567          66 :                 if (so->orderByTypes[i] == FLOAT8OID)
     568             :                 {
     569             : #ifndef USE_FLOAT8_BYVAL
     570             :                     /* must free any old value to avoid memory leakage */
     571          66 :                     if (!scan->xs_orderbynulls[i])
     572          58 :                         pfree(DatumGetPointer(scan->xs_orderbyvals[i]));
     573             : #endif
     574          66 :                     scan->xs_orderbyvals[i] = Float8GetDatum(item->distances[i]);
     575          66 :                     scan->xs_orderbynulls[i] = false;
     576             :                 }
     577           0 :                 else if (so->orderByTypes[i] == FLOAT4OID)
     578             :                 {
     579             :                     /* convert distance function's result to ORDER BY type */
     580             : #ifndef USE_FLOAT4_BYVAL
     581             :                     /* must free any old value to avoid memory leakage */
     582             :                     if (!scan->xs_orderbynulls[i])
     583             :                         pfree(DatumGetPointer(scan->xs_orderbyvals[i]));
     584             : #endif
     585           0 :                     scan->xs_orderbyvals[i] = Float4GetDatum((float4) item->distances[i]);
     586           0 :                     scan->xs_orderbynulls[i] = false;
     587             :                 }
     588             :                 else
     589             :                 {
     590             :                     /*
     591             :                      * If the ordering operator's return value is anything
     592             :                      * else, we don't know how to convert the float8 bound
     593             :                      * calculated by the distance function to that.  The
     594             :                      * executor won't actually need the order by values we
     595             :                      * return here, if there are no lossy results, so only
     596             :                      * insist on converting if the *recheck flag is set.
     597             :                      */
     598           0 :                     if (scan->xs_recheckorderby)
     599           0 :                         elog(ERROR, "GiST operator family's FOR ORDER BY operator must return float8 or float4 if the distance function is lossy");
     600           0 :                     scan->xs_orderbynulls[i] = true;
     601             :                 }
     602             :             }
     603             : 
     604             :             /* in an index-only scan, also return the reconstructed tuple. */
     605          66 :             if (scan->xs_want_itup)
     606          45 :                 scan->xs_hitup = item->data.heap.recontup;
     607          66 :             res = true;
     608             :         }
     609             :         else
     610             :         {
     611             :             /* visit an index page, extract its items into queue */
     612           7 :             CHECK_FOR_INTERRUPTS();
     613             : 
     614           7 :             gistScanPage(scan, item, item->distances, NULL, NULL);
     615             :         }
     616             : 
     617          73 :         pfree(item);
     618          73 :     } while (!res);
     619             : 
     620          71 :     return res;
     621             : }
     622             : 
     623             : /*
     624             :  * gistgettuple() -- Get the next tuple in the scan
     625             :  */
     626             : bool
     627       36734 : gistgettuple(IndexScanDesc scan, ScanDirection dir)
     628             : {
     629       36734 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     630             : 
     631       36734 :     if (dir != ForwardScanDirection)
     632           0 :         elog(ERROR, "GiST only supports forward scan direction");
     633             : 
     634       36734 :     if (!so->qual_ok)
     635           0 :         return false;
     636             : 
     637       36734 :     if (so->firstCall)
     638             :     {
     639             :         /* Begin the scan by processing the root page */
     640             :         GISTSearchItem fakeItem;
     641             : 
     642          99 :         pgstat_count_index_scan(scan->indexRelation);
     643             : 
     644          99 :         so->firstCall = false;
     645          99 :         so->curPageData = so->nPageData = 0;
     646          99 :         scan->xs_hitup = NULL;
     647          99 :         if (so->pageDataCxt)
     648          50 :             MemoryContextReset(so->pageDataCxt);
     649             : 
     650          99 :         fakeItem.blkno = GIST_ROOT_BLKNO;
     651          99 :         memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
     652          99 :         gistScanPage(scan, &fakeItem, NULL, NULL, NULL);
     653             :     }
     654             : 
     655       36734 :     if (scan->numberOfOrderBys > 0)
     656             :     {
     657             :         /* Must fetch tuples in strict distance order */
     658          71 :         return getNextNearest(scan);
     659             :     }
     660             :     else
     661             :     {
     662             :         /* Fetch tuples index-page-at-a-time */
     663             :         for (;;)
     664             :         {
     665       37043 :             if (so->curPageData < so->nPageData)
     666             :             {
     667       36582 :                 if (scan->kill_prior_tuple && so->curPageData > 0)
     668             :                 {
     669             : 
     670           1 :                     if (so->killedItems == NULL)
     671             :                     {
     672           1 :                         MemoryContext oldCxt =
     673           1 :                         MemoryContextSwitchTo(so->giststate->scanCxt);
     674             : 
     675           1 :                         so->killedItems =
     676           1 :                             (OffsetNumber *) palloc(MaxIndexTuplesPerPage
     677             :                                                     * sizeof(OffsetNumber));
     678             : 
     679           1 :                         MemoryContextSwitchTo(oldCxt);
     680             :                     }
     681           1 :                     if (so->numKilled < MaxIndexTuplesPerPage)
     682           2 :                         so->killedItems[so->numKilled++] =
     683           1 :                             so->pageData[so->curPageData - 1].offnum;
     684             :                 }
     685             :                 /* continuing to return tuples from a leaf page */
     686       36582 :                 scan->xs_ctup.t_self = so->pageData[so->curPageData].heapPtr;
     687       36582 :                 scan->xs_recheck = so->pageData[so->curPageData].recheck;
     688             : 
     689             :                 /* in an index-only scan, also return the reconstructed tuple */
     690       36582 :                 if (scan->xs_want_itup)
     691       35089 :                     scan->xs_hitup = so->pageData[so->curPageData].recontup;
     692             : 
     693       36582 :                 so->curPageData++;
     694             : 
     695       36582 :                 return true;
     696             :             }
     697             : 
     698             :             /*
     699             :              * Check the last returned tuple and add it to killitems if
     700             :              * necessary
     701             :              */
     702         461 :             if (scan->kill_prior_tuple
     703           0 :                 && so->curPageData > 0
     704           0 :                 && so->curPageData == so->nPageData)
     705             :             {
     706             : 
     707           0 :                 if (so->killedItems == NULL)
     708             :                 {
     709           0 :                     MemoryContext oldCxt =
     710           0 :                     MemoryContextSwitchTo(so->giststate->scanCxt);
     711             : 
     712           0 :                     so->killedItems =
     713           0 :                         (OffsetNumber *) palloc(MaxIndexTuplesPerPage
     714             :                                                 * sizeof(OffsetNumber));
     715             : 
     716           0 :                     MemoryContextSwitchTo(oldCxt);
     717             :                 }
     718           0 :                 if (so->numKilled < MaxIndexTuplesPerPage)
     719           0 :                     so->killedItems[so->numKilled++] =
     720           0 :                         so->pageData[so->curPageData - 1].offnum;
     721             :             }
     722             :             /* find and process the next index page */
     723             :             do
     724             :             {
     725             :                 GISTSearchItem *item;
     726             : 
     727         595 :                 if ((so->curBlkno != InvalidBlockNumber) && (so->numKilled > 0))
     728           0 :                     gistkillitems(scan);
     729             : 
     730         595 :                 item = getNextGISTSearchItem(so);
     731             : 
     732         595 :                 if (!item)
     733          81 :                     return false;
     734             : 
     735         514 :                 CHECK_FOR_INTERRUPTS();
     736             : 
     737             :                 /* save current item BlockNumber for next gistkillitems() call */
     738         514 :                 so->curBlkno = item->blkno;
     739             : 
     740             :                 /*
     741             :                  * While scanning a leaf page, ItemPointers of matching heap
     742             :                  * tuples are stored in so->pageData.  If there are any on
     743             :                  * this page, we fall out of the inner "do" and loop around to
     744             :                  * return them.
     745             :                  */
     746         514 :                 gistScanPage(scan, item, item->distances, NULL, NULL);
     747             : 
     748         514 :                 pfree(item);
     749         514 :             } while (so->nPageData == 0);
     750         380 :         }
     751             :     }
     752             : }
     753             : 
     754             : /*
     755             :  * gistgetbitmap() -- Get a bitmap of all heap tuple locations
     756             :  */
     757             : int64
     758          14 : gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
     759             : {
     760          14 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     761          14 :     int64       ntids = 0;
     762             :     GISTSearchItem fakeItem;
     763             : 
     764          14 :     if (!so->qual_ok)
     765           0 :         return 0;
     766             : 
     767          14 :     pgstat_count_index_scan(scan->indexRelation);
     768             : 
     769             :     /* Begin the scan by processing the root page */
     770          14 :     so->curPageData = so->nPageData = 0;
     771          14 :     scan->xs_hitup = NULL;
     772          14 :     if (so->pageDataCxt)
     773           0 :         MemoryContextReset(so->pageDataCxt);
     774             : 
     775          14 :     fakeItem.blkno = GIST_ROOT_BLKNO;
     776          14 :     memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
     777          14 :     gistScanPage(scan, &fakeItem, NULL, tbm, &ntids);
     778             : 
     779             :     /*
     780             :      * While scanning a leaf page, ItemPointers of matching heap tuples will
     781             :      * be stored directly into tbm, so we don't need to deal with them here.
     782             :      */
     783             :     for (;;)
     784             :     {
     785         217 :         GISTSearchItem *item = getNextGISTSearchItem(so);
     786             : 
     787         217 :         if (!item)
     788          14 :             break;
     789             : 
     790         203 :         CHECK_FOR_INTERRUPTS();
     791             : 
     792         203 :         gistScanPage(scan, item, item->distances, tbm, &ntids);
     793             : 
     794         203 :         pfree(item);
     795         203 :     }
     796             : 
     797          14 :     return ntids;
     798             : }
     799             : 
     800             : /*
     801             :  * Can we do index-only scans on the given index column?
     802             :  *
     803             :  * Opclasses that implement a fetch function support index-only scans.
     804             :  */
     805             : bool
     806         176 : gistcanreturn(Relation index, int attno)
     807             : {
     808         176 :     if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)))
     809         134 :         return true;
     810             :     else
     811          42 :         return false;
     812             : }

Generated by: LCOV version 1.11