Change the planner to allow indexscan qualification clauses to use

nonconsecutive columns of a multicolumn index, as per discussion around
mid-May (pghackers thread "Best way to scan on-disk bitmaps").  This
turns out to require only minimal changes in btree, and so far as I can
see none at all in GiST.  btcostestimate did need some work, but its
original assumption that index selectivity == heap selectivity was
quite bogus even before this.
This commit is contained in:
Tom Lane 2005-06-13 23:14:49 +00:00
parent 077811605e
commit c186c93148
12 changed files with 208 additions and 114 deletions

View File

@ -1,6 +1,6 @@
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
$PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.102 2005/05/17 21:46:09 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.103 2005/06/13 23:14:47 tgl Exp $
-->
<chapter id="catalogs">
@ -358,6 +358,14 @@
<entry>Does the access method support multicolumn indexes?</entry>
</row>
<row>
<entry><structfield>amoptionalkey</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>Does the access method support a scan without any constraint
for the first index column?</entry>
</row>
<row>
<entry><structfield>amindexnulls</structfield></entry>
<entry><type>bool</type></entry>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.5 2005/06/05 22:32:53 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.6 2005/06/13 23:14:47 tgl Exp $
-->
<chapter id="indexam">
@ -100,21 +100,30 @@ $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.5 2005/06/05 22:32:53 tgl Exp $
<structfield>amconcurrent</structfield> in <xref linkend="index-locking">.
The <structfield>amcanmulticol</structfield> flag asserts that the
access method supports multi-column indexes, while
<structfield>amoptionalkey</structfield> asserts that it allows scans
where no indexable restriction clause is given for the first index column.
When <structfield>amcanmulticol</structfield> is false,
<structfield>amoptionalkey</structfield> essentially says whether the
access method allows full-index scans without any restriction clause.
Access methods that support multiple index columns <emphasis>must</>
support scans that omit restrictions on any or all of the columns after
the first; however they are permitted to require some restriction to
appear for the first index column, and this is signaled by setting
<structfield>amoptionalkey</structfield> false.
<structfield>amindexnulls</structfield> asserts that index entries are
created for NULL key values. Since most indexable operators are
strict and hence cannot return TRUE for NULL inputs,
it is at first sight attractive to not store index entries for NULLs:
they could never be returned by an index scan anyway. However, this
argument fails for a full-table index scan (one with no scan keys);
such a scan should include null rows. In practice this means that
indexes that support ordered scans (have <structfield>amorderstrategy</>
nonzero) must index nulls, since the planner might decide to use such a
scan as a substitute for sorting. Such indexes must also be willing to
run a scan with no scan keys at all. Another restriction is that an index
argument fails when an index scan has no restriction clause for a given
index column. In practice this means that
indexes that have <structfield>amoptionalkey</structfield> true must
index nulls, since the planner might decide to use such an index
with no scan keys at all. A related restriction is that an index
access method that supports multiple index columns <emphasis>must</>
support indexing null values in columns after the first, because the planner
will assume the index can be used for queries on just the first
column(s). For example, consider an index on (a,b) and a query with
will assume the index can be used for queries that do not restrict
these columns. For example, consider an index on (a,b) and a query with
<literal>WHERE a = 4</literal>. The system will assume the index can be
used to scan for rows with <literal>a = 4</literal>, which is wrong if the
index omits rows where <literal>b</> is null.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.82 2005/05/27 23:31:20 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.83 2005/06/13 23:14:48 tgl Exp $
*
* INTERFACE ROUTINES
* index_open - open an index relation by relation OID
@ -25,7 +25,6 @@
* index_getmulti - get multiple tuples from a scan
* index_bulk_delete - bulk deletion of index tuples
* index_vacuum_cleanup - post-deletion cleanup of an index
* index_cost_estimator - fetch amcostestimate procedure OID
* index_getprocid - get a support procedure OID
* index_getprocinfo - get a support procedure's lookup info
*
@ -718,27 +717,6 @@ index_vacuum_cleanup(Relation indexRelation,
return result;
}
/* ----------------
* index_cost_estimator
*
* Fetch the amcostestimate procedure OID for an index.
*
* We could combine fetching and calling the procedure,
* as index_insert does for example; but that would require
* importing a bunch of planner/optimizer stuff into this file.
* ----------------
*/
RegProcedure
index_cost_estimator(Relation indexRelation)
{
FmgrInfo *procedure;
RELATION_CHECKS;
GET_REL_PROCEDURE(amcostestimate);
return procedure->fn_oid;
}
/* ----------------
* index_getprocid
*

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.91 2005/03/29 00:16:52 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.92 2005/06/13 23:14:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -594,15 +594,17 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
}
/*
* Done if that was the last attribute.
* Done if that was the last attribute, or if next key
* is not in sequence (implying no boundary key is available
* for the next attribute).
*/
if (i >= so->numberOfKeys)
if (i >= so->numberOfKeys ||
cur->sk_attno != curattr + 1)
break;
/*
* Reset for next attr, which should be in sequence.
* Reset for next attr.
*/
Assert(cur->sk_attno == curattr + 1);
curattr = cur->sk_attno;
chosen = NULL;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.62 2004/12/31 21:59:22 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.63 2005/06/13 23:14:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -190,7 +190,9 @@ _bt_formitem(IndexTuple itup)
* matched to continue the scan. In general, numberOfRequiredKeys is equal
* to the number of keys for leading attributes with "=" keys, plus the
* key(s) for the first non "=" attribute, which can be seen to be correct
* by considering the above example.
* by considering the above example. Note in particular that if there are no
* keys for a given attribute, the keys for subsequent attributes can never
* be required; for instance "WHERE y = 4" requires a full-index scan.
*
* If possible, redundant keys are eliminated: we keep only the tightest
* >/>= bound and the tightest </<= bound, and if there's an = key then
@ -248,8 +250,8 @@ _bt_preprocess_keys(IndexScanDesc scan)
outkeys = so->keyData;
cur = &inkeys[0];
/* we check that input keys are correctly ordered */
if (cur->sk_attno != 1)
elog(ERROR, "key(s) for attribute 1 missed");
if (cur->sk_attno < 1)
elog(ERROR, "btree index keys must be ordered by attribute");
/* We can short-circuit most of the work if there's just one key */
if (numberOfKeys == 1)
@ -270,7 +272,8 @@ _bt_preprocess_keys(IndexScanDesc scan)
}
memcpy(outkeys, inkeys, sizeof(ScanKeyData));
so->numberOfKeys = 1;
so->numberOfRequiredKeys = 1;
if (cur->sk_attno == 1)
so->numberOfRequiredKeys = 1;
return;
}
@ -324,8 +327,8 @@ _bt_preprocess_keys(IndexScanDesc scan)
int priorNumberOfEqualCols = numberOfEqualCols;
/* check input keys are correctly ordered */
if (i < numberOfKeys && cur->sk_attno != attno + 1)
elog(ERROR, "key(s) for attribute %d missed", attno + 1);
if (i < numberOfKeys && cur->sk_attno < attno)
elog(ERROR, "btree index keys must be ordered by attribute");
/*
* If = has been specified, no other key will be used. In case

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.183 2005/06/10 22:25:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.184 2005/06/13 23:14:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -87,7 +87,8 @@ static Const *string_to_const(const char *str, Oid datatype);
*
* To be considered for an index scan, an index must match one or more
* restriction clauses or join clauses from the query's qual condition,
* or match the query's ORDER BY condition.
* or match the query's ORDER BY condition, or have a predicate that
* matches the query's qual condition.
*
* There are two basic kinds of index scans. A "plain" index scan uses
* only restriction clauses (possibly none at all) in its indexqual,
@ -210,6 +211,7 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
* 'clauses' is the current list of clauses (RestrictInfo nodes)
* 'outer_clauses' is the list of additional upper-level clauses
* 'istoplevel' is true if clauses are the rel's top-level restriction list
* (outer_clauses must be NIL when this is true)
* 'isjoininner' is true if forming an inner indexscan (so some of the
* given clauses are join clauses)
* 'outer_relids' identifies the outer side of the join (pass NULL
@ -295,13 +297,12 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
* selectivity of the predicate might alone make the index useful.
*
* Note: not all index AMs support scans with no restriction clauses.
* We assume here that the AM does so if and only if it supports
* ordered scans. (It would probably be better if there were a
* specific flag for this in pg_am, but there's not.)
* We can't generate a scan over an index with amoptionalkey = false
* unless there's at least one restriction clause.
*/
if (restrictclauses != NIL ||
useful_pathkeys != NIL ||
(index->indpred != NIL && index_is_ordered))
(index->amoptionalkey &&
(useful_pathkeys != NIL || index->indpred != NIL)))
{
ipath = create_index_path(root, index,
restrictclauses,
@ -608,6 +609,11 @@ bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths)
* group_clauses_by_indexkey
* Find restriction clauses that can be used with an index.
*
* Returns a list of sublists of RestrictInfo nodes for clauses that can be
* used with this index. Each sublist contains clauses that can be used
* with one index key (in no particular order); the top list is ordered by
* index key. (This is depended on by expand_indexqual_conditions().)
*
* As explained in the comments for find_usable_indexes(), we can use
* clauses from either of the given lists, but the result is required to
* use at least one clause from the "current clauses" list. We return
@ -616,18 +622,14 @@ bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths)
* outer_relids determines what Vars will be allowed on the other side
* of a possible index qual; see match_clause_to_indexcol().
*
* Returns a list of sublists of RestrictInfo nodes for clauses that can be
* used with this index. Each sublist contains clauses that can be used
* with one index key (in no particular order); the top list is ordered by
* index key. (This is depended on by expand_indexqual_conditions().)
* If the index has amoptionalkey = false, we give up and return NIL when
* there are no restriction clauses matching the first index key. Otherwise,
* we return NIL if there are no restriction clauses matching any index key.
* A non-NIL result will have one (possibly empty) sublist for each index key.
*
* Note that in a multi-key index, we stop if we find a key that cannot be
* used with any clause. For example, given an index on (A,B,C), we might
* return ((C1 C2) (C3 C4)) if we find that clauses C1 and C2 use column A,
* clauses C3 and C4 use column B, and no clauses use column C. But if
* no clauses match B we will return ((C1 C2)), whether or not there are
* clauses matching column C, because the executor couldn't use them anyway.
* Therefore, there are no empty sublists in the result.
* Example: given an index on (A,B,C), we would return ((C1 C2) () (C3 C4))
* if we find that clauses C1 and C2 use column A, clauses C3 and C4 use
* column C, and no clauses use column B.
*/
List *
group_clauses_by_indexkey(IndexOptInfo *index,
@ -680,11 +682,10 @@ group_clauses_by_indexkey(IndexOptInfo *index,
}
/*
* If no clauses match this key, we're done; we don't want to look
* at keys to its right.
* If no clauses match this key, check for amoptionalkey restriction.
*/
if (clausegroup == NIL)
break;
if (clausegroup == NIL && !index->amoptionalkey && indexcol == 0)
return NIL;
clausegroup_list = lappend(clausegroup_list, clausegroup);
@ -1581,11 +1582,9 @@ match_special_index_operator(Expr *clause, Oid opclass,
* will know what to do with.
*
* The input list is ordered by index key, and so the output list is too.
* (The latter is not depended on by any part of the planner, so far as I can
* tell; but some parts of the executor do assume that the indexqual list
* ultimately delivered to the executor is so ordered. One such place is
* _bt_preprocess_keys() in the btree support. Perhaps that ought to be fixed
* someday --- tgl 7/00)
* (The latter is not depended on by any part of the core planner, I believe,
* but parts of the executor require it, and so do the amcostestimate
* functions.)
*/
List *
expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.111 2005/06/05 22:32:56 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.112 2005/06/13 23:14:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -161,7 +161,8 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
}
info->relam = indexRelation->rd_rel->relam;
info->amcostestimate = index_cost_estimator(indexRelation);
info->amcostestimate = indexRelation->rd_am->amcostestimate;
info->amoptionalkey = indexRelation->rd_am->amoptionalkey;
/*
* Fetch the ordering operators associated with the index, if

View File

@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.181 2005/06/10 22:25:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.182 2005/06/13 23:14:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -4195,18 +4195,23 @@ string_to_bytea_const(const char *str, size_t str_len)
* don't have any better idea about how to estimate. Index-type-specific
* knowledge can be incorporated in the type-specific routines.
*
* One bit of index-type-specific knowledge we can relatively easily use
* in genericcostestimate is the estimate of the number of index tuples
* visited. If numIndexTuples is not 0 then it is used as the estimate,
* otherwise we compute a generic estimate.
*
*-------------------------------------------------------------------------
*/
static void
genericcostestimate(PlannerInfo *root,
IndexOptInfo *index, List *indexQuals,
double numIndexTuples,
Cost *indexStartupCost,
Cost *indexTotalCost,
Selectivity *indexSelectivity,
double *indexCorrelation)
{
double numIndexTuples;
double numIndexPages;
QualCost index_qual_cost;
double qual_op_cost;
@ -4254,20 +4259,20 @@ genericcostestimate(PlannerInfo *root,
JOIN_INNER);
/*
* Estimate the number of tuples that will be visited. We do it in
* this rather peculiar-looking way in order to get the right answer
* for partial indexes. We can bound the number of tuples by the
* index size, in any case.
* If caller didn't give us an estimate, estimate the number of index
* tuples that will be visited. We do it in this rather peculiar-looking
* way in order to get the right answer for partial indexes.
*/
numIndexTuples = *indexSelectivity * index->rel->tuples;
if (numIndexTuples > index->tuples)
numIndexTuples = index->tuples;
if (numIndexTuples <= 0.0)
numIndexTuples = *indexSelectivity * index->rel->tuples;
/*
* Always estimate at least one tuple is touched, even when
* We can bound the number of tuples by the index size in any case.
* Also, always estimate at least one tuple is touched, even when
* indexSelectivity estimate is tiny.
*/
if (numIndexTuples > index->tuples)
numIndexTuples = index->tuples;
if (numIndexTuples < 1.0)
numIndexTuples = 1.0;
@ -4337,8 +4342,95 @@ btcostestimate(PG_FUNCTION_ARGS)
Oid relid;
AttrNumber colnum;
HeapTuple tuple;
double numIndexTuples;
List *indexBoundQuals;
int indexcol;
bool eqQualHere;
ListCell *l;
genericcostestimate(root, index, indexQuals,
/*
* For a btree scan, only leading '=' quals plus inequality quals
* for the immediately next attribute contribute to index selectivity
* (these are the "boundary quals" that determine the starting and
* stopping points of the index scan). Additional quals can suppress
* visits to the heap, so it's OK to count them in indexSelectivity,
* but they should not count for estimating numIndexTuples. So we must
* examine the given indexQuals to find out which ones count as boundary
* quals. We rely on the knowledge that they are given in index column
* order.
*/
indexBoundQuals = NIL;
indexcol = 0;
eqQualHere = false;
foreach(l, indexQuals)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
Expr *clause;
Oid clause_op;
int op_strategy;
Assert(IsA(rinfo, RestrictInfo));
clause = rinfo->clause;
Assert(IsA(clause, OpExpr));
clause_op = ((OpExpr *) clause)->opno;
if (match_index_to_operand(get_leftop(clause), indexcol, index))
{
/* clause_op is correct */
}
else if (match_index_to_operand(get_rightop(clause), indexcol, index))
{
/* Must flip operator to get the opclass member */
clause_op = get_commutator(clause_op);
}
else
{
/* Must be past the end of quals for indexcol, try next */
if (!eqQualHere)
break; /* done if no '=' qual for indexcol */
indexcol++;
eqQualHere = false;
if (match_index_to_operand(get_leftop(clause), indexcol, index))
{
/* clause_op is correct */
}
else if (match_index_to_operand(get_rightop(clause),
indexcol, index))
{
/* Must flip operator to get the opclass member */
clause_op = get_commutator(clause_op);
}
else
{
/* No quals for new indexcol, so we are done */
break;
}
}
op_strategy = get_op_opclass_strategy(clause_op,
index->classlist[indexcol]);
Assert(op_strategy != 0); /* not a member of opclass?? */
if (op_strategy == BTEqualStrategyNumber)
eqQualHere = true;
indexBoundQuals = lappend(indexBoundQuals, rinfo);
}
/*
* If index is unique and we found an '=' clause for each column,
* we can just assume numIndexTuples = 1 and skip the expensive
* clauselist_selectivity calculations.
*/
if (index->unique && indexcol == index->ncolumns - 1 && eqQualHere)
numIndexTuples = 1.0;
else
{
Selectivity btreeSelectivity;
btreeSelectivity = clauselist_selectivity(root, indexBoundQuals,
index->rel->relid,
JOIN_INNER);
numIndexTuples = btreeSelectivity * index->rel->tuples;
}
genericcostestimate(root, index, indexQuals, numIndexTuples,
indexStartupCost, indexTotalCost,
indexSelectivity, indexCorrelation);
@ -4414,7 +4506,7 @@ rtcostestimate(PG_FUNCTION_ARGS)
Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
genericcostestimate(root, index, indexQuals,
genericcostestimate(root, index, indexQuals, 0.0,
indexStartupCost, indexTotalCost,
indexSelectivity, indexCorrelation);
@ -4432,7 +4524,7 @@ hashcostestimate(PG_FUNCTION_ARGS)
Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
genericcostestimate(root, index, indexQuals,
genericcostestimate(root, index, indexQuals, 0.0,
indexStartupCost, indexTotalCost,
indexSelectivity, indexCorrelation);
@ -4450,7 +4542,7 @@ gistcostestimate(PG_FUNCTION_ARGS)
Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
genericcostestimate(root, index, indexQuals,
genericcostestimate(root, index, indexQuals, 0.0,
indexStartupCost, indexTotalCost,
indexSelectivity, indexCorrelation);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/access/genam.h,v 1.51 2005/05/27 23:31:21 tgl Exp $
* $PostgreSQL: pgsql/src/include/access/genam.h,v 1.52 2005/06/13 23:14:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -101,7 +101,6 @@ extern IndexBulkDeleteResult *index_bulk_delete(Relation indexRelation,
extern IndexBulkDeleteResult *index_vacuum_cleanup(Relation indexRelation,
IndexVacuumCleanupInfo *info,
IndexBulkDeleteResult *stats);
extern RegProcedure index_cost_estimator(Relation indexRelation);
extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
uint16 procnum);
extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.274 2005/06/13 02:26:50 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.275 2005/06/13 23:14:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200506121
#define CATALOG_VERSION_NO 200506131
#endif

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.33 2005/04/14 01:38:20 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.34 2005/06/13 23:14:49 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -48,6 +48,7 @@ CATALOG(pg_am,2601)
* Zero if AM is not ordered. */
bool amcanunique; /* does AM support UNIQUE indexes? */
bool amcanmulticol; /* does AM support multi-column indexes? */
bool amoptionalkey; /* can query omit key for the first column? */
bool amindexnulls; /* does AM support NULL index entries? */
bool amconcurrent; /* does AM support concurrent updates? */
regproc aminsert; /* "insert this tuple" function */
@ -75,42 +76,43 @@ typedef FormData_pg_am *Form_pg_am;
* compiler constants for pg_am
* ----------------
*/
#define Natts_pg_am 20
#define Natts_pg_am 21
#define Anum_pg_am_amname 1
#define Anum_pg_am_amstrategies 2
#define Anum_pg_am_amsupport 3
#define Anum_pg_am_amorderstrategy 4
#define Anum_pg_am_amcanunique 5
#define Anum_pg_am_amcanmulticol 6
#define Anum_pg_am_amindexnulls 7
#define Anum_pg_am_amconcurrent 8
#define Anum_pg_am_aminsert 9
#define Anum_pg_am_ambeginscan 10
#define Anum_pg_am_amgettuple 11
#define Anum_pg_am_amgetmulti 12
#define Anum_pg_am_amrescan 13
#define Anum_pg_am_amendscan 14
#define Anum_pg_am_ammarkpos 15
#define Anum_pg_am_amrestrpos 16
#define Anum_pg_am_ambuild 17
#define Anum_pg_am_ambulkdelete 18
#define Anum_pg_am_amvacuumcleanup 19
#define Anum_pg_am_amcostestimate 20
#define Anum_pg_am_amoptionalkey 7
#define Anum_pg_am_amindexnulls 8
#define Anum_pg_am_amconcurrent 9
#define Anum_pg_am_aminsert 10
#define Anum_pg_am_ambeginscan 11
#define Anum_pg_am_amgettuple 12
#define Anum_pg_am_amgetmulti 13
#define Anum_pg_am_amrescan 14
#define Anum_pg_am_amendscan 15
#define Anum_pg_am_ammarkpos 16
#define Anum_pg_am_amrestrpos 17
#define Anum_pg_am_ambuild 18
#define Anum_pg_am_ambulkdelete 19
#define Anum_pg_am_amvacuumcleanup 20
#define Anum_pg_am_amcostestimate 21
/* ----------------
* initial contents of pg_am
* ----------------
*/
DATA(insert OID = 402 ( rtree 8 3 0 f f f f rtinsert rtbeginscan rtgettuple rtgetmulti rtrescan rtendscan rtmarkpos rtrestrpos rtbuild rtbulkdelete - rtcostestimate ));
DATA(insert OID = 402 ( rtree 8 3 0 f f f f f rtinsert rtbeginscan rtgettuple rtgetmulti rtrescan rtendscan rtmarkpos rtrestrpos rtbuild rtbulkdelete - rtcostestimate ));
DESCR("r-tree index access method");
DATA(insert OID = 403 ( btree 5 1 1 t t t t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate ));
DATA(insert OID = 403 ( btree 5 1 1 t t t t t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate ));
DESCR("b-tree index access method");
#define BTREE_AM_OID 403
DATA(insert OID = 405 ( hash 1 1 0 f f f t hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete - hashcostestimate ));
DATA(insert OID = 405 ( hash 1 1 0 f f f f t hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete - hashcostestimate ));
DESCR("hash index access method");
#define HASH_AM_OID 405
DATA(insert OID = 783 ( gist 100 7 0 f t f f gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete - gistcostestimate ));
DATA(insert OID = 783 ( gist 100 7 0 f t f f f gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete - gistcostestimate ));
DESCR("GiST index access method");
#define GIST_AM_OID 783

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.114 2005/06/10 03:32:25 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.115 2005/06/13 23:14:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -328,6 +328,7 @@ typedef struct IndexOptInfo
bool predOK; /* true if predicate matches query */
bool unique; /* true if a unique index */
bool amoptionalkey; /* can query omit key for the first column? */
} IndexOptInfo;