Add support for index-only scans in GiST.
authorHeikki Linnakangas
Thu, 26 Mar 2015 17:12:00 +0000 (19:12 +0200)
committerHeikki Linnakangas
Thu, 26 Mar 2015 17:12:00 +0000 (19:12 +0200)
This adds a new GiST opclass method, 'fetch', which is used to reconstruct
the original Datum from the value stored in the index. Also, the 'canreturn'
index AM interface function gains a new 'attno' argument. That makes it
possible to use index-only scans on a multi-column index where some of the
opclasses support index-only scans but some do not.

This patch adds support in the box and point opclasses. Other opclasses
can added later as follow-on patches (btree_gist would be particularly
interesting).

Anastasia Lubennikova, with additional fixes and modifications by me.

24 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/gist.sgml
doc/src/sgml/indexam.sgml
src/backend/access/gist/gist.c
src/backend/access/gist/gistget.c
src/backend/access/gist/gistproc.c
src/backend/access/gist/gistscan.c
src/backend/access/gist/gistutil.c
src/backend/access/index/indexam.c
src/backend/access/spgist/spgscan.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/util/plancat.c
src/include/access/genam.h
src/include/access/gist.h
src/include/access/gist_private.h
src/include/catalog/catversion.h
src/include/catalog/pg_am.h
src/include/catalog/pg_amproc.h
src/include/catalog/pg_proc.h
src/include/nodes/relation.h
src/include/utils/geo_decls.h
src/test/regress/expected/create_index.out
src/test/regress/expected/gist.out
src/test/regress/sql/gist.sql

index dfed546f5133b11d01ad6f794d6270960c7be3ae..d0b78f27827cd5b1dd883af248657f33b802d013 100644 (file)
       amcanreturn
       regproc
       pg_proc.oid
-      Function to check whether index supports index-only scans,
-       or zero if none
+      Function to check whether an index column supports index-only
+       scans. Can be zero if index-only scans are never supported.
      
 
      
index 31ce2790047e5487d5e24e08200eefaaa2a5c85c..e7d1ff9d83f4f9b469480dc6dfc22303a2d51c4a 100644 (file)
@@ -266,7 +266,7 @@ CREATE INDEX ON my_table USING gist (my_inet_column inet_ops);
 
  
    There are seven methods that an index operator class for
-   GiST must provide, and an eighth that is optional.
+   GiST must provide, and two that are optional.
    Correctness of the index is ensured
    by proper implementation of the same, consistent
    and union methods, while efficiency (size and speed) of the
@@ -282,7 +282,8 @@ CREATE INDEX ON my_table USING gist (my_inet_column inet_ops);
    of the CREATE OPERATOR CLASS command can be used.
    The optional eighth method is distance, which is needed
    if the operator class wishes to support ordered scans (nearest-neighbor
-   searches).
+   searches). The optional ninth method fetch is needed if the
+   operator class wishes to support index-only scans.
  
 
  
@@ -506,7 +507,7 @@ my_compress(PG_FUNCTION_ARGS)
       
        The reverse of the compress method.  Converts the
        index representation of the data item into a format that can be
-       manipulated by the database.
+       manipulated by the other GiST methods in the operator class.
       
 
       
@@ -807,6 +808,72 @@ my_distance(PG_FUNCTION_ARGS)
      
     
 
