Thanks for answer.
Now it seems to be applied correctly.

2015-02-12 3:12 GMT+04:00 Thom Brown <t...@linux.com>:

> On 11 February 2015 at 22:50, Anastasia Lubennikova <
> lubennikov...@gmail.com> wrote:
>
>> Finally there is a new version of patch (in attachments).
>> It provides multicolumn index-only scan for GiST indexes.
>>
>> - Memory leak is fixed.
>> - little code cleanup
>> - example of performance test in attachmens
>> - function OIDs have debugging values (1111*) just to avoid merge
>> conflicts while testing patch
>>
>> Wiki page of the project is
>>
>> https://wiki.postgresql.org/wiki/Support_for_Index-only_scans_for_GIST_GSoC_2014
>>
>> Waiting for feedback.
>>
>
> Hi Anastasia.  Thanks for the updated patch.  I've just tried applying it
> to head and it doesn't appear to apply cleanly.
>
> $ patch -p1 < ~/Downloads/indexonlyscan_gist_2.0.patch
> (Stripping trailing CRs from patch; use --binary to disable.)
> patching file src/backend/access/gist/gist.c
> Hunk #1 succeeded at 1404 (offset 9 lines).
> Hunk #2 succeeded at 1434 (offset 9 lines).
> (Stripping trailing CRs from patch; use --binary to disable.)
> patching file src/backend/access/gist/gistget.c
> Hunk #1 succeeded at 227 (offset 1 line).
> Hunk #2 succeeded at 243 (offset 1 line).
> Hunk #3 succeeded at 293 (offset -4 lines).
> Hunk #4 succeeded at 330 (offset -4 lines).
> Hunk #5 succeeded at 365 (offset -5 lines).
> Hunk #6 succeeded at 444 (offset -27 lines).
> Hunk #7 succeeded at 474 (offset -27 lines).
> Hunk #8 FAILED at 518.
> Hunk #9 succeeded at 507 (offset -28 lines).
> Hunk #10 succeeded at 549 with fuzz 1 (offset -28 lines).
> Hunk #11 FAILED at 601.
> 2 out of 11 hunks FAILED -- saving rejects to file
> src/backend/access/gist/gistget.c.rej
> ...
>
> --
> Thom
>



-- 
Best regards,
Lubennikova Anastasia
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index db2a452..53750da 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -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
@@ -1426,6 +1434,22 @@ initGISTstate(Relation index)
 	return giststate;
 }
 
+/*
+ * Gistcanreturn is supposed to be true if ANY FetchFn method is defined.
+ * If FetchFn exists it would be used in index-only scan
+ * Thus the responsibility rests with the opclass developer.
+ */
+
+Datum
+gistcanreturn(PG_FUNCTION_ARGS) {
+	Relation index = (Relation) PG_GETARG_POINTER(0);
+	int i = PG_GETARG_INT32(1);
+	if (OidIsValid(index_getprocid(index, i+1, GIST_FETCH_PROC)))
+		PG_RETURN_BOOL(true);
+	else
+		PG_RETURN_BOOL(false);
+}
+
 void
 freeGISTstate(GISTSTATE *giststate)
 {
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index 717cb85..0925e56 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -227,8 +227,10 @@ gistindex_keytest(IndexScanDesc scan,
  * If tbm/ntids aren't NULL, we are doing an amgetbitmap scan, and heap
  * 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,
+ * tuple TIDs are returned into so->pageData. For an ordered indexscan,
  * heap tuple TIDs are pushed into individual search queue items.
+ * If index-only scan is possible, heap tuples themselves are returned
+ * into so->pageData or into search queue.
  *
  * 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
@@ -241,6 +243,10 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
 	Buffer		buffer;
 	Page		page;
+	GISTSTATE *giststate = so->giststate;
+	Relation r = scan->indexRelation;
+	bool        isnull[INDEX_MAX_KEYS];
+	GISTSearchHeapItem *tmpListItem;
 	GISTPageOpaque opaque;
 	OffsetNumber maxoff;
 	OffsetNumber i;
@@ -287,8 +293,6 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 		MemoryContextSwitchTo(oldcxt);
 	}
 
-	so->nPageData = so->curPageData = 0;
-
 	/*
 	 * check all tuples on page
 	 */
@@ -326,11 +330,20 @@ 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
+			 */
+
+			/* form tmpListItem and fill it with data to add into so->pageData */
+			tmpListItem = palloc(sizeof(GISTSearchHeapItem));
+			tmpListItem->heapPtr = it->t_tid;
+			tmpListItem->recheck = recheck;
+			/*
+			 * If index-only scan is possible add the data fetched from index field
 			 */
-			so->pageData[so->nPageData].heapPtr = it->t_tid;
-			so->pageData[so->nPageData].recheck = recheck;
-			so->nPageData++;
+			if (scan->xs_want_itup)
+				tmpListItem->ftup = gistFetchTuple(giststate, r, it, isnull);
+
+			so->pageData = lappend(so->pageData, tmpListItem);
 		}
 		else
 		{
@@ -352,6 +365,13 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
 				item->blkno = InvalidBlockNumber;
 				item->data.heap.heapPtr = it->t_tid;
 				item->data.heap.recheck = recheck;
+
+				/*
+				 * If index-only scan is possible add the data fetched from index field
+				 */
+				if (scan->xs_want_itup) {
+					item->data.heap.ftup = gistFetchTuple(giststate, r, it, isnull);
+				}
 			}
 			else
 			{
@@ -424,6 +444,11 @@ 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;
+			/*
+			 * If index-only scan is possible fill the slot with data fetched from index field
+			 */
+			if(scan->xs_want_itup)
+				scan->xs_itup = item->data.heap.ftup;
 			res = true;
 		}
 		else
