Commit e89f14e2 authored by Tom Lane's avatar Tom Lane

Refactor index cost estimation functions in view of IndexClause changes.

Get rid of deconstruct_indexquals() in favor of just iterating over the
IndexClause list directly.  The extra services that that function used to
provide, such as hiding clause commutation and associating the right index
column with each clause, are no longer useful given the new data structure.
I'd originally thought that it'd provide a useful amount of abstraction
by freeing callers from paying attention to the exact clause type of each
indexqual, but that hope proves to have been vain, because few callers can
ignore the semantic differences between different clause types.  Indeed,
removing it results in a net code savings, and probably some cycles shaved
by not having to build an extra list-of-structs data structure.

Also, export a few formerly-static support functions, with the goal
of allowing extension AMs to write functionality equivalent to
genericcostestimate() without pointless code duplication.

Discussion: https://postgr.es/m/24586.1550106354@sss.pgh.pa.us
parent fc6c7274
...@@ -27,19 +27,15 @@ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -27,19 +27,15 @@ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
double *indexPages) double *indexPages)
{ {
IndexOptInfo *index = path->indexinfo; IndexOptInfo *index = path->indexinfo;
List *qinfos;
GenericCosts costs; GenericCosts costs;
/* Do preliminary analysis of indexquals */
qinfos = deconstruct_indexquals(path);
MemSet(&costs, 0, sizeof(costs)); MemSet(&costs, 0, sizeof(costs));
/* We have to visit all index tuples anyway */ /* We have to visit all index tuples anyway */
costs.numIndexTuples = index->tuples; costs.numIndexTuples = index->tuples;
/* Use generic estimate */ /* Use generic estimate */
genericcostestimate(root, path, loop_count, qinfos, &costs); genericcostestimate(root, path, loop_count, &costs);
*indexStartupCost = costs.indexStartupCost; *indexStartupCost = costs.indexStartupCost;
*indexTotalCost = costs.indexTotalCost; *indexTotalCost = costs.indexTotalCost;
......
...@@ -197,7 +197,6 @@ static bool get_actual_variable_range(PlannerInfo *root, ...@@ -197,7 +197,6 @@ static bool get_actual_variable_range(PlannerInfo *root,
Oid sortop, Oid sortop,
Datum *min, Datum *max); Datum *min, Datum *max);
static RelOptInfo *find_join_input_rel(PlannerInfo *root, Relids relids); static RelOptInfo *find_join_input_rel(PlannerInfo *root, Relids relids);
static List *add_predicate_to_quals(IndexOptInfo *index, List *indexQuals);
/* /*
...@@ -5251,9 +5250,11 @@ find_join_input_rel(PlannerInfo *root, Relids relids) ...@@ -5251,9 +5250,11 @@ find_join_input_rel(PlannerInfo *root, Relids relids)
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
/* Extract the actual indexquals (as RestrictInfos) from an IndexClause list */ /*
static List * * Extract the actual indexquals (as RestrictInfos) from an IndexClause list
get_index_quals(List *indexclauses) */
List *
get_quals_from_indexclauses(List *indexclauses)
{ {
List *result = NIL; List *result = NIL;
ListCell *lc; ListCell *lc;
...@@ -5273,114 +5274,60 @@ get_index_quals(List *indexclauses) ...@@ -5273,114 +5274,60 @@ get_index_quals(List *indexclauses)
return result; return result;
} }
List * /*
deconstruct_indexquals(IndexPath *path) * Compute the total evaluation cost of the comparison operands in a list
* of index qual expressions. Since we know these will be evaluated just
* once per scan, there's no need to distinguish startup from per-row cost.
*
* This can be used either on the result of get_quals_from_indexclauses(),
* or directly on an indexorderbys list. In both cases, we expect that the
* index key expression is on the left side of binary clauses.
*/
Cost
index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
{ {
List *result = NIL; Cost qual_arg_cost = 0;
ListCell *lc; ListCell *lc;
foreach(lc, path->indexclauses) foreach(lc, indexquals)
{ {
IndexClause *iclause = lfirst_node(IndexClause, lc); Expr *clause = (Expr *) lfirst(lc);
int indexcol = iclause->indexcol; Node *other_operand;
ListCell *lc2; QualCost index_qual_cost;
foreach(lc2, iclause->indexquals)
{
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
Expr *clause = rinfo->clause;
IndexQualInfo *qinfo;
qinfo = (IndexQualInfo *) palloc(sizeof(IndexQualInfo)); /*
qinfo->rinfo = rinfo; * Index quals will have RestrictInfos, indexorderbys won't. Look
qinfo->indexcol = indexcol; * through RestrictInfo if present.
*/
if (IsA(clause, RestrictInfo))
clause = ((RestrictInfo *) clause)->clause;
if (IsA(clause, OpExpr)) if (IsA(clause, OpExpr))
{ {
qinfo->clause_op = ((OpExpr *) clause)->opno; OpExpr *op = (OpExpr *) clause;
qinfo->other_operand = get_rightop(clause);
other_operand = (Node *) lsecond(op->args);
} }
else if (IsA(clause, RowCompareExpr)) else if (IsA(clause, RowCompareExpr))
{ {
RowCompareExpr *rc = (RowCompareExpr *) clause; RowCompareExpr *rc = (RowCompareExpr *) clause;
qinfo->clause_op = linitial_oid(rc->opnos); other_operand = (Node *) rc->rargs;
qinfo->other_operand = (Node *) rc->rargs;
} }
else if (IsA(clause, ScalarArrayOpExpr)) else if (IsA(clause, ScalarArrayOpExpr))
{ {
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
qinfo->clause_op = saop->opno; other_operand = (Node *) lsecond(saop->args);
qinfo->other_operand = (Node *) lsecond(saop->args);
} }
else if (IsA(clause, NullTest)) else if (IsA(clause, NullTest))
{ {
qinfo->clause_op = InvalidOid; other_operand = NULL;
qinfo->other_operand = NULL;
} }
else else
{ {
elog(ERROR, "unsupported indexqual type: %d", elog(ERROR, "unsupported indexqual type: %d",
(int) nodeTag(clause)); (int) nodeTag(clause));
}
result = lappend(result, qinfo);
}
}
return result;
}
/*
* Simple function to compute the total eval cost of the "other operands"
* in an IndexQualInfo list. Since we know these will be evaluated just
* once per scan, there's no need to distinguish startup from per-row cost.
*/
static Cost
other_operands_eval_cost(PlannerInfo *root, List *qinfos)
{
Cost qual_arg_cost = 0;
ListCell *lc;
foreach(lc, qinfos)
{
IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(lc);
QualCost index_qual_cost;
cost_qual_eval_node(&index_qual_cost, qinfo->other_operand, root);
qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple;
}
return qual_arg_cost;
}
/*
* Get other-operand eval cost for an index orderby list.
*
* Index orderby expressions aren't represented as RestrictInfos (since they
* aren't boolean, usually). So we can't apply deconstruct_indexquals to
* them. However, they are much simpler to deal with since they are always
* OpExprs and the index column is always on the left.
*/
static Cost
orderby_operands_eval_cost(PlannerInfo *root, IndexPath *path)
{
Cost qual_arg_cost = 0;
ListCell *lc;
foreach(lc, path->indexorderbys)
{
Expr *clause = (Expr *) lfirst(lc);
Node *other_operand;
QualCost index_qual_cost;
if (IsA(clause, OpExpr))
{
other_operand = get_rightop(clause);
}
else
{
elog(ERROR, "unsupported indexorderby type: %d",
(int) nodeTag(clause));
other_operand = NULL; /* keep compiler quiet */ other_operand = NULL; /* keep compiler quiet */
} }
...@@ -5394,11 +5341,10 @@ void ...@@ -5394,11 +5341,10 @@ void
genericcostestimate(PlannerInfo *root, genericcostestimate(PlannerInfo *root,
IndexPath *path, IndexPath *path,
double loop_count, double loop_count,
List *qinfos,
GenericCosts *costs) GenericCosts *costs)
{ {
IndexOptInfo *index = path->indexinfo; IndexOptInfo *index = path->indexinfo;
List *indexQuals = get_index_quals(path->indexclauses); List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
List *indexOrderBys = path->indexorderbys; List *indexOrderBys = path->indexorderbys;
Cost indexStartupCost; Cost indexStartupCost;
Cost indexTotalCost; Cost indexTotalCost;
...@@ -5420,7 +5366,7 @@ genericcostestimate(PlannerInfo *root, ...@@ -5420,7 +5366,7 @@ genericcostestimate(PlannerInfo *root,
* given indexquals to produce a more accurate idea of the index * given indexquals to produce a more accurate idea of the index
* selectivity. * selectivity.
*/ */
selectivityQuals = add_predicate_to_quals(index, indexQuals); selectivityQuals = add_predicate_to_index_quals(index, indexQuals);
/* /*
* Check for ScalarArrayOpExpr index quals, and estimate the number of * Check for ScalarArrayOpExpr index quals, and estimate the number of
...@@ -5563,8 +5509,8 @@ genericcostestimate(PlannerInfo *root, ...@@ -5563,8 +5509,8 @@ genericcostestimate(PlannerInfo *root,
* Detecting that that might be needed seems more expensive than it's * Detecting that that might be needed seems more expensive than it's
* worth, though, considering all the other inaccuracies here ... * worth, though, considering all the other inaccuracies here ...
*/ */
qual_arg_cost = other_operands_eval_cost(root, qinfos) + qual_arg_cost = index_other_operands_eval_cost(root, indexQuals) +
orderby_operands_eval_cost(root, path); index_other_operands_eval_cost(root, indexOrderBys);
qual_op_cost = cpu_operator_cost * qual_op_cost = cpu_operator_cost *
(list_length(indexQuals) + list_length(indexOrderBys)); (list_length(indexQuals) + list_length(indexOrderBys));
...@@ -5609,8 +5555,8 @@ genericcostestimate(PlannerInfo *root, ...@@ -5609,8 +5555,8 @@ genericcostestimate(PlannerInfo *root,
* predicate_implied_by() and clauselist_selectivity(), but might be * predicate_implied_by() and clauselist_selectivity(), but might be
* problematic if the result were passed to other things. * problematic if the result were passed to other things.
*/ */
static List * List *
add_predicate_to_quals(IndexOptInfo *index, List *indexQuals) add_predicate_to_index_quals(IndexOptInfo *index, List *indexQuals)
{ {
List *predExtraQuals = NIL; List *predExtraQuals = NIL;
ListCell *lc; ListCell *lc;
...@@ -5638,7 +5584,6 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -5638,7 +5584,6 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
double *indexPages) double *indexPages)
{ {
IndexOptInfo *index = path->indexinfo; IndexOptInfo *index = path->indexinfo;
List *qinfos;
GenericCosts costs; GenericCosts costs;
Oid relid; Oid relid;
AttrNumber colnum; AttrNumber colnum;
...@@ -5653,9 +5598,6 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -5653,9 +5598,6 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
double num_sa_scans; double num_sa_scans;
ListCell *lc; ListCell *lc;
/* Do preliminary analysis of indexquals */
qinfos = deconstruct_indexquals(path);
/* /*
* For a btree scan, only leading '=' quals plus inequality quals for the * For a btree scan, only leading '=' quals plus inequality quals for the
* immediately next attribute contribute to index selectivity (these are * immediately next attribute contribute to index selectivity (these are
...@@ -5679,31 +5621,51 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -5679,31 +5621,51 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
found_saop = false; found_saop = false;
found_is_null_op = false; found_is_null_op = false;
num_sa_scans = 1; num_sa_scans = 1;
foreach(lc, qinfos) foreach(lc, path->indexclauses)
{ {
IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(lc); IndexClause *iclause = lfirst_node(IndexClause, lc);
RestrictInfo *rinfo = qinfo->rinfo; ListCell *lc2;
Expr *clause = rinfo->clause;
Oid clause_op;
int op_strategy;
if (indexcol != qinfo->indexcol) if (indexcol != iclause->indexcol)
{ {
/* Beginning of a new column's quals */ /* Beginning of a new column's quals */
if (!eqQualHere) if (!eqQualHere)
break; /* done if no '=' qual for indexcol */ break; /* done if no '=' qual for indexcol */
eqQualHere = false; eqQualHere = false;
indexcol++; indexcol++;
if (indexcol != qinfo->indexcol) if (indexcol != iclause->indexcol)
break; /* no quals at all for indexcol */ break; /* no quals at all for indexcol */
} }
if (IsA(clause, ScalarArrayOpExpr)) /* Examine each indexqual associated with this index clause */
foreach(lc2, iclause->indexquals)
{
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
Expr *clause = rinfo->clause;
Oid clause_op = InvalidOid;
int op_strategy;
if (IsA(clause, OpExpr))
{
OpExpr *op = (OpExpr *) clause;
clause_op = op->opno;
}
else if (IsA(clause, RowCompareExpr))
{
RowCompareExpr *rc = (RowCompareExpr *) clause;
clause_op = linitial_oid(rc->opnos);
}
else if (IsA(clause, ScalarArrayOpExpr))
{ {
int alength = estimate_array_length(qinfo->other_operand); ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
Node *other_operand = (Node *) lsecond(saop->args);
int alength = estimate_array_length(other_operand);
clause_op = saop->opno;
found_saop = true; found_saop = true;
/* count up number of SA scans induced by indexBoundQuals only */ /* count number of SA scans induced by indexBoundQuals only */
if (alength > 1) if (alength > 1)
num_sa_scans *= alength; num_sa_scans *= alength;
} }
...@@ -5714,13 +5676,15 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -5714,13 +5676,15 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
if (nt->nulltesttype == IS_NULL) if (nt->nulltesttype == IS_NULL)
{ {
found_is_null_op = true; found_is_null_op = true;
/* IS NULL is like = for selectivity determination purposes */ /* IS NULL is like = for selectivity purposes */
eqQualHere = true; eqQualHere = true;
} }
} }
else
elog(ERROR, "unsupported indexqual type: %d",
(int) nodeTag(clause));
/* check for equality operator */ /* check for equality operator */
clause_op = qinfo->clause_op;
if (OidIsValid(clause_op)) if (OidIsValid(clause_op))
{ {
op_strategy = get_op_opfamily_strategy(clause_op, op_strategy = get_op_opfamily_strategy(clause_op,
...@@ -5732,6 +5696,7 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -5732,6 +5696,7 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
indexBoundQuals = lappend(indexBoundQuals, rinfo); indexBoundQuals = lappend(indexBoundQuals, rinfo);
} }
}
/* /*
* If index is unique and we found an '=' clause for each column, we can * If index is unique and we found an '=' clause for each column, we can
...@@ -5755,7 +5720,7 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -5755,7 +5720,7 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
* index-bound quals to produce a more accurate idea of the number of * index-bound quals to produce a more accurate idea of the number of
* rows covered by the bound conditions. * rows covered by the bound conditions.
*/ */
selectivityQuals = add_predicate_to_quals(index, indexBoundQuals); selectivityQuals = add_predicate_to_index_quals(index, indexBoundQuals);
btreeSelectivity = clauselist_selectivity(root, selectivityQuals, btreeSelectivity = clauselist_selectivity(root, selectivityQuals,
index->rel->relid, index->rel->relid,
...@@ -5777,7 +5742,7 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -5777,7 +5742,7 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
MemSet(&costs, 0, sizeof(costs)); MemSet(&costs, 0, sizeof(costs));
costs.numIndexTuples = numIndexTuples; costs.numIndexTuples = numIndexTuples;
genericcostestimate(root, path, loop_count, qinfos, &costs); genericcostestimate(root, path, loop_count, &costs);
/* /*
* Add a CPU-cost component to represent the costs of initial btree * Add a CPU-cost component to represent the costs of initial btree
...@@ -5924,15 +5889,11 @@ hashcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -5924,15 +5889,11 @@ hashcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
Selectivity *indexSelectivity, double *indexCorrelation, Selectivity *indexSelectivity, double *indexCorrelation,
double *indexPages) double *indexPages)
{ {
List *qinfos;
GenericCosts costs; GenericCosts costs;
/* Do preliminary analysis of indexquals */
qinfos = deconstruct_indexquals(path);
MemSet(&costs, 0, sizeof(costs)); MemSet(&costs, 0, sizeof(costs));
genericcostestimate(root, path, loop_count, qinfos, &costs); genericcostestimate(root, path, loop_count, &costs);
/* /*
* A hash index has no descent costs as such, since the index AM can go * A hash index has no descent costs as such, since the index AM can go
...@@ -5973,16 +5934,12 @@ gistcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -5973,16 +5934,12 @@ gistcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
double *indexPages) double *indexPages)
{ {
IndexOptInfo *index = path->indexinfo; IndexOptInfo *index = path->indexinfo;
List *qinfos;
GenericCosts costs; GenericCosts costs;
Cost descentCost; Cost descentCost;
/* Do preliminary analysis of indexquals */
qinfos = deconstruct_indexquals(path);
MemSet(&costs, 0, sizeof(costs)); MemSet(&costs, 0, sizeof(costs));
genericcostestimate(root, path, loop_count, qinfos, &costs); genericcostestimate(root, path, loop_count, &costs);
/* /*
* We model index descent costs similarly to those for btree, but to do * We model index descent costs similarly to those for btree, but to do
...@@ -6034,16 +5991,12 @@ spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -6034,16 +5991,12 @@ spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
double *indexPages) double *indexPages)
{ {
IndexOptInfo *index = path->indexinfo; IndexOptInfo *index = path->indexinfo;
List *qinfos;
GenericCosts costs; GenericCosts costs;
Cost descentCost; Cost descentCost;
/* Do preliminary analysis of indexquals */
qinfos = deconstruct_indexquals(path);
MemSet(&costs, 0, sizeof(costs)); MemSet(&costs, 0, sizeof(costs));
genericcostestimate(root, path, loop_count, qinfos, &costs); genericcostestimate(root, path, loop_count, &costs);
/* /*
* We model index descent costs similarly to those for btree, but to do * We model index descent costs similarly to those for btree, but to do
...@@ -6214,12 +6167,12 @@ gincost_pattern(IndexOptInfo *index, int indexcol, ...@@ -6214,12 +6167,12 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
static bool static bool
gincost_opexpr(PlannerInfo *root, gincost_opexpr(PlannerInfo *root,
IndexOptInfo *index, IndexOptInfo *index,
IndexQualInfo *qinfo, int indexcol,
OpExpr *clause,
GinQualCounts *counts) GinQualCounts *counts)
{ {
int indexcol = qinfo->indexcol; Oid clause_op = clause->opno;
Oid clause_op = qinfo->clause_op; Node *operand = (Node *) lsecond(clause->args);
Node *operand = qinfo->other_operand;
/* aggressively reduce to a constant, and look through relabeling */ /* aggressively reduce to a constant, and look through relabeling */
operand = estimate_expression_value(root, operand); operand = estimate_expression_value(root, operand);
...@@ -6264,13 +6217,13 @@ gincost_opexpr(PlannerInfo *root, ...@@ -6264,13 +6217,13 @@ gincost_opexpr(PlannerInfo *root,
static bool static bool
gincost_scalararrayopexpr(PlannerInfo *root, gincost_scalararrayopexpr(PlannerInfo *root,
IndexOptInfo *index, IndexOptInfo *index,
IndexQualInfo *qinfo, int indexcol,
ScalarArrayOpExpr *clause,
double numIndexEntries, double numIndexEntries,
GinQualCounts *counts) GinQualCounts *counts)
{ {
int indexcol = qinfo->indexcol; Oid clause_op = clause->opno;
Oid clause_op = qinfo->clause_op; Node *rightop = (Node *) lsecond(clause->args);
Node *rightop = qinfo->other_operand;
ArrayType *arrayval; ArrayType *arrayval;
int16 elmlen; int16 elmlen;
bool elmbyval; bool elmbyval;
...@@ -6282,7 +6235,7 @@ gincost_scalararrayopexpr(PlannerInfo *root, ...@@ -6282,7 +6235,7 @@ gincost_scalararrayopexpr(PlannerInfo *root,
int numPossible = 0; int numPossible = 0;
int i; int i;
Assert(((ScalarArrayOpExpr *) qinfo->rinfo->clause)->useOr); Assert(clause->useOr);
/* aggressively reduce to a constant, and look through relabeling */ /* aggressively reduce to a constant, and look through relabeling */
rightop = estimate_expression_value(root, rightop); rightop = estimate_expression_value(root, rightop);
...@@ -6383,10 +6336,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -6383,10 +6336,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
double *indexPages) double *indexPages)
{ {
IndexOptInfo *index = path->indexinfo; IndexOptInfo *index = path->indexinfo;
List *indexQuals = get_index_quals(path->indexclauses); List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
List *indexOrderBys = path->indexorderbys;
List *qinfos;
ListCell *l;
List *selectivityQuals; List *selectivityQuals;
double numPages = index->pages, double numPages = index->pages,
numTuples = index->tuples; numTuples = index->tuples;
...@@ -6406,9 +6356,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -6406,9 +6356,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
outer_scans; outer_scans;
Relation indexRel; Relation indexRel;
GinStatsData ginStats; GinStatsData ginStats;
ListCell *lc;
/* Do preliminary analysis of indexquals */
qinfos = deconstruct_indexquals(path);
/* /*
* Obtain statistical information from the meta page, if possible. Else * Obtain statistical information from the meta page, if possible. Else
...@@ -6490,7 +6438,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -6490,7 +6438,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
* quals to produce a more accurate idea of the number of rows covered by * quals to produce a more accurate idea of the number of rows covered by
* the bound conditions. * the bound conditions.
*/ */
selectivityQuals = add_predicate_to_quals(index, indexQuals); selectivityQuals = add_predicate_to_index_quals(index, indexQuals);
/* Estimate the fraction of main-table tuples that will be visited */ /* Estimate the fraction of main-table tuples that will be visited */
*indexSelectivity = clauselist_selectivity(root, selectivityQuals, *indexSelectivity = clauselist_selectivity(root, selectivityQuals,
...@@ -6515,16 +6463,22 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -6515,16 +6463,22 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
counts.arrayScans = 1; counts.arrayScans = 1;
matchPossible = true; matchPossible = true;
foreach(l, qinfos) foreach(lc, path->indexclauses)
{
IndexClause *iclause = lfirst_node(IndexClause, lc);
ListCell *lc2;
foreach(lc2, iclause->indexquals)
{ {
IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(l); RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
Expr *clause = qinfo->rinfo->clause; Expr *clause = rinfo->clause;
if (IsA(clause, OpExpr)) if (IsA(clause, OpExpr))
{ {
matchPossible = gincost_opexpr(root, matchPossible = gincost_opexpr(root,
index, index,
qinfo, iclause->indexcol,
(OpExpr *) clause,
&counts); &counts);
if (!matchPossible) if (!matchPossible)
break; break;
...@@ -6533,7 +6487,8 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -6533,7 +6487,8 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
{ {
matchPossible = gincost_scalararrayopexpr(root, matchPossible = gincost_scalararrayopexpr(root,
index, index,
qinfo, iclause->indexcol,
(ScalarArrayOpExpr *) clause,
numEntries, numEntries,
&counts); &counts);
if (!matchPossible) if (!matchPossible)
...@@ -6546,6 +6501,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -6546,6 +6501,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
(int) nodeTag(clause)); (int) nodeTag(clause));
} }
} }
}
/* Fall out if there were any provably-unsatisfiable quals */ /* Fall out if there were any provably-unsatisfiable quals */
if (!matchPossible) if (!matchPossible)
...@@ -6670,12 +6626,11 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -6670,12 +6626,11 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
dataPagesFetched * spc_random_page_cost; dataPagesFetched * spc_random_page_cost;
/* /*
* Add on index qual eval costs, much as in genericcostestimate * Add on index qual eval costs, much as in genericcostestimate. But we
* can disregard indexorderbys, since GIN doesn't support those.
*/ */
qual_arg_cost = other_operands_eval_cost(root, qinfos) + qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
orderby_operands_eval_cost(root, path); qual_op_cost = cpu_operator_cost * list_length(indexQuals);
qual_op_cost = cpu_operator_cost *
(list_length(indexQuals) + list_length(indexOrderBys));
*indexStartupCost += qual_arg_cost; *indexStartupCost += qual_arg_cost;
*indexTotalCost += qual_arg_cost; *indexTotalCost += qual_arg_cost;
...@@ -6693,11 +6648,10 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -6693,11 +6648,10 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
double *indexPages) double *indexPages)
{ {
IndexOptInfo *index = path->indexinfo; IndexOptInfo *index = path->indexinfo;
List *indexQuals = get_index_quals(path->indexclauses); List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
double numPages = index->pages; double numPages = index->pages;
RelOptInfo *baserel = index->rel; RelOptInfo *baserel = index->rel;
RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
List *qinfos;
Cost spc_seq_page_cost; Cost spc_seq_page_cost;
Cost spc_random_page_cost; Cost spc_random_page_cost;
double qual_arg_cost; double qual_arg_cost;
...@@ -6735,11 +6689,10 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -6735,11 +6689,10 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
*/ */
*indexCorrelation = 0; *indexCorrelation = 0;
qinfos = deconstruct_indexquals(path); foreach(l, path->indexclauses)
foreach(l, qinfos)
{ {
IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(l); IndexClause *iclause = lfirst_node(IndexClause, l);
AttrNumber attnum = index->indexkeys[qinfo->indexcol]; AttrNumber attnum = index->indexkeys[iclause->indexcol];
/* attempt to lookup stats in relation for this index column */ /* attempt to lookup stats in relation for this index column */
if (attnum != 0) if (attnum != 0)
...@@ -6774,7 +6727,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -6774,7 +6727,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
*/ */
/* get the attnum from the 0-based index. */ /* get the attnum from the 0-based index. */
attnum = qinfo->indexcol + 1; attnum = iclause->indexcol + 1;
if (get_index_stats_hook && if (get_index_stats_hook &&
(*get_index_stats_hook) (root, index->indexoid, attnum, &vardata)) (*get_index_stats_hook) (root, index->indexoid, attnum, &vardata))
...@@ -6853,10 +6806,10 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -6853,10 +6806,10 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
/* /*
* Compute the index qual costs, much as in genericcostestimate, to add to * Compute the index qual costs, much as in genericcostestimate, to add to
* the index costs. * the index costs. We can disregard indexorderbys, since BRIN doesn't
* support those.
*/ */
qual_arg_cost = other_operands_eval_cost(root, qinfos) + qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
orderby_operands_eval_cost(root, path);
/* /*
* Compute the startup cost as the cost to read the whole revmap * Compute the startup cost as the cost to read the whole revmap
......
...@@ -84,19 +84,6 @@ typedef struct VariableStatData ...@@ -84,19 +84,6 @@ typedef struct VariableStatData
} while(0) } while(0)
/*
* deconstruct_indexquals is a simple function to examine the indexquals
* attached to a proposed IndexPath. It returns a list of IndexQualInfo
* structs, one per qual expression.
*/
typedef struct
{
RestrictInfo *rinfo; /* the indexqual itself */
int indexcol; /* zero-based index column number */
Oid clause_op; /* qual's operator OID, if relevant */
Node *other_operand; /* non-index operand of qual's operator */
} IndexQualInfo;
/* /*
* genericcostestimate is a general-purpose estimator that can be used for * genericcostestimate is a general-purpose estimator that can be used for
* most index types. In some cases we use genericcostestimate as the base * most index types. In some cases we use genericcostestimate as the base
...@@ -200,10 +187,13 @@ extern void estimate_hash_bucket_stats(PlannerInfo *root, ...@@ -200,10 +187,13 @@ extern void estimate_hash_bucket_stats(PlannerInfo *root,
Selectivity *mcv_freq, Selectivity *mcv_freq,
Selectivity *bucketsize_frac); Selectivity *bucketsize_frac);
extern List *deconstruct_indexquals(IndexPath *path); extern List *get_quals_from_indexclauses(List *indexclauses);
extern Cost index_other_operands_eval_cost(PlannerInfo *root,
List *indexquals);
extern List *add_predicate_to_index_quals(IndexOptInfo *index,
List *indexQuals);
extern void genericcostestimate(PlannerInfo *root, IndexPath *path, extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, double loop_count,
List *qinfos,
GenericCosts *costs); GenericCosts *costs);
/* Functions in array_selfuncs.c */ /* Functions in array_selfuncs.c */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment