*** a/src/backend/access/gist/gist.c
--- b/src/backend/access/gist/gist.c
***************
*** 1379,1384 **** initGISTstate(Relation index)
--- 1379,1392 ----
  		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
***************
*** 1401,1406 **** initGISTstate(Relation index)
--- 1409,1430 ----
  	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)
  {
*** a/src/backend/access/gist/gistget.c
--- b/src/backend/access/gist/gistget.c
***************
*** 226,233 **** 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,
   * heap tuple TIDs are pushed into individual search queue items.
   *
   * 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
--- 226,235 ----
   * 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,
   * 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
***************
*** 240,245 **** gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
--- 242,251 ----
  	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;
***************
*** 288,297 **** gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
  
  		(void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew);
  
  		MemoryContextSwitchTo(oldcxt);
  	}
  
! 	so->nPageData = so->curPageData = 0;
  
  	/*
  	 * check all tuples on page
--- 294,305 ----
  
  		(void) rb_insert(so->queue, (RBNode *) tmpItem, &isNew);
  
+ 		/* Create new GISTSearchHeapItem to insert into pageData*/
+ 
  		MemoryContextSwitchTo(oldcxt);
  	}
  
! 	so->curPageData = NULL;
  
  	/*
  	 * check all tuples on page
***************
*** 330,340 **** gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
  		else if (scan->numberOfOrderBys == 0 && GistPageIsLeaf(page))
  		{
  			/*
! 			 * Non-ordered scan, so report heap tuples in so->pageData[]
  			 */
! 			so->pageData[so->nPageData].heapPtr = it->t_tid;
! 			so->pageData[so->nPageData].recheck = recheck;
! 			so->nPageData++;
  		}
  		else
  		{
--- 338,363 ----
  		else if (scan->numberOfOrderBys == 0 && GistPageIsLeaf(page))
  		{
  			/*
! 			 * Non-oredered scan, so tuples report in so->pageData
! 			 */
! 			oldcxt = MemoryContextSwitchTo(so->queueCxt);
! 			/* 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 fill the slot with data fetched from index field
  			 */
! 			if (scan->xs_want_itup)
! 				tmpListItem->ftup = gistFetchTuple(giststate, r, it, isnull);
! 
! 			so->pageData = lappend(so->pageData, tmpListItem);
! 			/*
! 			 * If it's first call of lappend() we should set so->curPageData not NULL
! 			 */
! 			if(so->curPageData == NULL)
! 				so->curPageData = list_head(so->pageData);
! 			MemoryContextSwitchTo(oldcxt);
  		}
  		else
  		{
***************
*** 357,362 **** gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem, double *myDistances,
--- 380,390 ----
  				item->blkno = InvalidBlockNumber;
  				item->data.heap.heapPtr = it->t_tid;
  				item->data.heap.recheck = recheck;
+ 				/*
+ 				 * If index-only scan is possible fill the slot with data fetched from index field
+ 				 */
+ 				if (scan->xs_want_itup)
+ 					item->data.heap.ftup = gistFetchTuple(giststate, r, it, isnull);
  			}
  			else
  			{
***************
*** 451,456 **** getNextNearest(IndexScanDesc scan)
--- 479,489 ----
  			/* 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
***************
*** 492,498 **** gistgettuple(PG_FUNCTION_ARGS)
  
  		so->firstCall = false;
  		so->curTreeItem = NULL;
! 		so->curPageData = so->nPageData = 0;
  
  		fakeItem.blkno = GIST_ROOT_BLKNO;
  		memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
--- 525,532 ----
  
  		so->firstCall = false;
  		so->curTreeItem = NULL;
! 
! 		so->curPageData = NULL;
  
  		fakeItem.blkno = GIST_ROOT_BLKNO;
  		memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
***************
*** 509,521 **** gistgettuple(PG_FUNCTION_ARGS)
  		/* Fetch tuples index-page-at-a-time */
  		for (;;)
  		{
! 			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;
! 				so->curPageData++;
! 				PG_RETURN_BOOL(true);
  			}
  
  			/* find and process the next index page */
--- 543,567 ----
  		/* Fetch tuples index-page-at-a-time */
  		for (;;)
  		{
! 			if(so->curPageData!=NULL)
  			{
  				/* continuing to return tuples from a leaf page */
! 				GISTSearchHeapItem *tmp = (GISTSearchHeapItem *)lfirst(so->curPageData);
! 				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;
! 
! 				ListCell *tmpPageData = so->curPageData;
! 				/* Go to the next ListCell */
! 				so->curPageData = lnext(so->curPageData);
! 				/*
! 				 * Delete ListCell that we have already read.
! 				 * It's always head of so->pageData
! 				 */
! 				so->pageData =  list_delete_cell(so->pageData, tmpPageData, NULL);
! 				PG_RETURN_BOOL(TRUE);
  			}
  
  			/* find and process the next index page */
***************
*** 537,543 **** gistgettuple(PG_FUNCTION_ARGS)
  				gistScanPage(scan, item, so->curTreeItem->distances, NULL, NULL);
  
  				pfree(item);
! 			} while (so->nPageData == 0);
  		}
  	}
  }
