diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index b8cc16f72a9268b83de14a4dddfc6f13d1fdd55f..d948ed487c6961ee36613839bb4fdb465b51710e 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -481,13 +481,6 @@
       <entry>Does the access method support multicolumn indexes?</entry>
      </row>
 
-     <row>
-      <entry><structfield>amcanreturn</structfield></entry>
-      <entry><type>bool</type></entry>
-      <entry></entry>
-      <entry>Can the access method return the contents of index entries?</entry>
-     </row>
-
      <row>
       <entry><structfield>amoptionalkey</structfield></entry>
       <entry><type>bool</type></entry>
@@ -622,6 +615,14 @@
       <entry>Post-<command>VACUUM</command> cleanup function</entry>
      </row>
 
+     <row>
+      <entry><structfield>amcanreturn</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Function to check whether index supports index-only scans,
+       or zero if none</entry>
+     </row>
+
      <row>
       <entry><structfield>amcostestimate</structfield></entry>
       <entry><type>regproc</type></entry>
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index a6e4466e8a15ba916a42b18b7eaee9c726f5f2b8..ebd32cc8d975bc9a2bbfbc9955ffea9ce788114c 100644
--- a/doc/src/sgml/indexam.sgml
+++ b/doc/src/sgml/indexam.sgml
@@ -134,11 +134,6 @@
    <structfield>amsearchnulls</structfield>, indicating that it supports
    <literal>IS NULL</> and <literal>IS NOT NULL</> clauses as search
    conditions.
-   An index method can also set <structfield>amcanreturn</structfield>,
-   indicating that it can support <firstterm>index-only scans</> by returning
-   the indexed column values for an index entry in the form of an IndexTuple.
-   (An example of an index AM that cannot do this is hash, which stores only
-   the hash values not the original data.)
   </para>
 
  </sect1>
