mirror of
https://github.com/postgres/postgres.git
synced 2025-05-24 00:03:23 -04:00
First cut at making indexscan cost estimates depend on correlation
between index order and table order.
This commit is contained in:
parent
e02033572d
commit
c23bc6fbb0
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$Header: /cvsroot/pgsql/doc/src/sgml/Attic/indexcost.sgml,v 2.6 2000/12/22 21:51:57 petere Exp $
|
$Header: /cvsroot/pgsql/doc/src/sgml/Attic/indexcost.sgml,v 2.7 2001/05/09 23:13:34 tgl Exp $
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<chapter id="indexcost">
|
<chapter id="indexcost">
|
||||||
@ -57,7 +57,8 @@ amcostestimate (Query *root,
|
|||||||
List *indexQuals,
|
List *indexQuals,
|
||||||
Cost *indexStartupCost,
|
Cost *indexStartupCost,
|
||||||
Cost *indexTotalCost,
|
Cost *indexTotalCost,
|
||||||
Selectivity *indexSelectivity);
|
Selectivity *indexSelectivity,
|
||||||
|
double *indexCorrelation);
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
The first four parameters are inputs:
|
The first four parameters are inputs:
|
||||||
@ -103,7 +104,7 @@ amcostestimate (Query *root,
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The last three parameters are pass-by-reference outputs:
|
The last four parameters are pass-by-reference outputs:
|
||||||
|
|
||||||
<variablelist>
|
<variablelist>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -132,6 +133,16 @@ amcostestimate (Query *root,
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>*indexCorrelation</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Set to correlation coefficient between index scan order and
|
||||||
|
underlying table's order
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
@ -172,6 +183,13 @@ amcostestimate (Query *root,
|
|||||||
tuples that actually pass the given qual conditions.
|
tuples that actually pass the given qual conditions.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The indexCorrelation should be set to the correlation (ranging between
|
||||||
|
-1.0 and 1.0) between the index order and the table order. This is used
|
||||||
|
to adjust the estimate for the cost of fetching tuples from the main
|
||||||
|
table.
|
||||||
|
</para>
|
||||||
|
|
||||||
<procedure>
|
<procedure>
|
||||||
<title>Cost Estimation</title>
|
<title>Cost Estimation</title>
|
||||||
<para>
|
<para>
|
||||||
@ -224,6 +242,14 @@ amcostestimate (Query *root,
|
|||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
|
<step>
|
||||||
|
<para>
|
||||||
|
Estimate the index correlation. For a simple ordered index on a single
|
||||||
|
field, this can be retrieved from pg_statistic. If the correlation
|
||||||
|
is not known, the conservative estimate is zero (no correlation).
|
||||||
|
</para>
|
||||||
|
</step>
|
||||||
</procedure>
|
</procedure>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -237,8 +263,8 @@ amcostestimate (Query *root,
|
|||||||
|
|
||||||
<programlisting>
|
<programlisting>
|
||||||
prorettype = 0
|
prorettype = 0
|
||||||
pronargs = 7
|
pronargs = 8
|
||||||
proargtypes = 0 0 0 0 0 0 0
|
proargtypes = 0 0 0 0 0 0 0 0
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
We use zero ("opaque") for all the arguments since none of them have types
|
We use zero ("opaque") for all the arguments since none of them have types
|
||||||
|
@ -31,17 +31,18 @@
|
|||||||
* result by interpolating between startup_cost and total_cost. In detail:
|
* result by interpolating between startup_cost and total_cost. In detail:
|
||||||
* actual_cost = startup_cost +
|
* actual_cost = startup_cost +
|
||||||
* (total_cost - startup_cost) * tuples_to_fetch / path->parent->rows;
|
* (total_cost - startup_cost) * tuples_to_fetch / path->parent->rows;
|
||||||
* Note that a relation's rows count (and, by extension, a Plan's plan_rows)
|
* Note that a base relation's rows count (and, by extension, plan_rows for
|
||||||
* are set without regard to any LIMIT, so that this equation works properly.
|
* plan nodes below the LIMIT node) are set without regard to any LIMIT, so
|
||||||
* (Also, these routines guarantee not to set the rows count to zero, so there
|
* that this equation works properly. (Also, these routines guarantee not to
|
||||||
* will be no zero divide.) The LIMIT is applied as a separate Plan node.
|
* set the rows count to zero, so there will be no zero divide.) The LIMIT is
|
||||||
|
* applied as a top-level plan node.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.72 2001/05/09 00:35:09 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.73 2001/05/09 23:13:34 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -205,12 +206,18 @@ cost_index(Path *path, Query *root,
|
|||||||
{
|
{
|
||||||
Cost startup_cost = 0;
|
Cost startup_cost = 0;
|
||||||
Cost run_cost = 0;
|
Cost run_cost = 0;
|
||||||
Cost cpu_per_tuple;
|
|
||||||
Cost indexStartupCost;
|
Cost indexStartupCost;
|
||||||
Cost indexTotalCost;
|
Cost indexTotalCost;
|
||||||
Selectivity indexSelectivity;
|
Selectivity indexSelectivity;
|
||||||
|
double indexCorrelation,
|
||||||
|
csquared;
|
||||||
|
Cost min_IO_cost,
|
||||||
|
max_IO_cost;
|
||||||
|
Cost cpu_per_tuple;
|
||||||
double tuples_fetched;
|
double tuples_fetched;
|
||||||
double pages_fetched;
|
double pages_fetched;
|
||||||
|
double T,
|
||||||
|
b;
|
||||||
|
|
||||||
/* Should only be applied to base relations */
|
/* Should only be applied to base relations */
|
||||||
Assert(IsA(baserel, RelOptInfo) &&IsA(index, IndexOptInfo));
|
Assert(IsA(baserel, RelOptInfo) &&IsA(index, IndexOptInfo));
|
||||||
@ -224,38 +231,52 @@ cost_index(Path *path, Query *root,
|
|||||||
* Call index-access-method-specific code to estimate the processing
|
* Call index-access-method-specific code to estimate the processing
|
||||||
* cost for scanning the index, as well as the selectivity of the
|
* cost for scanning the index, as well as the selectivity of the
|
||||||
* index (ie, the fraction of main-table tuples we will have to
|
* index (ie, the fraction of main-table tuples we will have to
|
||||||
* retrieve).
|
* retrieve) and its correlation to the main-table tuple order.
|
||||||
*/
|
*/
|
||||||
OidFunctionCall7(index->amcostestimate,
|
OidFunctionCall8(index->amcostestimate,
|
||||||
PointerGetDatum(root),
|
PointerGetDatum(root),
|
||||||
PointerGetDatum(baserel),
|
PointerGetDatum(baserel),
|
||||||
PointerGetDatum(index),
|
PointerGetDatum(index),
|
||||||
PointerGetDatum(indexQuals),
|
PointerGetDatum(indexQuals),
|
||||||
PointerGetDatum(&indexStartupCost),
|
PointerGetDatum(&indexStartupCost),
|
||||||
PointerGetDatum(&indexTotalCost),
|
PointerGetDatum(&indexTotalCost),
|
||||||
PointerGetDatum(&indexSelectivity));
|
PointerGetDatum(&indexSelectivity),
|
||||||
|
PointerGetDatum(&indexCorrelation));
|
||||||
|
|
||||||
/* all costs for touching index itself included here */
|
/* all costs for touching index itself included here */
|
||||||
startup_cost += indexStartupCost;
|
startup_cost += indexStartupCost;
|
||||||
run_cost += indexTotalCost - indexStartupCost;
|
run_cost += indexTotalCost - indexStartupCost;
|
||||||
|
|
||||||
/*
|
/*----------
|
||||||
* Estimate number of main-table tuples and pages fetched.
|
* Estimate number of main-table tuples and pages fetched.
|
||||||
*
|
*
|
||||||
* If the number of tuples is much smaller than the number of pages in
|
* When the index ordering is uncorrelated with the table ordering,
|
||||||
* the relation, each tuple will cost a separate nonsequential fetch.
|
* we use an approximation proposed by Mackert and Lohman, "Index Scans
|
||||||
* If it is comparable or larger, then probably we will be able to
|
* Using a Finite LRU Buffer: A Validated I/O Model", ACM Transactions
|
||||||
* avoid some fetches. We use a growth rate of log(#tuples/#pages +
|
* on Database Systems, Vol. 14, No. 3, September 1989, Pages 401-424.
|
||||||
* 1) --- probably totally bogus, but intuitively it gives the right
|
* The Mackert and Lohman approximation is that the number of pages
|
||||||
* shape of curve at least.
|
* fetched is
|
||||||
|
* PF =
|
||||||
|
* min(2TNs/(2T+Ns), T) when T <= b
|
||||||
|
* 2TNs/(2T+Ns) when T > b and Ns <= 2Tb/(2T-b)
|
||||||
|
* b + (Ns - 2Tb/(2T-b))*(T-b)/T when T > b and Ns > 2Tb/(2T-b)
|
||||||
|
* where
|
||||||
|
* T = # pages in table
|
||||||
|
* N = # tuples in table
|
||||||
|
* s = selectivity = fraction of table to be scanned
|
||||||
|
* b = # buffer pages available (we include kernel space here)
|
||||||
*
|
*
|
||||||
* XXX if the relation has recently been "clustered" using this index,
|
* When the index ordering is exactly correlated with the table ordering
|
||||||
* then in fact the target tuples will be highly nonuniformly
|
* (just after a CLUSTER, for example), the number of pages fetched should
|
||||||
* distributed, and we will be seriously overestimating the scan cost!
|
* be just sT. What's more, these will be sequential fetches, not the
|
||||||
* Currently we have no way to know whether the relation has been
|
* random fetches that occur in the uncorrelated case. So, depending on
|
||||||
* clustered, nor how much it's been modified since the last
|
* the extent of correlation, we should estimate the actual I/O cost
|
||||||
* clustering, so we ignore this effect. Would be nice to do better
|
* somewhere between s * T * 1.0 and PF * random_cost. We currently
|
||||||
* someday.
|
* interpolate linearly between these two endpoints based on the
|
||||||
|
* correlation squared (XXX is that appropriate?).
|
||||||
|
*
|
||||||
|
* In any case the number of tuples fetched is Ns.
|
||||||
|
*----------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
tuples_fetched = indexSelectivity * baserel->tuples;
|
tuples_fetched = indexSelectivity * baserel->tuples;
|
||||||
@ -263,24 +284,56 @@ cost_index(Path *path, Query *root,
|
|||||||
if (tuples_fetched < 1.0)
|
if (tuples_fetched < 1.0)
|
||||||
tuples_fetched = 1.0;
|
tuples_fetched = 1.0;
|
||||||
|
|
||||||
if (baserel->pages > 0)
|
/* This part is the Mackert and Lohman formula */
|
||||||
pages_fetched = ceil(baserel->pages *
|
|
||||||
log(tuples_fetched / baserel->pages + 1.0));
|
T = (baserel->pages > 1) ? (double) baserel->pages : 1.0;
|
||||||
|
b = (effective_cache_size > 1) ? effective_cache_size : 1.0;
|
||||||
|
|
||||||
|
if (T <= b)
|
||||||
|
{
|
||||||
|
pages_fetched =
|
||||||
|
(2.0 * T * tuples_fetched) / (2.0 * T + tuples_fetched);
|
||||||
|
if (pages_fetched > T)
|
||||||
|
pages_fetched = T;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
pages_fetched = tuples_fetched;
|
{
|
||||||
|
double lim;
|
||||||
|
|
||||||
|
lim = (2.0 * T * b) / (2.0 * T - b);
|
||||||
|
if (tuples_fetched <= lim)
|
||||||
|
{
|
||||||
|
pages_fetched =
|
||||||
|
(2.0 * T * tuples_fetched) / (2.0 * T + tuples_fetched);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pages_fetched =
|
||||||
|
b + (tuples_fetched - lim) * (T - b) / T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now estimate one nonsequential access per page fetched, plus
|
* min_IO_cost corresponds to the perfectly correlated case (csquared=1),
|
||||||
* appropriate CPU costs per tuple.
|
* max_IO_cost to the perfectly uncorrelated case (csquared=0). Note
|
||||||
|
* that we just charge random_page_cost per page in the uncorrelated
|
||||||
|
* case, rather than using cost_nonsequential_access, since we've already
|
||||||
|
* accounted for caching effects by using the Mackert model.
|
||||||
*/
|
*/
|
||||||
|
min_IO_cost = ceil(indexSelectivity * T);
|
||||||
/* disk costs for main table */
|
max_IO_cost = pages_fetched * random_page_cost;
|
||||||
run_cost += pages_fetched * cost_nonsequential_access(baserel->pages);
|
|
||||||
|
|
||||||
/* CPU costs */
|
|
||||||
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* Now interpolate based on estimated index order correlation
|
||||||
|
* to get total disk I/O cost for main table accesses.
|
||||||
|
*/
|
||||||
|
csquared = indexCorrelation * indexCorrelation;
|
||||||
|
|
||||||
|
run_cost += max_IO_cost + csquared * (min_IO_cost - max_IO_cost);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Estimate CPU costs per tuple.
|
||||||
|
*
|
||||||
* Normally the indexquals will be removed from the list of
|
* Normally the indexquals will be removed from the list of
|
||||||
* restriction clauses that we have to evaluate as qpquals, so we
|
* restriction clauses that we have to evaluate as qpquals, so we
|
||||||
* should subtract their costs from baserestrictcost. For a lossy
|
* should subtract their costs from baserestrictcost. For a lossy
|
||||||
@ -290,6 +343,8 @@ cost_index(Path *path, Query *root,
|
|||||||
* Rather than work out exactly how much to subtract, we don't
|
* Rather than work out exactly how much to subtract, we don't
|
||||||
* subtract anything in that case either.
|
* subtract anything in that case either.
|
||||||
*/
|
*/
|
||||||
|
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
|
||||||
|
|
||||||
if (!index->lossy && !is_injoin)
|
if (!index->lossy && !is_injoin)
|
||||||
cpu_per_tuple -= cost_qual_eval(indexQuals);
|
cpu_per_tuple -= cost_qual_eval(indexQuals);
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.31 2001/04/18 20:42:55 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.32 2001/05/09 23:13:35 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -28,8 +28,9 @@ typedef struct
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int varno;
|
int varno;
|
||||||
|
int varattno;
|
||||||
int sublevels_up;
|
int sublevels_up;
|
||||||
} contain_whole_tuple_var_context;
|
} contain_var_reference_context;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -39,8 +40,8 @@ typedef struct
|
|||||||
|
|
||||||
static bool pull_varnos_walker(Node *node,
|
static bool pull_varnos_walker(Node *node,
|
||||||
pull_varnos_context *context);
|
pull_varnos_context *context);
|
||||||
static bool contain_whole_tuple_var_walker(Node *node,
|
static bool contain_var_reference_walker(Node *node,
|
||||||
contain_whole_tuple_var_context *context);
|
contain_var_reference_context *context);
|
||||||
static bool contain_var_clause_walker(Node *node, void *context);
|
static bool contain_var_clause_walker(Node *node, void *context);
|
||||||
static bool pull_var_clause_walker(Node *node,
|
static bool pull_var_clause_walker(Node *node,
|
||||||
pull_var_clause_context *context);
|
pull_var_clause_context *context);
|
||||||
@ -129,10 +130,10 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* contain_whole_tuple_var
|
* contain_var_reference
|
||||||
*
|
*
|
||||||
* Detect whether a parsetree contains any references to the whole
|
* Detect whether a parsetree contains any references to a specified
|
||||||
* tuple of a given rtable entry (ie, a Var with varattno = 0).
|
* attribute of a specified rtable entry.
|
||||||
*
|
*
|
||||||
* NOTE: this is used on not-yet-planned expressions. It may therefore find
|
* NOTE: this is used on not-yet-planned expressions. It may therefore find
|
||||||
* bare SubLinks, and if so it needs to recurse into them to look for uplevel
|
* bare SubLinks, and if so it needs to recurse into them to look for uplevel
|
||||||
@ -140,11 +141,12 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
|
|||||||
* SubPlan, we only need to look at the parameters passed to the subplan.
|
* SubPlan, we only need to look at the parameters passed to the subplan.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
contain_whole_tuple_var(Node *node, int varno, int levelsup)
|
contain_var_reference(Node *node, int varno, int varattno, int levelsup)
|
||||||
{
|
{
|
||||||
contain_whole_tuple_var_context context;
|
contain_var_reference_context context;
|
||||||
|
|
||||||
context.varno = varno;
|
context.varno = varno;
|
||||||
|
context.varattno = varattno;
|
||||||
context.sublevels_up = levelsup;
|
context.sublevels_up = levelsup;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -154,15 +156,15 @@ contain_whole_tuple_var(Node *node, int varno, int levelsup)
|
|||||||
*/
|
*/
|
||||||
if (node && IsA(node, Query))
|
if (node && IsA(node, Query))
|
||||||
return query_tree_walker((Query *) node,
|
return query_tree_walker((Query *) node,
|
||||||
contain_whole_tuple_var_walker,
|
contain_var_reference_walker,
|
||||||
(void *) &context, true);
|
(void *) &context, true);
|
||||||
else
|
else
|
||||||
return contain_whole_tuple_var_walker(node, &context);
|
return contain_var_reference_walker(node, &context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
contain_whole_tuple_var_walker(Node *node,
|
contain_var_reference_walker(Node *node,
|
||||||
contain_whole_tuple_var_context *context)
|
contain_var_reference_context *context)
|
||||||
{
|
{
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
return false;
|
return false;
|
||||||
@ -171,8 +173,8 @@ contain_whole_tuple_var_walker(Node *node,
|
|||||||
Var *var = (Var *) node;
|
Var *var = (Var *) node;
|
||||||
|
|
||||||
if (var->varno == context->varno &&
|
if (var->varno == context->varno &&
|
||||||
var->varlevelsup == context->sublevels_up &&
|
var->varattno == context->varattno &&
|
||||||
var->varattno == InvalidAttrNumber)
|
var->varlevelsup == context->sublevels_up)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -187,10 +189,10 @@ contain_whole_tuple_var_walker(Node *node,
|
|||||||
*/
|
*/
|
||||||
Expr *expr = (Expr *) node;
|
Expr *expr = (Expr *) node;
|
||||||
|
|
||||||
if (contain_whole_tuple_var_walker((Node *) ((SubPlan *) expr->oper)->sublink->oper,
|
if (contain_var_reference_walker((Node *) ((SubPlan *) expr->oper)->sublink->oper,
|
||||||
context))
|
context))
|
||||||
return true;
|
return true;
|
||||||
if (contain_whole_tuple_var_walker((Node *) expr->args,
|
if (contain_var_reference_walker((Node *) expr->args,
|
||||||
context))
|
context))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
@ -202,16 +204,29 @@ contain_whole_tuple_var_walker(Node *node,
|
|||||||
|
|
||||||
context->sublevels_up++;
|
context->sublevels_up++;
|
||||||
result = query_tree_walker((Query *) node,
|
result = query_tree_walker((Query *) node,
|
||||||
contain_whole_tuple_var_walker,
|
contain_var_reference_walker,
|
||||||
(void *) context, true);
|
(void *) context, true);
|
||||||
context->sublevels_up--;
|
context->sublevels_up--;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return expression_tree_walker(node, contain_whole_tuple_var_walker,
|
return expression_tree_walker(node, contain_var_reference_walker,
|
||||||
(void *) context);
|
(void *) context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* contain_whole_tuple_var
|
||||||
|
*
|
||||||
|
* Detect whether a parsetree contains any references to the whole
|
||||||
|
* tuple of a given rtable entry (ie, a Var with varattno = 0).
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
contain_whole_tuple_var(Node *node, int varno, int levelsup)
|
||||||
|
{
|
||||||
|
return contain_var_reference(node, varno, InvalidAttrNumber, levelsup);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* contain_var_clause
|
* contain_var_clause
|
||||||
* Recursively scan a clause to discover whether it contains any Var nodes
|
* Recursively scan a clause to discover whether it contains any Var nodes
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.88 2001/05/07 00:43:23 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.89 2001/05/09 23:13:35 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -39,6 +39,7 @@
|
|||||||
#include "optimizer/cost.h"
|
#include "optimizer/cost.h"
|
||||||
#include "parser/parse_func.h"
|
#include "parser/parse_func.h"
|
||||||
#include "parser/parse_oper.h"
|
#include "parser/parse_oper.h"
|
||||||
|
#include "parser/parsetree.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/date.h"
|
#include "utils/date.h"
|
||||||
#include "utils/int8.h"
|
#include "utils/int8.h"
|
||||||
@ -818,7 +819,6 @@ eqjoinsel(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
#ifdef NOT_USED /* see neqjoinsel() before removing me! */
|
#ifdef NOT_USED /* see neqjoinsel() before removing me! */
|
||||||
Oid opid = PG_GETARG_OID(0);
|
Oid opid = PG_GETARG_OID(0);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Oid relid1 = PG_GETARG_OID(1);
|
Oid relid1 = PG_GETARG_OID(1);
|
||||||
AttrNumber attno1 = PG_GETARG_INT16(2);
|
AttrNumber attno1 = PG_GETARG_INT16(2);
|
||||||
@ -2230,16 +2230,14 @@ string_to_datum(const char *str, Oid datatype)
|
|||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static Datum
|
static void
|
||||||
genericcostestimate(PG_FUNCTION_ARGS)
|
genericcostestimate(Query *root, RelOptInfo *rel,
|
||||||
|
IndexOptInfo *index, List *indexQuals,
|
||||||
|
Cost *indexStartupCost,
|
||||||
|
Cost *indexTotalCost,
|
||||||
|
Selectivity *indexSelectivity,
|
||||||
|
double *indexCorrelation)
|
||||||
{
|
{
|
||||||
Query *root = (Query *) PG_GETARG_POINTER(0);
|
|
||||||
RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
|
|
||||||
IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
|
|
||||||
List *indexQuals = (List *) PG_GETARG_POINTER(3);
|
|
||||||
Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
|
|
||||||
Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
|
|
||||||
Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
|
|
||||||
double numIndexTuples;
|
double numIndexTuples;
|
||||||
double numIndexPages;
|
double numIndexPages;
|
||||||
|
|
||||||
@ -2275,33 +2273,134 @@ genericcostestimate(PG_FUNCTION_ARGS)
|
|||||||
*indexTotalCost = numIndexPages +
|
*indexTotalCost = numIndexPages +
|
||||||
(cpu_index_tuple_cost + cost_qual_eval(indexQuals)) * numIndexTuples;
|
(cpu_index_tuple_cost + cost_qual_eval(indexQuals)) * numIndexTuples;
|
||||||
|
|
||||||
PG_RETURN_VOID();
|
/*
|
||||||
|
* Generic assumption about index correlation: there isn't any.
|
||||||
|
*/
|
||||||
|
*indexCorrelation = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* For first cut, just use generic function for all index types.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
btcostestimate(PG_FUNCTION_ARGS)
|
btcostestimate(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
return genericcostestimate(fcinfo);
|
Query *root = (Query *) PG_GETARG_POINTER(0);
|
||||||
|
RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
|
||||||
|
IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
|
||||||
|
List *indexQuals = (List *) PG_GETARG_POINTER(3);
|
||||||
|
Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
|
||||||
|
Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
|
||||||
|
Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
|
||||||
|
double *indexCorrelation = (double *) PG_GETARG_POINTER(7);
|
||||||
|
|
||||||
|
genericcostestimate(root, rel, index, indexQuals,
|
||||||
|
indexStartupCost, indexTotalCost,
|
||||||
|
indexSelectivity, indexCorrelation);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it's a functional index, leave the default zero-correlation
|
||||||
|
* estimate in place. If not, and if we can get an estimate for
|
||||||
|
* the first variable's ordering correlation C from pg_statistic,
|
||||||
|
* estimate the index correlation as C / number-of-columns.
|
||||||
|
* (The idea here is that multiple columns dilute the importance
|
||||||
|
* of the first column's ordering, but don't negate it entirely.)
|
||||||
|
*/
|
||||||
|
if (index->indproc == InvalidOid)
|
||||||
|
{
|
||||||
|
Oid relid;
|
||||||
|
HeapTuple tuple;
|
||||||
|
|
||||||
|
relid = getrelid(lfirsti(rel->relids), root->rtable);
|
||||||
|
Assert(relid != InvalidOid);
|
||||||
|
tuple = SearchSysCache(STATRELATT,
|
||||||
|
ObjectIdGetDatum(relid),
|
||||||
|
Int16GetDatum(index->indexkeys[0]),
|
||||||
|
0, 0);
|
||||||
|
if (HeapTupleIsValid(tuple))
|
||||||
|
{
|
||||||
|
Oid typid;
|
||||||
|
int32 typmod;
|
||||||
|
float4 *numbers;
|
||||||
|
int nnumbers;
|
||||||
|
|
||||||
|
get_atttypetypmod(relid, index->indexkeys[0],
|
||||||
|
&typid, &typmod);
|
||||||
|
if (get_attstatsslot(tuple, typid, typmod,
|
||||||
|
STATISTIC_KIND_CORRELATION,
|
||||||
|
index->ordering[0],
|
||||||
|
NULL, NULL, &numbers, &nnumbers))
|
||||||
|
{
|
||||||
|
double varCorrelation;
|
||||||
|
int nKeys;
|
||||||
|
|
||||||
|
Assert(nnumbers == 1);
|
||||||
|
varCorrelation = numbers[0];
|
||||||
|
for (nKeys = 1; index->indexkeys[nKeys] != 0; nKeys++)
|
||||||
|
/*skip*/;
|
||||||
|
|
||||||
|
*indexCorrelation = varCorrelation / nKeys;
|
||||||
|
|
||||||
|
free_attstatsslot(typid, NULL, 0, numbers, nnumbers);
|
||||||
|
}
|
||||||
|
ReleaseSysCache(tuple);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
rtcostestimate(PG_FUNCTION_ARGS)
|
rtcostestimate(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
return genericcostestimate(fcinfo);
|
Query *root = (Query *) PG_GETARG_POINTER(0);
|
||||||
|
RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
|
||||||
|
IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
|
||||||
|
List *indexQuals = (List *) PG_GETARG_POINTER(3);
|
||||||
|
Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
|
||||||
|
Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
|
||||||
|
Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
|
||||||
|
double *indexCorrelation = (double *) PG_GETARG_POINTER(7);
|
||||||
|
|
||||||
|
genericcostestimate(root, rel, index, indexQuals,
|
||||||
|
indexStartupCost, indexTotalCost,
|
||||||
|
indexSelectivity, indexCorrelation);
|
||||||
|
|
||||||
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
hashcostestimate(PG_FUNCTION_ARGS)
|
hashcostestimate(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
return genericcostestimate(fcinfo);
|
Query *root = (Query *) PG_GETARG_POINTER(0);
|
||||||
|
RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
|
||||||
|
IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
|
||||||
|
List *indexQuals = (List *) PG_GETARG_POINTER(3);
|
||||||
|
Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
|
||||||
|
Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
|
||||||
|
Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
|
||||||
|
double *indexCorrelation = (double *) PG_GETARG_POINTER(7);
|
||||||
|
|
||||||
|
genericcostestimate(root, rel, index, indexQuals,
|
||||||
|
indexStartupCost, indexTotalCost,
|
||||||
|
indexSelectivity, indexCorrelation);
|
||||||
|
|
||||||
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
gistcostestimate(PG_FUNCTION_ARGS)
|
gistcostestimate(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
return genericcostestimate(fcinfo);
|
Query *root = (Query *) PG_GETARG_POINTER(0);
|
||||||
|
RelOptInfo *rel = (RelOptInfo *) PG_GETARG_POINTER(1);
|
||||||
|
IndexOptInfo *index = (IndexOptInfo *) PG_GETARG_POINTER(2);
|
||||||
|
List *indexQuals = (List *) PG_GETARG_POINTER(3);
|
||||||
|
Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
|
||||||
|
Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
|
||||||
|
Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
|
||||||
|
double *indexCorrelation = (double *) PG_GETARG_POINTER(7);
|
||||||
|
|
||||||
|
genericcostestimate(root, rel, index, indexQuals,
|
||||||
|
indexStartupCost, indexTotalCost,
|
||||||
|
indexSelectivity, indexCorrelation);
|
||||||
|
|
||||||
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
32
src/backend/utils/cache/lsyscache.c
vendored
32
src/backend/utils/cache/lsyscache.c
vendored
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.54 2001/05/09 00:35:09 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.55 2001/05/09 23:13:35 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Eventually, the index information should go through here, too.
|
* Eventually, the index information should go through here, too.
|
||||||
@ -185,6 +185,36 @@ get_atttypmod(Oid relid, AttrNumber attnum)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_atttypetypmod
|
||||||
|
*
|
||||||
|
* A two-fer: given the relation id and the attribute number,
|
||||||
|
* fetch both type OID and atttypmod in a single cache lookup.
|
||||||
|
*
|
||||||
|
* Unlike the otherwise-similar get_atttype/get_atttypmod, this routine
|
||||||
|
* raises an error if it can't obtain the information.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
get_atttypetypmod(Oid relid, AttrNumber attnum,
|
||||||
|
Oid *typid, int32 *typmod)
|
||||||
|
{
|
||||||
|
HeapTuple tp;
|
||||||
|
Form_pg_attribute att_tup;
|
||||||
|
|
||||||
|
tp = SearchSysCache(ATTNUM,
|
||||||
|
ObjectIdGetDatum(relid),
|
||||||
|
Int16GetDatum(attnum),
|
||||||
|
0, 0);
|
||||||
|
if (!HeapTupleIsValid(tp))
|
||||||
|
elog(ERROR, "cache lookup failed for relation %u attribute %d",
|
||||||
|
relid, attnum);
|
||||||
|
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
|
||||||
|
|
||||||
|
*typid = att_tup->atttypid;
|
||||||
|
*typmod = att_tup->atttypmod;
|
||||||
|
ReleaseSysCache(tp);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- INDEX CACHE ---------- */
|
/* ---------- INDEX CACHE ---------- */
|
||||||
|
|
||||||
/* watch this space...
|
/* watch this space...
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: pg_proc.h,v 1.184 2001/03/22 04:00:39 momjian Exp $
|
* $Id: pg_proc.h,v 1.185 2001/05/09 23:13:35 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* The script catalog/genbki.sh reads this file and generates .bki
|
* The script catalog/genbki.sh reads this file and generates .bki
|
||||||
@ -210,9 +210,9 @@ DESCR("not equal");
|
|||||||
DATA(insert OID = 89 ( version PGUID 12 f t f t 0 f 25 "" 100 0 0 100 pgsql_version - ));
|
DATA(insert OID = 89 ( version PGUID 12 f t f t 0 f 25 "" 100 0 0 100 pgsql_version - ));
|
||||||
DESCR("PostgreSQL version string");
|
DESCR("PostgreSQL version string");
|
||||||
|
|
||||||
DATA(insert OID = 1265 ( rtcostestimate PGUID 12 f t f t 7 f 0 "0 0 0 0 0 0 0" 100 0 0 100 rtcostestimate - ));
|
DATA(insert OID = 1265 ( rtcostestimate PGUID 12 f t f t 8 f 0 "0 0 0 0 0 0 0 0" 100 0 0 100 rtcostestimate - ));
|
||||||
DESCR("r-tree cost estimator");
|
DESCR("r-tree cost estimator");
|
||||||
DATA(insert OID = 1268 ( btcostestimate PGUID 12 f t f t 7 f 0 "0 0 0 0 0 0 0" 100 0 0 100 btcostestimate - ));
|
DATA(insert OID = 1268 ( btcostestimate PGUID 12 f t f t 8 f 0 "0 0 0 0 0 0 0 0" 100 0 0 100 btcostestimate - ));
|
||||||
DESCR("btree cost estimator");
|
DESCR("btree cost estimator");
|
||||||
|
|
||||||
/* OIDS 100 - 199 */
|
/* OIDS 100 - 199 */
|
||||||
@ -789,7 +789,7 @@ DESCR("convert name to char()");
|
|||||||
DATA(insert OID = 409 ( name PGUID 12 f t t t 1 f 19 "1042" 100 0 0 100 bpchar_name - ));
|
DATA(insert OID = 409 ( name PGUID 12 f t t t 1 f 19 "1042" 100 0 0 100 bpchar_name - ));
|
||||||
DESCR("convert char() to name");
|
DESCR("convert char() to name");
|
||||||
|
|
||||||
DATA(insert OID = 438 ( hashcostestimate PGUID 12 f t f t 7 f 0 "0 0 0 0 0 0 0" 100 0 0 100 hashcostestimate - ));
|
DATA(insert OID = 438 ( hashcostestimate PGUID 12 f t f t 8 f 0 "0 0 0 0 0 0 0 0" 100 0 0 100 hashcostestimate - ));
|
||||||
DESCR("hash index cost estimator");
|
DESCR("hash index cost estimator");
|
||||||
|
|
||||||
DATA(insert OID = 440 ( hashgettuple PGUID 12 f t f t 2 f 23 "0 0" 100 0 0 100 hashgettuple - ));
|
DATA(insert OID = 440 ( hashgettuple PGUID 12 f t f t 2 f 23 "0 0" 100 0 0 100 hashgettuple - ));
|
||||||
@ -1014,7 +1014,7 @@ DESCR("larger of two");
|
|||||||
DATA(insert OID = 771 ( int2smaller PGUID 12 f t t t 2 f 21 "21 21" 100 0 0 100 int2smaller - ));
|
DATA(insert OID = 771 ( int2smaller PGUID 12 f t t t 2 f 21 "21 21" 100 0 0 100 int2smaller - ));
|
||||||
DESCR("smaller of two");
|
DESCR("smaller of two");
|
||||||
|
|
||||||
DATA(insert OID = 772 ( gistcostestimate PGUID 12 f t f t 7 f 0 "0 0 0 0 0 0 0" 100 0 0 100 gistcostestimate - ));
|
DATA(insert OID = 772 ( gistcostestimate PGUID 12 f t f t 8 f 0 "0 0 0 0 0 0 0 0" 100 0 0 100 gistcostestimate - ));
|
||||||
DESCR("gist cost estimator");
|
DESCR("gist cost estimator");
|
||||||
DATA(insert OID = 774 ( gistgettuple PGUID 12 f t f t 2 f 23 "0 0" 100 0 0 100 gistgettuple - ));
|
DATA(insert OID = 774 ( gistgettuple PGUID 12 f t f t 2 f 23 "0 0" 100 0 0 100 gistgettuple - ));
|
||||||
DESCR("gist(internal)");
|
DESCR("gist(internal)");
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: var.h,v 1.13 2001/04/18 20:42:55 tgl Exp $
|
* $Id: var.h,v 1.14 2001/05/09 23:13:36 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -17,6 +17,8 @@
|
|||||||
#include "nodes/primnodes.h"
|
#include "nodes/primnodes.h"
|
||||||
|
|
||||||
extern List *pull_varnos(Node *node);
|
extern List *pull_varnos(Node *node);
|
||||||
|
extern bool contain_var_reference(Node *node, int varno, int varattno,
|
||||||
|
int levelsup);
|
||||||
extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
|
extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
|
||||||
extern bool contain_var_clause(Node *node);
|
extern bool contain_var_clause(Node *node);
|
||||||
extern List *pull_var_clause(Node *node, bool includeUpperVars);
|
extern List *pull_var_clause(Node *node, bool includeUpperVars);
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: lsyscache.h,v 1.32 2001/05/09 00:35:09 tgl Exp $
|
* $Id: lsyscache.h,v 1.33 2001/05/09 23:13:37 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -21,6 +21,8 @@ extern AttrNumber get_attnum(Oid relid, char *attname);
|
|||||||
extern Oid get_atttype(Oid relid, AttrNumber attnum);
|
extern Oid get_atttype(Oid relid, AttrNumber attnum);
|
||||||
extern bool get_attisset(Oid relid, char *attname);
|
extern bool get_attisset(Oid relid, char *attname);
|
||||||
extern int32 get_atttypmod(Oid relid, AttrNumber attnum);
|
extern int32 get_atttypmod(Oid relid, AttrNumber attnum);
|
||||||
|
extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
|
||||||
|
Oid *typid, int32 *typmod);
|
||||||
extern RegProcedure get_opcode(Oid opno);
|
extern RegProcedure get_opcode(Oid opno);
|
||||||
extern char *get_opname(Oid opno);
|
extern char *get_opname(Oid opno);
|
||||||
extern bool op_mergejoinable(Oid opno, Oid ltype, Oid rtype,
|
extern bool op_mergejoinable(Oid opno, Oid ltype, Oid rtype,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user