Teach the planner to support index access methods that only implement
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.
This commit is contained in:
parent
df8684c56f
commit
00ce73778b
@ -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
|
||||
-->
|
||||
@ -493,14 +493,14 @@
|
||||
<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>
|
||||
|
@ -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>
|
||||
@ -37,8 +37,10 @@
|
||||
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">
|
||||
|
@ -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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -45,6 +45,14 @@
|
||||
((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.
|
||||
|
@ -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.
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user