@@ -449,6 +474,7 @@ gistgettuple(PG_FUNCTION_ARGS)
 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
 	ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
 	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
+	bool first_tuple = true;
 
 	if (dir != ForwardScanDirection)
 		elog(ERROR, "GiST only supports forward scan direction");
@@ -464,7 +490,6 @@ gistgettuple(PG_FUNCTION_ARGS)
 		pgstat_count_index_scan(scan->indexRelation);
 
 		so->firstCall = false;
-		so->curPageData = so->nPageData = 0;
 
 		fakeItem.blkno = GIST_ROOT_BLKNO;
 		memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
@@ -481,13 +506,27 @@ gistgettuple(PG_FUNCTION_ARGS)
 		/* Fetch tuples index-page-at-a-time */
 		for (;;)
 		{
-			if (so->curPageData < so->nPageData)
+			if(list_length(so->pageData)>0)
 			{
 				/* 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;
-				so->curPageData++;
-				PG_RETURN_BOOL(true);
+				GISTSearchHeapItem *tmp = (GISTSearchHeapItem *)lfirst(list_head(so->pageData));
+				scan->xs_ctup.t_self = tmp->heapPtr;
+				scan->xs_recheck = tmp->recheck;
+				/* If index-only scan is possible, return fetched data*/
+				if(scan->xs_want_itup) {
+					scan->xs_itup = tmp->ftup;
+					if(!first_tuple)
+						pfree(tmp->ftup);
+					first_tuple=false;
+				}
+				pfree(tmp);
+
+				/*
+				 * Delete ListCell that we have already read.
+				 * It's always head of so->pageData
+				 */
+				so->pageData = list_delete_first(so->pageData);
+				PG_RETURN_BOOL(TRUE);
 			}
 
 			/* find and process the next index page */
@@ -509,7 +548,7 @@ gistgettuple(PG_FUNCTION_ARGS)
 				gistScanPage(scan, item, item->distances, NULL, NULL);
 
 				pfree(item);
-			} while (so->nPageData == 0);
+			} while (list_length(so->pageData)==0);
 		}
 	}
 }
@@ -532,7 +571,6 @@ gistgetbitmap(PG_FUNCTION_ARGS)
 	pgstat_count_index_scan(scan->indexRelation);
 
 	/* Begin the scan by processing the root page */
-	so->curPageData = so->nPageData = 0;
 
 	fakeItem.blkno = GIST_ROOT_BLKNO;
 	memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
