diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 2a8f978f85c..0ab11dda6d2 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ - + @@ -493,14 +493,14 @@ amgettuple regproc pg_proc.oid - Next valid tuple function + Next valid tuple function, or zero if none amgetbitmap regproc pg_proc.oid - Fetch all valid tuples function + Fetch all valid tuples function, or zero if none diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml index db16c1d4ee5..3643d706735 100644 --- a/doc/src/sgml/indexam.sgml +++ b/doc/src/sgml/indexam.sgml @@ -1,4 +1,4 @@ - + Index Access Method Interface Definition @@ -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. @@ -266,7 +268,7 @@ amoptions (ArrayType *reloptions, The function should construct a bytea value, which will be copied into the rd_options field of the index's relcache entry. The data contents of the 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 StdRdOptions. When 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 WHERE condition, often called a qualifier or scan key. The semantics of index scanning are described more fully in , - below. The scan-related functions that an index access method must provide - are: + below. An index access method can support plain index scans, + bitmap index scans, or both. The scan-related functions that an + index access method must or may provide are: @@ -326,6 +329,13 @@ amgettuple (IndexScanDesc scan, callers. + + The amgettuple function need only be provided if the access + method supports plain index scans. If it doesn't, the + amgettuple field in its pg_am row must + be set to zero. + + int64 @@ -349,6 +359,13 @@ amgetbitmap (IndexScanDesc scan, in . + + The amgetbitmap function need only be provided if the access + method supports bitmap index scans. If it doesn't, the + amgetbitmap field in its pg_am row must + be set to zero. + + void @@ -519,6 +536,12 @@ amrestrpos (IndexScanDesc scan); spelled out in . + + Note that it is permitted for an access method to implement only + amgetbitmap and not amgettuple, or vice versa, + if its internal implementation is unsuited to one API or the other. + + diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 6f27a191828..e99583ab99c 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -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. diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index e880e668cba..6225bc14fff 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -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. diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h index e25eccbdcff..7736cb6e58a 100644 --- a/src/include/catalog/pg_am.h +++ b/src/include/catalog/pg_am.h @@ -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 */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index e8e20d202b1..4f1bc4067d2 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -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;