--- 583,589 ----
  				gistScanPage(scan, item, so->curTreeItem->distances, NULL, NULL);
  
  				pfree(item);
! 			} while (list_length(so->pageData)==0);
  		}
  	}
  }
***************
*** 561,567 **** gistgetbitmap(PG_FUNCTION_ARGS)
  
  	/* Begin the scan by processing the root page */
  	so->curTreeItem = NULL;
! 	so->curPageData = so->nPageData = 0;
  
  	fakeItem.blkno = GIST_ROOT_BLKNO;
  	memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
--- 607,613 ----
  
  	/* Begin the scan by processing the root page */
  	so->curTreeItem = NULL;
! 	so->curPageData = NULL;
  
  	fakeItem.blkno = GIST_ROOT_BLKNO;
  	memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
*** a/src/backend/access/gist/gistproc.c
--- b/src/backend/access/gist/gistproc.c
***************
*** 152,157 **** gist_box_decompress(PG_FUNCTION_ARGS)
--- 152,167 ----
  }
  
  /*
+  * GiST Fetch method for boxes
+  * do not do anything --- we just use 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
***************
*** 1204,1209 **** gist_point_compress(PG_FUNCTION_ARGS)
--- 1214,1253 ----
  	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)))
*** a/src/backend/access/gist/gistscan.c
--- b/src/backend/access/gist/gistscan.c
***************
*** 133,138 **** gistbeginscan(PG_FUNCTION_ARGS)
--- 133,146 ----
  
  	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;
+ 	so->curPageData = NULL;
+ 	scan->xs_itupdesc = RelationGetDescr(r);
+ 
  	MemoryContextSwitchTo(oldCxt);
  
  	PG_RETURN_POINTER(scan);
*** a/src/backend/access/gist/gistutil.c
--- b/src/backend/access/gist/gistutil.c
***************
*** 618,623 **** gistFormTuple(GISTSTATE *giststate, Relation r,
--- 618,680 ----
  	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 in key field
+  */
+ IndexTuple
+ gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple, bool isnull[])
+ {
+ 	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;
+ 	}
+ 	res = index_form_tuple(giststate->tupdesc, fetchatt, isnull);
+ 
+ 	/*
+ 	 * The offset number on tuples on internal pages is unused. For historical
+ 	 * reasons, it is set 0xffff.
+ 	 */
+ 	ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff);
+ 	return res;
+ }
+ 
  float
  gistpenalty(GISTSTATE *giststate, int attno,
  			GISTENTRY *orig, bool isNullOrig,
*** a/src/backend/access/index/indexam.c
--- b/src/backend/access/index/indexam.c
***************
*** 722,732 **** index_vacuum_cleanup(IndexVacuumInfo *info,
  }
  
  /* ----------------
!  *		index_can_return - does index support index-only scans?
   * ----------------
   */
  bool
! index_can_return(Relation indexRelation)
  {
  	FmgrInfo   *procedure;
  
--- 722,732 ----
  }
  
  /* ----------------
!  *		index_can_return - does index column with number 'attno' supports index-only scans?
   * ----------------
   */
  bool
! index_can_return(Relation indexRelation, int attno)
  {
  	FmgrInfo   *procedure;
  
***************
*** 738,745 **** index_can_return(Relation indexRelation)
  
  	GET_REL_PROCEDURE(amcanreturn);
  
! 	return DatumGetBool(FunctionCall1(procedure,
! 									  PointerGetDatum(indexRelation)));
  }
  
  /* ----------------
--- 738,746 ----
  
  	GET_REL_PROCEDURE(amcanreturn);
  
! 	return DatumGetBool(FunctionCall2(procedure,
! 					  PointerGetDatum(indexRelation),
! 					  Int32GetDatum(attno)));
  }
  
  /* ----------------
*** a/src/backend/optimizer/path/indxpath.c
--- b/src/backend/optimizer/path/indxpath.c
***************
*** 1746,1759 **** check_index_only(RelOptInfo *rel, IndexOptInfo *index)
  	bool		result;
  	Bitmapset  *attrs_used = NULL;
  	Bitmapset  *index_attrs = NULL;
  	ListCell   *lc;
  	int			i;
  
! 	/* Index-only scans must be enabled, and index must be capable of them */
  	if (!enable_indexonlyscan)
  		return false;
- 	if (!index->canreturn)
- 		return false;
  
  	/*
  	 * Check that all needed attributes of the relation are available from the
--- 1746,1758 ----
  	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 */
  	if (!enable_indexonlyscan)
  		return false;
  
  	/*
  	 * Check that all needed attributes of the relation are available from the
***************
*** 1798,1811 **** check_index_only(RelOptInfo *rel, IndexOptInfo *index)
  		index_attrs =
  			bms_add_member(index_attrs,
  						   attno - FirstLowInvalidHeapAttributeNumber);
  	}
  
! 	/* Do we have all the necessary attributes? */
! 	result = bms_is_subset(attrs_used, index_attrs);
! 
  	bms_free(attrs_used);
  	bms_free(index_attrs);
! 
  	return result;
  }
  
