]> granicus.if.org Git - postgresql/commitdiff
Add support for index-only scans in GiST.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 26 Mar 2015 17:12:00 +0000 (19:12 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
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)
       <entry><structfield>amcanreturn</structfield></entry>
       <entry><type>regproc</type></entry>
       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
-      <entry>Function to check whether index supports index-only scans,
-       or zero if none</entry>
+      <entry>Function to check whether an index column supports index-only
+       scans. Can be zero if index-only scans are never supported.</entry>
      </row>
 
      <row>
index 31ce2790047e5487d5e24e08200eefaaa2a5c85c..e7d1ff9d83f4f9b469480dc6dfc22303a2d51c4a 100644 (file)
@@ -266,7 +266,7 @@ CREATE INDEX ON my_table USING gist (my_inet_column inet_ops);
 
  <para>
    There are seven methods that an index operator class for
-   <acronym>GiST</acronym> must provide, and an eighth that is optional.
+   <acronym>GiST</acronym> must provide, and two that are optional.
    Correctness of the index is ensured
    by proper implementation of the <function>same</>, <function>consistent</>
    and <function>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 <command>CREATE OPERATOR CLASS</> command can be used.
    The optional eighth method is <function>distance</>, which is needed
    if the operator class wishes to support ordered scans (nearest-neighbor
-   searches).
+   searches). The optional ninth method <function>fetch</> is needed if the
+   operator class wishes to support index-only scans.
  </para>
 
  <variablelist>
@@ -506,7 +507,7 @@ my_compress(PG_FUNCTION_ARGS)
       <para>
        The reverse of the <function>compress</function> 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.
       </para>
 
       <para>
@@ -807,6 +808,72 @@ my_distance(PG_FUNCTION_ARGS)
      </listitem>
     </varlistentry>
 
+    <varlistentry>
+     <term><function>fetch</></term>
+     <listitem>
+      <para>
+       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.
+      </para>
+
+      <para>
+        The <acronym>SQL</> declaration of the function must look like this:
+
+<programlisting>
+CREATE OR REPLACE FUNCTION my_fetch(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+</programlisting>
+
+        The argument is a pointer to a <structname>GISTENTRY</> struct. On
+        entry, its 'key' field contains a non-NULL leaf datum in its
+        compressed form. The return value is another <structname>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.
+       </para> 
+
+       <para>
+        The matching code in the C module could then follow this skeleton:
+
+<programlisting>
+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);
+}
+</programlisting>
+      </para>
+
+      <para>
+       If the compress method is lossy for leaf entries, the operator class
+       cannot support index-only scans, and must not define a 'fetch'
+       function.
+      </para>
+
+     </listitem>
+    </varlistentry>
   </variablelist>
 
   <para>
index 157047a23ab15ab86204db66b9e38d0aa64fae2f..1c09bae3955f88e3ea3d3c8150157b29c5531e99 100644 (file)
@@ -274,14 +274,15 @@ amvacuumcleanup (IndexVacuumInfo *info,
   <para>
 <programlisting>
 bool
-amcanreturn (Relation indexRelation);
+amcanreturn (Relation indexRelation, int attno);
 </programlisting>
-   Check whether the index can support <firstterm>index-only scans</> by
-   returning the indexed column values for an index entry in the form of an
-   <structname>IndexTuple</structname>.  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
-   <structfield>amcanreturn</> field to zero in <structname>pg_am</>.
+   Check whether the index can support <firstterm>index-only scans</> on the
+   given column, by returning the indexed column values for an index entry in
+   the form of an <structname>IndexTuple</structname>.  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 <structfield>amcanreturn</> field in its <structname>pg_am</> row can
+   be set to zero.
   </para>
 
   <para>
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;