+    
+     fetch
+     
+      
+       Converts the compressed index representation of the data item into the
+       original data type, for index-only scans. The returned data must be an
+       exact, non-lossy copy of the originally indexed value.
+      
+
+      
+        The SQL declaration of the function must look like this:
+
+
+CREATE OR REPLACE FUNCTION my_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+
+
+        The argument is a pointer to a GISTENTRY struct. On
+        entry, its 'key' field contains a non-NULL leaf datum in its
+        compressed form. The return value is another GISTENTRY
+        struct, whose 'key' field contains the same datum in the original,
+        uncompressed form. If the opclass' compress function does nothing for
+        leaf entries, the fetch method can return the argument as is.
+        
+
+       
+        The matching code in the C module could then follow this skeleton:
+
+
+Datum       my_fetch(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(my_fetch);
+
+Datum
+my_fetch(PG_FUNCTION_ARGS)
+{
+    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+    input_data_type *in = DatumGetP(entry->key);
+    fetched_data_type *fetched_data;
+    GISTENTRY  *retval;
+
+    retval = palloc(sizeof(GISTENTRY));
+    fetched_data = palloc(sizeof(fetched_data_type));
+
+    /*
+     * Convert 'fetched_data' into the a Datum of the original datatype.
+     */
+
+    /* fill *retval from fetch_data. */
+    gistentryinit(*retval, PointerGetDatum(converted_datum),
+                  entry->rel, entry->page, entry->offset, FALSE);
+
+    PG_RETURN_POINTER(retval);
+}
+
+      
+
+      
+       If the compress method is lossy for leaf entries, the operator class
+       cannot support index-only scans, and must not define a 'fetch'
+       function.
+      
+
+     
+    
   
 
   
index 157047a23ab15ab86204db66b9e38d0aa64fae2f..1c09bae3955f88e3ea3d3c8150157b29c5531e99 100644 (file)
@@ -274,14 +274,15 @@ amvacuumcleanup (IndexVacuumInfo *info,
   
 
 bool
-amcanreturn (Relation indexRelation);
+amcanreturn (Relation indexRelation, int attno);
 
-   Check whether the index can support index-only scans by
-   returning the indexed column values for an index entry in the form of an
-   IndexTuple.  Return TRUE if so, else FALSE.  If the index AM can never
-   support index-only scans (an example is hash, which stores only
-   the hash values not the original data), it is sufficient to set its
-   amcanreturn field to zero in pg_am.
+   Check whether the index can support index-only scans on the
+   given column, by returning the indexed column values for an index entry in
+   the form of an IndexTuple.  The attribute number
+   is 1-based, i.e. the first columns attno is 1. Returns TRUE if supported,
+   else FALSE.  If the access method does not support index-only scans at all,
+   the amcanreturn field in its pg_am row can
+   be set to zero.
   
 
   
index db2a452a4abb10e85d45d7cbc144ce27a854ecec..96b7701633f2013d36884765b137b2a16be56bc6 100644 (file)
@@ -1404,6 +1404,14 @@ initGISTstate(Relation index)
        else
            giststate->distanceFn[i].fn_oid = InvalidOid;
 
+       /* opclasses are not required to provide a Fetch method */
+       if (OidIsValid(index_getprocid(index, i + 1, GIST_FETCH_PROC)))
+           fmgr_info_copy(&(giststate->fetchFn[i]),
+                        index_getprocinfo(index, i + 1, GIST_FETCH_PROC),
+                          scanCxt);
+       else
+           giststate->fetchFn[i].fn_oid = InvalidOid;
+
        /*
         * If the index column has a specified collation, we should honor that
         * while doing comparisons.  However, we may have a collatable storage
index 717cb85f7739952a974815dbec3099307aa8f963..e4c00c2c9f5adf8b7e4e65024443b51766afccde 100644 (file)
@@ -228,7 +228,9 @@ gistindex_keytest(IndexScanDesc scan,
  * tuples should be reported directly into the bitmap.  If they are NULL,
  * we're doing a plain or ordered indexscan.  For a plain indexscan, heap
  * tuple TIDs are returned into so->pageData[].  For an ordered indexscan,
- * heap tuple TIDs are pushed into individual search queue items.
+ * heap tuple TIDs are pushed into individual search queue items.  In an
+ * index-only scan, reconstructed index tuples are returned along with the
+ * TIDs.
  *
  * If we detect that the index page has split since we saw its downlink
  * in the parent, we push its new right sibling onto the queue so the
@@ -239,6 +241,8 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
             TIDBitmap *tbm, int64 *ntids)
 {
    GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
+   GISTSTATE  *giststate = so->giststate;
+   Relation    r = scan->indexRelation;
    Buffer      buffer;
    Page        page;
    GISTPageOpaque opaque;
@@ -288,6 +292,8 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
    }
 
    so->nPageData = so->curPageData = 0;
+   if (so->pageDataCxt)
+       MemoryContextReset(so->pageDataCxt);
 
    /*
     * check all tuples on page
@@ -326,10 +332,21 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
        else if (scan->numberOfOrderBys == 0 && GistPageIsLeaf(page))
        {
            /*
-            * Non-ordered scan, so report heap tuples in so->pageData[]
+            * Non-ordered scan, so report tuples in so->pageData[]
             */
            so->pageData[so->nPageData].heapPtr = it->t_tid;
            so->pageData[so->nPageData].recheck = recheck;
+
+           /*
+            * In an index-only scan, also fetch the data from the tuple.
+            */
+           if (scan->xs_want_itup)
+           {
+               oldcxt = MemoryContextSwitchTo(so->pageDataCxt);
+               so->pageData[so->nPageData].ftup =
+                   gistFetchTuple(giststate, r, it);
+               MemoryContextSwitchTo(oldcxt);
+           }
            so->nPageData++;
        }
        else
@@ -352,6 +369,12 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
                item->blkno = InvalidBlockNumber;
                item->data.heap.heapPtr = it->t_tid;
                item->data.heap.recheck = recheck;
+
+               /*
+                * In an index-only scan, also fetch the data from the tuple.
+                */
+               if (scan->xs_want_itup)
+                   item->data.heap.ftup = gistFetchTuple(giststate, r, it);
            }
            else
            {
@@ -412,6 +435,13 @@ getNextNearest(IndexScanDesc scan)
    GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
    bool        res = false;
 
+   if (scan->xs_itup)
+   {
+       /* free previously returned tuple */
+       pfree(scan->xs_itup);
+       scan->xs_itup = NULL;
+   }
+
    do
    {
        GISTSearchItem *item = getNextGISTSearchItem(so);
@@ -424,6 +454,10 @@ getNextNearest(IndexScanDesc scan)
            /* found a heap item at currently minimal distance */
            scan->xs_ctup.t_self = item->data.heap.heapPtr;
            scan->xs_recheck = item->data.heap.recheck;
+
+           /* in an index-only scan, also return the reconstructed tuple. */
+           if (scan->xs_want_itup)
+               scan->xs_itup = item->data.heap.ftup;
            res = true;
        }
        else
@@ -465,6 +499,8 @@ gistgettuple(PG_FUNCTION_ARGS)
 
        so->firstCall = false;
        so->curPageData = so->nPageData = 0;
+       if (so->pageDataCxt)
+           MemoryContextReset(so->pageDataCxt);
 
        fakeItem.blkno = GIST_ROOT_BLKNO;
        memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
@@ -483,10 +519,17 @@ gistgettuple(PG_FUNCTION_ARGS)
        {
            if (so->curPageData < so->nPageData)
            {
+
                /* continuing to return tuples from a leaf page */
                scan->xs_ctup.t_self = so->pageData[so->curPageData].heapPtr;
                scan->xs_recheck = so->pageData[so->curPageData].recheck;
+
+               /* in an index-only scan, also return the reconstructed tuple */
+               if (scan->xs_want_itup)
+                   scan->xs_itup = so->pageData[so->curPageData].ftup;
+
                so->curPageData++;
+
                PG_RETURN_BOOL(true);
            }
 
@@ -533,6 +576,8 @@ gistgetbitmap(PG_FUNCTION_ARGS)
 
    /* Begin the scan by processing the root page */
    so->curPageData = so->nPageData = 0;
+   if (so->pageDataCxt)
+       MemoryContextReset(so->pageDataCxt);
 
    fakeItem.blkno = GIST_ROOT_BLKNO;
    memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
@@ -558,3 +603,20 @@ gistgetbitmap(PG_FUNCTION_ARGS)
 
    PG_RETURN_INT64(ntids);
 }
+
+/*
+ * Can we do index-only scans on the given index column?
+ *
+ * Opclasses that implement a fetch function support index-only scans.
+ */
+Datum
+gistcanreturn(PG_FUNCTION_ARGS)
+{
+   Relation    index = (Relation) PG_GETARG_POINTER(0);
+   int         attno = PG_GETARG_INT32(1);
+
+   if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)))
+       PG_RETURN_BOOL(true);
+   else
+       PG_RETURN_BOOL(false);
+}
index 9fab6c87c0573f7207471c97ea0847cc81e32ccf..9d21e3fb947eea13ff7a23b4a3f535679a849d05 100644 (file)
@@ -151,6 +151,16 @@ gist_box_decompress(PG_FUNCTION_ARGS)
    PG_RETURN_POINTER(PG_GETARG_POINTER(0));
 }
 