--- 1797,1813 ----
  		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? 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;
  }
  
*** a/src/backend/optimizer/util/plancat.c
--- b/src/backend/optimizer/util/plancat.c
***************
*** 207,212 **** get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
--- 207,213 ----
  			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,224 **** 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->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;
--- 215,225 ----
  				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->amcanorderbyop = indexRelation->rd_am->amcanorderbyop;
  			info->amoptionalkey = indexRelation->rd_am->amoptionalkey;
  			info->amsearcharray = indexRelation->rd_am->amsearcharray;
*** a/src/include/access/genam.h
--- b/src/include/access/genam.h
***************
*** 156,162 **** 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 RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
  				uint16 procnum);
  extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
--- 156,162 ----
  				  void *callback_state);
  extern IndexBulkDeleteResult *index_vacuum_cleanup(IndexVacuumInfo *info,
  					 IndexBulkDeleteResult *stats);
! 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,
*** a/src/include/access/gist.h
--- b/src/include/access/gist.h
***************
*** 33,39 ****
  #define GIST_PICKSPLIT_PROC				6
  #define GIST_EQUAL_PROC					7
  #define GIST_DISTANCE_PROC				8
! #define GISTNProcs						8
  
  /*
   * strategy numbers for GiST opclasses that want to implement the old
--- 33,40 ----
  #define GIST_PICKSPLIT_PROC				6
  #define GIST_EQUAL_PROC					7
  #define GIST_DISTANCE_PROC				8
! #define GIST_FETCH_PROC					9
! #define GISTNProcs					9
  
  /*
   * strategy numbers for GiST opclasses that want to implement the old
*** a/src/include/access/gist_private.h
--- b/src/include/access/gist_private.h
***************
*** 86,91 **** typedef struct GISTSTATE
--- 86,92 ----
  	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];
***************
*** 108,114 **** 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
   * there, instead of building a separate GISTSearchItem for each one.
   */
  