@@ -278,6 +273,19 @@ amvacuumcleanup (IndexVacuumInfo *info,
 
   <para>
 <programlisting>
+bool
+amcanreturn (Relation indexRelation);
+</programlisting>
+   Check whether the index can support <firstterm>index-only scans</> by
+   returning the indexed column values for an index entry in the form of an
+   IndexTuple.  Return TRUE if so, else FALSE.  If the index AM can never
+   support index-only scans (an example is hash, which stores only
+   the hash values not the original data), it is sufficient to set its
+   <structfield>amcanreturn</> field to zero in <structname>pg_am</>.
+  </para>
+
+  <para>
+<programlisting>
 void
 amcostestimate (PlannerInfo *root,
                 IndexOptInfo *index,
@@ -391,9 +399,9 @@ amgettuple (IndexScanDesc scan,
   </para>
 
   <para>
-   If the access method supports index-only scans (i.e.,
-   <structfield>amcanreturn</structfield> is TRUE in its <structname>pg_am</>
-   row), then on success it must also check
+   If the index supports index-only scans (i.e.,
+   <function>amcanreturn</function> returns TRUE for it),
+   then on success the AM must also check
    <literal>scan-&gt;xs_want_itup</>, and if that is true it must return
    the original indexed data for the index entry, in the form of an
    <structname>IndexTuple</> pointer stored at <literal>scan-&gt;xs_itup</>,
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 6d423a7d682f0b620680c47a5ef3d06eeebf3438..e5fb5183897c31b949363f96db2a8591db58bf19 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -26,6 +26,7 @@
  *		index_getbitmap - get all tuples from a scan
  *		index_bulk_delete	- bulk deletion of index tuples
  *		index_vacuum_cleanup	- post-deletion cleanup of an index
+ *		index_can_return	- does index support index-only scans?
  *		index_getprocid - get a support procedure OID
  *		index_getprocinfo - get a support procedure's lookup info
  *
@@ -711,6 +712,27 @@ index_vacuum_cleanup(IndexVacuumInfo *info,
 	return result;
 }
 
+/* ----------------
+ *		index_can_return - does index support index-only scans?
+ * ----------------
+ */
+bool
+index_can_return(Relation indexRelation)
+{
+	FmgrInfo   *procedure;
+
+	RELATION_CHECKS;
+
+	/* amcanreturn is optional; assume FALSE if not provided by AM */
+	if (!RegProcedureIsValid(indexRelation->rd_am->amcanreturn))
+		return false;
+
+	GET_REL_PROCEDURE(amcanreturn);
+
+	return DatumGetBool(FunctionCall1(procedure,
+									  PointerGetDatum(indexRelation)));
+}
+
 /* ----------------
  *		index_getprocid
  *
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index f3a1d256a059dbee332ad363b8ccbe651cfacb31..13c05525179ee99ef1fe57824694c37858e8e4f7 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -1091,3 +1091,14 @@ restart:
 		goto restart;
 	}
 }
+
+/*
+ *	btcanreturn() -- Check whether btree indexes support index-only scans.
+ *
+ * btrees always do, so this is trivial.
+ */
+Datum
+btcanreturn(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_BOOL(true);
+}
diff --git a/src/backend/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c
index ac309649682a41375b6fb385f35dd9124ba3d161..748265ecba7dbae82ed6e933b52f222d93daae6b 100644
--- a/src/backend/access/spgist/spgscan.c
+++ b/src/backend/access/spgist/spgscan.c
@@ -559,3 +559,10 @@ spggettuple(PG_FUNCTION_ARGS)
 
 	PG_RETURN_BOOL(false);
 }
+
+Datum
+spgcanreturn(PG_FUNCTION_ARGS)
+{
+	/* Not implemented yet */
+	PG_RETURN_BOOL(false);
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 74bc7ac7c634503c383ae127c31b7f4e9c447964..fa6749a2f9c875faf2e0d41203fd08b86cf14ff2 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -1075,10 +1075,10 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
 	ListCell   *lc;
 	int			i;
 
-	/* Index-only scans must be enabled, and AM must be capable of it */
+	/* Index-only scans must be enabled, and index must be capable of them */
 	if (!enable_indexonlyscan)
 		return false;
-	if (!index->amcanreturn)
+	if (!index->canreturn)
 		return false;
 
 	/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index de629e93c9adf6f925a49010dae2d82c2ca97181..77080192efda7c9464d6c70c30c346b4fbb82a7b 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -212,8 +212,8 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 
 			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->amcanreturn = indexRelation->rd_am->amcanreturn;
 			info->amoptionalkey = indexRelation->rd_am->amoptionalkey;
 			info->amsearcharray = indexRelation->rd_am->amsearcharray;
 			info->amsearchnulls = indexRelation->rd_am->amsearchnulls;
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index dd62680dd54890c54a60515de1c4267ec5d07375..48ebf1986c968153dea4d0bbb5d574356f1a5aac 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -156,6 +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 RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
 				uint16 procnum);
 extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 9a751ce100455f81740fa29491d03b050572bee9..3a3ff61e571633e32b3eeecd81beb07f76e521a4 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -607,6 +607,7 @@ extern Datum btmarkpos(PG_FUNCTION_ARGS);
 extern Datum btrestrpos(PG_FUNCTION_ARGS);
 extern Datum btbulkdelete(PG_FUNCTION_ARGS);
 extern Datum btvacuumcleanup(PG_FUNCTION_ARGS);
+extern Datum btcanreturn(PG_FUNCTION_ARGS);
 extern Datum btoptions(PG_FUNCTION_ARGS);
 
 /*
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index aa655a314020719abd812028bba8354371939849..df6fec6cf455939421e929542bc9840bbb7f50d5 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -182,6 +182,7 @@ extern Datum spgmarkpos(PG_FUNCTION_ARGS);
 extern Datum spgrestrpos(PG_FUNCTION_ARGS);
 extern Datum spggetbitmap(PG_FUNCTION_ARGS);
 extern Datum spggettuple(PG_FUNCTION_ARGS);
+extern Datum spgcanreturn(PG_FUNCTION_ARGS);
 
 /* spgutils.c */
 extern Datum spgoptions(PG_FUNCTION_ARGS);
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index fd424fa8d0c27ac9de417c3c5c0d06ed8e37d1cf..8bb4c5c58cda5700410528548f7c89459e89073d 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201112172
+#define CATALOG_VERSION_NO	201112181
 
 #endif
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 6fdd1d5b0525f73115a5e36efb887756fd1adde0..2289a8e64359b00938377d6af9538c9339b20f3a 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -45,7 +45,6 @@ CATALOG(pg_am,2601)
 	bool		amcanbackward;	/* does AM support backward scan? */
 	bool		amcanunique;	/* does AM support UNIQUE indexes? */
 	bool		amcanmulticol;	/* does AM support multi-column indexes? */
-	bool		amcanreturn;	/* can AM return IndexTuples? */
 	bool		amoptionalkey;	/* can query omit key for the first column? */
 	bool		amsearcharray;	/* can AM handle ScalarArrayOpExpr quals? */
 	bool		amsearchnulls;	/* can AM search for NULL/NOT NULL entries? */
@@ -65,6 +64,7 @@ CATALOG(pg_am,2601)
 	regproc		ambuildempty;	/* "build empty index" function */
 	regproc		ambulkdelete;	/* bulk-delete function */
 	regproc		amvacuumcleanup;	/* post-VACUUM cleanup function */
+	regproc		amcanreturn;	/* can indexscan return IndexTuples? */
 	regproc		amcostestimate; /* estimate cost of an indexscan */
 	regproc		amoptions;		/* parse AM-specific parameters */
 } FormData_pg_am;
@@ -89,26 +89,26 @@ typedef FormData_pg_am *Form_pg_am;
 #define Anum_pg_am_amcanbackward		6
 #define Anum_pg_am_amcanunique			7
 #define Anum_pg_am_amcanmulticol		8
-#define Anum_pg_am_amcanreturn			9
-#define Anum_pg_am_amoptionalkey		10
-#define Anum_pg_am_amsearcharray		11
-#define Anum_pg_am_amsearchnulls		12
-#define Anum_pg_am_amstorage			13
-#define Anum_pg_am_amclusterable		14
-#define Anum_pg_am_ampredlocks			15
-#define Anum_pg_am_amkeytype			16
-#define Anum_pg_am_aminsert				17
-#define Anum_pg_am_ambeginscan			18
-#define Anum_pg_am_amgettuple			19
-#define Anum_pg_am_amgetbitmap			20
-#define Anum_pg_am_amrescan				21
-#define Anum_pg_am_amendscan			22
-#define Anum_pg_am_ammarkpos			23
-#define Anum_pg_am_amrestrpos			24
-#define Anum_pg_am_ambuild				25
-#define Anum_pg_am_ambuildempty			26
-#define Anum_pg_am_ambulkdelete			27
-#define Anum_pg_am_amvacuumcleanup		28
+#define Anum_pg_am_amoptionalkey		9
+#define Anum_pg_am_amsearcharray		10
+#define Anum_pg_am_amsearchnulls		11
+#define Anum_pg_am_amstorage			12
+#define Anum_pg_am_amclusterable		13
+#define Anum_pg_am_ampredlocks			14
+#define Anum_pg_am_amkeytype			15
+#define Anum_pg_am_aminsert				16
+#define Anum_pg_am_ambeginscan			17
+#define Anum_pg_am_amgettuple			18
+#define Anum_pg_am_amgetbitmap			19
+#define Anum_pg_am_amrescan				20
+#define Anum_pg_am_amendscan			21
+#define Anum_pg_am_ammarkpos			22
+#define Anum_pg_am_amrestrpos			23
+#define Anum_pg_am_ambuild				24
+#define Anum_pg_am_ambuildempty			25
+#define Anum_pg_am_ambulkdelete			26
+#define Anum_pg_am_amvacuumcleanup		27
+#define Anum_pg_am_amcanreturn			28
 #define Anum_pg_am_amcostestimate		29
 #define Anum_pg_am_amoptions			30
 
@@ -117,19 +117,19 @@ typedef FormData_pg_am *Form_pg_am;
  * ----------------
  */
 
-DATA(insert OID = 403 (  btree		5 2 t f t t t t t t t f t t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbuildempty btbulkdelete btvacuumcleanup btcostestimate btoptions ));
+DATA(insert OID = 403 (  btree		5 2 t f t t t t t t f t t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbuildempty btbulkdelete btvacuumcleanup btcanreturn btcostestimate btoptions ));
 DESCR("b-tree index access method");
 #define BTREE_AM_OID 403
-DATA(insert OID = 405 (  hash		1 1 f f t f f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbuildempty hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
+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 f 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 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 5 f f f f t f t f f t f f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbuildempty ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
+DATA(insert OID = 2742 (  gin		0 5 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 ));
 DESCR("GIN index access method");
 #define GIN_AM_OID 2742
-DATA(insert OID = 4000 (  spgist	0 5 f f f f f f f f f f f f 0 spginsert spgbeginscan spggettuple spggetbitmap spgrescan spgendscan spgmarkpos spgrestrpos spgbuild spgbuildempty spgbulkdelete spgvacuumcleanup spgcostestimate spgoptions ));
+DATA(insert OID = 4000 (  spgist	0 5 f f f f f f f f f f f 0 spginsert spgbeginscan spggettuple spggetbitmap spgrescan spgendscan spgmarkpos spgrestrpos spgbuild spgbuildempty spgbulkdelete spgvacuumcleanup spgcanreturn spgcostestimate spgoptions ));
 DESCR("SP-GiST index access method");
 #define SPGIST_AM_OID 4000
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 6da3b421ae32a7e8453112e8f40302dfef4ca6dc..60e9feb2e321c0fce5b15858acad4ba13b782543 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -546,6 +546,8 @@ DATA(insert OID = 332 (  btbulkdelete	   PGNSP PGUID 12 1 0 0 0 f f f t f v 4 0
 DESCR("btree(internal)");
 DATA(insert OID = 972 (  btvacuumcleanup   PGNSP PGUID 12 1 0 0 0 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 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 t f v 9 0 2278 "2281 2281 2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ btcostestimate _null_ _null_ _null_ ));
 DESCR("btree(internal)");
 DATA(insert OID = 2785 (  btoptions		   PGNSP PGUID 12 1 0 0 0 f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_  btoptions _null_ _null_ _null_ ));
@@ -4506,6 +4508,8 @@ DATA(insert OID = 4011 (  spgbulkdelete    PGNSP PGUID 12 1 0 0 0 f f f t f v 4
 DESCR("spgist(internal)");
 DATA(insert OID = 4012 (  spgvacuumcleanup   PGNSP PGUID 12 1 0 0 0 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 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 t f v 9 0 2278 "2281 2281 2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ spgcostestimate _null_ _null_ _null_ ));
 DESCR("spgist(internal)");
 DATA(insert OID = 4014 (  spgoptions	   PGNSP PGUID 12 1 0 0 0 f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_  spgoptions _null_ _null_ _null_ ));
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index a400960a27242eb54c78c3e7d89494f1047c74db..74c060b9b66e937b8e0adda147b02a797fc2be75 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -490,8 +490,8 @@ 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		amcanreturn;	/* can AM return IndexTuples? */
 	bool		amoptionalkey;	/* can query omit key for the first column? */
 	bool		amsearcharray;	/* can AM handle ScalarArrayOpExpr quals? */
 	bool		amsearchnulls;	/* can AM search for NULL/NOT NULL entries? */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 173dc16a2531358f8bd10dab851519f7546888c8..70d16eb01e42c1b8e4decc4e8cc2028f8e55a31c 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -64,6 +64,7 @@ typedef struct RelationAmInfo
 	FmgrInfo	ambuildempty;
 	FmgrInfo	ambulkdelete;
 	FmgrInfo	amvacuumcleanup;
+	FmgrInfo	amcanreturn;
 	FmgrInfo	amcostestimate;
 	FmgrInfo	amoptions;
 } RelationAmInfo;