PostgreSQL Source Code  git master
index_selfuncs.h File Reference
#include "access/amapi.h"
Include dependency graph for index_selfuncs.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

void brincostestimate (struct PlannerInfo *root, struct IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
 
void btcostestimate (struct PlannerInfo *root, struct IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
 
void hashcostestimate (struct PlannerInfo *root, struct IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
 
void gistcostestimate (struct PlannerInfo *root, struct IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
 
void spgcostestimate (struct PlannerInfo *root, struct IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
 
void gincostestimate (struct PlannerInfo *root, struct IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
 

Function Documentation

◆ brincostestimate()

void brincostestimate ( struct PlannerInfo root,
struct IndexPath path,
double  loop_count,
Cost indexStartupCost,
Cost indexTotalCost,
Selectivity indexSelectivity,
double *  indexCorrelation,
double *  indexPages 
)

Definition at line 7981 of file selfuncs.c.

7985 {
7986  IndexOptInfo *index = path->indexinfo;
7987  List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
7988  double numPages = index->pages;
7989  RelOptInfo *baserel = index->rel;
7990  RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
7991  Cost spc_seq_page_cost;
7992  Cost spc_random_page_cost;
7993  double qual_arg_cost;
7994  double qualSelectivity;
7995  BrinStatsData statsData;
7996  double indexRanges;
7997  double minimalRanges;
7998  double estimatedRanges;
7999  double selec;
8000  Relation indexRel;
8001  ListCell *l;
8002  VariableStatData vardata;
8003 
8004  Assert(rte->rtekind == RTE_RELATION);
8005 
8006  /* fetch estimated page cost for the tablespace containing the index */
8007  get_tablespace_page_costs(index->reltablespace,
8008  &spc_random_page_cost,
8009  &spc_seq_page_cost);
8010 
8011  /*
8012  * Obtain some data from the index itself, if possible. Otherwise invent
8013  * some plausible internal statistics based on the relation page count.
8014  */
8015  if (!index->hypothetical)
8016  {
8017  /*
8018  * A lock should have already been obtained on the index in plancat.c.
8019  */
8020  indexRel = index_open(index->indexoid, NoLock);
8021  brinGetStats(indexRel, &statsData);
8022  index_close(indexRel, NoLock);
8023 
8024  /* work out the actual number of ranges in the index */
8025  indexRanges = Max(ceil((double) baserel->pages /
8026  statsData.pagesPerRange), 1.0);
8027  }
8028  else
8029  {
8030  /*
8031  * Assume default number of pages per range, and estimate the number
8032  * of ranges based on that.
8033  */
8034  indexRanges = Max(ceil((double) baserel->pages /
8036 
8038  statsData.revmapNumPages = (indexRanges / REVMAP_PAGE_MAXITEMS) + 1;
8039  }
8040 
8041  /*
8042  * Compute index correlation
8043  *
8044  * Because we can use all index quals equally when scanning, we can use
8045  * the largest correlation (in absolute value) among columns used by the
8046  * query. Start at zero, the worst possible case. If we cannot find any
8047  * correlation statistics, we will keep it as 0.
8048  */
8049  *indexCorrelation = 0;
8050 
8051  foreach(l, path->indexclauses)
8052  {
8053  IndexClause *iclause = lfirst_node(IndexClause, l);
8054  AttrNumber attnum = index->indexkeys[iclause->indexcol];
8055 
8056  /* attempt to lookup stats in relation for this index column */
8057  if (attnum != 0)
8058  {
8059  /* Simple variable -- look to stats for the underlying table */
8061  (*get_relation_stats_hook) (root, rte, attnum, &vardata))
8062  {
8063  /*
8064  * The hook took control of acquiring a stats tuple. If it
8065  * did supply a tuple, it'd better have supplied a freefunc.
8066  */
8067  if (HeapTupleIsValid(vardata.statsTuple) && !vardata.freefunc)
8068  elog(ERROR,
8069  "no function provided to release variable stats with");
8070  }
8071  else
8072  {
8073  vardata.statsTuple =
8074  SearchSysCache3(STATRELATTINH,
8075  ObjectIdGetDatum(rte->relid),
8077  BoolGetDatum(false));
8078  vardata.freefunc = ReleaseSysCache;
8079  }
8080  }
8081  else
8082  {
8083  /*
8084  * Looks like we've found an expression column in the index. Let's
8085  * see if there's any stats for it.
8086  */
8087 
8088  /* get the attnum from the 0-based index. */
8089  attnum = iclause->indexcol + 1;
8090 
8091  if (get_index_stats_hook &&
8092  (*get_index_stats_hook) (root, index->indexoid, attnum, &vardata))
8093  {
8094  /*
8095  * The hook took control of acquiring a stats tuple. If it
8096  * did supply a tuple, it'd better have supplied a freefunc.
8097  */
8098  if (HeapTupleIsValid(vardata.statsTuple) &&
8099  !vardata.freefunc)
8100  elog(ERROR, "no function provided to release variable stats with");
8101  }
8102  else
8103  {
8104  vardata.statsTuple = SearchSysCache3(STATRELATTINH,
8105  ObjectIdGetDatum(index->indexoid),
8107  BoolGetDatum(false));
8108  vardata.freefunc = ReleaseSysCache;
8109  }
8110  }
8111 
8112  if (HeapTupleIsValid(vardata.statsTuple))
8113  {
8114  AttStatsSlot sslot;
8115 
8116  if (get_attstatsslot(&sslot, vardata.statsTuple,
8117  STATISTIC_KIND_CORRELATION, InvalidOid,
8119  {
8120  double varCorrelation = 0.0;
8121 
8122  if (sslot.nnumbers > 0)
8123  varCorrelation = fabs(sslot.numbers[0]);
8124 
8125  if (varCorrelation > *indexCorrelation)
8126  *indexCorrelation = varCorrelation;
8127 
8128  free_attstatsslot(&sslot);
8129  }
8130  }
8131 
8132  ReleaseVariableStats(vardata);
8133  }
8134 
8135  qualSelectivity = clauselist_selectivity(root, indexQuals,
8136  baserel->relid,
8137  JOIN_INNER, NULL);
8138 
8139  /*
8140  * Now calculate the minimum possible ranges we could match with if all of
8141  * the rows were in the perfect order in the table's heap.
8142  */
8143  minimalRanges = ceil(indexRanges * qualSelectivity);
8144 
8145  /*
8146  * Now estimate the number of ranges that we'll touch by using the
8147  * indexCorrelation from the stats. Careful not to divide by zero (note
8148  * we're using the absolute value of the correlation).
8149  */
8150  if (*indexCorrelation < 1.0e-10)
8151  estimatedRanges = indexRanges;
8152  else
8153  estimatedRanges = Min(minimalRanges / *indexCorrelation, indexRanges);
8154 
8155  /* we expect to visit this portion of the table */
8156  selec = estimatedRanges / indexRanges;
8157 
8158  CLAMP_PROBABILITY(selec);
8159 
8160  *indexSelectivity = selec;
8161 
8162  /*
8163  * Compute the index qual costs, much as in genericcostestimate, to add to
8164  * the index costs. We can disregard indexorderbys, since BRIN doesn't
8165  * support those.
8166  */
8167  qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
8168 
8169  /*
8170  * Compute the startup cost as the cost to read the whole revmap
8171  * sequentially, including the cost to execute the index quals.
8172  */
8173  *indexStartupCost =
8174  spc_seq_page_cost * statsData.revmapNumPages * loop_count;
8175  *indexStartupCost += qual_arg_cost;
8176 
8177  /*
8178  * To read a BRIN index there might be a bit of back and forth over
8179  * regular pages, as revmap might point to them out of sequential order;
8180  * calculate the total cost as reading the whole index in random order.
8181  */
8182  *indexTotalCost = *indexStartupCost +
8183  spc_random_page_cost * (numPages - statsData.revmapNumPages) * loop_count;
8184 
8185  /*
8186  * Charge a small amount per range tuple which we expect to match to. This
8187  * is meant to reflect the costs of manipulating the bitmap. The BRIN scan
8188  * will set a bit for each page in the range when we find a matching
8189  * range, so we must multiply the charge by the number of pages in the
8190  * range.
8191  */
8192  *indexTotalCost += 0.1 * cpu_operator_cost * estimatedRanges *
8193  statsData.pagesPerRange;
8194 
8195  *indexPages = index->pages;
8196 }
int16 AttrNumber
Definition: attnum.h:21
void brinGetStats(Relation index, BrinStatsData *stats)
Definition: brin.c:1637
#define BRIN_DEFAULT_PAGES_PER_RANGE
Definition: brin.h:39
#define REVMAP_PAGE_MAXITEMS
Definition: brin_page.h:93
#define Min(x, y)
Definition: c.h:1004
#define Max(x, y)
Definition: c.h:998
#define Assert(condition)
Definition: c.h:858
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
Definition: clausesel.c:100
double cpu_operator_cost
Definition: costsize.c:134
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
#define NoLock
Definition: lockdefs.h:34
void free_attstatsslot(AttStatsSlot *sslot)
Definition: lsyscache.c:3344
bool get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple, int reqkind, Oid reqop, int flags)
Definition: lsyscache.c:3234
#define ATTSTATSSLOT_NUMBERS
Definition: lsyscache.h:43
double Cost
Definition: nodes.h:251
@ JOIN_INNER
Definition: nodes.h:293
@ RTE_RELATION
Definition: parsenodes.h:1017
#define planner_rt_fetch(rti, root)
Definition: pathnodes.h:570
int16 attnum
Definition: pg_attribute.h:74
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:172
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
#define InvalidOid
Definition: postgres_ext.h:36
tree ctl root
Definition: radixtree.h:1886
List * get_quals_from_indexclauses(List *indexclauses)
Definition: selfuncs.c:6468
get_index_stats_hook_type get_index_stats_hook
Definition: selfuncs.c:148
Cost index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
Definition: selfuncs.c:6498
get_relation_stats_hook_type get_relation_stats_hook
Definition: selfuncs.c:147
#define ReleaseVariableStats(vardata)
Definition: selfuncs.h:99
#define CLAMP_PROBABILITY(p)
Definition: selfuncs.h:63
void get_tablespace_page_costs(Oid spcid, double *spc_random_page_cost, double *spc_seq_page_cost)
Definition: spccache.c:182
float4 * numbers
Definition: lsyscache.h:56
int nnumbers
Definition: lsyscache.h:57
BlockNumber revmapNumPages
Definition: brin.h:35
BlockNumber pagesPerRange
Definition: brin.h:34
AttrNumber indexcol
Definition: pathnodes.h:1768
List * indexclauses
Definition: pathnodes.h:1718
IndexOptInfo * indexinfo
Definition: pathnodes.h:1717
Definition: pg_list.h:54
RTEKind rtekind
Definition: parsenodes.h:1047
Index relid
Definition: pathnodes.h:918
BlockNumber pages
Definition: pathnodes.h:948
HeapTuple statsTuple
Definition: selfuncs.h:89
void(* freefunc)(HeapTuple tuple)
Definition: selfuncs.h:91
Definition: type.h:95
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache3(int cacheId, Datum key1, Datum key2, Datum key3)
Definition: syscache.c:240

References Assert, attnum, ATTSTATSSLOT_NUMBERS, BoolGetDatum(), BRIN_DEFAULT_PAGES_PER_RANGE, brinGetStats(), CLAMP_PROBABILITY, clauselist_selectivity(), cpu_operator_cost, elog, ERROR, free_attstatsslot(), VariableStatData::freefunc, get_attstatsslot(), get_index_stats_hook, get_quals_from_indexclauses(), get_relation_stats_hook, get_tablespace_page_costs(), HeapTupleIsValid, index_close(), index_open(), index_other_operands_eval_cost(), IndexPath::indexclauses, IndexClause::indexcol, IndexPath::indexinfo, Int16GetDatum(), InvalidOid, JOIN_INNER, lfirst_node, Max, Min, AttStatsSlot::nnumbers, NoLock, AttStatsSlot::numbers, ObjectIdGetDatum(), RelOptInfo::pages, BrinStatsData::pagesPerRange, planner_rt_fetch, ReleaseSysCache(), ReleaseVariableStats, RangeTblEntry::relid, RelOptInfo::relid, REVMAP_PAGE_MAXITEMS, BrinStatsData::revmapNumPages, root, RTE_RELATION, RangeTblEntry::rtekind, SearchSysCache3(), and VariableStatData::statsTuple.

Referenced by brinhandler().

◆ btcostestimate()

void btcostestimate ( struct PlannerInfo root,
struct IndexPath path,
double  loop_count,
Cost indexStartupCost,
Cost indexTotalCost,
Selectivity indexSelectivity,
double *  indexCorrelation,
double *  indexPages 
)

Definition at line 6796 of file selfuncs.c.

6800 {
6801  IndexOptInfo *index = path->indexinfo;
6802  GenericCosts costs = {0};
6803  Oid relid;
6804  AttrNumber colnum;
6805  VariableStatData vardata = {0};
6806  double numIndexTuples;
6807  Cost descentCost;
6808  List *indexBoundQuals;
6809  int indexcol;
6810  bool eqQualHere;
6811  bool found_saop;
6812  bool found_is_null_op;
6813  double num_sa_scans;
6814  ListCell *lc;
6815 
6816  /*
6817  * For a btree scan, only leading '=' quals plus inequality quals for the
6818  * immediately next attribute contribute to index selectivity (these are
6819  * the "boundary quals" that determine the starting and stopping points of
6820  * the index scan). Additional quals can suppress visits to the heap, so
6821  * it's OK to count them in indexSelectivity, but they should not count
6822  * for estimating numIndexTuples. So we must examine the given indexquals
6823  * to find out which ones count as boundary quals. We rely on the
6824  * knowledge that they are given in index column order.
6825  *
6826  * For a RowCompareExpr, we consider only the first column, just as
6827  * rowcomparesel() does.
6828  *
6829  * If there's a ScalarArrayOpExpr in the quals, we'll actually perform up
6830  * to N index descents (not just one), but the ScalarArrayOpExpr's
6831  * operator can be considered to act the same as it normally does.
6832  */
6833  indexBoundQuals = NIL;
6834  indexcol = 0;
6835  eqQualHere = false;
6836  found_saop = false;
6837  found_is_null_op = false;
6838  num_sa_scans = 1;
6839  foreach(lc, path->indexclauses)
6840  {
6841  IndexClause *iclause = lfirst_node(IndexClause, lc);
6842  ListCell *lc2;
6843 
6844  if (indexcol != iclause->indexcol)
6845  {
6846  /* Beginning of a new column's quals */
6847  if (!eqQualHere)
6848  break; /* done if no '=' qual for indexcol */
6849  eqQualHere = false;
6850  indexcol++;
6851  if (indexcol != iclause->indexcol)
6852  break; /* no quals at all for indexcol */
6853  }
6854 
6855  /* Examine each indexqual associated with this index clause */
6856  foreach(lc2, iclause->indexquals)
6857  {
6858  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
6859  Expr *clause = rinfo->clause;
6860  Oid clause_op = InvalidOid;
6861  int op_strategy;
6862 
6863  if (IsA(clause, OpExpr))
6864  {
6865  OpExpr *op = (OpExpr *) clause;
6866 
6867  clause_op = op->opno;
6868  }
6869  else if (IsA(clause, RowCompareExpr))
6870  {
6871  RowCompareExpr *rc = (RowCompareExpr *) clause;
6872 
6873  clause_op = linitial_oid(rc->opnos);
6874  }
6875  else if (IsA(clause, ScalarArrayOpExpr))
6876  {
6877  ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
6878  Node *other_operand = (Node *) lsecond(saop->args);
6879  double alength = estimate_array_length(root, other_operand);
6880 
6881  clause_op = saop->opno;
6882  found_saop = true;
6883  /* estimate SA descents by indexBoundQuals only */
6884  if (alength > 1)
6885  num_sa_scans *= alength;
6886  }
6887  else if (IsA(clause, NullTest))
6888  {
6889  NullTest *nt = (NullTest *) clause;
6890 
6891  if (nt->nulltesttype == IS_NULL)
6892  {
6893  found_is_null_op = true;
6894  /* IS NULL is like = for selectivity purposes */
6895  eqQualHere = true;
6896  }
6897  }
6898  else
6899  elog(ERROR, "unsupported indexqual type: %d",
6900  (int) nodeTag(clause));
6901 
6902  /* check for equality operator */
6903  if (OidIsValid(clause_op))
6904  {
6905  op_strategy = get_op_opfamily_strategy(clause_op,
6906  index->opfamily[indexcol]);
6907  Assert(op_strategy != 0); /* not a member of opfamily?? */
6908  if (op_strategy == BTEqualStrategyNumber)
6909  eqQualHere = true;
6910  }
6911 
6912  indexBoundQuals = lappend(indexBoundQuals, rinfo);
6913  }
6914  }
6915 
6916  /*
6917  * If index is unique and we found an '=' clause for each column, we can
6918  * just assume numIndexTuples = 1 and skip the expensive
6919  * clauselist_selectivity calculations. However, a ScalarArrayOp or
6920  * NullTest invalidates that theory, even though it sets eqQualHere.
6921  */
6922  if (index->unique &&
6923  indexcol == index->nkeycolumns - 1 &&
6924  eqQualHere &&
6925  !found_saop &&
6926  !found_is_null_op)
6927  numIndexTuples = 1.0;
6928  else
6929  {
6930  List *selectivityQuals;
6931  Selectivity btreeSelectivity;
6932 
6933  /*
6934  * If the index is partial, AND the index predicate with the
6935  * index-bound quals to produce a more accurate idea of the number of
6936  * rows covered by the bound conditions.
6937  */
6938  selectivityQuals = add_predicate_to_index_quals(index, indexBoundQuals);
6939 
6940  btreeSelectivity = clauselist_selectivity(root, selectivityQuals,
6941  index->rel->relid,
6942  JOIN_INNER,
6943  NULL);
6944  numIndexTuples = btreeSelectivity * index->rel->tuples;
6945 
6946  /*
6947  * btree automatically combines individual ScalarArrayOpExpr primitive
6948  * index scans whenever the tuples covered by the next set of array
6949  * keys are close to tuples covered by the current set. That puts a
6950  * natural ceiling on the worst case number of descents -- there
6951  * cannot possibly be more than one descent per leaf page scanned.
6952  *
6953  * Clamp the number of descents to at most 1/3 the number of index
6954  * pages. This avoids implausibly high estimates with low selectivity
6955  * paths, where scans usually require only one or two descents. This
6956  * is most likely to help when there are several SAOP clauses, where
6957  * naively accepting the total number of distinct combinations of
6958  * array elements as the number of descents would frequently lead to
6959  * wild overestimates.
6960  *
6961  * We somewhat arbitrarily don't just make the cutoff the total number
6962  * of leaf pages (we make it 1/3 the total number of pages instead) to
6963  * give the btree code credit for its ability to continue on the leaf
6964  * level with low selectivity scans.
6965  */
6966  num_sa_scans = Min(num_sa_scans, ceil(index->pages * 0.3333333));
6967  num_sa_scans = Max(num_sa_scans, 1);
6968 
6969  /*
6970  * As in genericcostestimate(), we have to adjust for any
6971  * ScalarArrayOpExpr quals included in indexBoundQuals, and then round
6972  * to integer.
6973  *
6974  * It is tempting to make genericcostestimate behave as if SAOP
6975  * clauses work in almost the same way as scalar operators during
6976  * btree scans, making the top-level scan look like a continuous scan
6977  * (as opposed to num_sa_scans-many primitive index scans). After
6978  * all, btree scans mostly work like that at runtime. However, such a
6979  * scheme would badly bias genericcostestimate's simplistic approach
6980  * to calculating numIndexPages through prorating.
6981  *
6982  * Stick with the approach taken by non-native SAOP scans for now.
6983  * genericcostestimate will use the Mackert-Lohman formula to
6984  * compensate for repeat page fetches, even though that definitely
6985  * won't happen during btree scans (not for leaf pages, at least).
6986  * We're usually very pessimistic about the number of primitive index
6987  * scans that will be required, but it's not clear how to do better.
6988  */
6989  numIndexTuples = rint(numIndexTuples / num_sa_scans);
6990  }
6991 
6992  /*
6993  * Now do generic index cost estimation.
6994  */
6995  costs.numIndexTuples = numIndexTuples;
6996  costs.num_sa_scans = num_sa_scans;
6997 
6998  genericcostestimate(root, path, loop_count, &costs);
6999 
7000  /*
7001  * Add a CPU-cost component to represent the costs of initial btree
7002  * descent. We don't charge any I/O cost for touching upper btree levels,
7003  * since they tend to stay in cache, but we still have to do about log2(N)
7004  * comparisons to descend a btree of N leaf tuples. We charge one
7005  * cpu_operator_cost per comparison.
7006  *
7007  * If there are ScalarArrayOpExprs, charge this once per estimated SA
7008  * index descent. The ones after the first one are not startup cost so
7009  * far as the overall plan goes, so just add them to "total" cost.
7010  */
7011  if (index->tuples > 1) /* avoid computing log(0) */
7012  {
7013  descentCost = ceil(log(index->tuples) / log(2.0)) * cpu_operator_cost;
7014  costs.indexStartupCost += descentCost;
7015  costs.indexTotalCost += costs.num_sa_scans * descentCost;
7016  }
7017 
7018  /*
7019  * Even though we're not charging I/O cost for touching upper btree pages,
7020  * it's still reasonable to charge some CPU cost per page descended
7021  * through. Moreover, if we had no such charge at all, bloated indexes
7022  * would appear to have the same search cost as unbloated ones, at least
7023  * in cases where only a single leaf page is expected to be visited. This
7024  * cost is somewhat arbitrarily set at 50x cpu_operator_cost per page
7025  * touched. The number of such pages is btree tree height plus one (ie,
7026  * we charge for the leaf page too). As above, charge once per estimated
7027  * SA index descent.
7028  */
7029  descentCost = (index->tree_height + 1) * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
7030  costs.indexStartupCost += descentCost;
7031  costs.indexTotalCost += costs.num_sa_scans * descentCost;
7032 
7033  /*
7034  * If we can get an estimate of the first column's ordering correlation C
7035  * from pg_statistic, estimate the index correlation as C for a
7036  * single-column index, or C * 0.75 for multiple columns. (The idea here
7037  * is that multiple columns dilute the importance of the first column's
7038  * ordering, but don't negate it entirely. Before 8.0 we divided the
7039  * correlation by the number of columns, but that seems too strong.)
7040  */
7041  if (index->indexkeys[0] != 0)
7042  {
7043  /* Simple variable --- look to stats for the underlying table */
7044  RangeTblEntry *rte = planner_rt_fetch(index->rel->relid, root);
7045 
7046  Assert(rte->rtekind == RTE_RELATION);
7047  relid = rte->relid;
7048  Assert(relid != InvalidOid);
7049  colnum = index->indexkeys[0];
7050 
7052  (*get_relation_stats_hook) (root, rte, colnum, &vardata))
7053  {
7054  /*
7055  * The hook took control of acquiring a stats tuple. If it did
7056  * supply a tuple, it'd better have supplied a freefunc.
7057  */
7058  if (HeapTupleIsValid(vardata.statsTuple) &&
7059  !vardata.freefunc)
7060  elog(ERROR, "no function provided to release variable stats with");
7061  }
7062  else
7063  {
7064  vardata.statsTuple = SearchSysCache3(STATRELATTINH,
7065  ObjectIdGetDatum(relid),
7066  Int16GetDatum(colnum),
7067  BoolGetDatum(rte->inh));
7068  vardata.freefunc = ReleaseSysCache;
7069  }
7070  }
7071  else
7072  {
7073  /* Expression --- maybe there are stats for the index itself */
7074  relid = index->indexoid;
7075  colnum = 1;
7076 
7077  if (get_index_stats_hook &&
7078  (*get_index_stats_hook) (root, relid, colnum, &vardata))
7079  {
7080  /*
7081  * The hook took control of acquiring a stats tuple. If it did
7082  * supply a tuple, it'd better have supplied a freefunc.
7083  */
7084  if (HeapTupleIsValid(vardata.statsTuple) &&
7085  !vardata.freefunc)
7086  elog(ERROR, "no function provided to release variable stats with");
7087  }
7088  else
7089  {
7090  vardata.statsTuple = SearchSysCache3(STATRELATTINH,
7091  ObjectIdGetDatum(relid),
7092  Int16GetDatum(colnum),
7093  BoolGetDatum(false));
7094  vardata.freefunc = ReleaseSysCache;
7095  }
7096  }
7097 
7098  if (HeapTupleIsValid(vardata.statsTuple))
7099  {
7100  Oid sortop;
7101  AttStatsSlot sslot;
7102 
7103  sortop = get_opfamily_member(index->opfamily[0],
7104  index->opcintype[0],
7105  index->opcintype[0],
7107  if (OidIsValid(sortop) &&
7108  get_attstatsslot(&sslot, vardata.statsTuple,
7109  STATISTIC_KIND_CORRELATION, sortop,
7111  {
7112  double varCorrelation;
7113 
7114  Assert(sslot.nnumbers == 1);
7115  varCorrelation = sslot.numbers[0];
7116 
7117  if (index->reverse_sort[0])
7118  varCorrelation = -varCorrelation;
7119 
7120  if (index->nkeycolumns > 1)
7121  costs.indexCorrelation = varCorrelation * 0.75;
7122  else
7123  costs.indexCorrelation = varCorrelation;
7124 
7125  free_attstatsslot(&sslot);
7126  }
7127  }
7128 
7129  ReleaseVariableStats(vardata);
7130 
7131  *indexStartupCost = costs.indexStartupCost;
7132  *indexTotalCost = costs.indexTotalCost;
7133  *indexSelectivity = costs.indexSelectivity;
7134  *indexCorrelation = costs.indexCorrelation;
7135  *indexPages = costs.numIndexPages;
7136 }
#define OidIsValid(objectId)
Definition: c.h:775
List * lappend(List *list, void *datum)
Definition: list.c:339
int get_op_opfamily_strategy(Oid opno, Oid opfamily)
Definition: lsyscache.c:83
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:166
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
#define nodeTag(nodeptr)
Definition: nodes.h:133
double Selectivity
Definition: nodes.h:250
#define NIL
Definition: pg_list.h:68
#define lsecond(l)
Definition: pg_list.h:183
#define linitial_oid(l)
Definition: pg_list.h:180
unsigned int Oid
Definition: postgres_ext.h:31
@ IS_NULL
Definition: primnodes.h:1948
#define DEFAULT_PAGE_CPU_MULTIPLIER
Definition: selfuncs.c:144
double estimate_array_length(PlannerInfo *root, Node *arrayexpr)
Definition: selfuncs.c:2136
void genericcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, GenericCosts *costs)
Definition: selfuncs.c:6552
List * add_predicate_to_index_quals(IndexOptInfo *index, List *indexQuals)
Definition: selfuncs.c:6775
#define BTLessStrategyNumber
Definition: stratnum.h:29
#define BTEqualStrategyNumber
Definition: stratnum.h:31
Selectivity indexSelectivity
Definition: selfuncs.h:127
Cost indexStartupCost
Definition: selfuncs.h:125
double indexCorrelation
Definition: selfuncs.h:128
double num_sa_scans
Definition: selfuncs.h:134
Cost indexTotalCost
Definition: selfuncs.h:126
double numIndexPages
Definition: selfuncs.h:131
double numIndexTuples
Definition: selfuncs.h:132
List * indexquals
Definition: pathnodes.h:1766
Definition: nodes.h:129
NullTestType nulltesttype
Definition: primnodes.h:1955
Oid opno
Definition: primnodes.h:818
Expr * clause
Definition: pathnodes.h:2571

References add_predicate_to_index_quals(), ScalarArrayOpExpr::args, Assert, ATTSTATSSLOT_NUMBERS, BoolGetDatum(), BTEqualStrategyNumber, BTLessStrategyNumber, RestrictInfo::clause, clauselist_selectivity(), cpu_operator_cost, DEFAULT_PAGE_CPU_MULTIPLIER, elog, ERROR, estimate_array_length(), free_attstatsslot(), VariableStatData::freefunc, genericcostestimate(), get_attstatsslot(), get_index_stats_hook, get_op_opfamily_strategy(), get_opfamily_member(), get_relation_stats_hook, HeapTupleIsValid, IndexPath::indexclauses, IndexClause::indexcol, GenericCosts::indexCorrelation, IndexPath::indexinfo, IndexClause::indexquals, GenericCosts::indexSelectivity, GenericCosts::indexStartupCost, GenericCosts::indexTotalCost, RangeTblEntry::inh, Int16GetDatum(), InvalidOid, IS_NULL, IsA, JOIN_INNER, lappend(), lfirst_node, linitial_oid, lsecond, Max, Min, NIL, AttStatsSlot::nnumbers, nodeTag, NullTest::nulltesttype, GenericCosts::num_sa_scans, AttStatsSlot::numbers, GenericCosts::numIndexPages, GenericCosts::numIndexTuples, ObjectIdGetDatum(), OidIsValid, OpExpr::opno, ScalarArrayOpExpr::opno, planner_rt_fetch, ReleaseSysCache(), ReleaseVariableStats, RangeTblEntry::relid, root, RTE_RELATION, RangeTblEntry::rtekind, SearchSysCache3(), and VariableStatData::statsTuple.

Referenced by bthandler().

◆ gincostestimate()

void gincostestimate ( struct PlannerInfo root,
struct IndexPath path,
double  loop_count,
Cost indexStartupCost,
Cost indexTotalCost,
Selectivity indexSelectivity,
double *  indexCorrelation,
double *  indexPages 
)

Definition at line 7591 of file selfuncs.c.

7595 {
7596  IndexOptInfo *index = path->indexinfo;
7597  List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
7598  List *selectivityQuals;
7599  double numPages = index->pages,
7600  numTuples = index->tuples;
7601  double numEntryPages,
7602  numDataPages,
7603  numPendingPages,
7604  numEntries;
7605  GinQualCounts counts;
7606  bool matchPossible;
7607  bool fullIndexScan;
7608  double partialScale;
7609  double entryPagesFetched,
7610  dataPagesFetched,
7611  dataPagesFetchedBySel;
7612  double qual_op_cost,
7613  qual_arg_cost,
7614  spc_random_page_cost,
7615  outer_scans;
7616  Cost descentCost;
7617  Relation indexRel;
7618  GinStatsData ginStats;
7619  ListCell *lc;
7620  int i;
7621 
7622  /*
7623  * Obtain statistical information from the meta page, if possible. Else
7624  * set ginStats to zeroes, and we'll cope below.
7625  */
7626  if (!index->hypothetical)
7627  {
7628  /* Lock should have already been obtained in plancat.c */
7629  indexRel = index_open(index->indexoid, NoLock);
7630  ginGetStats(indexRel, &ginStats);
7631  index_close(indexRel, NoLock);
7632  }
7633  else
7634  {
7635  memset(&ginStats, 0, sizeof(ginStats));
7636  }
7637 
7638  /*
7639  * Assuming we got valid (nonzero) stats at all, nPendingPages can be
7640  * trusted, but the other fields are data as of the last VACUUM. We can
7641  * scale them up to account for growth since then, but that method only
7642  * goes so far; in the worst case, the stats might be for a completely
7643  * empty index, and scaling them will produce pretty bogus numbers.
7644  * Somewhat arbitrarily, set the cutoff for doing scaling at 4X growth; if
7645  * it's grown more than that, fall back to estimating things only from the
7646  * assumed-accurate index size. But we'll trust nPendingPages in any case
7647  * so long as it's not clearly insane, ie, more than the index size.
7648  */
7649  if (ginStats.nPendingPages < numPages)
7650  numPendingPages = ginStats.nPendingPages;
7651  else
7652  numPendingPages = 0;
7653 
7654  if (numPages > 0 && ginStats.nTotalPages <= numPages &&
7655  ginStats.nTotalPages > numPages / 4 &&
7656  ginStats.nEntryPages > 0 && ginStats.nEntries > 0)
7657  {
7658  /*
7659  * OK, the stats seem close enough to sane to be trusted. But we
7660  * still need to scale them by the ratio numPages / nTotalPages to
7661  * account for growth since the last VACUUM.
7662  */
7663  double scale = numPages / ginStats.nTotalPages;
7664 
7665  numEntryPages = ceil(ginStats.nEntryPages * scale);
7666  numDataPages = ceil(ginStats.nDataPages * scale);
7667  numEntries = ceil(ginStats.nEntries * scale);
7668  /* ensure we didn't round up too much */
7669  numEntryPages = Min(numEntryPages, numPages - numPendingPages);
7670  numDataPages = Min(numDataPages,
7671  numPages - numPendingPages - numEntryPages);
7672  }
7673  else
7674  {
7675  /*
7676  * We might get here because it's a hypothetical index, or an index
7677  * created pre-9.1 and never vacuumed since upgrading (in which case
7678  * its stats would read as zeroes), or just because it's grown too
7679  * much since the last VACUUM for us to put our faith in scaling.
7680  *
7681  * Invent some plausible internal statistics based on the index page
7682  * count (and clamp that to at least 10 pages, just in case). We
7683  * estimate that 90% of the index is entry pages, and the rest is data
7684  * pages. Estimate 100 entries per entry page; this is rather bogus
7685  * since it'll depend on the size of the keys, but it's more robust
7686  * than trying to predict the number of entries per heap tuple.
7687  */
7688  numPages = Max(numPages, 10);
7689  numEntryPages = floor((numPages - numPendingPages) * 0.90);
7690  numDataPages = numPages - numPendingPages - numEntryPages;
7691  numEntries = floor(numEntryPages * 100);
7692  }
7693 
7694  /* In an empty index, numEntries could be zero. Avoid divide-by-zero */
7695  if (numEntries < 1)
7696  numEntries = 1;
7697 
7698  /*
7699  * If the index is partial, AND the index predicate with the index-bound
7700  * quals to produce a more accurate idea of the number of rows covered by
7701  * the bound conditions.
7702  */
7703  selectivityQuals = add_predicate_to_index_quals(index, indexQuals);
7704 
7705  /* Estimate the fraction of main-table tuples that will be visited */
7706  *indexSelectivity = clauselist_selectivity(root, selectivityQuals,
7707  index->rel->relid,
7708  JOIN_INNER,
7709  NULL);
7710 
7711  /* fetch estimated page cost for tablespace containing index */
7712  get_tablespace_page_costs(index->reltablespace,
7713  &spc_random_page_cost,
7714  NULL);
7715 
7716  /*
7717  * Generic assumption about index correlation: there isn't any.
7718  */
7719  *indexCorrelation = 0.0;
7720 
7721  /*
7722  * Examine quals to estimate number of search entries & partial matches
7723  */
7724  memset(&counts, 0, sizeof(counts));
7725  counts.arrayScans = 1;
7726  matchPossible = true;
7727 
7728  foreach(lc, path->indexclauses)
7729  {
7730  IndexClause *iclause = lfirst_node(IndexClause, lc);
7731  ListCell *lc2;
7732 
7733  foreach(lc2, iclause->indexquals)
7734  {
7735  RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
7736  Expr *clause = rinfo->clause;
7737 
7738  if (IsA(clause, OpExpr))
7739  {
7740  matchPossible = gincost_opexpr(root,
7741  index,
7742  iclause->indexcol,
7743  (OpExpr *) clause,
7744  &counts);
7745  if (!matchPossible)
7746  break;
7747  }
7748  else if (IsA(clause, ScalarArrayOpExpr))
7749  {
7750  matchPossible = gincost_scalararrayopexpr(root,
7751  index,
7752  iclause->indexcol,
7753  (ScalarArrayOpExpr *) clause,
7754  numEntries,
7755  &counts);
7756  if (!matchPossible)
7757  break;
7758  }
7759  else
7760  {
7761  /* shouldn't be anything else for a GIN index */
7762  elog(ERROR, "unsupported GIN indexqual type: %d",
7763  (int) nodeTag(clause));
7764  }
7765  }
7766  }
7767 
7768  /* Fall out if there were any provably-unsatisfiable quals */
7769  if (!matchPossible)
7770  {
7771  *indexStartupCost = 0;
7772  *indexTotalCost = 0;
7773  *indexSelectivity = 0;
7774  return;
7775  }
7776 
7777  /*
7778  * If attribute has a full scan and at the same time doesn't have normal
7779  * scan, then we'll have to scan all non-null entries of that attribute.
7780  * Currently, we don't have per-attribute statistics for GIN. Thus, we
7781  * must assume the whole GIN index has to be scanned in this case.
7782  */
7783  fullIndexScan = false;
7784  for (i = 0; i < index->nkeycolumns; i++)
7785  {
7786  if (counts.attHasFullScan[i] && !counts.attHasNormalScan[i])
7787  {
7788  fullIndexScan = true;
7789  break;
7790  }
7791  }
7792 
7793  if (fullIndexScan || indexQuals == NIL)
7794  {
7795  /*
7796  * Full index scan will be required. We treat this as if every key in
7797  * the index had been listed in the query; is that reasonable?
7798  */
7799  counts.partialEntries = 0;
7800  counts.exactEntries = numEntries;
7801  counts.searchEntries = numEntries;
7802  }
7803 
7804  /* Will we have more than one iteration of a nestloop scan? */
7805  outer_scans = loop_count;
7806 
7807  /*
7808  * Compute cost to begin scan, first of all, pay attention to pending
7809  * list.
7810  */
7811  entryPagesFetched = numPendingPages;
7812 
7813  /*
7814  * Estimate number of entry pages read. We need to do
7815  * counts.searchEntries searches. Use a power function as it should be,
7816  * but tuples on leaf pages usually is much greater. Here we include all
7817  * searches in entry tree, including search of first entry in partial
7818  * match algorithm
7819  */
7820  entryPagesFetched += ceil(counts.searchEntries * rint(pow(numEntryPages, 0.15)));
7821 
7822  /*
7823  * Add an estimate of entry pages read by partial match algorithm. It's a
7824  * scan over leaf pages in entry tree. We haven't any useful stats here,
7825  * so estimate it as proportion. Because counts.partialEntries is really
7826  * pretty bogus (see code above), it's possible that it is more than
7827  * numEntries; clamp the proportion to ensure sanity.
7828  */
7829  partialScale = counts.partialEntries / numEntries;
7830  partialScale = Min(partialScale, 1.0);
7831 
7832  entryPagesFetched += ceil(numEntryPages * partialScale);
7833 
7834  /*
7835  * Partial match algorithm reads all data pages before doing actual scan,
7836  * so it's a startup cost. Again, we haven't any useful stats here, so
7837  * estimate it as proportion.
7838  */
7839  dataPagesFetched = ceil(numDataPages * partialScale);
7840 
7841  *indexStartupCost = 0;
7842  *indexTotalCost = 0;
7843 
7844  /*
7845  * Add a CPU-cost component to represent the costs of initial entry btree
7846  * descent. We don't charge any I/O cost for touching upper btree levels,
7847  * since they tend to stay in cache, but we still have to do about log2(N)
7848  * comparisons to descend a btree of N leaf tuples. We charge one
7849  * cpu_operator_cost per comparison.
7850  *
7851  * If there are ScalarArrayOpExprs, charge this once per SA scan. The
7852  * ones after the first one are not startup cost so far as the overall
7853  * plan is concerned, so add them only to "total" cost.
7854  */
7855  if (numEntries > 1) /* avoid computing log(0) */
7856  {
7857  descentCost = ceil(log(numEntries) / log(2.0)) * cpu_operator_cost;
7858  *indexStartupCost += descentCost * counts.searchEntries;
7859  *indexTotalCost += counts.arrayScans * descentCost * counts.searchEntries;
7860  }
7861 
7862  /*
7863  * Add a cpu cost per entry-page fetched. This is not amortized over a
7864  * loop.
7865  */
7866  *indexStartupCost += entryPagesFetched * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
7867  *indexTotalCost += entryPagesFetched * counts.arrayScans * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
7868 
7869  /*
7870  * Add a cpu cost per data-page fetched. This is also not amortized over a
7871  * loop. Since those are the data pages from the partial match algorithm,
7872  * charge them as startup cost.
7873  */
7874  *indexStartupCost += DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost * dataPagesFetched;
7875 
7876  /*
7877  * Since we add the startup cost to the total cost later on, remove the
7878  * initial arrayscan from the total.
7879  */
7880  *indexTotalCost += dataPagesFetched * (counts.arrayScans - 1) * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
7881 
7882  /*
7883  * Calculate cache effects if more than one scan due to nestloops or array
7884  * quals. The result is pro-rated per nestloop scan, but the array qual
7885  * factor shouldn't be pro-rated (compare genericcostestimate).
7886  */
7887  if (outer_scans > 1 || counts.arrayScans > 1)
7888  {
7889  entryPagesFetched *= outer_scans * counts.arrayScans;
7890  entryPagesFetched = index_pages_fetched(entryPagesFetched,
7891  (BlockNumber) numEntryPages,
7892  numEntryPages, root);
7893  entryPagesFetched /= outer_scans;
7894  dataPagesFetched *= outer_scans * counts.arrayScans;
7895  dataPagesFetched = index_pages_fetched(dataPagesFetched,
7896  (BlockNumber) numDataPages,
7897  numDataPages, root);
7898  dataPagesFetched /= outer_scans;
7899  }
7900 
7901  /*
7902  * Here we use random page cost because logically-close pages could be far
7903  * apart on disk.
7904  */
7905  *indexStartupCost += (entryPagesFetched + dataPagesFetched) * spc_random_page_cost;
7906 
7907  /*
7908  * Now compute the number of data pages fetched during the scan.
7909  *
7910  * We assume every entry to have the same number of items, and that there
7911  * is no overlap between them. (XXX: tsvector and array opclasses collect
7912  * statistics on the frequency of individual keys; it would be nice to use
7913  * those here.)
7914  */
7915  dataPagesFetched = ceil(numDataPages * counts.exactEntries / numEntries);
7916 
7917  /*
7918  * If there is a lot of overlap among the entries, in particular if one of
7919  * the entries is very frequent, the above calculation can grossly
7920  * under-estimate. As a simple cross-check, calculate a lower bound based
7921  * on the overall selectivity of the quals. At a minimum, we must read
7922  * one item pointer for each matching entry.
7923  *
7924  * The width of each item pointer varies, based on the level of
7925  * compression. We don't have statistics on that, but an average of
7926  * around 3 bytes per item is fairly typical.
7927  */
7928  dataPagesFetchedBySel = ceil(*indexSelectivity *
7929  (numTuples / (BLCKSZ / 3)));
7930  if (dataPagesFetchedBySel > dataPagesFetched)
7931  dataPagesFetched = dataPagesFetchedBySel;
7932 
7933  /* Add one page cpu-cost to the startup cost */
7934  *indexStartupCost += DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost * counts.searchEntries;
7935 
7936  /*
7937  * Add once again a CPU-cost for those data pages, before amortizing for
7938  * cache.
7939  */
7940  *indexTotalCost += dataPagesFetched * counts.arrayScans * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
7941 
7942  /* Account for cache effects, the same as above */
7943  if (outer_scans > 1 || counts.arrayScans > 1)
7944  {
7945  dataPagesFetched *= outer_scans * counts.arrayScans;
7946  dataPagesFetched = index_pages_fetched(dataPagesFetched,
7947  (BlockNumber) numDataPages,
7948  numDataPages, root);
7949  dataPagesFetched /= outer_scans;
7950  }
7951 
7952  /* And apply random_page_cost as the cost per page */
7953  *indexTotalCost += *indexStartupCost +
7954  dataPagesFetched * spc_random_page_cost;
7955 
7956  /*
7957  * Add on index qual eval costs, much as in genericcostestimate. We charge
7958  * cpu but we can disregard indexorderbys, since GIN doesn't support
7959  * those.
7960  */
7961  qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
7962  qual_op_cost = cpu_operator_cost * list_length(indexQuals);
7963 
7964  *indexStartupCost += qual_arg_cost;
7965  *indexTotalCost += qual_arg_cost;
7966 
7967  /*
7968  * Add a cpu cost per search entry, corresponding to the actual visited
7969  * entries.
7970  */
7971  *indexTotalCost += (counts.searchEntries * counts.arrayScans) * (qual_op_cost);
7972  /* Now add a cpu cost per tuple in the posting lists / trees */
7973  *indexTotalCost += (numTuples * *indexSelectivity) * (cpu_index_tuple_cost);
7974  *indexPages = dataPagesFetched;
7975 }
uint32 BlockNumber
Definition: block.h:31
double index_pages_fetched(double tuples_fetched, BlockNumber pages, double index_pages, PlannerInfo *root)
Definition: costsize.c:908
double cpu_index_tuple_cost
Definition: costsize.c:133
void ginGetStats(Relation index, GinStatsData *stats)
Definition: ginutil.c:624
int i
Definition: isn.c:73
static int list_length(const List *l)
Definition: pg_list.h:152
static int scale
Definition: pgbench.c:181
static bool gincost_scalararrayopexpr(PlannerInfo *root, IndexOptInfo *index, int indexcol, ScalarArrayOpExpr *clause, double numIndexEntries, GinQualCounts *counts)
Definition: selfuncs.c:7475
static bool gincost_opexpr(PlannerInfo *root, IndexOptInfo *index, int indexcol, OpExpr *clause, GinQualCounts *counts)
Definition: selfuncs.c:7425
bool attHasNormalScan[INDEX_MAX_KEYS]
Definition: selfuncs.c:7298
double exactEntries
Definition: selfuncs.c:7300
double arrayScans
Definition: selfuncs.c:7302
double partialEntries
Definition: selfuncs.c:7299
bool attHasFullScan[INDEX_MAX_KEYS]
Definition: selfuncs.c:7297
double searchEntries
Definition: selfuncs.c:7301
BlockNumber nDataPages
Definition: gin.h:47
BlockNumber nPendingPages
Definition: gin.h:44
BlockNumber nEntryPages
Definition: gin.h:46
int64 nEntries
Definition: gin.h:48
BlockNumber nTotalPages
Definition: gin.h:45

References add_predicate_to_index_quals(), GinQualCounts::arrayScans, GinQualCounts::attHasFullScan, GinQualCounts::attHasNormalScan, RestrictInfo::clause, clauselist_selectivity(), cpu_index_tuple_cost, cpu_operator_cost, DEFAULT_PAGE_CPU_MULTIPLIER, elog, ERROR, GinQualCounts::exactEntries, get_quals_from_indexclauses(), get_tablespace_page_costs(), gincost_opexpr(), gincost_scalararrayopexpr(), ginGetStats(), i, index_close(), index_open(), index_other_operands_eval_cost(), index_pages_fetched(), IndexPath::indexclauses, IndexClause::indexcol, IndexPath::indexinfo, IndexClause::indexquals, IsA, JOIN_INNER, lfirst_node, list_length(), Max, Min, GinStatsData::nDataPages, GinStatsData::nEntries, GinStatsData::nEntryPages, NIL, nodeTag, NoLock, GinStatsData::nPendingPages, GinStatsData::nTotalPages, GinQualCounts::partialEntries, root, scale, and GinQualCounts::searchEntries.

Referenced by ginhandler().

◆ gistcostestimate()

void gistcostestimate ( struct PlannerInfo root,
struct IndexPath path,
double  loop_count,
Cost indexStartupCost,
Cost indexTotalCost,
Selectivity indexSelectivity,
double *  indexCorrelation,
double *  indexPages 
)

Definition at line 7181 of file selfuncs.c.

7185 {
7186  IndexOptInfo *index = path->indexinfo;
7187  GenericCosts costs = {0};
7188  Cost descentCost;
7189 
7190  genericcostestimate(root, path, loop_count, &costs);
7191 
7192  /*
7193  * We model index descent costs similarly to those for btree, but to do
7194  * that we first need an idea of the tree height. We somewhat arbitrarily
7195  * assume that the fanout is 100, meaning the tree height is at most
7196  * log100(index->pages).
7197  *
7198  * Although this computation isn't really expensive enough to require
7199  * caching, we might as well use index->tree_height to cache it.
7200  */
7201  if (index->tree_height < 0) /* unknown? */
7202  {
7203  if (index->pages > 1) /* avoid computing log(0) */
7204  index->tree_height = (int) (log(index->pages) / log(100.0));
7205  else
7206  index->tree_height = 0;
7207  }
7208 
7209  /*
7210  * Add a CPU-cost component to represent the costs of initial descent. We
7211  * just use log(N) here not log2(N) since the branching factor isn't
7212  * necessarily two anyway. As for btree, charge once per SA scan.
7213  */
7214  if (index->tuples > 1) /* avoid computing log(0) */
7215  {
7216  descentCost = ceil(log(index->tuples)) * cpu_operator_cost;
7217  costs.indexStartupCost += descentCost;
7218  costs.indexTotalCost += costs.num_sa_scans * descentCost;
7219  }
7220 
7221  /*
7222  * Likewise add a per-page charge, calculated the same as for btrees.
7223  */
7224  descentCost = (index->tree_height + 1) * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
7225  costs.indexStartupCost += descentCost;
7226  costs.indexTotalCost += costs.num_sa_scans * descentCost;
7227 
7228  *indexStartupCost = costs.indexStartupCost;
7229  *indexTotalCost = costs.indexTotalCost;
7230  *indexSelectivity = costs.indexSelectivity;
7231  *indexCorrelation = costs.indexCorrelation;
7232  *indexPages = costs.numIndexPages;
7233 }

References cpu_operator_cost, DEFAULT_PAGE_CPU_MULTIPLIER, genericcostestimate(), GenericCosts::indexCorrelation, IndexPath::indexinfo, GenericCosts::indexSelectivity, GenericCosts::indexStartupCost, GenericCosts::indexTotalCost, GenericCosts::num_sa_scans, GenericCosts::numIndexPages, and root.

Referenced by gisthandler().

◆ hashcostestimate()

void hashcostestimate ( struct PlannerInfo root,
struct IndexPath path,
double  loop_count,
Cost indexStartupCost,
Cost indexTotalCost,
Selectivity indexSelectivity,
double *  indexCorrelation,
double *  indexPages 
)

Definition at line 7139 of file selfuncs.c.

7143 {
7144  GenericCosts costs = {0};
7145 
7146  genericcostestimate(root, path, loop_count, &costs);
7147 
7148  /*
7149  * A hash index has no descent costs as such, since the index AM can go
7150  * directly to the target bucket after computing the hash value. There
7151  * are a couple of other hash-specific costs that we could conceivably add
7152  * here, though:
7153  *
7154  * Ideally we'd charge spc_random_page_cost for each page in the target
7155  * bucket, not just the numIndexPages pages that genericcostestimate
7156  * thought we'd visit. However in most cases we don't know which bucket
7157  * that will be. There's no point in considering the average bucket size
7158  * because the hash AM makes sure that's always one page.
7159  *
7160  * Likewise, we could consider charging some CPU for each index tuple in
7161  * the bucket, if we knew how many there were. But the per-tuple cost is
7162  * just a hash value comparison, not a general datatype-dependent
7163  * comparison, so any such charge ought to be quite a bit less than
7164  * cpu_operator_cost; which makes it probably not worth worrying about.
7165  *
7166  * A bigger issue is that chance hash-value collisions will result in
7167  * wasted probes into the heap. We don't currently attempt to model this
7168  * cost on the grounds that it's rare, but maybe it's not rare enough.
7169  * (Any fix for this ought to consider the generic lossy-operator problem,
7170  * though; it's not entirely hash-specific.)
7171  */
7172 
7173  *indexStartupCost = costs.indexStartupCost;
7174  *indexTotalCost = costs.indexTotalCost;
7175  *indexSelectivity = costs.indexSelectivity;
7176  *indexCorrelation = costs.indexCorrelation;
7177  *indexPages = costs.numIndexPages;
7178 }

References genericcostestimate(), GenericCosts::indexCorrelation, GenericCosts::indexSelectivity, GenericCosts::indexStartupCost, GenericCosts::indexTotalCost, GenericCosts::numIndexPages, and root.

Referenced by hashhandler().

◆ spgcostestimate()

void spgcostestimate ( struct PlannerInfo root,
struct IndexPath path,
double  loop_count,
Cost indexStartupCost,
Cost indexTotalCost,
Selectivity indexSelectivity,
double *  indexCorrelation,
double *  indexPages 
)

Definition at line 7236 of file selfuncs.c.

7240 {
7241  IndexOptInfo *index = path->indexinfo;
7242  GenericCosts costs = {0};
7243  Cost descentCost;
7244 
7245  genericcostestimate(root, path, loop_count, &costs);
7246 
7247  /*
7248  * We model index descent costs similarly to those for btree, but to do
7249  * that we first need an idea of the tree height. We somewhat arbitrarily
7250  * assume that the fanout is 100, meaning the tree height is at most
7251  * log100(index->pages).
7252  *
7253  * Although this computation isn't really expensive enough to require
7254  * caching, we might as well use index->tree_height to cache it.
7255  */
7256  if (index->tree_height < 0) /* unknown? */
7257  {
7258  if (index->pages > 1) /* avoid computing log(0) */
7259  index->tree_height = (int) (log(index->pages) / log(100.0));
7260  else
7261  index->tree_height = 0;
7262  }
7263 
7264  /*
7265  * Add a CPU-cost component to represent the costs of initial descent. We
7266  * just use log(N) here not log2(N) since the branching factor isn't
7267  * necessarily two anyway. As for btree, charge once per SA scan.
7268  */
7269  if (index->tuples > 1) /* avoid computing log(0) */
7270  {
7271  descentCost = ceil(log(index->tuples)) * cpu_operator_cost;
7272  costs.indexStartupCost += descentCost;
7273  costs.indexTotalCost += costs.num_sa_scans * descentCost;
7274  }
7275 
7276  /*
7277  * Likewise add a per-page charge, calculated the same as for btrees.
7278  */
7279  descentCost = (index->tree_height + 1) * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
7280  costs.indexStartupCost += descentCost;
7281  costs.indexTotalCost += costs.num_sa_scans * descentCost;
7282 
7283  *indexStartupCost = costs.indexStartupCost;
7284  *indexTotalCost = costs.indexTotalCost;
7285  *indexSelectivity = costs.indexSelectivity;
7286  *indexCorrelation = costs.indexCorrelation;
7287  *indexPages = costs.numIndexPages;
7288 }

References cpu_operator_cost, DEFAULT_PAGE_CPU_MULTIPLIER, genericcostestimate(), GenericCosts::indexCorrelation, IndexPath::indexinfo, GenericCosts::indexSelectivity, GenericCosts::indexStartupCost, GenericCosts::indexTotalCost, GenericCosts::num_sa_scans, GenericCosts::numIndexPages, and root.

Referenced by spghandler().