--- 109,115 ----
   * 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
   * there, instead of building a separate GISTSearchItem for each one.
   */
  
***************
*** 117,122 **** typedef struct GISTSearchHeapItem
--- 118,124 ----
  {
  	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 */
***************
*** 167,175 **** 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 */
  } GISTScanOpaqueData;
  
  typedef GISTScanOpaqueData *GISTScanOpaque;
--- 169,177 ----
  	double	   *distances;		/* output area for gistindex_keytest */
  
  	/* In a non-ordered search, returnable heap items are stored here: */
! 	List *pageData;
! 	ListCell *curPageData; /* next item to return from pageData */
! 
  } GISTScanOpaqueData;
  
  typedef GISTScanOpaqueData *GISTScanOpaque;
***************
*** 423,428 **** typedef struct GiSTOptions
--- 425,431 ----
  /* 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);
***************
*** 522,527 **** extern bool gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b);
--- 525,535 ----
  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,
*** a/src/include/catalog/pg_am.h
--- b/src/include/catalog/pg_am.h
***************
*** 123,129 **** 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 ));
  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 ));
--- 123,129 ----
  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 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 ));
*** a/src/include/catalog/pg_amproc.h
--- b/src/include/catalog/pg_amproc.h
***************
*** 188,193 **** DATA(insert (	1029   600 600 5 2581 ));
--- 188,194 ----
  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 3253 ));
  DATA(insert (	2593   603 603 1 2578 ));
  DATA(insert (	2593   603 603 2 2583 ));
  DATA(insert (	2593   603 603 3 2579 ));
***************
*** 195,200 **** DATA(insert (	2593   603 603 4 2580 ));
--- 196,202 ----
  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 3252 ));
  DATA(insert (	2594   604 604 1 2585 ));
  DATA(insert (	2594   604 604 2 2583 ));
  DATA(insert (	2594   604 604 3 2586 ));
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 558,564 **** 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_ ));
  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)");
--- 558,564 ----
  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 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)");
***************
*** 941,946 **** DATA(insert OID = 776 (  gistbulkdelete    PGNSP PGUID 12 1 0 0 0 f f f f t f v
--- 941,948 ----
  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 = 3251 (  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_ ));
***************
*** 3996,4001 **** DATA(insert OID = 2579 (  gist_box_compress		PGNSP PGUID 12 1 0 0 0 f f f f t f
--- 3998,4005 ----
  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 = 3252 (  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_ ));
***************
*** 4014,4019 **** DATA(insert OID = 2592 (  gist_circle_compress	PGNSP PGUID 12 1 0 0 0 f f f f t
--- 4018,4025 ----
  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 = 3253 (  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_ ));
***************
*** 4905,4911 **** 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_ ));
  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)");
--- 4911,4917 ----
  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 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)");
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 526,532 **** 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? */
--- 526,532 ----
  	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 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? */
*** a/src/include/utils/geo_decls.h
--- b/src/include/utils/geo_decls.h
***************
*** 411,416 **** extern Datum gist_box_picksplit(PG_FUNCTION_ARGS);
--- 411,417 ----
  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);
***************
*** 418,423 **** extern Datum gist_circle_consistent(PG_FUNCTION_ARGS);
--- 419,426 ----
  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);
*** a/src/test/regress/expected/create_index.out
--- b/src/test/regress/expected/create_index.out
***************
*** 378,384 **** SELECT * FROM fast_emp4000
  ----------------------------------------------------------------
   Sort
     Sort Key: ((home_base[0])[0])
!    ->  Index Scan using grect2ind on fast_emp4000
           Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
  (4 rows)
  