index 9fab6c8..e1355dd 100644
--- a/src/backend/access/gist/gistproc.c
+++ b/src/backend/access/gist/gistproc.c
@@ -152,6 +152,16 @@ gist_box_decompress(PG_FUNCTION_ARGS)
 }
 
 /*
+ * 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)
  *
  * As in the R-tree paper, we use change in area as our penalty metric
@@ -1186,6 +1196,41 @@ gist_point_compress(PG_FUNCTION_ARGS)
 	PG_RETURN_POINTER(entry);
 }
 
+/*
+ * GiST Fetch method for point
+ * get point coordinates from it's bounding box coordinates
+ * and form new gistentry
+ */
+Datum
+gist_point_fetch(PG_FUNCTION_ARGS)
+{
+	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	GISTENTRY  *retval;
+		retval = palloc(sizeof(GISTENTRY));
+		if (DatumGetBoxP(entry->key) != NULL)
+		{
+			BOX	*in = DatumGetBoxP(entry->key);
+			Point	*r;
+
+			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);
+
+		}
+		else
+		{
+			gistentryinit(*retval, (Datum) 0,
+						  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)))
diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c
index cc8d818..98a1b54 100644
--- a/src/backend/access/gist/gistscan.c
+++ b/src/backend/access/gist/gistscan.c
@@ -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.
+	 */
+	so->pageData = NULL;
+	scan->xs_itupdesc = RelationGetDescr(r);
+
 	MemoryContextSwitchTo(oldCxt);
 
 	PG_RETURN_POINTER(scan);
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 38af0e0..b03966a 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -618,6 +618,65 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
 	return res;
 }
 
+/*
+ * initialize a GiST entry with fetched value in key field
+ */
+void
+gistfentryinit(GISTSTATE *giststate, int nkey,
+			   GISTENTRY *e, Datum k, Relation r,
+			   Page pg, OffsetNumber o, bool l, bool isNull)
+{
+
+	if (!isNull)
+	{
+
+		GISTENTRY  *fep;
+
+		gistentryinit(*e, k, r, pg, o, l);
+
+
+		fep = (GISTENTRY *)
+			DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
+										   giststate->supportCollation[nkey],
+											  PointerGetDatum(e)));
+		/* fecthFn returns the given pointer */
+		if (fep != e)
+			gistentryinit(*e, fep->key, fep->rel, fep->page, fep->offset,
+						  fep->leafkey);
+	}
+	else
+		gistentryinit(*e, (Datum) 0, r, pg, o, l);
+}
+
+/*
+ * Fetch all keys in tuple.
+ * returns new IndexTuple that contains GISTENTRY with fetched data
+ */
+IndexTuple
+gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple, bool isnull[])
+{
+	MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt);
+	GISTENTRY	fentry[INDEX_MAX_KEYS];
+	Datum		fetchatt[INDEX_MAX_KEYS];
+	int		i;
+	IndexTuple	res;
+
+
+	for (i = 0; i < r->rd_att->natts; i++)
+	{
+		Datum datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
+		gistfentryinit(giststate, i, &fentry[i],
+					   datum, r, NULL, (OffsetNumber) 0,
+					   FALSE, FALSE);
+
+		fetchatt[i] = fentry[i].key;
+
+	}
+	MemoryContextSwitchTo(oldcxt);
+
+	return index_form_tuple(giststate->tupdesc, fetchatt, isnull);
+}
+
 float
 gistpenalty(GISTSTATE *giststate, int attno,
 			GISTENTRY *orig, bool isNullOrig,
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 00c1d69..62b10ce 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -722,11 +722,11 @@ index_vacuum_cleanup(IndexVacuumInfo *info,
 }
 
 /* ----------------
- *		index_can_return - does index support index-only scans?
+ *		index_can_return - does index column with number 'attno' supports index-only scans?
  * ----------------
  */
 bool
-index_can_return(Relation indexRelation)
+index_can_return(Relation indexRelation, int attno)
 {
 	FmgrInfo   *procedure;
 
@@ -738,8 +738,9 @@ index_can_return(Relation indexRelation)
 
 	GET_REL_PROCEDURE(amcanreturn);
 
-	return DatumGetBool(FunctionCall1(procedure,
-									  PointerGetDatum(indexRelation)));
+	return DatumGetBool(FunctionCall2(procedure,
+					  PointerGetDatum(indexRelation),
+					  Int32GetDatum(attno)));
 }
 
 /* ----------------
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index b86a3cd..8e08f2e 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -1782,14 +1782,13 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
 	bool		result;
 	Bitmapset  *attrs_used = NULL;
 	Bitmapset  *index_attrs = NULL;
+	Bitmapset  *index_only_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
@@ -1834,14 +1833,17 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
 		index_attrs =
 			bms_add_member(index_attrs,
 						   attno - FirstLowInvalidHeapAttributeNumber);
+		if (index->canreturn[i])
+			index_only_attrs = bms_add_member(index_only_attrs,
+						   attno - FirstLowInvalidHeapAttributeNumber);
 	}
 
-	/* Do we have all the necessary attributes? */
-	result = bms_is_subset(attrs_used, index_attrs);
-
+	/* Do we have all the necessary attributes? And do all of them support index-only scan? */
+	result = ((bms_is_subset(attrs_used, index_attrs))&&
+			(bms_is_subset(attrs_used, index_only_attrs)));
 	bms_free(attrs_used);
 	bms_free(index_attrs);
-
+	bms_free(index_only_attrs);
 	return result;
 }
 
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index fb7db6d..192f418 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -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);
 			}
 
 			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;
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index d1d6247..d86590a 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -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,
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 01f0a70..50261b8 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -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
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 382826e..14f83d4 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -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];
@@ -109,7 +110,7 @@ typedef struct GISTSTATE
  * In a non-ordered search (no order-by operators), the RBTree degenerates
  * to a single item, which we use as a queue of unvisited index pages only.
  * In this case matched heap items from the current index leaf page are
- * remembered in GISTScanOpaqueData.pageData[] and returned directly from
+ * remembered in GISTScanOpaqueData.pageData and returned directly from
  * there, instead of building a separate GISTSearchItem for each one.
  */
 
@@ -118,6 +119,7 @@ typedef struct GISTSearchHeapItem
 {
 	ItemPointerData heapPtr;
 	bool		recheck;		/* T if quals must be rechecked */
+	IndexTuple ftup;		/* Tuple contains datum fetched from key for index-only scans */
 } GISTSearchHeapItem;
 
 /* Unvisited item, either index page or heap tuple */
@@ -153,9 +155,8 @@ typedef struct GISTScanOpaqueData
 	double	   *distances;		/* output area for gistindex_keytest */
 
 	/* In a non-ordered search, returnable heap items are stored here: */
-	GISTSearchHeapItem pageData[BLCKSZ / sizeof(IndexTupleData)];
-	OffsetNumber nPageData;		/* number of valid items in array */
-	OffsetNumber curPageData;	/* next item to return */
+	List *pageData;
+
 } GISTScanOpaqueData;
 
 typedef GISTScanOpaqueData *GISTScanOpaque;
@@ -408,6 +409,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);
@@ -508,6 +510,10 @@ 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 void gistfentryinit(GISTSTATE *giststate, int nkey,
+			   GISTENTRY *e, Datum k, Relation r,
+			   Page pg, OffsetNumber o, bool l, bool isNull);
+extern IndexTuple gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple, bool *isnull);
 extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
 				 GISTENTRY *entry1, bool isnull1,
 				 GISTENTRY *entry2, bool isnull2,
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 0531222..79609f7 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -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 ));
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index 49d3d13..c71cb8f 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -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 11113 ));
 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 11112 ));
 DATA(insert (	2594   604 604 1 2585 ));
 DATA(insert (	2594   604 604 2 2583 ));
 DATA(insert (	2594   604 604 3 2586 ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 9edfdb8..dc8ddea 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -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 1 0 16 "2281 2281" _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)");
@@ -981,6 +981,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 = 11111 (  gistcanreturn	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "2281 2281" _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_ ));
@@ -4062,6 +4064,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 = 11112 (  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_ ));
@@ -4080,6 +4084,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 = 11113 (  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_ ));
@@ -5012,7 +5018,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 1 0 16 "2281 2281" _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)");
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 6845a40..e134e38 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -533,7 +533,7 @@ 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		*canreturn;		/* can index columns 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? */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 0b6d3c3..786096e 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -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);
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 5603817..abe64e5 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -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)
diff --git a/src/test/regress/expected/gist_indexonly.out b/src/test/regress/expected/gist_indexonly.out
new file mode 100644
index 0000000..0a51cdd
--- /dev/null
+++ b/src/test/regress/expected/gist_indexonly.out
@@ -0,0 +1,50 @@
+--
+-- Test Index-only scan plan on GiST indexes
+--
+CREATE TABLE gist_tbl (b box, p point);
+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) FROM generate_series(0,10000) as i;
+vacuum analyze;
+SET enable_seqscan=off;
+SET enable_bitmapscan=off;
+-- Check singlecolumn index-only scan for point opclass
+CREATE INDEX gist_tbl_point_index ON gist_tbl USING gist (p);
+EXPLAIN (COSTS OFF)
+select p from gist_tbl where p <@ box(point(0,0),  point(100,100)) and length(p::text) < 10;
+                       QUERY PLAN                       
+--------------------------------------------------------
+ Index Only Scan using gist_tbl_point_index on gist_tbl
+   Index Cond: (p <@ '(100,100),(0,0)'::box)
+   Filter: (length((p)::text) < 10)
+(3 rows)
+
+DROP INDEX gist_tbl_point_index;
+-- Check singlecolumn index-only scan for box opclass
+CREATE INDEX gist_tbl_box_index ON gist_tbl USING gist (b);
+vacuum analyze;
+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)
+
+DROP INDEX gist_tbl_box_index;
+-- Check multicolumn indexonlyscan for gist
+CREATE INDEX gist_tbl_multi_index ON gist_tbl USING gist (b, p);
+vacuum analyze;
+EXPLAIN (COSTS OFF)
+select b, p from gist_tbl where ( (b <@ box(point(5,5),  point(6,6))) and (p <@ box(point(5,5),  point(5.5,5.5))));
+                                 QUERY PLAN                                  
+-----------------------------------------------------------------------------
+ Index Only Scan using gist_tbl_multi_index on gist_tbl
+   Index Cond: ((b <@ '(6,6),(5,5)'::box) AND (p <@ '(5.5,5.5),(5,5)'::box))
+(2 rows)
+
+DROP INDEX gist_tbl_multi_index;
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+RESET enable_indexscan;
+RESET enable_indexonlyscan;
+DROP TABLE gist_tbl;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index e0ae2f2..758c326 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -96,7 +96,7 @@ test: rules
 # ----------
 # Another group of parallel tests
 # ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast equivclass
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb indirect_toast equivclass gist_indexonly
 # ----------
 # Another group of parallel tests
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 7f762bd..47803a3 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -91,6 +91,7 @@ test: portals
 test: arrays
 test: btree_index
 test: hash_index
