OSDN Git Service

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 2a8f978..0ab11dd 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 db16c1d..3643d70 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>
@@ -327,6 +330,13 @@ amgettuple (IndexScanDesc scan,
   </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
 amgetbitmap (IndexScanDesc scan,
@@ -350,6 +360,13 @@ amgetbitmap (IndexScanDesc scan,
   </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
 amrescan (IndexScanDesc scan,
@@ -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 6f27a19..e99583a 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);
@@ -282,6 +297,24 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
                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
                 * at top level we know it's not OK (since predOK is exactly whether
@@ -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
@@ -1759,11 +1813,6 @@ best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel,
                                                                                                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 e880e66..6225bc1 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 e25eccb..7736cb6 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 e8e20d2..4f1bc40 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;