--- 378,384 ----
  ----------------------------------------------------------------
   Sort
     Sort Key: ((home_base[0])[0])
!    ->  Index Only Scan using grect2ind on fast_emp4000
           Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
  (4 rows)
  
***************
*** 396,402 **** SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
                           QUERY PLAN                          
  -------------------------------------------------------------
   Aggregate
!    ->  Index Scan using grect2ind on fast_emp4000
           Index Cond: (home_base && '(1000,1000),(0,0)'::box)
  (3 rows)
  
--- 396,402 ----
                           QUERY PLAN                          
  -------------------------------------------------------------
   Aggregate
!    ->  Index Only Scan using grect2ind on fast_emp4000
           Index Cond: (home_base && '(1000,1000),(0,0)'::box)
  (3 rows)
  
***************
*** 408,417 **** 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                    
! --------------------------------------------------
   Aggregate
!    ->  Index Scan using grect2ind on fast_emp4000
           Index Cond: (home_base IS NULL)
  (3 rows)
  
--- 408,417 ----
  
  EXPLAIN (COSTS OFF)
  SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
!                       QUERY PLAN                       
! -------------------------------------------------------
   Aggregate
!    ->  Index Only Scan using grect2ind on fast_emp4000
           Index Cond: (home_base IS NULL)
  (3 rows)
  
***************
*** 495,501 **** SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)';
                       QUERY PLAN                     
  ----------------------------------------------------
   Aggregate
!    ->  Index Scan using gpointind on point_tbl
           Index Cond: (f1 <@ '(100,100),(0,0)'::box)
  (3 rows)
  
--- 495,501 ----
                       QUERY PLAN                     
  ----------------------------------------------------
   Aggregate
!    ->  Index Only Scan using gpointind on point_tbl
           Index Cond: (f1 <@ '(100,100),(0,0)'::box)
  (3 rows)
  
***************
*** 510,517 **** 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)
  (3 rows)
  
  SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1;
--- 510,517 ----
                       QUERY PLAN                     
  ----------------------------------------------------
   Aggregate
!    ->  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;
***************
*** 525,531 **** 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 Cond: (f1 <@ '((0,0),(0,100),(100,100),(50,50),(100,0),(0,0))'::polygon)
  (3 rows)
  
--- 525,531 ----
                                         QUERY PLAN                                       
  ----------------------------------------------------------------------------------------
   Aggregate
!    ->  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)
  
***************
*** 540,546 **** SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>';
                       QUERY PLAN                     
  ----------------------------------------------------
   Aggregate
!    ->  Index Scan using gpointind on point_tbl
           Index Cond: (f1 <@ '<(50,50),50>'::circle)
  (3 rows)
  
--- 540,546 ----
                       QUERY PLAN                     
  ----------------------------------------------------
   Aggregate
!    ->  Index Only Scan using gpointind on point_tbl
           Index Cond: (f1 <@ '<(50,50),50>'::circle)
  (3 rows)
  
***************
*** 552,561 **** 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                    
! -------------------------------------------------
   Aggregate
!    ->  Index Scan using gpointind on point_tbl p
           Index Cond: (f1 << '(0,0)'::point)
  (3 rows)
  
--- 552,561 ----
  
  EXPLAIN (COSTS OFF)
  SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)';
!                       QUERY PLAN                      
! ------------------------------------------------------
   Aggregate
!    ->  Index Only Scan using gpointind on point_tbl p
           Index Cond: (f1 << '(0,0)'::point)
  (3 rows)
  
***************
*** 567,576 **** 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                    
! -------------------------------------------------
   Aggregate
!    ->  Index Scan using gpointind on point_tbl p
           Index Cond: (f1 >> '(0,0)'::point)
  (3 rows)
  
--- 567,576 ----
  
  EXPLAIN (COSTS OFF)
  SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)';
!                       QUERY PLAN                      
! ------------------------------------------------------
   Aggregate
