Teach the planner to support index access methods that only implement
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 5 Mar 2009 23:06:45 +0000 (23:06 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 5 Mar 2009 23:06:45 +0000 (23:06 +0000)
amgettuple or only implement amgetbitmap, instead of the former assumption
that every AM supports both APIs.  Extracted with minor editorialization
from Teodor's fast-GIN-insert patch; whatever becomes of that, this seems
like a simple and reasonable generalization of the index AM interface spec.

doc/src/sgml/catalogs.sgml
doc/src/sgml/indexam.sgml
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/util/plancat.c
src/include/catalog/pg_am.h
src/include/nodes/relation.h

index 2a8f978f85c34a3e893fd61b9ce05873bc32d2bd..0ab11dda6d2416e540b6e07546c007e777a381e9 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.198 2009/02/24 10:06:31 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.199 2009/03/05 23:06:45 tgl Exp $ -->
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
  -->
       <entry><structfield>amgettuple</structfield></entry>
       <entry><type>regproc</type></entry>
       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
-      <entry><quote>Next valid tuple</quote> function</entry>
+      <entry><quote>Next valid tuple</quote> function, or zero if none</entry>
      </row>
 
      <row>
       <entry><structfield>amgetbitmap</structfield></entry>
       <entry><type>regproc</type></entry>
       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
-      <entry><quote>Fetch all valid tuples</quote> function</entry>
+      <entry><quote>Fetch all valid tuples</quote> function, or zero if none</entry>
      </row>
 
      <row>
index db16c1d4ee5aa3ee41e161aa01bbc51480266223..3643d706735ba6d35c1a9be45fd4142c99d161e4 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.28 2008/10/17 22:10:29 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.29 2009/03/05 23:06:45 tgl Exp $ -->
 
 <chapter id="indexam">
  <title>Index Access Method Interface Definition</title>
    extant versions of the same logical row; to an index, each tuple is
    an independent object that needs its own index entry.  Thus, an
    update of a row always creates all-new index entries for the row, even if
-   the key values did not change.  Index entries for dead tuples are
-   reclaimed (by vacuuming) when the dead tuples themselves are reclaimed.
+   the key values did not change.  (HOT tuples are an exception to this
+   statement; but indexes do not deal with those, either.)  Index entries for
+   dead tuples are reclaimed (by vacuuming) when the dead tuples themselves
+   are reclaimed.
   </para>
 
  <sect1 id="index-catalog">
@@ -266,7 +268,7 @@ amoptions (ArrayType *reloptions,
    The function should construct a <type>bytea</> value, which will be copied
    into the <structfield>rd_options</> field of the index's relcache entry.
    The data contents of the <type>bytea</> value are open for the access
-   method to define, but the standard access methods currently all use struct
+   method to define; most of the standard access methods use struct
    <structname>StdRdOptions</>.
    When <parameter>validate</> is true, the function should report a suitable
    error message if any of the options are unrecognized or have invalid
@@ -283,8 +285,9 @@ amoptions (ArrayType *reloptions,
    an indexable <literal>WHERE</> condition, often called a
    <firstterm>qualifier</> or <firstterm>scan key</>.  The semantics of
    index scanning are described more fully in <xref linkend="index-scanning">,
-   below.  The scan-related functions that an index access method must provide
-   are:
+   below.  An index access method can support <quote>plain</> index scans,
+   <quote>bitmap</> index scans, or both.  The scan-related functions that an
+   index access method must or may provide are:
   </para>
 
   <para>
@@ -326,6 +329,13 @@ amgettuple (IndexScanDesc scan,
    callers.
   </para>
 
+  <para>
+   The <function>amgettuple</> function need only be provided if the access
+   method supports <quote>plain</> index scans.  If it doesn't, the
+   <structfield>amgettuple</> field in its <structname>pg_am</> row must
+   be set to zero.
+  </para>
+
   <para>
 <programlisting>
 int64
@@ -349,6 +359,13 @@ amgetbitmap (IndexScanDesc scan,
    in <xref linkend="index-scanning">.
   </para>
 
+  <para>
+   The <function>amgetbitmap</> function need only be provided if the access
+   method supports <quote>bitmap</> index scans.  If it doesn't, the
+   <structfield>amgetbitmap</> field in its <structname>pg_am</> row must
+   be set to zero.
+  </para>
+
   <para>
 <programlisting>
 void
@@ -519,6 +536,12 @@ amrestrpos (IndexScanDesc scan);
    spelled out in <xref linkend="index-locking">.
   </para>
 
+  <para>
+   Note that it is permitted for an access method to implement only
+   <function>amgetbitmap</> and not <function>amgettuple</>, or vice versa,
+   if its internal implementation is unsuited to one API or the other.
+  </para>
+
  </sect1>
 
  <sect1 id="index-locking">
index 6f27a191828dc0995854fe33d7f81da18036142a..e99583ab99c5e85dfe443aec7281d73c86a84fcc 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.236 2009/02/15 20:16:21 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.237 2009/03/05 23:06:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
    ((opfamily) == BOOL_BTREE_FAM_OID || (opfamily) == BOOL_HASH_FAM_OID)
 
 
+/* Whether we are looking for plain indexscan, bitmap scan, or either */
+typedef enum
+{
+   ST_INDEXSCAN,               /* must support amgettuple */
+   ST_BITMAPSCAN,              /* must support amgetbitmap */
+   ST_ANYSCAN                  /* either is okay */
+} ScanTypeControl;
+
 /* Per-path data used within choose_bitmap_and() */
 typedef struct
 {
@@ -58,7 +66,7 @@ typedef struct
 static List *find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
                    List *clauses, List *outer_clauses,
                    bool istoplevel, RelOptInfo *outer_rel,
-                   SaOpControl saop_control);
+                   SaOpControl saop_control, ScanTypeControl scantype);
 static List *find_saop_paths(PlannerInfo *root, RelOptInfo *rel,
                List *clauses, List *outer_clauses,
                bool istoplevel, RelOptInfo *outer_rel);
@@ -168,12 +176,16 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
     */
    indexpaths = find_usable_indexes(root, rel,
                                     rel->baserestrictinfo, NIL,
-                                    true, NULL, SAOP_FORBID);
+                                    true, NULL, SAOP_FORBID, ST_ANYSCAN);
 
    /*
-    * We can submit them all to add_path.  (This generates access paths for
-    * plain IndexScan plans.)  However, for the next step we will only want
-    * the ones that have some selectivity; we must discard anything that was
+    * Submit all the ones that can form plain IndexScan plans to add_path.
+    * (A plain IndexPath always represents a plain IndexScan plan; however
+    * some of the indexes might support only bitmap scans, and those we
+    * mustn't submit to add_path here.)  Also, pick out the ones that might
+    * be useful as bitmap scans.  For that, we must discard indexes that
+    * don't support bitmap scans, and we also are only interested in paths
+    * that have some selectivity; we should discard anything that was
     * generated solely for ordering purposes.
     */
    bitindexpaths = NIL;
@@ -181,9 +193,11 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
    {
        IndexPath  *ipath = (IndexPath *) lfirst(l);
 
-       add_path(rel, (Path *) ipath);
+       if (ipath->indexinfo->amhasgettuple)
+           add_path(rel, (Path *) ipath);
 
-       if (ipath->indexselectivity < 1.0 &&
+       if (ipath->indexinfo->amhasgetbitmap &&
+           ipath->indexselectivity < 1.0 &&
            !ScanDirectionIsBackward(ipath->indexscandir))
            bitindexpaths = lappend(bitindexpaths, ipath);
    }
@@ -254,6 +268,7 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
  * 'outer_rel' is the outer side of the join if forming an inner indexscan
  *     (so some of the given clauses are join clauses); NULL if not
  * 'saop_control' indicates whether ScalarArrayOpExpr clauses can be used
+ * 'scantype' indicates whether we need plain or bitmap scan support
  *
  * Note: check_partial_indexes() must have been run previously.
  *----------
@@ -262,7 +277,7 @@ static List *
 find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
                    List *clauses, List *outer_clauses,
                    bool istoplevel, RelOptInfo *outer_rel,
-                   SaOpControl saop_control)
+                   SaOpControl saop_control, ScanTypeControl scantype)
 {
    Relids      outer_relids = outer_rel ? outer_rel->relids : NULL;
    bool        possibly_useful_pathkeys = has_useful_pathkeys(root, rel);
@@ -281,6 +296,24 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
        bool        found_clause;
        bool        index_is_ordered;
 
+       /*
+        * Check that index supports the desired scan type(s)
+        */
+       switch (scantype)
+       {
+           case ST_INDEXSCAN:
+               if (!index->amhasgettuple)
+                   continue;
+               break;
+           case ST_BITMAPSCAN:
+               if (!index->amhasgetbitmap)
+                   continue;
+               break;
+           case ST_ANYSCAN:
+               /* either or both are OK */
+               break;
+       }
+
        /*
         * Ignore partial indexes that do not match the query.  If a partial
         * index is marked predOK then we know it's OK; otherwise, if we are
@@ -445,7 +478,7 @@ find_saop_paths(PlannerInfo *root, RelOptInfo *rel,
    return find_usable_indexes(root, rel,
                               clauses, outer_clauses,
                               istoplevel, outer_rel,
-                              SAOP_REQUIRE);
+                              SAOP_REQUIRE, ST_BITMAPSCAN);
 }
 
 
@@ -507,7 +540,8 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
                                              all_clauses,
                                              false,
                                              outer_rel,
-                                             SAOP_ALLOW);
+                                             SAOP_ALLOW,
+                                             ST_BITMAPSCAN);
                /* Recurse in case there are sub-ORs */
                indlist = list_concat(indlist,
                                      generate_bitmap_or_paths(root, rel,
@@ -524,7 +558,8 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
                                              all_clauses,
                                              false,
                                              outer_rel,
-                                             SAOP_ALLOW);
+                                             SAOP_ALLOW,
+                                             ST_BITMAPSCAN);
            }
 
            /*
@@ -1641,6 +1676,7 @@ best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel,
    List       *clause_list;
    List       *indexpaths;
    List       *bitindexpaths;
+   List       *allindexpaths;
    ListCell   *l;
    InnerIndexscanInfo *info;
    MemoryContext oldcontext;
@@ -1736,18 +1772,36 @@ best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel,
     * Find all the index paths that are usable for this join, except for
     * stuff involving OR and ScalarArrayOpExpr clauses.
     */
-   indexpaths = find_usable_indexes(root, rel,
-                                    clause_list, NIL,
-                                    false, outer_rel,
-                                    SAOP_FORBID);
+   allindexpaths = find_usable_indexes(root, rel,
+                                       clause_list, NIL,
+                                       false, outer_rel,
+                                       SAOP_FORBID,
+                                       ST_ANYSCAN);
+
+   /*
+    * Include the ones that are usable as plain indexscans in indexpaths, and
+    * include the ones that are usable as bitmap scans in bitindexpaths.
+    */
+   indexpaths = bitindexpaths = NIL;
+   foreach(l, allindexpaths)
+   {
+       IndexPath  *ipath = (IndexPath *) lfirst(l);
+
+       if (ipath->indexinfo->amhasgettuple)
+           indexpaths = lappend(indexpaths, ipath);
+
+       if (ipath->indexinfo->amhasgetbitmap)
+           bitindexpaths = lappend(bitindexpaths, ipath);
+   }
 
    /*
     * Generate BitmapOrPaths for any suitable OR-clauses present in the
     * clause list.
     */
-   bitindexpaths = generate_bitmap_or_paths(root, rel,
-                                            clause_list, NIL,
-                                            outer_rel);
+   bitindexpaths = list_concat(bitindexpaths,
+                               generate_bitmap_or_paths(root, rel,
+                                                        clause_list, NIL,
+                                                        outer_rel));
 
    /*
     * Likewise, generate paths using ScalarArrayOpExpr clauses; these can't
@@ -1758,11 +1812,6 @@ best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel,
                                                clause_list, NIL,
                                                false, outer_rel));
 
-   /*
-    * Include the regular index paths in bitindexpaths.
-    */
-   bitindexpaths = list_concat(bitindexpaths, list_copy(indexpaths));
-
    /*
     * If we found anything usable, generate a BitmapHeapPath for the most
     * promising combination of bitmap index paths.
index e880e668cbacafd943100b39808dfd5e71a29fa3..6225bc14fffbf3ac3f111b65e549459570131e1a 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.155 2009/02/15 20:16:21 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.156 2009/03/05 23:06:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -214,6 +214,8 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
            info->amcostestimate = indexRelation->rd_am->amcostestimate;
            info->amoptionalkey = indexRelation->rd_am->amoptionalkey;
            info->amsearchnulls = indexRelation->rd_am->amsearchnulls;
+           info->amhasgettuple = OidIsValid(indexRelation->rd_am->amgettuple);
+           info->amhasgetbitmap = OidIsValid(indexRelation->rd_am->amgetbitmap);
 
            /*
             * Fetch the ordering operators associated with the index, if any.
index e25eccbdcffa291e69a8d952f612f0f5c1b8ada2..7736cb6e58a114bb37d621b9c1304d8a05f03ee0 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.60 2009/01/01 17:23:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.61 2009/03/05 23:06:45 tgl Exp $
  *
  * NOTES
  *     the genbki.sh script reads this file and generates .bki
@@ -52,8 +52,8 @@ CATALOG(pg_am,2601)
    Oid         amkeytype;      /* type of data in index, or InvalidOid */
    regproc     aminsert;       /* "insert this tuple" function */
    regproc     ambeginscan;    /* "start new scan" function */
-   regproc     amgettuple;     /* "next valid tuple" function */
-   regproc     amgetbitmap;    /* "fetch all valid tuples" function */
+   regproc     amgettuple;     /* "next valid tuple" function, or 0 */
+   regproc     amgetbitmap;    /* "fetch all valid tuples" function, or 0 */
    regproc     amrescan;       /* "restart this scan" function */
    regproc     amendscan;      /* "end this scan" function */
    regproc     ammarkpos;      /* "mark current scan position" function */
index e8e20d202b15506e408b0ad9e036ce1b19e952c5..4f1bc4067d21f9ea5aedd47a676b562033d3f35e 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.169 2009/02/25 03:30:37 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.170 2009/03/05 23:06:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -441,6 +441,8 @@ typedef struct IndexOptInfo
    bool        unique;         /* true if a unique index */
    bool        amoptionalkey;  /* can query omit key for the first column? */
    bool        amsearchnulls;  /* can AM search for NULL index entries? */
+   bool        amhasgettuple;  /* does AM have amgettuple interface? */
+   bool        amhasgetbitmap; /* does AM have amgetbitmap interface? */
 } IndexOptInfo;