refactor-index-cost-estimate-logic-poc.patch
text/x-diff
Filename: refactor-index-cost-estimate-logic-poc.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| contrib/bloom/blcost.c | 0 | 0 |
| src/backend/utils/adt/selfuncs.c | 0 | 0 |
| src/include/utils/selfuncs.h | 0 | 0 |
diff --git a/contrib/bloom/blcost.c b/contrib/bloom/blcost.c
index 2d8a7f1..f9fe57f 100644
*** a/contrib/bloom/blcost.c
--- b/contrib/bloom/blcost.c
*************** blcostestimate(PlannerInfo *root, IndexP
*** 27,45 ****
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
- List *qinfos;
GenericCosts costs;
- /* Do preliminary analysis of indexquals */
- qinfos = deconstruct_indexquals(path);
-
MemSet(&costs, 0, sizeof(costs));
/* We have to visit all index tuples anyway */
costs.numIndexTuples = index->tuples;
/* Use generic estimate */
! genericcostestimate(root, path, loop_count, qinfos, &costs);
*indexStartupCost = costs.indexStartupCost;
*indexTotalCost = costs.indexTotalCost;
--- 27,41 ----
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
GenericCosts costs;
MemSet(&costs, 0, sizeof(costs));
/* We have to visit all index tuples anyway */
costs.numIndexTuples = index->tuples;
/* Use generic estimate */
! genericcostestimate(root, path, loop_count, &costs);
*indexStartupCost = costs.indexStartupCost;
*indexTotalCost = costs.indexTotalCost;
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index b9f99fa..3e30b57 100644
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** static Const *string_to_const(const char
*** 228,234 ****
static Const *string_to_bytea_const(const char *str, size_t str_len);
static IndexQualInfo *deconstruct_indexqual(RestrictInfo *rinfo,
IndexOptInfo *index, int indexcol);
- static List *add_predicate_to_quals(IndexOptInfo *index, List *indexQuals);
/*
--- 228,233 ----
*************** string_to_bytea_const(const char *str, s
*** 6570,6578 ****
*-------------------------------------------------------------------------
*/
! /* Extract the actual indexquals (as RestrictInfos) from an IndexClause list */
! static List *
! get_index_quals(List *indexclauses)
{
List *result = NIL;
ListCell *lc;
--- 6569,6579 ----
*-------------------------------------------------------------------------
*/
! /*
! * Extract the actual indexquals (as RestrictInfos) from an IndexClause list
! */
! List *
! get_quals_from_indexclauses(List *indexclauses)
{
List *result = NIL;
ListCell *lc;
*************** deconstruct_indexqual(RestrictInfo *rinf
*** 6679,6732 ****
}
/*
! * 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 */
}
--- 6680,6737 ----
}
/*
! * 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)
{
Cost qual_arg_cost = 0;
ListCell *lc;
! foreach(lc, indexquals)
{
Expr *clause = (Expr *) lfirst(lc);
Node *other_operand;
QualCost index_qual_cost;
+ /*
+ * Index quals will have RestrictInfos, indexorderbys won't. Look
+ * through RestrictInfo if present.
+ */
+ if (IsA(clause, RestrictInfo))
+ clause = ((RestrictInfo *) clause)->clause;
+
if (IsA(clause, OpExpr))
{
! OpExpr *op = (OpExpr *) clause;
!
! other_operand = (Node *) lsecond(op->args);
! }
! else if (IsA(clause, RowCompareExpr))
! {
! RowCompareExpr *rc = (RowCompareExpr *) clause;
!
! other_operand = (Node *) rc->rargs;
! }
! else if (IsA(clause, ScalarArrayOpExpr))
! {
! ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
!
! other_operand = (Node *) lsecond(saop->args);
! }
! else if (IsA(clause, NullTest))
! {
! other_operand = NULL;
}
else
{
! elog(ERROR, "unsupported indexqual type: %d",
(int) nodeTag(clause));
other_operand = NULL; /* keep compiler quiet */
}
*************** void
*** 6741,6751 ****
genericcostestimate(PlannerInfo *root,
IndexPath *path,
double loop_count,
- List *qinfos,
GenericCosts *costs)
{
IndexOptInfo *index = path->indexinfo;
! List *indexQuals = get_index_quals(path->indexclauses);
List *indexOrderBys = path->indexorderbys;
Cost indexStartupCost;
Cost indexTotalCost;
--- 6746,6755 ----
genericcostestimate(PlannerInfo *root,
IndexPath *path,
double loop_count,
GenericCosts *costs)
{
IndexOptInfo *index = path->indexinfo;
! List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
List *indexOrderBys = path->indexorderbys;
Cost indexStartupCost;
Cost indexTotalCost;
*************** genericcostestimate(PlannerInfo *root,
*** 6767,6773 ****
* given indexquals to produce a more accurate idea of the index
* selectivity.
*/
! selectivityQuals = add_predicate_to_quals(index, indexQuals);
/*
* Check for ScalarArrayOpExpr index quals, and estimate the number of
--- 6771,6777 ----
* given indexquals to produce a more accurate idea of the index
* selectivity.
*/
! selectivityQuals = add_predicate_to_index_quals(index, indexQuals);
/*
* Check for ScalarArrayOpExpr index quals, and estimate the number of
*************** genericcostestimate(PlannerInfo *root,
*** 6910,6917 ****
* Detecting that that might be needed seems more expensive than it's
* worth, though, considering all the other inaccuracies here ...
*/
! qual_arg_cost = other_operands_eval_cost(root, qinfos) +
! orderby_operands_eval_cost(root, path);
qual_op_cost = cpu_operator_cost *
(list_length(indexQuals) + list_length(indexOrderBys));
--- 6914,6921 ----
* Detecting that that might be needed seems more expensive than it's
* worth, though, considering all the other inaccuracies here ...
*/
! qual_arg_cost = index_other_operands_eval_cost(root, indexQuals) +
! index_other_operands_eval_cost(root, indexOrderBys);
qual_op_cost = cpu_operator_cost *
(list_length(indexQuals) + list_length(indexOrderBys));
*************** genericcostestimate(PlannerInfo *root,
*** 6956,6963 ****
* predicate_implied_by() and clauselist_selectivity(), but might be
* problematic if the result were passed to other things.
*/
! static List *
! add_predicate_to_quals(IndexOptInfo *index, List *indexQuals)
{
List *predExtraQuals = NIL;
ListCell *lc;
--- 6960,6967 ----
* predicate_implied_by() and clauselist_selectivity(), but might be
* problematic if the result were passed to other things.
*/
! List *
! add_predicate_to_index_quals(IndexOptInfo *index, List *indexQuals)
{
List *predExtraQuals = NIL;
ListCell *lc;
*************** btcostestimate(PlannerInfo *root, IndexP
*** 6985,6991 ****
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
- List *qinfos;
GenericCosts costs;
Oid relid;
AttrNumber colnum;
--- 6989,6994 ----
*************** btcostestimate(PlannerInfo *root, IndexP
*** 7000,7008 ****
double num_sa_scans;
ListCell *lc;
- /* Do preliminary analysis of indexquals */
- qinfos = deconstruct_indexquals(path);
-
/*
* For a btree scan, only leading '=' quals plus inequality quals for the
* immediately next attribute contribute to index selectivity (these are
--- 7003,7008 ----
*************** btcostestimate(PlannerInfo *root, IndexP
*** 7026,7083 ****
found_saop = false;
found_is_null_op = false;
num_sa_scans = 1;
! foreach(lc, qinfos)
{
! IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(lc);
! RestrictInfo *rinfo = qinfo->rinfo;
! Expr *clause = rinfo->clause;
! Oid clause_op;
! int op_strategy;
! if (indexcol != qinfo->indexcol)
{
/* Beginning of a new column's quals */
if (!eqQualHere)
break; /* done if no '=' qual for indexcol */
eqQualHere = false;
indexcol++;
! if (indexcol != qinfo->indexcol)
break; /* no quals at all for indexcol */
}
! if (IsA(clause, ScalarArrayOpExpr))
! {
! int alength = estimate_array_length(qinfo->other_operand);
! found_saop = true;
! /* count up number of SA scans induced by indexBoundQuals only */
! if (alength > 1)
! num_sa_scans *= alength;
! }
! else if (IsA(clause, NullTest))
{
! NullTest *nt = (NullTest *) clause;
! if (nt->nulltesttype == IS_NULL)
{
! found_is_null_op = true;
! /* IS NULL is like = for selectivity determination purposes */
! eqQualHere = true;
}
! }
! /* check for equality operator */
! clause_op = qinfo->clause_op;
! if (OidIsValid(clause_op))
! {
! op_strategy = get_op_opfamily_strategy(clause_op,
! index->opfamily[indexcol]);
! Assert(op_strategy != 0); /* not a member of opfamily?? */
! if (op_strategy == BTEqualStrategyNumber)
! eqQualHere = true;
! }
! indexBoundQuals = lappend(indexBoundQuals, rinfo);
}
/*
--- 7026,7109 ----
found_saop = false;
found_is_null_op = false;
num_sa_scans = 1;
! foreach(lc, path->indexclauses)
{
! IndexClause *iclause = lfirst_node(IndexClause, lc);
! List *indexquals;
! ListCell *lc2;
! if (indexcol != iclause->indexcol)
{
/* Beginning of a new column's quals */
if (!eqQualHere)
break; /* done if no '=' qual for indexcol */
eqQualHere = false;
indexcol++;
! if (indexcol != iclause->indexcol)
break; /* no quals at all for indexcol */
}
! /* Examine each indexqual associated with this index clause */
! if (iclause->indexquals)
! indexquals = iclause->indexquals;
! else
! indexquals = list_make1(iclause->rinfo);
! foreach(lc2, 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))
! {
! 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;
! /* count number of SA scans induced by indexBoundQuals only */
! if (alength > 1)
! num_sa_scans *= alength;
! }
! else if (IsA(clause, NullTest))
! {
! NullTest *nt = (NullTest *) clause;
!
! if (nt->nulltesttype == IS_NULL)
! {
! found_is_null_op = true;
! /* IS NULL is like = for selectivity purposes */
! eqQualHere = true;
! }
! }
!
! /* check for equality operator */
! if (OidIsValid(clause_op))
! {
! op_strategy = get_op_opfamily_strategy(clause_op,
! index->opfamily[indexcol]);
! Assert(op_strategy != 0); /* not a member of opfamily?? */
! if (op_strategy == BTEqualStrategyNumber)
! eqQualHere = true;
! }
!
! indexBoundQuals = lappend(indexBoundQuals, rinfo);
! }
}
/*
*************** btcostestimate(PlannerInfo *root, IndexP
*** 7102,7108 ****
* index-bound quals to produce a more accurate idea of the number of
* rows covered by the bound conditions.
*/
! selectivityQuals = add_predicate_to_quals(index, indexBoundQuals);
btreeSelectivity = clauselist_selectivity(root, selectivityQuals,
index->rel->relid,
--- 7128,7134 ----
* index-bound quals to produce a more accurate idea of the number of
* rows covered by the bound conditions.
*/
! selectivityQuals = add_predicate_to_index_quals(index, indexBoundQuals);
btreeSelectivity = clauselist_selectivity(root, selectivityQuals,
index->rel->relid,
*************** btcostestimate(PlannerInfo *root, IndexP
*** 7124,7130 ****
MemSet(&costs, 0, sizeof(costs));
costs.numIndexTuples = numIndexTuples;
! genericcostestimate(root, path, loop_count, qinfos, &costs);
/*
* Add a CPU-cost component to represent the costs of initial btree
--- 7150,7156 ----
MemSet(&costs, 0, sizeof(costs));
costs.numIndexTuples = numIndexTuples;
! genericcostestimate(root, path, loop_count, &costs);
/*
* Add a CPU-cost component to represent the costs of initial btree
*************** hashcostestimate(PlannerInfo *root, Inde
*** 7271,7285 ****
Selectivity *indexSelectivity, double *indexCorrelation,
double *indexPages)
{
- List *qinfos;
GenericCosts costs;
- /* Do preliminary analysis of indexquals */
- qinfos = deconstruct_indexquals(path);
-
MemSet(&costs, 0, sizeof(costs));
! genericcostestimate(root, path, loop_count, qinfos, &costs);
/*
* A hash index has no descent costs as such, since the index AM can go
--- 7297,7307 ----
Selectivity *indexSelectivity, double *indexCorrelation,
double *indexPages)
{
GenericCosts costs;
MemSet(&costs, 0, sizeof(costs));
! genericcostestimate(root, path, loop_count, &costs);
/*
* A hash index has no descent costs as such, since the index AM can go
*************** gistcostestimate(PlannerInfo *root, Inde
*** 7320,7335 ****
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
- List *qinfos;
GenericCosts costs;
Cost descentCost;
- /* Do preliminary analysis of indexquals */
- qinfos = deconstruct_indexquals(path);
-
MemSet(&costs, 0, sizeof(costs));
! genericcostestimate(root, path, loop_count, qinfos, &costs);
/*
* We model index descent costs similarly to those for btree, but to do
--- 7342,7353 ----
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
GenericCosts costs;
Cost descentCost;
MemSet(&costs, 0, sizeof(costs));
! genericcostestimate(root, path, loop_count, &costs);
/*
* We model index descent costs similarly to those for btree, but to do
*************** spgcostestimate(PlannerInfo *root, Index
*** 7381,7396 ****
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
- List *qinfos;
GenericCosts costs;
Cost descentCost;
- /* Do preliminary analysis of indexquals */
- qinfos = deconstruct_indexquals(path);
-
MemSet(&costs, 0, sizeof(costs));
! genericcostestimate(root, path, loop_count, qinfos, &costs);
/*
* We model index descent costs similarly to those for btree, but to do
--- 7399,7410 ----
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
GenericCosts costs;
Cost descentCost;
MemSet(&costs, 0, sizeof(costs));
! genericcostestimate(root, path, loop_count, &costs);
/*
* We model index descent costs similarly to those for btree, but to do
*************** gincostestimate(PlannerInfo *root, Index
*** 7730,7737 ****
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
! List *indexQuals = get_index_quals(path->indexclauses);
! List *indexOrderBys = path->indexorderbys;
List *qinfos;
ListCell *l;
List *selectivityQuals;
--- 7744,7750 ----
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
! List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
List *qinfos;
ListCell *l;
List *selectivityQuals;
*************** gincostestimate(PlannerInfo *root, Index
*** 7837,7843 ****
* quals to produce a more accurate idea of the number of rows covered by
* the bound conditions.
*/
! selectivityQuals = add_predicate_to_quals(index, indexQuals);
/* Estimate the fraction of main-table tuples that will be visited */
*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
--- 7850,7856 ----
* quals to produce a more accurate idea of the number of rows covered by
* the bound conditions.
*/
! selectivityQuals = add_predicate_to_index_quals(index, indexQuals);
/* Estimate the fraction of main-table tuples that will be visited */
*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
*************** gincostestimate(PlannerInfo *root, Index
*** 8017,8028 ****
dataPagesFetched * spc_random_page_cost;
/*
! * Add on index qual eval costs, much as in genericcostestimate
*/
! qual_arg_cost = other_operands_eval_cost(root, qinfos) +
! orderby_operands_eval_cost(root, path);
! qual_op_cost = cpu_operator_cost *
! (list_length(indexQuals) + list_length(indexOrderBys));
*indexStartupCost += qual_arg_cost;
*indexTotalCost += qual_arg_cost;
--- 8030,8040 ----
dataPagesFetched * spc_random_page_cost;
/*
! * Add on index qual eval costs, much as in genericcostestimate. But we
! * can disregard indexorderbys, since GIN doesn't support those.
*/
! qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
! qual_op_cost = cpu_operator_cost * list_length(indexQuals);
*indexStartupCost += qual_arg_cost;
*indexTotalCost += qual_arg_cost;
*************** brincostestimate(PlannerInfo *root, Inde
*** 8040,8050 ****
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
! List *indexQuals = get_index_quals(path->indexclauses);
double numPages = index->pages;
RelOptInfo *baserel = index->rel;
RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
- List *qinfos;
Cost spc_seq_page_cost;
Cost spc_random_page_cost;
double qual_arg_cost;
--- 8052,8061 ----
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
! List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
double numPages = index->pages;
RelOptInfo *baserel = index->rel;
RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
Cost spc_seq_page_cost;
Cost spc_random_page_cost;
double qual_arg_cost;
*************** brincostestimate(PlannerInfo *root, Inde
*** 8082,8092 ****
*/
*indexCorrelation = 0;
! qinfos = deconstruct_indexquals(path);
! foreach(l, qinfos)
{
! IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(l);
! AttrNumber attnum = index->indexkeys[qinfo->indexcol];
/* attempt to lookup stats in relation for this index column */
if (attnum != 0)
--- 8093,8102 ----
*/
*indexCorrelation = 0;
! foreach(l, path->indexclauses)
{
! IndexClause *iclause = lfirst_node(IndexClause, l);
! AttrNumber attnum = index->indexkeys[iclause->indexcol];
/* attempt to lookup stats in relation for this index column */
if (attnum != 0)
*************** brincostestimate(PlannerInfo *root, Inde
*** 8121,8127 ****
*/
/* get the attnum from the 0-based index. */
! attnum = qinfo->indexcol + 1;
if (get_index_stats_hook &&
(*get_index_stats_hook) (root, index->indexoid, attnum, &vardata))
--- 8131,8137 ----
*/
/* get the attnum from the 0-based index. */
! attnum = iclause->indexcol + 1;
if (get_index_stats_hook &&
(*get_index_stats_hook) (root, index->indexoid, attnum, &vardata))
*************** brincostestimate(PlannerInfo *root, Inde
*** 8200,8209 ****
/*
* Compute the index qual costs, much as in genericcostestimate, to add to
! * the index costs.
*/
! qual_arg_cost = other_operands_eval_cost(root, qinfos) +
! orderby_operands_eval_cost(root, path);
/*
* Compute the startup cost as the cost to read the whole revmap
--- 8210,8219 ----
/*
* Compute the index qual costs, much as in genericcostestimate, to add to
! * the index costs. We can disregard indexorderbys, since BRIN doesn't
! * support those.
*/
! qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
/*
* Compute the startup cost as the cost to read the whole revmap
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index 087b56f..a617884 100644
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** extern void estimate_hash_bucket_stats(P
*** 213,222 ****
Selectivity *mcv_freq,
Selectivity *bucketsize_frac);
extern List *deconstruct_indexquals(IndexPath *path);
extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count,
- List *qinfos,
GenericCosts *costs);
/* Functions in array_selfuncs.c */
--- 213,224 ----
Selectivity *mcv_freq,
Selectivity *bucketsize_frac);
+ extern List *get_quals_from_indexclauses(List *indexclauses);
extern List *deconstruct_indexquals(IndexPath *path);
+ 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,
double loop_count,
GenericCosts *costs);
/* Functions in array_selfuncs.c */