!    ->  Index Only Scan using gpointind on point_tbl p
           Index Cond: (f1 >> '(0,0)'::point)
  (3 rows)
  
***************
*** 582,591 **** 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                    
! -------------------------------------------------
   Aggregate
!    ->  Index Scan using gpointind on point_tbl p
           Index Cond: (f1 <^ '(0,0)'::point)
  (3 rows)
  
--- 582,591 ----
  
  EXPLAIN (COSTS OFF)
  SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)';
!                       QUERY PLAN                      
! ------------------------------------------------------
   Aggregate
!    ->  Index Only Scan using gpointind on point_tbl p
           Index Cond: (f1 <^ '(0,0)'::point)
  (3 rows)
  
***************
*** 597,606 **** 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                    
! -------------------------------------------------
   Aggregate
!    ->  Index Scan using gpointind on point_tbl p
           Index Cond: (f1 >^ '(0,0)'::point)
  (3 rows)
  
--- 597,606 ----
  
  EXPLAIN (COSTS OFF)
  SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)';
!                       QUERY PLAN                      
! ------------------------------------------------------
   Aggregate
!    ->  Index Only Scan using gpointind on point_tbl p
           Index Cond: (f1 >^ '(0,0)'::point)
  (3 rows)
  
***************
*** 612,621 **** 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                    
! -------------------------------------------------
   Aggregate
!    ->  Index Scan using gpointind on point_tbl p
           Index Cond: (f1 ~= '(-5,-12)'::point)
  (3 rows)
  
--- 612,621 ----
  
  EXPLAIN (COSTS OFF)
  SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
!                       QUERY PLAN                      
! ------------------------------------------------------
   Aggregate
!    ->  Index Only Scan using gpointind on point_tbl p
           Index Cond: (f1 ~= '(-5,-12)'::point)
  (3 rows)
  
***************
*** 627,635 **** 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
     Order By: (f1 <-> '(0,1)'::point)
  (2 rows)
  
--- 627,635 ----
  
  EXPLAIN (COSTS OFF)
  SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
!                   QUERY PLAN                  
! ----------------------------------------------
!  Index Only Scan using gpointind on point_tbl
     Order By: (f1 <-> '(0,1)'::point)
  (2 rows)
  
***************
*** 647,655 **** 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
     Index Cond: (f1 IS NULL)
  (2 rows)
  
--- 647,655 ----
  
  EXPLAIN (COSTS OFF)
  SELECT * FROM point_tbl WHERE f1 IS NULL;
!                   QUERY PLAN                  
! ----------------------------------------------
!  Index Only Scan using gpointind on point_tbl
     Index Cond: (f1 IS NULL)
  (2 rows)
  
***************
*** 661,669 **** 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
     Index Cond: (f1 IS NOT NULL)
     Order By: (f1 <-> '(0,1)'::point)
  (3 rows)
--- 661,669 ----
  
  EXPLAIN (COSTS OFF)
  SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
!                   QUERY PLAN                  
! ----------------------------------------------
!  Index Only Scan using gpointind on point_tbl
     Index Cond: (f1 IS NOT NULL)
     Order By: (f1 <-> '(0,1)'::point)
  (3 rows)
***************
*** 683,689 **** 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 Cond: (f1 <@ '(10,10),(-10,-10)'::box)
     Order By: (f1 <-> '(0,1)'::point)
  (3 rows)
--- 683,689 ----
  SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
                     QUERY PLAN                   
  ------------------------------------------------
!  Index Only Scan using gpointind on point_tbl
     Index Cond: (f1 <@ '(10,10),(-10,-10)'::box)
     Order By: (f1 <-> '(0,1)'::point)
  (3 rows)
*** /dev/null
--- b/src/test/regress/expected/gist_indexonly.out
***************
*** 0 ****
--- 1,61 ----
+ --
+ -- 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 TO false;
+ SET enable_bitmapscan TO false;
+ SET enable_indexscan TO false;
+ SET enable_indexonlyscan TO true;
+ -- 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)) limit 10;
+                           QUERY PLAN                          
+ --------------------------------------------------------------
+  Limit
+    ->  Index Only Scan using gist_tbl_point_index on gist_tbl
+          Index Cond: (p <@ '(100,100),(0,0)'::box)
+ (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);
+ EXPLAIN (COSTS OFF)
+ select b from gist_tbl where b <@ box(point(5,5),  point(6,6)) limit 10;
+                          QUERY PLAN                         
+ ------------------------------------------------------------
+  Limit
+    ->  Index Only Scan using gist_tbl_box_index on gist_tbl
+          Index Cond: (b <@ '(6,6),(5,5)'::box)
+ (3 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);
+ EXPLAIN (COSTS OFF)
+ select * from gist_tbl where ( (b <@ box(point(5,5),  point(6,6))) and (p <@ box(point(0,0),  point(100,100)))) limit 10;
+                                     QUERY PLAN                                     
+ -----------------------------------------------------------------------------------
+  Limit
+    ->  Index Only Scan using gist_tbl_multi_index on gist_tbl
+          Index Cond: ((b <@ '(6,6),(5,5)'::box) AND (p <@ '(100,100),(0,0)'::box))
+ (3 rows)
+ 
+ EXPLAIN (COSTS OFF)
+ select * from gist_tbl where ( (b <@ box(point(5,5),  point(6,6))) or (p <@ box(point(0,0),  point(100,100)))) limit 10;
+                                   QUERY PLAN                                  
+ ------------------------------------------------------------------------------
+  Limit
+    ->  Seq Scan on gist_tbl
+          Filter: ((b <@ '(6,6),(5,5)'::box) OR (p <@ '(100,100),(0,0)'::box))
+ (3 rows)
+ 
+ DROP INDEX gist_tbl_multi_index;
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
+ RESET enable_indexonlyscan;
+ DROP TABLE gist_tbl;
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 78,84 **** ignore: random
  # ----------
  # Another group of parallel tests
  # ----------
! test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join aggregates transactions random portals arrays btree_index hash_index update namespace prepared_xacts delete
  
  # ----------
  # Another group of parallel tests
--- 78,84 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join aggregates transactions random portals arrays btree_index hash_index gist_indexonly update namespace prepared_xacts delete
  
  # ----------
  # Another group of parallel tests
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 91,96 **** test: portals
--- 91,97 ----
  test: arrays
  test: btree_index
  test: hash_index
+ test: gist_indexonly
  test: update
  test: delete
  test: namespace
*** /dev/null
--- b/src/test/regress/sql/gist_indexonly.sql
***************
*** 0 ****
--- 1,51 ----
+ --
+ -- 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 TO false;
+ SET enable_bitmapscan TO false;
+ SET enable_indexscan TO false;
+ SET enable_indexonlyscan TO true;
+ 
+ -- 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)) limit 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);
+ EXPLAIN (COSTS OFF)
+ select b from gist_tbl where b <@ box(point(5,5),  point(6,6)) limit 10;
+ 
+ DROP INDEX gist_tbl_box_index;
+ 
+ -- Check multicolumn indexonlyscan for gist
+ 
+ CREATE INDEX gist_tbl_multi_index ON gist_tbl USING gist (b, p);
+ 
+ EXPLAIN (COSTS OFF)
+ select * from gist_tbl where ( (b <@ box(point(5,5),  point(6,6))) and (p <@ box(point(0,0),  point(100,100)))) limit 10;
+ 
+ EXPLAIN (COSTS OFF)
+ select * from gist_tbl where ( (b <@ box(point(5,5),  point(6,6))) or (p <@ box(point(0,0),  point(100,100)))) limit 10;
+ 
+ DROP INDEX gist_tbl_multi_index;
+ 
+ RESET enable_seqscan;
+ RESET enable_bitmapscan;
+ RESET enable_indexscan;
+ RESET enable_indexonlyscan;
+ 
+ DROP TABLE gist_tbl;