LCOV - code coverage report
Current view: top level - src/backend/access/gist - gistscan.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 99 103 96.1 %
Date: 2017-09-29 15:12:54 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * gistscan.c
       4             :  *    routines to manage scans on GiST index relations
       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/gistscan.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/gist_private.h"
      18             : #include "access/gistscan.h"
      19             : #include "access/relscan.h"
      20             : #include "utils/lsyscache.h"
      21             : #include "utils/memutils.h"
      22             : #include "utils/rel.h"
      23             : 
      24             : 
      25             : /*
      26             :  * Pairing heap comparison function for the GISTSearchItem queue
      27             :  */
      28             : static int
      29        3322 : pairingheap_GISTSearchItem_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg)
      30             : {
      31        3322 :     const GISTSearchItem *sa = (const GISTSearchItem *) a;
      32        3322 :     const GISTSearchItem *sb = (const GISTSearchItem *) b;
      33        3322 :     IndexScanDesc scan = (IndexScanDesc) arg;
      34             :     int         i;
      35             : 
      36             :     /* Order according to distance comparison */
      37        3322 :     for (i = 0; i < scan->numberOfOrderBys; i++)
      38             :     {
      39         895 :         if (sa->distances[i] != sb->distances[i])
      40         895 :             return (sa->distances[i] < sb->distances[i]) ? 1 : -1;
      41             :     }
      42             : 
      43             :     /* Heap items go before inner pages, to ensure a depth-first search */
      44        2427 :     if (GISTSearchItemIsHeap(*sa) && !GISTSearchItemIsHeap(*sb))
      45           0 :         return 1;
      46        2427 :     if (!GISTSearchItemIsHeap(*sa) && GISTSearchItemIsHeap(*sb))
      47           0 :         return -1;
      48             : 
      49        2427 :     return 0;
      50             : }
      51             : 
      52             : 
      53             : /*
      54             :  * Index AM API functions for scanning GiST indexes
      55             :  */
      56             : 
      57             : IndexScanDesc
      58         110 : gistbeginscan(Relation r, int nkeys, int norderbys)
      59             : {
      60             :     IndexScanDesc scan;
      61             :     GISTSTATE  *giststate;
      62             :     GISTScanOpaque so;
      63             :     MemoryContext oldCxt;
      64             : 
      65         110 :     scan = RelationGetIndexScan(r, nkeys, norderbys);
      66             : 
      67             :     /* First, set up a GISTSTATE with a scan-lifespan memory context */
      68         110 :     giststate = initGISTstate(scan->indexRelation);
      69             : 
      70             :     /*
      71             :      * Everything made below is in the scanCxt, or is a child of the scanCxt,
      72             :      * so it'll all go away automatically in gistendscan.
      73             :      */
      74         110 :     oldCxt = MemoryContextSwitchTo(giststate->scanCxt);
      75             : 
      76             :     /* initialize opaque data */
      77         110 :     so = (GISTScanOpaque) palloc0(sizeof(GISTScanOpaqueData));
      78         110 :     so->giststate = giststate;
      79         110 :     giststate->tempCxt = createTempGistContext();
      80         110 :     so->queue = NULL;
      81         110 :     so->queueCxt = giststate->scanCxt;    /* see gistrescan */
      82             : 
      83             :     /* workspaces with size dependent on numberOfOrderBys: */
      84         110 :     so->distances = palloc(sizeof(double) * scan->numberOfOrderBys);
      85         110 :     so->qual_ok = true;          /* in case there are zero keys */
      86         110 :     if (scan->numberOfOrderBys > 0)
      87             :     {
      88           8 :         scan->xs_orderbyvals = palloc0(sizeof(Datum) * scan->numberOfOrderBys);
      89           8 :         scan->xs_orderbynulls = palloc(sizeof(bool) * scan->numberOfOrderBys);
      90           8 :         memset(scan->xs_orderbynulls, true, sizeof(bool) * scan->numberOfOrderBys);
      91             :     }
      92             : 
      93         110 :     so->killedItems = NULL;      /* until needed */
      94         110 :     so->numKilled = 0;
      95         110 :     so->curBlkno = InvalidBlockNumber;
      96         110 :     so->curPageLSN = InvalidXLogRecPtr;
      97             : 
      98         110 :     scan->opaque = so;
      99             : 
     100             :     /*
     101             :      * All fields required for index-only scans are initialized in gistrescan,
     102             :      * as we don't know yet if we're doing an index-only scan or not.
     103             :      */
     104             : 
     105         110 :     MemoryContextSwitchTo(oldCxt);
     106             : 
     107         110 :     return scan;
     108             : }
     109             : 
     110             : void
     111         113 : gistrescan(IndexScanDesc scan, ScanKey key, int nkeys,
     112             :            ScanKey orderbys, int norderbys)
     113             : {
     114             :     /* nkeys and norderbys arguments are ignored */
     115         113 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     116             :     bool        first_time;
     117             :     int         i;
     118             :     MemoryContext oldCxt;
     119             : 
     120             :     /* rescan an existing indexscan --- reset state */
     121             : 
     122             :     /*
     123             :      * The first time through, we create the search queue in the scanCxt.
     124             :      * Subsequent times through, we create the queue in a separate queueCxt,
     125             :      * which is created on the second call and reset on later calls.  Thus, in
     126             :      * the common case where a scan is only rescan'd once, we just put the
     127             :      * queue in scanCxt and don't pay the overhead of making a second memory
     128             :      * context.  If we do rescan more than once, the first queue is just left
     129             :      * for dead until end of scan; this small wastage seems worth the savings
     130             :      * in the common case.
     131             :      */
     132         113 :     if (so->queue == NULL)
     133             :     {
     134             :         /* first time through */
     135         110 :         Assert(so->queueCxt == so->giststate->scanCxt);
     136         110 :         first_time = true;
     137             :     }
     138           3 :     else if (so->queueCxt == so->giststate->scanCxt)
     139             :     {
     140             :         /* second time through */
     141           2 :         so->queueCxt = AllocSetContextCreate(so->giststate->scanCxt,
     142             :                                              "GiST queue context",
     143             :                                              ALLOCSET_DEFAULT_SIZES);
     144           2 :         first_time = false;
     145             :     }
     146             :     else
     147             :     {
     148             :         /* third or later time through */
     149           1 :         MemoryContextReset(so->queueCxt);
     150           1 :         first_time = false;
     151             :     }
     152             : 
     153             :     /*
     154             :      * If we're doing an index-only scan, on the first call, also initialize a
     155             :      * tuple descriptor to represent the returned index tuples and create a
     156             :      * memory context to hold them during the scan.
     157             :      */
     158         113 :     if (scan->xs_want_itup && !scan->xs_hitupdesc)
     159             :     {
     160             :         int         natts;
     161             :         int         attno;
     162             : 
     163             :         /*
     164             :          * The storage type of the index can be different from the original
     165             :          * datatype being indexed, so we cannot just grab the index's tuple
     166             :          * descriptor. Instead, construct a descriptor with the original data
     167             :          * types.
     168             :          */
     169          48 :         natts = RelationGetNumberOfAttributes(scan->indexRelation);
     170          48 :         so->giststate->fetchTupdesc = CreateTemplateTupleDesc(natts, false);
     171          96 :         for (attno = 1; attno <= natts; attno++)
     172             :         {
     173          48 :             TupleDescInitEntry(so->giststate->fetchTupdesc, attno, NULL,
     174          48 :                                scan->indexRelation->rd_opcintype[attno - 1],
     175             :                                -1, 0);
     176             :         }
     177          48 :         scan->xs_hitupdesc = so->giststate->fetchTupdesc;
     178             : 
     179             :         /* Also create a memory context that will hold the returned tuples */
     180          48 :         so->pageDataCxt = AllocSetContextCreate(so->giststate->scanCxt,
     181             :                                                 "GiST page data context",
     182             :                                                 ALLOCSET_DEFAULT_SIZES);
     183             :     }
     184             : 
     185             :     /* create new, empty pairing heap for search queue */
     186         113 :     oldCxt = MemoryContextSwitchTo(so->queueCxt);
     187         113 :     so->queue = pairingheap_allocate(pairingheap_GISTSearchItem_cmp, scan);
     188         113 :     MemoryContextSwitchTo(oldCxt);
     189             : 
     190         113 :     so->firstCall = true;
     191             : 
     192             :     /* Update scan key, if a new one is given */
     193         113 :     if (key && scan->numberOfKeys > 0)
     194             :     {
     195         110 :         void      **fn_extras = NULL;
     196             : 
     197             :         /*
     198             :          * If this isn't the first time through, preserve the fn_extra
     199             :          * pointers, so that if the consistentFns are using them to cache
     200             :          * data, that data is not leaked across a rescan.
     201             :          */
     202         110 :         if (!first_time)
     203             :         {
     204           3 :             fn_extras = (void **) palloc(scan->numberOfKeys * sizeof(void *));
     205           6 :             for (i = 0; i < scan->numberOfKeys; i++)
     206           3 :                 fn_extras[i] = scan->keyData[i].sk_func.fn_extra;
     207             :         }
     208             : 
     209         110 :         memmove(scan->keyData, key,
     210         110 :                 scan->numberOfKeys * sizeof(ScanKeyData));
     211             : 
     212             :         /*
     213             :          * Modify the scan key so that the Consistent method is called for all
     214             :          * comparisons. The original operator is passed to the Consistent
     215             :          * function in the form of its strategy number, which is available
     216             :          * from the sk_strategy field, and its subtype from the sk_subtype
     217             :          * field.
     218             :          *
     219             :          * Next, if any of keys is a NULL and that key is not marked with
     220             :          * SK_SEARCHNULL/SK_SEARCHNOTNULL then nothing can be found (ie, we
     221             :          * assume all indexable operators are strict).
     222             :          */
     223         110 :         so->qual_ok = true;
     224             : 
     225         238 :         for (i = 0; i < scan->numberOfKeys; i++)
     226             :         {
     227         128 :             ScanKey     skey = scan->keyData + i;
     228             : 
     229             :             /*
     230             :              * Copy consistent support function to ScanKey structure instead
     231             :              * of function implementing filtering operator.
     232             :              */
     233         256 :             fmgr_info_copy(&(skey->sk_func),
     234         128 :                            &(so->giststate->consistentFn[skey->sk_attno - 1]),
     235         128 :                            so->giststate->scanCxt);
     236             : 
     237             :             /* Restore prior fn_extra pointers, if not first time */
     238         128 :             if (!first_time)
     239           3 :                 skey->sk_func.fn_extra = fn_extras[i];
     240             : 
     241         128 :             if (skey->sk_flags & SK_ISNULL)
     242             :             {
     243           4 :                 if (!(skey->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL)))
     244           0 :                     so->qual_ok = false;
     245             :             }
     246             :         }
     247             : 
     248         110 :         if (!first_time)
     249           3 :             pfree(fn_extras);
     250             :     }
     251             : 
     252             :     /* Update order-by key, if a new one is given */
     253         113 :     if (orderbys && scan->numberOfOrderBys > 0)
     254             :     {
     255          10 :         void      **fn_extras = NULL;
     256             : 
     257             :         /* As above, preserve fn_extra if not first time through */
     258          10 :         if (!first_time)
     259             :         {
     260           2 :             fn_extras = (void **) palloc(scan->numberOfOrderBys * sizeof(void *));
     261           4 :             for (i = 0; i < scan->numberOfOrderBys; i++)
     262           2 :                 fn_extras[i] = scan->orderByData[i].sk_func.fn_extra;
     263             :         }
     264             : 
     265          10 :         memmove(scan->orderByData, orderbys,
     266          10 :                 scan->numberOfOrderBys * sizeof(ScanKeyData));
     267             : 
     268          10 :         so->orderByTypes = (Oid *) palloc(scan->numberOfOrderBys * sizeof(Oid));
     269             : 
     270             :         /*
     271             :          * Modify the order-by key so that the Distance method is called for
     272             :          * all comparisons. The original operator is passed to the Distance
     273             :          * function in the form of its strategy number, which is available
     274             :          * from the sk_strategy field, and its subtype from the sk_subtype
     275             :          * field.
     276             :          */
     277          20 :         for (i = 0; i < scan->numberOfOrderBys; i++)
     278             :         {
     279          10 :             ScanKey     skey = scan->orderByData + i;
     280          10 :             FmgrInfo   *finfo = &(so->giststate->distanceFn[skey->sk_attno - 1]);
     281             : 
     282             :             /* Check we actually have a distance function ... */
     283          10 :             if (!OidIsValid(finfo->fn_oid))
     284           0 :                 elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
     285             :                      GIST_DISTANCE_PROC, skey->sk_attno,
     286             :                      RelationGetRelationName(scan->indexRelation));
     287             : 
     288             :             /*
     289             :              * Look up the datatype returned by the original ordering
     290             :              * operator. GiST always uses a float8 for the distance function,
     291             :              * but the ordering operator could be anything else.
     292             :              *
     293             :              * XXX: The distance function is only allowed to be lossy if the
     294             :              * ordering operator's result type is float4 or float8.  Otherwise
     295             :              * we don't know how to return the distance to the executor.  But
     296             :              * we cannot check that here, as we won't know if the distance
     297             :              * function is lossy until it returns *recheck = true for the
     298             :              * first time.
     299             :              */
     300          10 :             so->orderByTypes[i] = get_func_rettype(skey->sk_func.fn_oid);
     301             : 
     302             :             /*
     303             :              * Copy distance support function to ScanKey structure instead of
     304             :              * function implementing ordering operator.
     305             :              */
     306          10 :             fmgr_info_copy(&(skey->sk_func), finfo, so->giststate->scanCxt);
     307             : 
     308             :             /* Restore prior fn_extra pointers, if not first time */
     309          10 :             if (!first_time)
     310           2 :                 skey->sk_func.fn_extra = fn_extras[i];
     311             :         }
     312             : 
     313          10 :         if (!first_time)
     314           2 :             pfree(fn_extras);
     315             :     }
     316             : 
     317             :     /* any previous xs_hitup will have been pfree'd in context resets above */
     318         113 :     scan->xs_hitup = NULL;
     319         113 : }
     320             : 
     321             : void
     322         104 : gistendscan(IndexScanDesc scan)
     323             : {
     324         104 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     325             : 
     326             :     /*
     327             :      * freeGISTstate is enough to clean up everything made by gistbeginscan,
     328             :      * as well as the queueCxt if there is a separate context for it.
     329             :      */
     330         104 :     freeGISTstate(so->giststate);
     331         104 : }

Generated by: LCOV version 1.11