Replace simple constant pg_am.amcanreturn with an AM support function.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 18 Dec 2011 20:49:00 +0000 (15:49 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 18 Dec 2011 20:50:37 +0000 (15:50 -0500)
The need for this was debated when we put in the index-only-scan feature,
but at the time we had no near-term expectation of having AMs that could
support such scans for only some indexes; so we kept it simple.  However,
the SP-GiST AM forces the issue, so let's fix it.

This patch only installs the new API; no behavior actually changes.

15 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/indexam.sgml
src/backend/access/index/indexam.c
src/backend/access/nbtree/nbtree.c
src/backend/access/spgist/spgscan.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/util/plancat.c
src/include/access/genam.h
src/include/access/nbtree.h
src/include/access/spgist.h
src/include/catalog/catversion.h
src/include/catalog/pg_am.h
src/include/catalog/pg_proc.h
src/include/nodes/relation.h
src/include/utils/rel.h

index b8cc16f72a9268b83de14a4dddfc6f13d1fdd55f..d948ed487c6961ee36613839bb4fdb465b51710e 100644 (file)
       <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>
       <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>
index a6e4466e8a15ba916a42b18b7eaee9c726f5f2b8..ebd32cc8d975bc9a2bbfbc9955ffea9ce788114c 100644 (file)
    <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</>,
index 6d423a7d682f0b620680c47a5ef3d06eeebf3438..e5fb5183897c31b949363f96db2a8591db58bf19 100644 (file)
@@ -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
  *
index f3a1d256a059dbee332ad363b8ccbe651cfacb31..13c05525179ee99ef1fe57824694c37858e8e4f7 100644 (file)
@@ -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);
+}
index ac309649682a41375b6fb385f35dd9124ba3d161..748265ecba7dbae82ed6e933b52f222d93daae6b 100644 (file)
@@ -559,3 +559,10 @@ spggettuple(PG_FUNCTION_ARGS)
 
    PG_RETURN_BOOL(false);
 }
+
+Datum
+spgcanreturn(PG_FUNCTION_ARGS)
+{
+   /* Not implemented yet */
+   PG_RETURN_BOOL(false);
+}
index 74bc7ac7c634503c383ae127c31b7f4e9c447964..fa6749a2f9c875faf2e0d41203fd08b86cf14ff2 100644 (file)
@@ -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;
 
    /*
index de629e93c9adf6f925a49010dae2d82c2ca97181..77080192efda7c9464d6c70c30c346b4fbb82a7b 100644 (file)
@@ -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;
index dd62680dd54890c54a60515de1c4267ec5d07375..48ebf1986c968153dea4d0bbb5d574356f1a5aac 100644 (file)
@@ -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,
index 9a751ce100455f81740fa29491d03b050572bee9..3a3ff61e571633e32b3eeecd81beb07f76e521a4 100644 (file)
@@ -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);
 
 /*
index aa655a314020719abd812028bba8354371939849..df6fec6cf455939421e929542bc9840bbb7f50d5 100644 (file)
@@ -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);
index fd424fa8d0c27ac9de417c3c5c0d06ed8e37d1cf..8bb4c5c58cda5700410528548f7c89459e89073d 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 201112172
+#define CATALOG_VERSION_NO 201112181
 
 #endif
index 6fdd1d5b0525f73115a5e36efb887756fd1adde0..2289a8e64359b00938377d6af9538c9339b20f3a 100644 (file)
@@ -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
 
index 6da3b421ae32a7e8453112e8f40302dfef4ca6dc..60e9feb2e321c0fce5b15858acad4ba13b782543 100644 (file)
@@ -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_ ));
index a400960a27242eb54c78c3e7d89494f1047c74db..74c060b9b66e937b8e0adda147b02a797fc2be75 100644 (file)
@@ -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? */
index 173dc16a2531358f8bd10dab851519f7546888c8..70d16eb01e42c1b8e4decc4e8cc2028f8e55a31c 100644 (file)
@@ -64,6 +64,7 @@ typedef struct RelationAmInfo
    FmgrInfo    ambuildempty;
    FmgrInfo    ambulkdelete;
    FmgrInfo    amvacuumcleanup;
+   FmgrInfo    amcanreturn;
    FmgrInfo    amcostestimate;
    FmgrInfo    amoptions;
 } RelationAmInfo;