+/*
+ * GiST Fetch method for boxes
+ * do not do anything --- we just return the stored box as is.
+ */
+Datum
+gist_box_fetch(PG_FUNCTION_ARGS)
+{
+   PG_RETURN_POINTER(PG_GETARG_POINTER(0));
+}
+
 /*
  * The GiST Penalty method for boxes (also used for points)
  *
@@ -1186,6 +1196,33 @@ gist_point_compress(PG_FUNCTION_ARGS)
    PG_RETURN_POINTER(entry);
 }
 
+/*
+ * GiST Fetch method for point
+ *
+ * Get point coordinates from its bounding box coordinates and form new
+ * gistentry.
+ */
+Datum
+gist_point_fetch(PG_FUNCTION_ARGS)
+{
+   GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+   BOX        *in = DatumGetBoxP(entry->key);
+   Point      *r;
+   GISTENTRY  *retval;
+
+   retval = palloc(sizeof(GISTENTRY));
+
+   r = (Point *) palloc(sizeof(Point));
+   r->x = in->high.x;
+   r->y = in->high.y;
+   gistentryinit(*retval, PointerGetDatum(r),
+                 entry->rel, entry->page,
+                 entry->offset, FALSE);
+
+   PG_RETURN_POINTER(retval);
+}
+
+
 #define point_point_distance(p1,p2) \
    DatumGetFloat8(DirectFunctionCall2(point_distance, \
                                       PointPGetDatum(p1), PointPGetDatum(p2)))
index 991858ff43f837fd1a6d53d16321c33cd0cedae8..3522d75a49674ff05f9ffefc6e192556da892efe 100644 (file)
@@ -88,6 +88,13 @@ gistbeginscan(PG_FUNCTION_ARGS)
 
    scan->opaque = so;
 
+   /*
+    * All fields required for index-only scans are null until gistrescan.
+    * However, we set up scan->xs_itupdesc whether we'll need it or not,
+    * since that's cheap.
+    */
+   scan->xs_itupdesc = RelationGetDescr(r);
+
    MemoryContextSwitchTo(oldCxt);
 
    PG_RETURN_POINTER(scan);
@@ -141,6 +148,17 @@ gistrescan(PG_FUNCTION_ARGS)
        first_time = false;
    }
 
+   /*
+    * If we're doing an index-only scan, also create a memory context to hold
+    * the returned tuples.
+    */
+   if (scan->xs_want_itup && so->pageDataCxt == NULL)
+       so->pageDataCxt = AllocSetContextCreate(so->giststate->scanCxt,
+                                               "GiST page data context",
+                                               ALLOCSET_DEFAULT_MINSIZE,
+                                               ALLOCSET_DEFAULT_INITSIZE,
+                                               ALLOCSET_DEFAULT_MAXSIZE);
+
    /* create new, empty RBTree for search queue */
    oldCxt = MemoryContextSwitchTo(so->queueCxt);
    so->queue = pairingheap_allocate(pairingheap_GISTSearchItem_cmp, scan);
index 824c40eb203fc9cf4a378f6fb3d13ac15e4db6bc..1680251a18b647998e8aeab9c507b71b62220a8a 100644 (file)
@@ -294,8 +294,9 @@ gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
 
    for (i = 0; i < r->rd_att->natts; i++)
    {
-       Datum       datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
+       Datum       datum;
 
+       datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
        gistdentryinit(giststate, i, &attdata[i],
                       datum, r, p, o,
                       FALSE, isnull[i]);
@@ -598,6 +599,67 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
    return res;
 }
 
+/*
+ * initialize a GiST entry with fetched value in key field
+ */
+static Datum
+gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
+{
+   GISTENTRY   fentry;
+   GISTENTRY  *fep;
+
+   gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
+
+   fep = (GISTENTRY *)
+       DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
+                                         giststate->supportCollation[nkey],
+                                         PointerGetDatum(&fentry)));
+
+   /* fetchFn set 'key', return it to the caller */
+   return fep->key;
+}
+
+/*
+ * Fetch all keys in tuple.
+ * returns new IndexTuple that contains GISTENTRY with fetched data
+ */
+IndexTuple
+gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple)
+{
+   MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt);
+   Datum       fetchatt[INDEX_MAX_KEYS];
+   bool        isnull[INDEX_MAX_KEYS];
+   int         i;
+
+   for (i = 0; i < r->rd_att->natts; i++)
+   {
+       Datum       datum;
+
+       datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
+
+       if (giststate->fetchFn[i].fn_oid != InvalidOid)
+       {
+           if (!isnull[i])
+               fetchatt[i] = gistFetchAtt(giststate, i, datum, r);
+           else
+               fetchatt[i] = (Datum) 0;
+       }
+       else
+       {
+           /*
+            * Index-only scans not supported for this column. Since the
+            * planner chose an index-only scan anyway, it is not interested
+            * in this column, and we can replace it with a NULL.
+            */
+           isnull[i] = true;
+           fetchatt[i] = (Datum) 0;
+       }
+   }
+   MemoryContextSwitchTo(oldcxt);
+
+   return index_form_tuple(giststate->tupdesc, fetchatt, isnull);
+}
+
 float
 gistpenalty(GISTSTATE *giststate, int attno,
            GISTENTRY *orig, bool isNullOrig,
index 00c1d6937665f32c88f3ddb2bff3ac130afebd58..2b27e732f13e5c9e056d3b130b1b929b29143b32 100644 (file)
@@ -722,11 +722,14 @@ index_vacuum_cleanup(IndexVacuumInfo *info,
 }
 
 /* ----------------
- *     index_can_return - does index support index-only scans?
+ *     index_can_return
+ *
+ *     Does the index access method support index-only scans for the given
+ *     column?
  * ----------------
  */
 bool
-index_can_return(Relation indexRelation)
+index_can_return(Relation indexRelation, int attno)
 {
    FmgrInfo   *procedure;
 
@@ -738,8 +741,9 @@ index_can_return(Relation indexRelation)
 
    GET_REL_PROCEDURE(amcanreturn);
 
-   return DatumGetBool(FunctionCall1(procedure,
-                                     PointerGetDatum(indexRelation)));
+   return DatumGetBool(FunctionCall2(procedure,
+                                     PointerGetDatum(indexRelation),
+                                     Int32GetDatum(attno)));
 }
 
 /* ----------------
index 3c79fb99b889f2e3d80d00051beb4c2ab04922aa..06c6944fc72c67326514b92efff5efacbc46070c 100644 (file)
@@ -658,6 +658,7 @@ Datum
 spgcanreturn(PG_FUNCTION_ARGS)
 {
    Relation    index = (Relation) PG_GETARG_POINTER(0);
+   /* int          i = PG_GETARG_INT32(1); */
    SpGistCache *cache;
 
    /* We can do it if the opclass config function says so */
index 49ab3666b93b881c5895c319dd0fd1be3da80b88..fdd6baba6c674c8a8a909804af5d6cfc6209f8c6 100644 (file)
@@ -1786,15 +1786,13 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
 {
    bool        result;
    Bitmapset  *attrs_used = NULL;
-   Bitmapset  *index_attrs = NULL;
+   Bitmapset  *index_canreturn_attrs = NULL;
    ListCell   *lc;
    int         i;
 
-   /* Index-only scans must be enabled, and index must be capable of them */
+   /* Index-only scans must be enabled */
    if (!enable_indexonlyscan)
        return false;
-   if (!index->canreturn)
-       return false;
 
    /*
     * Check that all needed attributes of the relation are available from the
@@ -1824,7 +1822,10 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
        pull_varattnos((Node *) rinfo->clause, rel->relid, &attrs_used);
    }
 
-   /* Construct a bitmapset of columns stored in the index. */
+   /*
+    * Construct a bitmapset of columns that the index can return back in an
+    * index-only scan.
+    */
    for (i = 0; i < index->ncolumns; i++)
    {
        int         attno = index->indexkeys[i];
@@ -1836,16 +1837,17 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
        if (attno == 0)
            continue;
 
-       index_attrs =
-           bms_add_member(index_attrs,
-                          attno - FirstLowInvalidHeapAttributeNumber);
+       if (index->canreturn[i])
+           index_canreturn_attrs =
+               bms_add_member(index_canreturn_attrs,
+                              attno - FirstLowInvalidHeapAttributeNumber);
    }
 
    /* Do we have all the necessary attributes? */
-   result = bms_is_subset(attrs_used, index_attrs);
+   result = bms_is_subset(attrs_used, index_canreturn_attrs);
 
    bms_free(attrs_used);
-   bms_free(index_attrs);
+   bms_free(index_canreturn_attrs);
 
    return result;
 }
index 313a5c1ab2bc7f3270126620a94a225c54a1ff65..8abed2ae0dada01bf56fd080e041f1b481ee612f 100644 (file)
@@ -207,6 +207,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
            info->indexcollations = (Oid *) palloc(sizeof(Oid) * ncolumns);
            info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns);
            info->opcintype = (Oid *) palloc(sizeof(Oid) * ncolumns);
