mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Another round of planner/optimizer work. This is just restructuring and
code cleanup; no major improvements yet. However, EXPLAIN does produce more intuitive outputs for nested loops with indexscans now...
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.55 1999/11/23 20:07:00 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.56 2000/01/09 00:26:37 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -15,8 +15,6 @@
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
|
||||
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/paths.h"
|
||||
@@ -37,10 +35,7 @@
|
||||
bool
|
||||
path_is_cheaper(Path *path1, Path *path2)
|
||||
{
|
||||
Cost cost1 = path1->path_cost;
|
||||
Cost cost2 = path2->path_cost;
|
||||
|
||||
return (bool) (cost1 < cost2);
|
||||
return (bool) (path1->path_cost < path2->path_cost);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -60,8 +55,8 @@ set_cheapest(RelOptInfo *parent_rel, List *pathlist)
|
||||
List *p;
|
||||
Path *cheapest_so_far;
|
||||
|
||||
Assert(pathlist != NIL);
|
||||
Assert(IsA(parent_rel, RelOptInfo));
|
||||
Assert(pathlist != NIL);
|
||||
|
||||
cheapest_so_far = (Path *) lfirst(pathlist);
|
||||
|
||||
@@ -192,18 +187,11 @@ Path *
|
||||
create_seqscan_path(RelOptInfo *rel)
|
||||
{
|
||||
Path *pathnode = makeNode(Path);
|
||||
int relid = 0;
|
||||
|
||||
pathnode->pathtype = T_SeqScan;
|
||||
pathnode->parent = rel;
|
||||
pathnode->path_cost = 0.0;
|
||||
pathnode->pathkeys = NIL; /* seqscan has unordered result */
|
||||
|
||||
if (rel->relids != NIL) /* can this happen? */
|
||||
relid = lfirsti(rel->relids);
|
||||
|
||||
pathnode->path_cost = cost_seqscan(relid,
|
||||
rel->pages, rel->tuples);
|
||||
pathnode->path_cost = cost_seqscan(rel);
|
||||
|
||||
return pathnode;
|
||||
}
|
||||
@@ -222,7 +210,7 @@ create_seqscan_path(RelOptInfo *rel)
|
||||
IndexPath *
|
||||
create_index_path(Query *root,
|
||||
RelOptInfo *rel,
|
||||
RelOptInfo *index,
|
||||
IndexOptInfo *index,
|
||||
List *restriction_clauses)
|
||||
{
|
||||
IndexPath *pathnode = makeNode(IndexPath);
|
||||
@@ -239,8 +227,7 @@ create_index_path(Query *root,
|
||||
* conditions. If we do have restriction conditions to use, they
|
||||
* will get inserted below.
|
||||
*/
|
||||
Assert(length(index->relids) == 1);
|
||||
pathnode->indexid = index->relids;
|
||||
pathnode->indexid = lconsi(index->indexoid, NIL);
|
||||
pathnode->indexqual = lcons(NIL, NIL);
|
||||
pathnode->joinrelids = NIL; /* no join clauses here */
|
||||
|
||||
@@ -250,13 +237,9 @@ create_index_path(Query *root,
|
||||
* We have no restriction clauses, so compute scan cost using
|
||||
* selectivity of 1.0.
|
||||
*/
|
||||
pathnode->path.path_cost = cost_index(lfirsti(index->relids),
|
||||
pathnode->path.path_cost = cost_index(rel, index,
|
||||
index->pages,
|
||||
1.0,
|
||||
rel->pages,
|
||||
rel->tuples,
|
||||
index->pages,
|
||||
index->tuples,
|
||||
(Selectivity) 1.0,
|
||||
false);
|
||||
}
|
||||
else
|
||||
@@ -266,9 +249,8 @@ create_index_path(Query *root,
|
||||
* restriction clause(s). Also, place indexqual in path node.
|
||||
*/
|
||||
List *indexquals;
|
||||
float npages;
|
||||
float selec;
|
||||
Cost clausesel;
|
||||
long npages;
|
||||
Selectivity selec;
|
||||
|
||||
indexquals = get_actual_clauses(restriction_clauses);
|
||||
/* expand special operators to indexquals the executor can handle */
|
||||
@@ -280,39 +262,15 @@ create_index_path(Query *root,
|
||||
lfirst(pathnode->indexqual) = indexquals;
|
||||
|
||||
index_selectivity(root,
|
||||
lfirsti(rel->relids),
|
||||
lfirsti(index->relids),
|
||||
rel,
|
||||
index,
|
||||
indexquals,
|
||||
&npages,
|
||||
&selec);
|
||||
|
||||
pathnode->path.path_cost = cost_index(lfirsti(index->relids),
|
||||
(int) npages,
|
||||
selec,
|
||||
rel->pages,
|
||||
rel->tuples,
|
||||
index->pages,
|
||||
index->tuples,
|
||||
pathnode->path.path_cost = cost_index(rel, index,
|
||||
npages, selec,
|
||||
false);
|
||||
|
||||
/*
|
||||
* Set selectivities of clauses used with index to the selectivity
|
||||
* of this index, subdividing the selectivity equally over each of
|
||||
* the clauses. To the extent that index_selectivity() can make a
|
||||
* better estimate of the joint selectivity of these clauses than
|
||||
* the product of individual estimates from compute_clause_selec()
|
||||
* would be, this should give us a more accurate estimate of the
|
||||
* total selectivity of all the clauses.
|
||||
*
|
||||
* XXX If there is more than one useful index for this rel, and the
|
||||
* indexes can be used with different but overlapping groups of
|
||||
* restriction clauses, we may end up with too optimistic an estimate,
|
||||
* since set_clause_selectivities() will save the minimum of the
|
||||
* per-clause selectivity estimated with each index. But that should
|
||||
* be fairly unlikely for typical index usage.
|
||||
*/
|
||||
clausesel = pow(selec, 1.0 / (double) length(restriction_clauses));
|
||||
set_clause_selectivities(restriction_clauses, clausesel);
|
||||
}
|
||||
|
||||
return pathnode;
|
||||
@@ -331,14 +289,12 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
|
||||
|
||||
pathnode->path.pathtype = T_TidScan;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->path.path_cost = 0.0;
|
||||
pathnode->path.pathkeys = NIL;
|
||||
|
||||
pathnode->path.path_cost = cost_tidscan(tideval);
|
||||
pathnode->path.path_cost = cost_tidscan(rel, tideval);
|
||||
/* divide selectivity for each clause to get an equal selectivity
|
||||
* as IndexScan does OK ?
|
||||
*/
|
||||
pathnode->tideval = copyObject(tideval);
|
||||
pathnode->tideval = copyObject(tideval); /* is copy really necessary? */
|
||||
pathnode->unjoined_relids = NIL;
|
||||
|
||||
return pathnode;
|
||||
@@ -350,7 +306,6 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
|
||||
* relations.
|
||||
*
|
||||
* 'joinrel' is the join relation.
|
||||
* 'outer_rel' is the outer join relation
|
||||
* 'outer_path' is the outer path
|
||||
* 'inner_path' is the inner path
|
||||
* 'pathkeys' are the path keys of the new join path
|
||||
@@ -360,7 +315,6 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
|
||||
*/
|
||||
NestPath *
|
||||
create_nestloop_path(RelOptInfo *joinrel,
|
||||
RelOptInfo *outer_rel,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *pathkeys)
|
||||
@@ -371,15 +325,10 @@ create_nestloop_path(RelOptInfo *joinrel,
|
||||
pathnode->path.parent = joinrel;
|
||||
pathnode->outerjoinpath = outer_path;
|
||||
pathnode->innerjoinpath = inner_path;
|
||||
pathnode->pathinfo = joinrel->restrictinfo;
|
||||
pathnode->path.pathkeys = pathkeys;
|
||||
|
||||
pathnode->path.path_cost = cost_nestloop(outer_path->path_cost,
|
||||
inner_path->path_cost,
|
||||
outer_rel->size,
|
||||
inner_path->parent->size,
|
||||
page_size(outer_rel->size,
|
||||
outer_rel->width),
|
||||
pathnode->path.path_cost = cost_nestloop(outer_path,
|
||||
inner_path,
|
||||
IsA(inner_path, IndexPath));
|
||||
|
||||
return pathnode;
|
||||
@@ -391,10 +340,6 @@ create_nestloop_path(RelOptInfo *joinrel,
|
||||
* two relations
|
||||
*
|
||||
* 'joinrel' is the join relation
|
||||
* 'outersize' is the number of tuples in the outer relation
|
||||
* 'innersize' is the number of tuples in the inner relation
|
||||
* 'outerwidth' is the number of bytes per tuple in the outer relation
|
||||
* 'innerwidth' is the number of bytes per tuple in the inner relation
|
||||
* 'outer_path' is the outer path
|
||||
* 'inner_path' is the inner path
|
||||
* 'pathkeys' are the path keys of the new join path
|
||||
@@ -405,10 +350,6 @@ create_nestloop_path(RelOptInfo *joinrel,
|
||||
*/
|
||||
MergePath *
|
||||
create_mergejoin_path(RelOptInfo *joinrel,
|
||||
int outersize,
|
||||
int innersize,
|
||||
int outerwidth,
|
||||
int innerwidth,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *pathkeys,
|
||||
@@ -433,19 +374,14 @@ create_mergejoin_path(RelOptInfo *joinrel,
|
||||
pathnode->jpath.path.parent = joinrel;
|
||||
pathnode->jpath.outerjoinpath = outer_path;
|
||||
pathnode->jpath.innerjoinpath = inner_path;
|
||||
pathnode->jpath.pathinfo = joinrel->restrictinfo;
|
||||
pathnode->jpath.path.pathkeys = pathkeys;
|
||||
pathnode->path_mergeclauses = mergeclauses;
|
||||
pathnode->outersortkeys = outersortkeys;
|
||||
pathnode->innersortkeys = innersortkeys;
|
||||
pathnode->jpath.path.path_cost = cost_mergejoin(outer_path->path_cost,
|
||||
inner_path->path_cost,
|
||||
pathnode->jpath.path.path_cost = cost_mergejoin(outer_path,
|
||||
inner_path,
|
||||
outersortkeys,
|
||||
innersortkeys,
|
||||
outersize,
|
||||
innersize,
|
||||
outerwidth,
|
||||
innerwidth);
|
||||
innersortkeys);
|
||||
|
||||
return pathnode;
|
||||
}
|
||||
@@ -455,10 +391,6 @@ create_mergejoin_path(RelOptInfo *joinrel,
|
||||
* Creates a pathnode corresponding to a hash join between two relations.
|
||||
*
|
||||
* 'joinrel' is the join relation
|
||||
* 'outersize' is the number of tuples in the outer relation
|
||||
* 'innersize' is the number of tuples in the inner relation
|
||||
* 'outerwidth' is the number of bytes per tuple in the outer relation
|
||||
* 'innerwidth' is the number of bytes per tuple in the inner relation
|
||||
* 'outer_path' is the cheapest outer path
|
||||
* 'inner_path' is the cheapest inner path
|
||||
* 'hashclauses' is a list of the hash join clause (always a 1-element list)
|
||||
@@ -467,14 +399,10 @@ create_mergejoin_path(RelOptInfo *joinrel,
|
||||
*/
|
||||
HashPath *
|
||||
create_hashjoin_path(RelOptInfo *joinrel,
|
||||
int outersize,
|
||||
int innersize,
|
||||
int outerwidth,
|
||||
int innerwidth,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *hashclauses,
|
||||
Cost innerdisbursion)
|
||||
Selectivity innerdisbursion)
|
||||
{
|
||||
HashPath *pathnode = makeNode(HashPath);
|
||||
|
||||
@@ -482,14 +410,11 @@ create_hashjoin_path(RelOptInfo *joinrel,
|
||||
pathnode->jpath.path.parent = joinrel;
|
||||
pathnode->jpath.outerjoinpath = outer_path;
|
||||
pathnode->jpath.innerjoinpath = inner_path;
|
||||
pathnode->jpath.pathinfo = joinrel->restrictinfo;
|
||||
/* A hashjoin never has pathkeys, since its ordering is unpredictable */
|
||||
pathnode->jpath.path.pathkeys = NIL;
|
||||
pathnode->path_hashclauses = hashclauses;
|
||||
pathnode->jpath.path.path_cost = cost_hashjoin(outer_path->path_cost,
|
||||
inner_path->path_cost,
|
||||
outersize, innersize,
|
||||
outerwidth, innerwidth,
|
||||
pathnode->jpath.path.path_cost = cost_hashjoin(outer_path,
|
||||
inner_path,
|
||||
innerdisbursion);
|
||||
|
||||
return pathnode;
|
||||
|
||||
@@ -8,14 +8,15 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.40 1999/11/22 17:56:17 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.41 2000/01/09 00:26:40 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <math.h>
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catname.h"
|
||||
@@ -23,20 +24,12 @@
|
||||
#include "catalog/pg_inherits.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
static void IndexSelectivity(Oid indexrelid, Oid baserelid, int nIndexKeys,
|
||||
Oid *operatorObjectIds,
|
||||
AttrNumber *varAttributeNumbers,
|
||||
Datum *constValues,
|
||||
int *constFlags,
|
||||
float *idxPages,
|
||||
float *idxSelec);
|
||||
|
||||
|
||||
/*
|
||||
* relation_info -
|
||||
* Retrieves catalog information for a given relation.
|
||||
@@ -47,39 +40,33 @@ static void IndexSelectivity(Oid indexrelid, Oid baserelid, int nIndexKeys,
|
||||
*/
|
||||
void
|
||||
relation_info(Query *root, Index relid,
|
||||
bool *hasindex, int *pages, int *tuples)
|
||||
bool *hasindex, long *pages, double *tuples)
|
||||
{
|
||||
Oid relationObjectId = getrelid(relid, root->rtable);
|
||||
HeapTuple relationTuple;
|
||||
Form_pg_class relation;
|
||||
Oid relationObjectId;
|
||||
|
||||
relationObjectId = getrelid(relid, root->rtable);
|
||||
relationTuple = SearchSysCacheTuple(RELOID,
|
||||
ObjectIdGetDatum(relationObjectId),
|
||||
0, 0, 0);
|
||||
if (HeapTupleIsValid(relationTuple))
|
||||
{
|
||||
relation = (Form_pg_class) GETSTRUCT(relationTuple);
|
||||
|
||||
*hasindex = (relation->relhasindex) ? true : false;
|
||||
*pages = relation->relpages;
|
||||
*tuples = relation->reltuples;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!HeapTupleIsValid(relationTuple))
|
||||
elog(ERROR, "relation_info: Relation %u not found",
|
||||
relationObjectId);
|
||||
}
|
||||
relation = (Form_pg_class) GETSTRUCT(relationTuple);
|
||||
|
||||
*hasindex = (relation->relhasindex) ? true : false;
|
||||
*pages = relation->relpages;
|
||||
*tuples = relation->reltuples;
|
||||
}
|
||||
|
||||
/*
|
||||
* find_secondary_indexes
|
||||
* Creates a list of RelOptInfo nodes containing information for each
|
||||
* Creates a list of IndexOptInfo nodes containing information for each
|
||||
* secondary index defined on the given relation.
|
||||
*
|
||||
* 'relid' is the RT index of the relation for which indices are being located
|
||||
*
|
||||
* Returns a list of new index RelOptInfo nodes.
|
||||
* Returns a list of new IndexOptInfo nodes.
|
||||
*/
|
||||
List *
|
||||
find_secondary_indexes(Query *root, Index relid)
|
||||
@@ -105,7 +92,7 @@ find_secondary_indexes(Query *root, Index relid)
|
||||
while (HeapTupleIsValid(indexTuple = heap_getnext(scan, 0)))
|
||||
{
|
||||
Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||
RelOptInfo *info = makeNode(RelOptInfo);
|
||||
IndexOptInfo *info = makeNode(IndexOptInfo);
|
||||
int i;
|
||||
Relation indexRelation;
|
||||
uint16 amstrategy;
|
||||
@@ -120,7 +107,7 @@ find_secondary_indexes(Query *root, Index relid)
|
||||
info->ordering = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS+1));
|
||||
|
||||
/* Extract info from the pg_index tuple */
|
||||
info->relids = lconsi(index->indexrelid, NIL);
|
||||
info->indexoid = index->indexrelid;
|
||||
info->indproc = index->indproc; /* functional index ?? */
|
||||
if (VARSIZE(&index->indpred) != 0) /* partial index ?? */
|
||||
{
|
||||
@@ -172,17 +159,6 @@ find_secondary_indexes(Query *root, Index relid)
|
||||
info->ordering[i] = ((Form_pg_amop) GETSTRUCT(amopTuple))->amopopr;
|
||||
}
|
||||
|
||||
info->indexed = false; /* not indexed itself */
|
||||
info->size = 0;
|
||||
info->width = 0;
|
||||
info->targetlist = NIL;
|
||||
info->pathlist = NIL;
|
||||
info->cheapestpath = NULL;
|
||||
info->pruneable = true;
|
||||
info->restrictinfo = NIL;
|
||||
info->joininfo = NIL;
|
||||
info->innerjoin = NIL;
|
||||
|
||||
indexes = lcons(info, indexes);
|
||||
}
|
||||
|
||||
@@ -200,77 +176,211 @@ find_secondary_indexes(Query *root, Index relid)
|
||||
* but here we consider the cost of just one pass.
|
||||
*
|
||||
* 'root' is the query root
|
||||
* 'relid' is the RT index of the relation being scanned
|
||||
* 'indexid' is the OID of the index to be used
|
||||
* 'rel' is the relation being scanned
|
||||
* 'index' is the index to be used
|
||||
* 'indexquals' is the list of qual condition exprs (implicit AND semantics)
|
||||
* '*idxPages' receives an estimate of the number of index pages touched
|
||||
* '*idxSelec' receives an estimate of selectivity of the scan
|
||||
* '*idxSelec' receives an estimate of selectivity of the scan, ie fraction
|
||||
* of the relation's tuples that will be retrieved
|
||||
*/
|
||||
void
|
||||
index_selectivity(Query *root,
|
||||
int relid,
|
||||
Oid indexid,
|
||||
RelOptInfo *rel,
|
||||
IndexOptInfo *index,
|
||||
List *indexquals,
|
||||
float *idxPages,
|
||||
float *idxSelec)
|
||||
long *idxPages,
|
||||
Selectivity *idxSelec)
|
||||
{
|
||||
int nclauses = length(indexquals);
|
||||
Oid *opno_array;
|
||||
AttrNumber *attno_array;
|
||||
Datum *value_array;
|
||||
int *flag_array;
|
||||
int relid;
|
||||
Oid baserelid,
|
||||
indexrelid;
|
||||
HeapTuple indRel,
|
||||
indexTuple;
|
||||
Form_pg_class indexrelation;
|
||||
Oid relam;
|
||||
Form_pg_index pgindex;
|
||||
int nIndexKeys;
|
||||
float64data npages,
|
||||
select,
|
||||
fattr_select;
|
||||
bool nphack = false;
|
||||
List *q;
|
||||
int i;
|
||||
|
||||
if (nclauses <= 0)
|
||||
{
|
||||
*idxPages = 0.0;
|
||||
*idxSelec = 1.0;
|
||||
return;
|
||||
}
|
||||
opno_array = (Oid *) palloc(nclauses * sizeof(Oid));
|
||||
attno_array = (AttrNumber *) palloc(nclauses * sizeof(AttrNumber));
|
||||
value_array = (Datum *) palloc(nclauses * sizeof(Datum));
|
||||
flag_array = (int *) palloc(nclauses * sizeof(int));
|
||||
Assert(length(rel->relids) == 1); /* must be a base rel */
|
||||
relid = lfirsti(rel->relids);
|
||||
|
||||
baserelid = getrelid(relid, root->rtable);
|
||||
indexrelid = index->indexoid;
|
||||
|
||||
indRel = SearchSysCacheTuple(RELOID,
|
||||
ObjectIdGetDatum(indexrelid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(indRel))
|
||||
elog(ERROR, "index_selectivity: index %u not found in pg_class",
|
||||
indexrelid);
|
||||
indexrelation = (Form_pg_class) GETSTRUCT(indRel);
|
||||
relam = indexrelation->relam;
|
||||
|
||||
indexTuple = SearchSysCacheTuple(INDEXRELID,
|
||||
ObjectIdGetDatum(indexrelid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(indexTuple))
|
||||
elog(ERROR, "index_selectivity: index %u not found in pg_index",
|
||||
indexrelid);
|
||||
pgindex = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||
|
||||
nIndexKeys = 1;
|
||||
while (pgindex->indclass[nIndexKeys] != InvalidOid)
|
||||
nIndexKeys++;
|
||||
|
||||
/*
|
||||
* Hack for non-functional btree npages estimation: npages =
|
||||
* index_pages * selectivity_of_1st_attr_clause(s) - vadim 04/24/97
|
||||
*/
|
||||
if (relam == BTREE_AM_OID && pgindex->indproc == InvalidOid)
|
||||
nphack = true;
|
||||
|
||||
npages = 0.0;
|
||||
select = 1.0;
|
||||
fattr_select = 1.0;
|
||||
|
||||
i = 0;
|
||||
foreach(q, indexquals)
|
||||
{
|
||||
Node *expr = (Node *) lfirst(q);
|
||||
Oid opno;
|
||||
int dummyrelid;
|
||||
AttrNumber attno;
|
||||
Datum value;
|
||||
int flag;
|
||||
Oid indclass;
|
||||
HeapTuple amopTuple;
|
||||
Form_pg_amop amop;
|
||||
float64 amopnpages,
|
||||
amopselect;
|
||||
|
||||
/*
|
||||
* Extract info from clause.
|
||||
*/
|
||||
if (is_opclause(expr))
|
||||
opno_array[i] = ((Oper *) ((Expr *) expr)->oper)->opno;
|
||||
opno = ((Oper *) ((Expr *) expr)->oper)->opno;
|
||||
else
|
||||
opno_array[i] = InvalidOid;
|
||||
opno = InvalidOid;
|
||||
get_relattval(expr, relid, &dummyrelid, &attno, &value, &flag);
|
||||
|
||||
get_relattval(expr, relid, &dummyrelid, &attno_array[i],
|
||||
&value_array[i], &flag_array[i]);
|
||||
i++;
|
||||
/*
|
||||
* Find the AM class for this key.
|
||||
*/
|
||||
if (pgindex->indproc != InvalidOid)
|
||||
{
|
||||
/*
|
||||
* Functional index: AM class is the first one defined since
|
||||
* functional indices have exactly one key.
|
||||
*/
|
||||
indclass = pgindex->indclass[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
indclass = InvalidOid;
|
||||
for (i = 0; pgindex->indkey[i]; i++)
|
||||
{
|
||||
if (attno == pgindex->indkey[i])
|
||||
{
|
||||
indclass = pgindex->indclass[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!OidIsValid(indclass))
|
||||
{
|
||||
/*
|
||||
* Presumably this means that we are using a functional index
|
||||
* clause and so had no variable to match to the index key ...
|
||||
* if not we are in trouble.
|
||||
*/
|
||||
elog(NOTICE, "index_selectivity: no key %d in index %u",
|
||||
attno, indexrelid);
|
||||
continue;
|
||||
}
|
||||
|
||||
amopTuple = SearchSysCacheTuple(AMOPOPID,
|
||||
ObjectIdGetDatum(indclass),
|
||||
ObjectIdGetDatum(opno),
|
||||
ObjectIdGetDatum(relam),
|
||||
0);
|
||||
if (!HeapTupleIsValid(amopTuple))
|
||||
{
|
||||
/*
|
||||
* We might get here because indxpath.c selected a binary-
|
||||
* compatible index. Try again with the compatible operator.
|
||||
*/
|
||||
if (opno != InvalidOid)
|
||||
{
|
||||
opno = indexable_operator((Expr *) expr, indclass, relam,
|
||||
((flag & SEL_RIGHT) != 0));
|
||||
amopTuple = SearchSysCacheTuple(AMOPOPID,
|
||||
ObjectIdGetDatum(indclass),
|
||||
ObjectIdGetDatum(opno),
|
||||
ObjectIdGetDatum(relam),
|
||||
0);
|
||||
}
|
||||
if (!HeapTupleIsValid(amopTuple))
|
||||
elog(ERROR, "index_selectivity: no amop %u %u %u",
|
||||
indclass, opno, relam);
|
||||
}
|
||||
amop = (Form_pg_amop) GETSTRUCT(amopTuple);
|
||||
|
||||
if (!nphack)
|
||||
{
|
||||
amopnpages = (float64) fmgr(amop->amopnpages,
|
||||
(char *) opno,
|
||||
(char *) baserelid,
|
||||
(char *) (int) attno,
|
||||
(char *) value,
|
||||
(char *) flag,
|
||||
(char *) nIndexKeys,
|
||||
(char *) indexrelid);
|
||||
if (PointerIsValid(amopnpages))
|
||||
npages += *amopnpages;
|
||||
}
|
||||
|
||||
amopselect = (float64) fmgr(amop->amopselect,
|
||||
(char *) opno,
|
||||
(char *) baserelid,
|
||||
(char *) (int) attno,
|
||||
(char *) value,
|
||||
(char *) flag,
|
||||
(char *) nIndexKeys,
|
||||
(char *) indexrelid);
|
||||
if (PointerIsValid(amopselect))
|
||||
{
|
||||
select *= *amopselect;
|
||||
if (nphack && attno == pgindex->indkey[0])
|
||||
fattr_select *= *amopselect;
|
||||
}
|
||||
}
|
||||
|
||||
IndexSelectivity(indexid,
|
||||
getrelid(relid, root->rtable),
|
||||
nclauses,
|
||||
opno_array,
|
||||
attno_array,
|
||||
value_array,
|
||||
flag_array,
|
||||
idxPages,
|
||||
idxSelec);
|
||||
|
||||
pfree(opno_array);
|
||||
pfree(attno_array);
|
||||
pfree(value_array);
|
||||
pfree(flag_array);
|
||||
/*
|
||||
* Estimation of npages below is hack of course, but it's better than
|
||||
* it was before. - vadim 04/09/97
|
||||
*/
|
||||
if (nphack)
|
||||
{
|
||||
npages = fattr_select * indexrelation->relpages;
|
||||
*idxPages = (long) ceil((double) npages);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nIndexKeys > 1)
|
||||
npages = npages / (1.0 + nIndexKeys);
|
||||
*idxPages = (long) ceil((double) (npages / nIndexKeys));
|
||||
}
|
||||
*idxSelec = select;
|
||||
}
|
||||
|
||||
/*
|
||||
* restriction_selectivity
|
||||
*
|
||||
* NOTE: The routine is now merged with RestrictionClauseSelectivity
|
||||
* as defined in plancat.c
|
||||
*
|
||||
* Returns the selectivity of a specified operator.
|
||||
* This code executes registered procedures stored in the
|
||||
* operator relation, by calling the function manager.
|
||||
@@ -279,7 +389,7 @@ index_selectivity(Query *root,
|
||||
* relation OIDs or attribute numbers are 0, then the clause
|
||||
* isn't of the form (op var const).
|
||||
*/
|
||||
Cost
|
||||
Selectivity
|
||||
restriction_selectivity(Oid functionObjectId,
|
||||
Oid operatorObjectId,
|
||||
Oid relationObjectId,
|
||||
@@ -297,28 +407,25 @@ restriction_selectivity(Oid functionObjectId,
|
||||
(char *) constFlag,
|
||||
NULL);
|
||||
if (!PointerIsValid(result))
|
||||
elog(ERROR, "RestrictionClauseSelectivity: bad pointer");
|
||||
elog(ERROR, "restriction_selectivity: bad pointer");
|
||||
|
||||
if (*result < 0.0 || *result > 1.0)
|
||||
elog(ERROR, "RestrictionClauseSelectivity: bad value %lf",
|
||||
*result);
|
||||
elog(ERROR, "restriction_selectivity: bad value %lf", *result);
|
||||
|
||||
return (Cost) *result;
|
||||
return (Selectivity) *result;
|
||||
}
|
||||
|
||||
/*
|
||||
* join_selectivity
|
||||
* Similarly, this routine is merged with JoinClauseSelectivity in
|
||||
* plancat.c
|
||||
*
|
||||
* Returns the selectivity of an operator, given the join clause
|
||||
* information.
|
||||
* Returns the selectivity of an operator, given the join clause
|
||||
* information.
|
||||
*
|
||||
* XXX The assumption in the selectivity procedures is that if the
|
||||
* relation OIDs or attribute numbers are 0, then the clause
|
||||
* isn't of the form (op var var).
|
||||
*/
|
||||
Cost
|
||||
Selectivity
|
||||
join_selectivity(Oid functionObjectId,
|
||||
Oid operatorObjectId,
|
||||
Oid relationObjectId1,
|
||||
@@ -336,13 +443,12 @@ join_selectivity(Oid functionObjectId,
|
||||
(char *) (int) attributeNumber2,
|
||||
NULL);
|
||||
if (!PointerIsValid(result))
|
||||
elog(ERROR, "JoinClauseSelectivity: bad pointer");
|
||||
elog(ERROR, "join_selectivity: bad pointer");
|
||||
|
||||
if (*result < 0.0 || *result > 1.0)
|
||||
elog(ERROR, "JoinClauseSelectivity: bad value %lf",
|
||||
*result);
|
||||
elog(ERROR, "join_selectivity: bad value %lf", *result);
|
||||
|
||||
return (Cost) *result;
|
||||
return (Selectivity) *result;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -421,172 +527,3 @@ VersionGetParents(Oid verrelid)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* IndexSelectivity
|
||||
*
|
||||
* Calls the 'amopnpages' and 'amopselect' functions for each
|
||||
* AM operator when a given index (specified by 'indexrelid') is used.
|
||||
* The total number of pages and product of the selectivities are returned.
|
||||
*
|
||||
* Assumption: the attribute numbers and operator ObjectIds are in order
|
||||
* WRT to each other (otherwise, you have no way of knowing which
|
||||
* AM operator class or attribute number corresponds to which operator.
|
||||
*
|
||||
* 'nIndexKeys' is the number of qual clauses in use
|
||||
* 'varAttributeNumbers' contains attribute numbers for variables
|
||||
* 'constValues' contains the constant values
|
||||
* 'constFlags' describes how to treat the constants in each clause
|
||||
*/
|
||||
static void
|
||||
IndexSelectivity(Oid indexrelid,
|
||||
Oid baserelid,
|
||||
int nIndexKeys,
|
||||
Oid *operatorObjectIds,
|
||||
AttrNumber *varAttributeNumbers,
|
||||
Datum *constValues,
|
||||
int *constFlags,
|
||||
float *idxPages,
|
||||
float *idxSelec)
|
||||
{
|
||||
int i,
|
||||
n;
|
||||
HeapTuple indexTuple,
|
||||
amopTuple,
|
||||
indRel;
|
||||
Form_pg_class indexrelation;
|
||||
Form_pg_index index;
|
||||
Form_pg_amop amop;
|
||||
Oid indclass;
|
||||
float64data npages,
|
||||
select;
|
||||
float64 amopnpages,
|
||||
amopselect;
|
||||
Oid relam;
|
||||
bool nphack = false;
|
||||
float64data fattr_select = 1.0;
|
||||
|
||||
indRel = SearchSysCacheTuple(RELOID,
|
||||
ObjectIdGetDatum(indexrelid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(indRel))
|
||||
elog(ERROR, "IndexSelectivity: index %u not found",
|
||||
indexrelid);
|
||||
indexrelation = (Form_pg_class) GETSTRUCT(indRel);
|
||||
relam = indexrelation->relam;
|
||||
|
||||
indexTuple = SearchSysCacheTuple(INDEXRELID,
|
||||
ObjectIdGetDatum(indexrelid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(indexTuple))
|
||||
elog(ERROR, "IndexSelectivity: index %u not found",
|
||||
indexrelid);
|
||||
index = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||
|
||||
/*
|
||||
* Hack for non-functional btree npages estimation: npages =
|
||||
* index_pages * selectivity_of_1st_attr_clause(s) - vadim 04/24/97
|
||||
*/
|
||||
if (relam == BTREE_AM_OID &&
|
||||
varAttributeNumbers[0] != InvalidAttrNumber)
|
||||
nphack = true;
|
||||
|
||||
npages = 0.0;
|
||||
select = 1.0;
|
||||
for (n = 0; n < nIndexKeys; n++)
|
||||
{
|
||||
/*
|
||||
* Find the AM class for this key.
|
||||
*
|
||||
* If the first attribute number is invalid then we have a functional
|
||||
* index, and AM class is the first one defined since functional
|
||||
* indices have exactly one key.
|
||||
*/
|
||||
indclass = (varAttributeNumbers[0] == InvalidAttrNumber) ?
|
||||
index->indclass[0] : InvalidOid;
|
||||
i = 0;
|
||||
while ((i < nIndexKeys) && (indclass == InvalidOid))
|
||||
{
|
||||
if (varAttributeNumbers[n] == index->indkey[i])
|
||||
{
|
||||
indclass = index->indclass[i];
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (!OidIsValid(indclass))
|
||||
{
|
||||
|
||||
/*
|
||||
* Presumably this means that we are using a functional index
|
||||
* clause and so had no variable to match to the index key ...
|
||||
* if not we are in trouble.
|
||||
*/
|
||||
elog(NOTICE, "IndexSelectivity: no key %d in index %u",
|
||||
varAttributeNumbers[n], indexrelid);
|
||||
continue;
|
||||
}
|
||||
|
||||
amopTuple = SearchSysCacheTuple(AMOPOPID,
|
||||
ObjectIdGetDatum(indclass),
|
||||
ObjectIdGetDatum(operatorObjectIds[n]),
|
||||
ObjectIdGetDatum(relam),
|
||||
0);
|
||||
if (!HeapTupleIsValid(amopTuple))
|
||||
elog(ERROR, "IndexSelectivity: no amop %u %u %u",
|
||||
indclass, operatorObjectIds[n], relam);
|
||||
amop = (Form_pg_amop) GETSTRUCT(amopTuple);
|
||||
|
||||
if (!nphack)
|
||||
{
|
||||
amopnpages = (float64) fmgr(amop->amopnpages,
|
||||
(char *) operatorObjectIds[n],
|
||||
(char *) baserelid,
|
||||
(char *) (int) varAttributeNumbers[n],
|
||||
(char *) constValues[n],
|
||||
(char *) constFlags[n],
|
||||
(char *) nIndexKeys,
|
||||
(char *) indexrelid);
|
||||
if (PointerIsValid(amopnpages))
|
||||
npages += *amopnpages;
|
||||
}
|
||||
|
||||
amopselect = (float64) fmgr(amop->amopselect,
|
||||
(char *) operatorObjectIds[n],
|
||||
(char *) baserelid,
|
||||
(char *) (int) varAttributeNumbers[n],
|
||||
(char *) constValues[n],
|
||||
(char *) constFlags[n],
|
||||
(char *) nIndexKeys,
|
||||
(char *) indexrelid);
|
||||
|
||||
if (PointerIsValid(amopselect))
|
||||
{
|
||||
select *= *amopselect;
|
||||
if (nphack && varAttributeNumbers[n] == index->indkey[0])
|
||||
fattr_select *= *amopselect;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Estimation of npages below is hack of course, but it's better than
|
||||
* it was before. - vadim 04/09/97
|
||||
*/
|
||||
if (nphack)
|
||||
{
|
||||
npages = fattr_select * indexrelation->relpages;
|
||||
*idxPages = ceil((double) npages);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nIndexKeys > 1)
|
||||
npages = npages / (1.0 + nIndexKeys);
|
||||
*idxPages = ceil((double) (npages / nIndexKeys));
|
||||
}
|
||||
*idxSelec = select;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.19 1999/08/16 02:17:58 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.20 2000/01/09 00:26:41 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -37,21 +37,15 @@ get_base_rel(Query *root, int relid)
|
||||
{
|
||||
rel = makeNode(RelOptInfo);
|
||||
rel->relids = relids;
|
||||
rel->indexed = false;
|
||||
rel->pages = 0;
|
||||
rel->tuples = 0;
|
||||
rel->size = 0;
|
||||
rel->rows = 0;
|
||||
rel->width = 0;
|
||||
rel->targetlist = NIL;
|
||||
rel->pathlist = NIL;
|
||||
rel->cheapestpath = (Path *) NULL;
|
||||
rel->pruneable = true;
|
||||
rel->classlist = NULL;
|
||||
rel->indexkeys = NULL;
|
||||
rel->ordering = NULL;
|
||||
rel->relam = InvalidOid;
|
||||
rel->indproc = InvalidOid;
|
||||
rel->indpred = NIL;
|
||||
rel->indexed = false;
|
||||
rel->pages = 0;
|
||||
rel->tuples = 0;
|
||||
rel->restrictinfo = NIL;
|
||||
rel->joininfo = NIL;
|
||||
rel->innerjoin = NIL;
|
||||
|
||||
Reference in New Issue
Block a user