mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-06 15:24:56 +08:00
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:
parent
077811605e
commit
c186c93148
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user