Oid sortop,
Datum *min, Datum *max);
static RelOptInfo *find_join_input_rel(PlannerInfo *root, Relids relids);
-static List *add_predicate_to_quals(IndexOptInfo *index, List *indexQuals);
/*
*-------------------------------------------------------------------------
*/
-/* Extract the actual indexquals (as RestrictInfos) from an IndexClause list */
-static List *
-get_index_quals(List *indexclauses)
+/*
+ * Extract the actual indexquals (as RestrictInfos) from an IndexClause list
+ */
+List *
+get_quals_from_indexclauses(List *indexclauses)
{
List *result = NIL;
ListCell *lc;
return result;
}
-List *
-deconstruct_indexquals(IndexPath *path)
-{
- List *result = NIL;
- ListCell *lc;
-
- foreach(lc, path->indexclauses)
- {
- IndexClause *iclause = lfirst_node(IndexClause, lc);
- int indexcol = iclause->indexcol;
- ListCell *lc2;
-
- foreach(lc2, iclause->indexquals)
- {
- RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
- Expr *clause = rinfo->clause;
- IndexQualInfo *qinfo;
-
- qinfo = (IndexQualInfo *) palloc(sizeof(IndexQualInfo));
- qinfo->rinfo = rinfo;
- qinfo->indexcol = indexcol;
-
- if (IsA(clause, OpExpr))
- {
- qinfo->clause_op = ((OpExpr *) clause)->opno;
- qinfo->other_operand = get_rightop(clause);
- }
- else if (IsA(clause, RowCompareExpr))
- {
- RowCompareExpr *rc = (RowCompareExpr *) clause;
-
- qinfo->clause_op = linitial_oid(rc->opnos);
- qinfo->other_operand = (Node *) rc->rargs;
- }
- else if (IsA(clause, ScalarArrayOpExpr))
- {
- ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
-
- qinfo->clause_op = saop->opno;
- qinfo->other_operand = (Node *) lsecond(saop->args);
- }
- else if (IsA(clause, NullTest))
- {
- qinfo->clause_op = InvalidOid;
- qinfo->other_operand = NULL;
- }
- else
- {
- elog(ERROR, "unsupported indexqual type: %d",
- (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
+ * 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.
- */
-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.
+ * 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.
*/
-static Cost
-orderby_operands_eval_cost(PlannerInfo *root, IndexPath *path)
+Cost
+index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
{
Cost qual_arg_cost = 0;
ListCell *lc;
- foreach(lc, path->indexorderbys)
+ 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))
{
- other_operand = get_rightop(clause);
+ 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 indexorderby type: %d",
+ elog(ERROR, "unsupported indexqual type: %d",
(int) nodeTag(clause));
other_operand = NULL; /* keep compiler quiet */
}
genericcostestimate(PlannerInfo *root,
IndexPath *path,
double loop_count,
- List *qinfos,
GenericCosts *costs)
{
IndexOptInfo *index = path->indexinfo;
- List *indexQuals = get_index_quals(path->indexclauses);
+ List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
List *indexOrderBys = path->indexorderbys;
Cost indexStartupCost;
Cost indexTotalCost;
* given indexquals to produce a more accurate idea of the index
* 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
* 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_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));
* 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 *
+add_predicate_to_index_quals(IndexOptInfo *index, List *indexQuals)
{
List *predExtraQuals = NIL;
ListCell *lc;
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
- List *qinfos;
GenericCosts costs;
Oid relid;
AttrNumber colnum;
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
found_saop = false;
found_is_null_op = false;
num_sa_scans = 1;
- foreach(lc, qinfos)
+ foreach(lc, path->indexclauses)
{
- IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(lc);
- RestrictInfo *rinfo = qinfo->rinfo;
- Expr *clause = rinfo->clause;
- Oid clause_op;
- int op_strategy;
+ IndexClause *iclause = lfirst_node(IndexClause, lc);
+ ListCell *lc2;
- if (indexcol != qinfo->indexcol)
+ 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 != qinfo->indexcol)
+ if (indexcol != iclause->indexcol)
break; /* no quals at all for indexcol */
}
- if (IsA(clause, ScalarArrayOpExpr))
+ /* Examine each indexqual associated with this index clause */
+ foreach(lc2, iclause->indexquals)
{
- int alength = estimate_array_length(qinfo->other_operand);
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
+ Expr *clause = rinfo->clause;
+ Oid clause_op = InvalidOid;
+ int op_strategy;
- 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 (IsA(clause, OpExpr))
+ {
+ OpExpr *op = (OpExpr *) clause;
+
+ clause_op = op->opno;
+ }
+ else if (IsA(clause, RowCompareExpr))
+ {
+ RowCompareExpr *rc = (RowCompareExpr *) clause;
- if (nt->nulltesttype == IS_NULL)
+ clause_op = linitial_oid(rc->opnos);
+ }
+ else if (IsA(clause, ScalarArrayOpExpr))
{
- found_is_null_op = true;
- /* IS NULL is like = for selectivity determination purposes */
- eqQualHere = true;
+ 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;
- /* 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;
- }
+ if (nt->nulltesttype == IS_NULL)
+ {
+ found_is_null_op = true;
+ /* IS NULL is like = for selectivity purposes */
+ eqQualHere = true;
+ }
+ }
+ else
+ elog(ERROR, "unsupported indexqual type: %d",
+ (int) nodeTag(clause));
- indexBoundQuals = lappend(indexBoundQuals, rinfo);
+ /* 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);
+ }
}
/*
* 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);
+ selectivityQuals = add_predicate_to_index_quals(index, indexBoundQuals);
btreeSelectivity = clauselist_selectivity(root, selectivityQuals,
index->rel->relid,
MemSet(&costs, 0, sizeof(costs));
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
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);
+ genericcostestimate(root, path, loop_count, &costs);
/*
* A hash index has no descent costs as such, since the index AM can go
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);
+ genericcostestimate(root, path, loop_count, &costs);
/*
* We model index descent costs similarly to those for btree, but to do
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);
+ genericcostestimate(root, path, loop_count, &costs);
/*
* We model index descent costs similarly to those for btree, but to do
static bool
gincost_opexpr(PlannerInfo *root,
IndexOptInfo *index,
- IndexQualInfo *qinfo,
+ int indexcol,
+ OpExpr *clause,
GinQualCounts *counts)
{
- int indexcol = qinfo->indexcol;
- Oid clause_op = qinfo->clause_op;
- Node *operand = qinfo->other_operand;
+ Oid clause_op = clause->opno;
+ Node *operand = (Node *) lsecond(clause->args);
/* aggressively reduce to a constant, and look through relabeling */
operand = estimate_expression_value(root, operand);
static bool
gincost_scalararrayopexpr(PlannerInfo *root,
IndexOptInfo *index,
- IndexQualInfo *qinfo,
+ int indexcol,
+ ScalarArrayOpExpr *clause,
double numIndexEntries,
GinQualCounts *counts)
{
- int indexcol = qinfo->indexcol;
- Oid clause_op = qinfo->clause_op;
- Node *rightop = qinfo->other_operand;
+ Oid clause_op = clause->opno;
+ Node *rightop = (Node *) lsecond(clause->args);
ArrayType *arrayval;
int16 elmlen;
bool elmbyval;
int numPossible = 0;
int i;
- Assert(((ScalarArrayOpExpr *) qinfo->rinfo->clause)->useOr);
+ Assert(clause->useOr);
/* aggressively reduce to a constant, and look through relabeling */
rightop = estimate_expression_value(root, rightop);
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
- List *indexQuals = get_index_quals(path->indexclauses);
- List *indexOrderBys = path->indexorderbys;
- List *qinfos;
- ListCell *l;
+ List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
List *selectivityQuals;
double numPages = index->pages,
numTuples = index->tuples;
outer_scans;
Relation indexRel;
GinStatsData ginStats;
-
- /* Do preliminary analysis of indexquals */
- qinfos = deconstruct_indexquals(path);
+ ListCell *lc;
/*
* Obtain statistical information from the meta page, if possible. Else
* quals to produce a more accurate idea of the number of rows covered by
* 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 */
*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
counts.arrayScans = 1;
matchPossible = true;
- foreach(l, qinfos)
+ foreach(lc, path->indexclauses)
{
- IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(l);
- Expr *clause = qinfo->rinfo->clause;
+ IndexClause *iclause = lfirst_node(IndexClause, lc);
+ ListCell *lc2;
- if (IsA(clause, OpExpr))
- {
- matchPossible = gincost_opexpr(root,
- index,
- qinfo,
- &counts);
- if (!matchPossible)
- break;
- }
- else if (IsA(clause, ScalarArrayOpExpr))
- {
- matchPossible = gincost_scalararrayopexpr(root,
- index,
- qinfo,
- numEntries,
- &counts);
- if (!matchPossible)
- break;
- }
- else
+ foreach(lc2, iclause->indexquals)
{
- /* shouldn't be anything else for a GIN index */
- elog(ERROR, "unsupported GIN indexqual type: %d",
- (int) nodeTag(clause));
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
+ Expr *clause = rinfo->clause;
+
+ if (IsA(clause, OpExpr))
+ {
+ matchPossible = gincost_opexpr(root,
+ index,
+ iclause->indexcol,
+ (OpExpr *) clause,
+ &counts);
+ if (!matchPossible)
+ break;
+ }
+ else if (IsA(clause, ScalarArrayOpExpr))
+ {
+ matchPossible = gincost_scalararrayopexpr(root,
+ index,
+ iclause->indexcol,
+ (ScalarArrayOpExpr *) clause,
+ numEntries,
+ &counts);
+ if (!matchPossible)
+ break;
+ }
+ else
+ {
+ /* shouldn't be anything else for a GIN index */
+ elog(ERROR, "unsupported GIN indexqual type: %d",
+ (int) nodeTag(clause));
+ }
}
}
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) +
- orderby_operands_eval_cost(root, path);
- qual_op_cost = cpu_operator_cost *
- (list_length(indexQuals) + list_length(indexOrderBys));
+ 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;
double *indexPages)
{
IndexOptInfo *index = path->indexinfo;
- List *indexQuals = get_index_quals(path->indexclauses);
+ List *indexQuals = get_quals_from_indexclauses(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;
*/
*indexCorrelation = 0;
- qinfos = deconstruct_indexquals(path);
- foreach(l, qinfos)
+ foreach(l, path->indexclauses)
{
- IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(l);
- AttrNumber attnum = index->indexkeys[qinfo->indexcol];
+ 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)
*/
/* get the attnum from the 0-based index. */
- attnum = qinfo->indexcol + 1;
+ attnum = iclause->indexcol + 1;
if (get_index_stats_hook &&
(*get_index_stats_hook) (root, index->indexoid, attnum, &vardata))
/*
* 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) +
- orderby_operands_eval_cost(root, path);
+ qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
/*
* Compute the startup cost as the cost to read the whole revmap