+           info->canreturn = (bool *) palloc(sizeof(bool) * ncolumns);
 
            for (i = 0; i < ncolumns; i++)
            {
@@ -214,11 +215,11 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
                info->indexcollations[i] = indexRelation->rd_indcollation[i];
                info->opfamily[i] = indexRelation->rd_opfamily[i];
                info->opcintype[i] = indexRelation->rd_opcintype[i];
+               info->canreturn[i] = index_can_return(indexRelation, i + 1);
            }
 
            info->relam = indexRelation->rd_rel->relam;
            info->amcostestimate = indexRelation->rd_am->amcostestimate;
-           info->canreturn = index_can_return(indexRelation);
            info->amcanorderbyop = indexRelation->rd_am->amcanorderbyop;
            info->amoptionalkey = indexRelation->rd_am->amoptionalkey;
            info->amsearcharray = indexRelation->rd_am->amsearcharray;
index d1d624721dbcb23c54442bc72e29424581d33f57..d86590ac111e6064c06a85b4dbc7b9979b9192b2 100644 (file)
@@ -156,7 +156,7 @@ extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info,
                  void *callback_state);
 extern IndexBulkDeleteResult *index_vacuum_cleanup(IndexVacuumInfo *info,
                     IndexBulkDeleteResult *stats);
-extern bool index_can_return(Relation indexRelation);
+extern bool index_can_return(Relation indexRelation, int attno);
 extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
                uint16 procnum);
 extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
index 01f0a70feee35a39f68f7522452628b6f43fd909..50261b8bdd5d4686a27e722c1f0317ccaec78ad4 100644 (file)
@@ -33,7 +33,8 @@
 #define GIST_PICKSPLIT_PROC                6
 #define GIST_EQUAL_PROC                    7
 #define GIST_DISTANCE_PROC             8
-#define GISTNProcs                     8
+#define GIST_FETCH_PROC                    9
+#define GISTNProcs                 9
 
 /*
  * strategy numbers for GiST opclasses that want to implement the old
index 955068791682fc66df9ab66394f8849482494bde..3693893e261dde172cb690d2262342a8ca312ca2 100644 (file)
@@ -87,6 +87,7 @@ typedef struct GISTSTATE
    FmgrInfo    picksplitFn[INDEX_MAX_KEYS];
    FmgrInfo    equalFn[INDEX_MAX_KEYS];
    FmgrInfo    distanceFn[INDEX_MAX_KEYS];
+   FmgrInfo    fetchFn[INDEX_MAX_KEYS];
 
    /* Collations to pass to the support functions */
    Oid         supportCollation[INDEX_MAX_KEYS];
@@ -118,6 +119,8 @@ typedef struct GISTSearchHeapItem
 {
    ItemPointerData heapPtr;
    bool        recheck;        /* T if quals must be rechecked */
+   IndexTuple  ftup;           /* data fetched back from the index, used in
+                                * index-only scans */
 } GISTSearchHeapItem;
 
 /* Unvisited item, either index page or heap tuple */
@@ -157,6 +160,8 @@ typedef struct GISTScanOpaqueData
    GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
    OffsetNumber nPageData;     /* number of valid items in array */
    OffsetNumber curPageData;   /* next item to return */
+   MemoryContext pageDataCxt;  /* context holding the fetched tuples, for
+                                  index-only scans */
 } GISTScanOpaqueData;
 
 typedef GISTScanOpaqueData *GISTScanOpaque;
@@ -409,6 +414,7 @@ typedef struct GiSTOptions
 /* gist.c */
 extern Datum gistbuildempty(PG_FUNCTION_ARGS);
 extern Datum gistinsert(PG_FUNCTION_ARGS);
