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->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->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;