+test: gist_indexonly
 test: update
 test: delete
 test: namespace
diff --git a/src/test/regress/sql/gist_indexonly.sql b/src/test/regress/sql/gist_indexonly.sql
new file mode 100644
index 0000000..49da865
--- /dev/null
+++ b/src/test/regress/sql/gist_indexonly.sql
@@ -0,0 +1,43 @@
+--
+-- Test Index-only scan plan on GiST indexes
+--
+
+CREATE TABLE gist_tbl (b box, p point);
+
+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) FROM generate_series(0,10000) as i;
+
+vacuum analyze;
+
+SET enable_seqscan=off;
+SET enable_bitmapscan=off;
+
+-- Check singlecolumn index-only scan for point opclass
+
+CREATE INDEX gist_tbl_point_index ON gist_tbl USING gist (p);
+EXPLAIN (COSTS OFF)
+select p from gist_tbl where p <@ box(point(0,0),  point(100,100)) and length(p::text) < 10;
+DROP INDEX gist_tbl_point_index;
+
+-- Check singlecolumn index-only scan for box opclass
+
+CREATE INDEX gist_tbl_box_index ON gist_tbl USING gist (b);
+vacuum analyze;
+EXPLAIN (COSTS OFF)
+select b from gist_tbl where b <@ box(point(5,5),  point(6,6));
+DROP INDEX gist_tbl_box_index;
+
+-- Check multicolumn indexonlyscan for gist
+
+CREATE INDEX gist_tbl_multi_index ON gist_tbl USING gist (b, p);
+vacuum analyze;
+EXPLAIN (COSTS OFF)
+select b, p from gist_tbl where ( (b <@ box(point(5,5),  point(6,6))) and (p <@ box(point(5,5),  point(5.5,5.5))));
+DROP INDEX gist_tbl_multi_index;
+
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+RESET enable_indexscan;
+RESET enable_indexonlyscan;
+
+DROP TABLE gist_tbl;
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to