+extern Datum gistcanreturn(PG_FUNCTION_ARGS);
 extern MemoryContext createTempGistContext(void);
 extern GISTSTATE *initGISTstate(Relation index);
 extern void freeGISTstate(GISTSTATE *giststate);
@@ -504,7 +510,8 @@ extern void gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
 extern bool gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b);
 extern void gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p,
                  OffsetNumber o, GISTENTRY *attdata, bool *isnull);
-
+extern IndexTuple gistFetchTuple(GISTSTATE *giststate, Relation r,
+              IndexTuple tuple);
 extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
                 GISTENTRY *entry1, bool isnull1,
                 GISTENTRY *entry2, bool isnull2,
index da6035f2c587c0a3ea3aa0346eb806e0a197c677..3d50f704021e8222f1779d7a1476fa965ce931a5 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 201503191
+#define CATALOG_VERSION_NO 201503261
 
 #endif
index 0531222a7eaa0a8504b01dff373cf210c5d13de7..79609f7774c82a6081e4a645fa8dbf83d613b0da 100644 (file)
@@ -123,7 +123,7 @@ DESCR("b-tree index access method");
 DATA(insert OID = 405 (  hash      1 1 f f t f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbuildempty hashbulkdelete hashvacuumcleanup - hashcostestimate hashoptions ));
 DESCR("hash index access method");
 #define HASH_AM_OID 405
-DATA(insert OID = 783 (  gist      0 8 f t f f t t f t t t f 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbuildempty gistbulkdelete gistvacuumcleanup - gistcostestimate gistoptions ));
+DATA(insert OID = 783 (  gist      0 9 f t f f t t f t t t f 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbuildempty gistbulkdelete gistvacuumcleanup gistcanreturn gistcostestimate gistoptions ));
 DESCR("GiST index access method");
 #define GIST_AM_OID 783
 DATA(insert OID = 2742 (  gin      0 6 f f f f t t f f t f f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbuildempty ginbulkdelete ginvacuumcleanup - gincostestimate ginoptions ));
index 49d3d13efbb5eb034129e7c06f584d1eb62c4c86..612a9d242e327b08a95b87ddef24b5bd34d3f07f 100644 (file)
@@ -191,6 +191,7 @@ DATA(insert (   1029   600 600 5 2581 ));
 DATA(insert (  1029   600 600 6 2582 ));
 DATA(insert (  1029   600 600 7 2584 ));
 DATA(insert (  1029   600 600 8 3064 ));
+DATA(insert (  1029   600 600 9 3282 ));
 DATA(insert (  2593   603 603 1 2578 ));
 DATA(insert (  2593   603 603 2 2583 ));
 DATA(insert (  2593   603 603 3 2579 ));
@@ -198,6 +199,7 @@ DATA(insert (   2593   603 603 4 2580 ));
 DATA(insert (  2593   603 603 5 2581 ));
 DATA(insert (  2593   603 603 6 2582 ));
 DATA(insert (  2593   603 603 7 2584 ));
+DATA(insert (  2593   603 603 9 3281 ));
 DATA(insert (  2594   604 604 1 2585 ));
 DATA(insert (  2594   604 604 2 2583 ));
 DATA(insert (  2594   604 604 3 2586 ));
index 3c218a3987346282d8e744fbc49c36149966cac7..77b77176a334c17e02fddc40cdb0cc6bb996eefe 100644 (file)
@@ -558,7 +558,7 @@ DATA(insert OID = 332 (  btbulkdelete      PGNSP PGUID 12 1 0 0 0 f f f f t f v 4
 DESCR("btree(internal)");
 DATA(insert OID = 972 (  btvacuumcleanup   PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ btvacuumcleanup _null_ _null_ _null_ ));
 DESCR("btree(internal)");
-DATA(insert OID = 276 (  btcanreturn      PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "2281" _null_ _null_ _null_ _null_ btcanreturn _null_ _null_ _null_ ));
+DATA(insert OID = 276 (  btcanreturn      PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ btcanreturn _null_ _null_ _null_ ));
 DESCR("btree(internal)");
 DATA(insert OID = 1268 (  btcostestimate   PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ btcostestimate _null_ _null_ _null_ ));
 DESCR("btree(internal)");
@@ -987,6 +987,8 @@ DATA(insert OID = 776 (  gistbulkdelete    PGNSP PGUID 12 1 0 0 0 f f f f t f v
 DESCR("gist(internal)");
 DATA(insert OID = 2561 (  gistvacuumcleanup   PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ gistvacuumcleanup _null_ _null_ _null_ ));
 DESCR("gist(internal)");
+DATA(insert OID = 3280 (  gistcanreturn       PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ gistcanreturn _null_ _null_ _null_ ));
+DESCR("gist(internal)");
 DATA(insert OID = 772 (  gistcostestimate  PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ gistcostestimate _null_ _null_ _null_ ));
 DESCR("gist(internal)");
 DATA(insert OID = 2787 (  gistoptions     PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_  gistoptions _null_ _null_ _null_ ));
@@ -4089,6 +4091,8 @@ DATA(insert OID = 2579 (  gist_box_compress       PGNSP PGUID 12 1 0 0 0 f f f f t f
 DESCR("GiST support");
 DATA(insert OID = 2580 (  gist_box_decompress  PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_box_decompress _null_ _null_ _null_ ));
 DESCR("GiST support");
+DATA(insert OID = 3281 (  gist_box_fetch   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_box_fetch _null_ _null_ _null_ ));
+DESCR("GiST support");
 DATA(insert OID = 2581 (  gist_box_penalty     PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_  gist_box_penalty _null_ _null_ _null_ ));
 DESCR("GiST support");
 DATA(insert OID = 2582 (  gist_box_picksplit   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_   gist_box_picksplit _null_ _null_ _null_ ));
@@ -4107,6 +4111,8 @@ DATA(insert OID = 2592 (  gist_circle_compress    PGNSP PGUID 12 1 0 0 0 f f f f t
 DESCR("GiST support");
 DATA(insert OID = 1030 (  gist_point_compress  PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_point_compress _null_ _null_ _null_ ));
 DESCR("GiST support");
+DATA(insert OID = 3282 (  gist_point_fetch PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ gist_point_fetch _null_ _null_ _null_ ));
+DESCR("GiST support");
 DATA(insert OID = 2179 (  gist_point_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 5 0 16 "2281 600 23 26 2281" _null_ _null_ _null_ _null_  gist_point_consistent _null_ _null_ _null_ ));
 DESCR("GiST support");
 DATA(insert OID = 3064 (  gist_point_distance  PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 701 "2281 600 23 26" _null_ _null_ _null_ _null_   gist_point_distance _null_ _null_ _null_ ));
@@ -5039,7 +5045,7 @@ DATA(insert OID = 4011 (  spgbulkdelete    PGNSP PGUID 12 1 0 0 0 f f f f t f v
 DESCR("spgist(internal)");
 DATA(insert OID = 4012 (  spgvacuumcleanup  PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ spgvacuumcleanup _null_ _null_ _null_ ));
 DESCR("spgist(internal)");
-DATA(insert OID = 4032 (  spgcanreturn    PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "2281" _null_ _null_ _null_ _null_ spgcanreturn _null_ _null_ _null_ ));
+DATA(insert OID = 4032 (  spgcanreturn    PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ spgcanreturn _null_ _null_ _null_ ));
 DESCR("spgist(internal)");
 DATA(insert OID = 4013 (  spgcostestimate  PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ spgcostestimate _null_ _null_ _null_ ));
 DESCR("spgist(internal)");
index 334cf51976bce067c5b942dc125f916c08614482..401a686664de329781234282a11100ccb7da4f3b 100644 (file)
@@ -520,6 +520,8 @@ typedef struct IndexOptInfo
    Oid        *sortopfamily;   /* OIDs of btree opfamilies, if orderable */
    bool       *reverse_sort;   /* is sort order descending? */
    bool       *nulls_first;    /* do NULLs come first in the sort order? */
+   bool       *canreturn;      /* which index cols can be returned in an
+                                  index-only scan? */
    Oid         relam;          /* OID of the access method (in pg_am) */
 
    RegProcedure amcostestimate;    /* OID of the access method's cost fcn */
@@ -533,7 +535,6 @@ typedef struct IndexOptInfo
    bool        unique;         /* true if a unique index */
    bool        immediate;      /* is uniqueness enforced immediately? */
    bool        hypothetical;   /* true if index doesn't really exist */
-   bool        canreturn;      /* can index return IndexTuples? */
    bool        amcanorderbyop; /* does AM support order by operator result? */
    bool        amoptionalkey;  /* can query omit key for the first column? */
    bool        amsearcharray;  /* can AM handle ScalarArrayOpExpr quals? */
index 8da6c6c987b2318d8dc724a2a219fb3ea6e70f44..2a91620db7463aca0751bc54f3334b43103f77b9 100644 (file)
@@ -410,6 +410,7 @@ extern Datum gist_box_picksplit(PG_FUNCTION_ARGS);
 extern Datum gist_box_consistent(PG_FUNCTION_ARGS);
 extern Datum gist_box_penalty(PG_FUNCTION_ARGS);
 extern Datum gist_box_same(PG_FUNCTION_ARGS);
+extern Datum gist_box_fetch(PG_FUNCTION_ARGS);
 extern Datum gist_poly_compress(PG_FUNCTION_ARGS);
 extern Datum gist_poly_consistent(PG_FUNCTION_ARGS);
 extern Datum gist_circle_compress(PG_FUNCTION_ARGS);
@@ -417,6 +418,8 @@ extern Datum gist_circle_consistent(PG_FUNCTION_ARGS);
 extern Datum gist_point_compress(PG_FUNCTION_ARGS);
 extern Datum gist_point_consistent(PG_FUNCTION_ARGS);
 extern Datum gist_point_distance(PG_FUNCTION_ARGS);
+extern Datum gist_point_fetch(PG_FUNCTION_ARGS);
+
 
 /* geo_selfuncs.c */
 extern Datum areasel(PG_FUNCTION_ARGS);
index 5603817c7731b1be9273e8d12471a9c628d5a925..abe64e597c74ccb0aa4c679bd85b34a5d047055c 100644 (file)
@@ -384,7 +384,7 @@ SELECT * FROM fast_emp4000
 ----------------------------------------------------------------
  Sort
    Sort Key: ((home_base[0])[0])
-   ->  Index Scan using grect2ind on fast_emp4000
+   ->  Index Only Scan using grect2ind on fast_emp4000
          Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
 (4 rows)
 
@@ -402,7 +402,7 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
                          QUERY PLAN                          
 -------------------------------------------------------------
  Aggregate
-   ->  Index Scan using grect2ind on fast_emp4000
+   ->  Index Only Scan using grect2ind on fast_emp4000
          Index Cond: (home_base && '(1000,1000),(0,0)'::box)
 (3 rows)
 
@@ -414,10 +414,10 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
 
 EXPLAIN (COSTS OFF)
 SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
-                    QUERY PLAN                    
---------------------------------------------------
+                      QUERY PLAN                       
+-------------------------------------------------------
  Aggregate
-   ->  Index Scan using grect2ind on fast_emp4000
+   ->  Index Only Scan using grect2ind on fast_emp4000
          Index Cond: (home_base IS NULL)
 (3 rows)
 
@@ -501,7 +501,7 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)';
                      QUERY PLAN                     
 ----------------------------------------------------
  Aggregate
-   ->  Index Scan using gpointind on point_tbl
+   ->  Index Only Scan using gpointind on point_tbl
          Index Cond: (f1 <@ '(100,100),(0,0)'::box)
 (3 rows)
 
@@ -516,8 +516,8 @@ SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1;
                      QUERY PLAN                     
 ----------------------------------------------------
  Aggregate
-   ->  Index Scan using gpointind on point_tbl
-         Index Cond: ('(100,100),(0,0)'::box @> f1)
+   ->  Index Only Scan using gpointind on point_tbl
+         Index Cond: (f1 <@ '(100,100),(0,0)'::box)
 (3 rows)
 
 SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1;
@@ -531,7 +531,7 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,
                                        QUERY PLAN                                       
 ----------------------------------------------------------------------------------------
  Aggregate
-   ->  Index Scan using gpointind on point_tbl
+   ->  Index Only Scan using gpointind on point_tbl
          Index Cond: (f1 <@ '((0,0),(0,100),(100,100),(50,50),(100,0),(0,0))'::polygon)
 (3 rows)
 
@@ -546,7 +546,7 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>';
                      QUERY PLAN                     
 ----------------------------------------------------
  Aggregate
-   ->  Index Scan using gpointind on point_tbl
+   ->  Index Only Scan using gpointind on point_tbl
          Index Cond: (f1 <@ '<(50,50),50>'::circle)
 (3 rows)
 
@@ -558,10 +558,10 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>';
 
 EXPLAIN (COSTS OFF)
 SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)';
-                   QUERY PLAN                    
--------------------------------------------------
+                      QUERY PLAN                      
+------------------------------------------------------
  Aggregate
-   ->  Index Scan using gpointind on point_tbl p
+   ->  Index Only Scan using gpointind on point_tbl p
          Index Cond: (f1 << '(0,0)'::point)
 (3 rows)
 
@@ -573,10 +573,10 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)';
 
 EXPLAIN (COSTS OFF)
 SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)';
-                   QUERY PLAN                    
--------------------------------------------------
+                      QUERY PLAN                      
+------------------------------------------------------
  Aggregate
-   ->  Index Scan using gpointind on point_tbl p
+   ->  Index Only Scan using gpointind on point_tbl p
          Index Cond: (f1 >> '(0,0)'::point)
 (3 rows)
 
@@ -588,10 +588,10 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)';
 
 EXPLAIN (COSTS OFF)
 SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)';
-                   QUERY PLAN                    
--------------------------------------------------
+                      QUERY PLAN                      
+------------------------------------------------------
  Aggregate
-   ->  Index Scan using gpointind on point_tbl p
+   ->  Index Only Scan using gpointind on point_tbl p
          Index Cond: (f1 <^ '(0,0)'::point)
 (3 rows)
 
@@ -603,10 +603,10 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)';
 
 EXPLAIN (COSTS OFF)
 SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)';
-                   QUERY PLAN                    
--------------------------------------------------
+                      QUERY PLAN                      
+------------------------------------------------------
  Aggregate
-   ->  Index Scan using gpointind on point_tbl p
+   ->  Index Only Scan using gpointind on point_tbl p
          Index Cond: (f1 >^ '(0,0)'::point)
 (3 rows)
 
@@ -618,10 +618,10 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)';
 
 EXPLAIN (COSTS OFF)
 SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
-                   QUERY PLAN                    
--------------------------------------------------
+                      QUERY PLAN                      
+------------------------------------------------------
  Aggregate
-   ->  Index Scan using gpointind on point_tbl p
+   ->  Index Only Scan using gpointind on point_tbl p
          Index Cond: (f1 ~= '(-5,-12)'::point)
 (3 rows)
 
@@ -633,9 +633,9 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
 
 EXPLAIN (COSTS OFF)
 SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
-               QUERY PLAN                
------------------------------------------
- Index Scan using gpointind on point_tbl
+                  QUERY PLAN                  
+----------------------------------------------
+ Index Only Scan using gpointind on point_tbl
    Order By: (f1 <-> '(0,1)'::point)
 (2 rows)
 
@@ -653,9 +653,9 @@ SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
 
 EXPLAIN (COSTS OFF)
 SELECT * FROM point_tbl WHERE f1 IS NULL;
-               QUERY PLAN                
------------------------------------------
- Index Scan using gpointind on point_tbl
+                  QUERY PLAN                  
+----------------------------------------------
+ Index Only Scan using gpointind on point_tbl
    Index Cond: (f1 IS NULL)
 (2 rows)
 
@@ -667,9 +667,9 @@ SELECT * FROM point_tbl WHERE f1 IS NULL;
 
 EXPLAIN (COSTS OFF)
 SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
-               QUERY PLAN                
------------------------------------------
- Index Scan using gpointind on point_tbl
+                  QUERY PLAN                  
+----------------------------------------------
+ Index Only Scan using gpointind on point_tbl
    Index Cond: (f1 IS NOT NULL)
    Order By: (f1 <-> '(0,1)'::point)
 (3 rows)
@@ -689,7 +689,7 @@ EXPLAIN (COSTS OFF)
 SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
                    QUERY PLAN                   
 ------------------------------------------------
- Index Scan using gpointind on point_tbl
+ Index Only Scan using gpointind on point_tbl
    Index Cond: (f1 <@ '(10,10),(-10,-10)'::box)
    Order By: (f1 <-> '(0,1)'::point)
 (3 rows)
index 7bceb7399934eec7a10b477a2f41aa531e714997..42f6891ffeec00b73c0a379b446218958d414619 100644 (file)
@@ -17,3 +17,149 @@ delete from gist_point_tbl where id % 2 = 1;
 -- would exercise it)
 delete from gist_point_tbl where id < 10000;
 vacuum gist_point_tbl;
+--
+-- Test Index-only plans on GiST indexes
+--
+create table gist_tbl (b box, p point, c circle);
+insert into gist_tbl
+select box(point(0.05*i, 0.05*i), point(0.05*i, 0.05*i)),
+       point(0.05*i, 0.05*i),
+       circle(point(0.05*i, 0.05*i), 1.0)
+from generate_series(0,10000) as i;
+vacuum analyze;
+set enable_seqscan=off;
+set enable_bitmapscan=off;
+set enable_indexonlyscan=on;
+-- Test index-only scan with point opclass
+create index gist_tbl_point_index on gist_tbl using gist (p);
+-- check that the planner chooses an index-only scan
+explain (costs off)
+select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5));
+                       QUERY PLAN                       
+--------------------------------------------------------
+ Index Only Scan using gist_tbl_point_index on gist_tbl
+   Index Cond: (p <@ '(0.5,0.5),(0,0)'::box)
+(2 rows)
+
+-- execute the same
+select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5));
+      p      
+-------------
+ (0,0)
+ (0.05,0.05)
+ (0.1,0.1)
+ (0.15,0.15)
+ (0.2,0.2)
+ (0.25,0.25)
+ (0.3,0.3)
+ (0.35,0.35)
+ (0.4,0.4)
+ (0.45,0.45)
+ (0.5,0.5)
+(11 rows)
+
+-- Also test an index-only knn-search
+explain (costs off)
+select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
+order by p <-> point(0.2, 0.2);
+                       QUERY PLAN                       
+--------------------------------------------------------
+ Index Only Scan using gist_tbl_point_index on gist_tbl
+   Index Cond: (p <@ '(0.5,0.5),(0,0)'::box)
+   Order By: (p <-> '(0.2,0.2)'::point)
+(3 rows)
+
+select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
+order by p <-> point(0.2, 0.2);
+      p      
+-------------
+ (0.2,0.2)
+ (0.25,0.25)
+ (0.15,0.15)
+ (0.3,0.3)
+ (0.1,0.1)
+ (0.35,0.35)
+ (0.05,0.05)
+ (0,0)
+ (0.4,0.4)
+ (0.45,0.45)
+ (0.5,0.5)
+(11 rows)
+
+drop index gist_tbl_point_index;
+-- Test index-only scan with box opclass
+create index gist_tbl_box_index on gist_tbl using gist (b);
+-- check that the planner chooses an index-only scan
+explain (costs off)
+select b from gist_tbl where b <@ box(point(5,5), point(6,6));
+                      QUERY PLAN                      
+------------------------------------------------------
+ Index Only Scan using gist_tbl_box_index on gist_tbl
+   Index Cond: (b <@ '(6,6),(5,5)'::box)
+(2 rows)
+
+-- execute the same
+select b from gist_tbl where b <@ box(point(5,5), point(6,6));
+            b            
+-------------------------
+ (5,5),(5,5)
+ (5.05,5.05),(5.05,5.05)
+ (5.1,5.1),(5.1,5.1)
+ (5.15,5.15),(5.15,5.15)
+ (5.2,5.2),(5.2,5.2)
+ (5.25,5.25),(5.25,5.25)
+ (5.3,5.3),(5.3,5.3)
+ (5.35,5.35),(5.35,5.35)
+ (5.4,5.4),(5.4,5.4)
+ (5.45,5.45),(5.45,5.45)
+ (5.5,5.5),(5.5,5.5)
+ (5.55,5.55),(5.55,5.55)
+ (5.6,5.6),(5.6,5.6)
+ (5.65,5.65),(5.65,5.65)
+ (5.7,5.7),(5.7,5.7)
+ (5.75,5.75),(5.75,5.75)
+ (5.8,5.8),(5.8,5.8)
+ (5.85,5.85),(5.85,5.85)
+ (5.9,5.9),(5.9,5.9)
+ (5.95,5.95),(5.95,5.95)
+ (6,6),(6,6)
+(21 rows)
+
+drop index gist_tbl_box_index;
+-- Test that an index-only scan is not chosen, when the query involves the
+-- circle column (the circle opclass does not support index-only scans).
+create index gist_tbl_multi_index on gist_tbl using gist (p, c);
+explain (costs off)
+select p, c from gist_tbl
+where p <@ box(point(5,5), point(6, 6));
+                    QUERY PLAN                     
+---------------------------------------------------
+ Index Scan using gist_tbl_multi_index on gist_tbl
+   Index Cond: (p <@ '(6,6),(5,5)'::box)
+(2 rows)
+
+-- execute the same
+select b, p from gist_tbl
+where b <@ box(point(4.5, 4.5), point(5.5, 5.5))
+and p <@ box(point(5,5), point(6, 6));
+            b            |      p      
+-------------------------+-------------
+ (5,5),(5,5)             | (5,5)
+ (5.05,5.05),(5.05,5.05) | (5.05,5.05)
+ (5.1,5.1),(5.1,5.1)     | (5.1,5.1)
+ (5.15,5.15),(5.15,5.15) | (5.15,5.15)
+ (5.2,5.2),(5.2,5.2)     | (5.2,5.2)
+ (5.25,5.25),(5.25,5.25) | (5.25,5.25)
+ (5.3,5.3),(5.3,5.3)     | (5.3,5.3)
+ (5.35,5.35),(5.35,5.35) | (5.35,5.35)
+ (5.4,5.4),(5.4,5.4)     | (5.4,5.4)
+ (5.45,5.45),(5.45,5.45) | (5.45,5.45)
+ (5.5,5.5),(5.5,5.5)     | (5.5,5.5)
+(11 rows)
+
+drop index gist_tbl_multi_index;
+-- Clean up
+reset enable_seqscan;
+reset enable_bitmapscan;
+reset enable_indexonlyscan;
+drop table gist_tbl;
index 8c345d8b9d61214ea8ebb24184d9200d44e62360..d6cbc21717f62a1f3be4dd8fa6e60522f7d4783e 100644 (file)
@@ -23,3 +23,76 @@ delete from gist_point_tbl where id % 2 = 1;
 delete from gist_point_tbl where id < 10000;
 
 vacuum gist_point_tbl;
+
+
+--
+-- Test Index-only plans on GiST indexes
+--
+
+create table gist_tbl (b box, p point, c circle);
+
+insert into gist_tbl
+select box(point(0.05*i, 0.05*i), point(0.05*i, 0.05*i)),
+       point(0.05*i, 0.05*i),
+       circle(point(0.05*i, 0.05*i), 1.0)
+from generate_series(0,10000) as i;
+
+vacuum analyze;
+
+set enable_seqscan=off;
+set enable_bitmapscan=off;
+set enable_indexonlyscan=on;
+
+-- Test index-only scan with point opclass
+create index gist_tbl_point_index on gist_tbl using gist (p);
+
+-- check that the planner chooses an index-only scan
+explain (costs off)
+select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5));
+
+-- execute the same
+select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5));
+
+-- Also test an index-only knn-search
+explain (costs off)
+select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
+order by p <-> point(0.2, 0.2);
+
+select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
+order by p <-> point(0.2, 0.2);
+
+drop index gist_tbl_point_index;
+
+-- Test index-only scan with box opclass
+create index gist_tbl_box_index on gist_tbl using gist (b);
+
+-- check that the planner chooses an index-only scan
+explain (costs off)
+select b from gist_tbl where b <@ box(point(5,5), point(6,6));
+
+-- execute the same
+select b from gist_tbl where b <@ box(point(5,5), point(6,6));
+
+drop index gist_tbl_box_index;
+
+-- Test that an index-only scan is not chosen, when the query involves the
+-- circle column (the circle opclass does not support index-only scans).
+create index gist_tbl_multi_index on gist_tbl using gist (p, c);
+
+explain (costs off)
+select p, c from gist_tbl
+where p <@ box(point(5,5), point(6, 6));
+
+-- execute the same
+select b, p from gist_tbl
+where b <@ box(point(4.5, 4.5), point(5.5, 5.5))
+and p <@ box(point(5,5), point(6, 6));
+
+drop index gist_tbl_multi_index;
+
+-- Clean up
+reset enable_seqscan;
+reset enable_bitmapscan;
+reset enable_indexonlyscan;
+
+drop table gist_tbl;