PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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 8531 of file selfuncs.c.

8535{
8536 IndexOptInfo *index = path->indexinfo;
8537 List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
8538 double numPages = index->pages;
8539 RelOptInfo *baserel = index->rel;
8540 RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
8541 Cost spc_seq_page_cost;
8542 Cost spc_random_page_cost;
8543 double qual_arg_cost;
8544 double qualSelectivity;
8545 BrinStatsData statsData;
8546 double indexRanges;
8547 double minimalRanges;
8548 double estimatedRanges;
8549 double selec;
8550 Relation indexRel;
8551 ListCell *l;
8552 VariableStatData vardata;
8553
8554 Assert(rte->rtekind == RTE_RELATION);
8555
8556 /* fetch estimated page cost for the tablespace containing the index */
8557 get_tablespace_page_costs(index->reltablespace,
8558 &spc_random_page_cost,
8559 &spc_seq_page_cost);
8560
8561 /*
8562 * Obtain some data from the index itself, if possible. Otherwise invent
8563 * some plausible internal statistics based on the relation page count.
8564 */
8565 if (!index->hypothetical)
8566 {
8567 /*
8568 * A lock should have already been obtained on the index in plancat.c.
8569 */
8570 indexRel = index_open(index->indexoid, NoLock);
8571 brinGetStats(indexRel, &statsData);
8572 index_close(indexRel, NoLock);
8573
8574 /* work out the actual number of ranges in the index */
8575 indexRanges = Max(ceil((double) baserel->pages /
8576 statsData.pagesPerRange), 1.0);
8577 }
8578 else
8579 {
8580 /*
8581 * Assume default number of pages per range, and estimate the number
8582 * of ranges based on that.
8583 */
8584 indexRanges = Max(ceil((double) baserel->pages /
8586
8588 statsData.revmapNumPages = (indexRanges / REVMAP_PAGE_MAXITEMS) + 1;
8589 }
8590
8591 /*
8592 * Compute index correlation
8593 *
8594 * Because we can use all index quals equally when scanning, we can use
8595 * the largest correlation (in absolute value) among columns used by the
8596 * query. Start at zero, the worst possible case. If we cannot find any
8597 * correlation statistics, we will keep it as 0.
8598 */
8599 *indexCorrelation = 0;
8600
8601 foreach(l, path->indexclauses)
8602 {
8603 IndexClause *iclause = lfirst_node(IndexClause, l);
8604 AttrNumber attnum = index->indexkeys[iclause->indexcol];
8605
8606 /* attempt to lookup stats in relation for this index column */
8607 if (attnum != 0)
8608 {
8609 /* Simple variable -- look to stats for the underlying table */
8611 (*get_relation_stats_hook) (root, rte, attnum, &vardata))
8612 {
8613 /*
8614 * The hook took control of acquiring a stats tuple. If it
8615 * did supply a tuple, it'd better have supplied a freefunc.
8616 */
8617 if (HeapTupleIsValid(vardata.statsTuple) && !vardata.freefunc)
8618 elog(ERROR,
8619 "no function provided to release variable stats with");
8620 }
8621 else
8622 {
8623 vardata.statsTuple =
8624 SearchSysCache3(STATRELATTINH,
8625 ObjectIdGetDatum(rte->relid),
8627 BoolGetDatum(false));
8628 vardata.freefunc = ReleaseSysCache;
8629 }
8630 }
8631 else
8632 {
8633 /*
8634 * Looks like we've found an expression column in the index. Let's
8635 * see if there's any stats for it.
8636 */
8637
8638 /* get the attnum from the 0-based index. */
8639 attnum = iclause->indexcol + 1;
8640
8642 (*get_index_stats_hook) (root, index->indexoid, attnum, &vardata))
8643 {
8644 /*
8645 * The hook took control of acquiring a stats tuple. If it
8646 * did supply a tuple, it'd better have supplied a freefunc.
8647 */
8648 if (HeapTupleIsValid(vardata.statsTuple) &&
8649 !vardata.freefunc)
8650 elog(ERROR, "no function provided to release variable stats with");
8651 }
8652 else
8653 {
8654 vardata.statsTuple = SearchSysCache3(STATRELATTINH,
8655 ObjectIdGetDatum(index->indexoid),
8657 BoolGetDatum(false));
8658 vardata.freefunc = ReleaseSysCache;
8659 }
8660 }
8661
8662 if (HeapTupleIsValid(vardata.statsTuple))
8663 {
8664 AttStatsSlot sslot;
8665
8666 if (get_attstatsslot(&sslot, vardata.statsTuple,
8667 STATISTIC_KIND_CORRELATION, InvalidOid,
8669 {
8670 double varCorrelation = 0.0;
8671
8672 if (sslot.nnumbers > 0)
8673 varCorrelation = fabs(sslot.numbers[0]);
8674
8675 if (varCorrelation > *indexCorrelation)
8676 *indexCorrelation = varCorrelation;
8677
8678 free_attstatsslot(&sslot);
8679 }
8680 }
8681
8682 ReleaseVariableStats(vardata);
8683 }
8684
8685 qualSelectivity = clauselist_selectivity(root, indexQuals,
8686 baserel->relid,
8687 JOIN_INNER, NULL);
8688
8689 /*
8690 * Now calculate the minimum possible ranges we could match with if all of
8691 * the rows were in the perfect order in the table's heap.
8692 */
8693 minimalRanges = ceil(indexRanges * qualSelectivity);
8694
8695 /*
8696 * Now estimate the number of ranges that we'll touch by using the
8697 * indexCorrelation from the stats. Careful not to divide by zero (note
8698 * we're using the absolute value of the correlation).
8699 */
8700 if (*indexCorrelation < 1.0e-10)
8701 estimatedRanges = indexRanges;
8702 else
8703 estimatedRanges = Min(minimalRanges / *indexCorrelation, indexRanges);
8704
8705 /* we expect to visit this portion of the table */
8706 selec = estimatedRanges / indexRanges;
8707
8708 CLAMP_PROBABILITY(selec);
8709
8710 *indexSelectivity = selec;
8711
8712 /*
8713 * Compute the index qual costs, much as in genericcostestimate, to add to
8714 * the index costs. We can disregard indexorderbys, since BRIN doesn't
8715 * support those.
8716 */
8717 qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
8718
8719 /*
8720 * Compute the startup cost as the cost to read the whole revmap
8721 * sequentially, including the cost to execute the index quals.
8722 */
8723 *indexStartupCost =
8724 spc_seq_page_cost * statsData.revmapNumPages * loop_count;
8725 *indexStartupCost += qual_arg_cost;
8726
8727 /*
8728 * To read a BRIN index there might be a bit of back and forth over
8729 * regular pages, as revmap might point to them out of sequential order;
8730 * calculate the total cost as reading the whole index in random order.
8731 */
8732 *indexTotalCost = *indexStartupCost +
8733 spc_random_page_cost * (numPages - statsData.revmapNumPages) * loop_count;
8734
8735 /*
8736 * Charge a small amount per range tuple which we expect to match to. This
8737 * is meant to reflect the costs of manipulating the bitmap. The BRIN scan
8738 * will set a bit for each page in the range when we find a matching
8739 * range, so we must multiply the charge by the number of pages in the
8740 * range.
8741 */
8742 *indexTotalCost += 0.1 * cpu_operator_cost * estimatedRanges *
8743 statsData.pagesPerRange;
8744
8745 *indexPages = index->pages;
8746}
int16 AttrNumber
Definition: attnum.h:21
void brinGetStats(Relation index, BrinStatsData *stats)
Definition: brin.c:1648
#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:975
#define Max(x, y)
Definition: c.h:969
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
Assert(PointerIsAligned(start, uint64))
#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:3484
bool get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple, int reqkind, Oid reqop, int flags)
Definition: lsyscache.c:3374
#define ATTSTATSSLOT_NUMBERS
Definition: lsyscache.h:44
double Cost
Definition: nodes.h:257
@ JOIN_INNER
Definition: nodes.h:299
@ RTE_RELATION
Definition: parsenodes.h:1026
#define planner_rt_fetch(rti, root)
Definition: pathnodes.h:597
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:177
static Datum BoolGetDatum(bool X)
Definition: postgres.h:107
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
#define InvalidOid
Definition: postgres_ext.h:35
tree ctl root
Definition: radixtree.h:1857
List * get_quals_from_indexclauses(List *indexclauses)
Definition: selfuncs.c:6851
get_index_stats_hook_type get_index_stats_hook
Definition: selfuncs.c:149
Cost index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
Definition: selfuncs.c:6881
get_relation_stats_hook_type get_relation_stats_hook
Definition: selfuncs.c:148
#define ReleaseVariableStats(vardata)
Definition: selfuncs.h:100
#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:57
int nnumbers
Definition: lsyscache.h:58
BlockNumber revmapNumPages
Definition: brin.h:35
BlockNumber pagesPerRange
Definition: brin.h:34
AttrNumber indexcol
Definition: pathnodes.h:1898
List * indexclauses
Definition: pathnodes.h:1848
IndexOptInfo * indexinfo
Definition: pathnodes.h:1847
Definition: pg_list.h:54
RTEKind rtekind
Definition: parsenodes.h:1061
Index relid
Definition: pathnodes.h:945
BlockNumber pages
Definition: pathnodes.h:975
HeapTuple statsTuple
Definition: selfuncs.h:89
void(* freefunc)(HeapTuple tuple)
Definition: selfuncs.h:91
Definition: type.h:96
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache3(int cacheId, Datum key1, Datum key2, Datum key3)
Definition: syscache.c:243

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, 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 7226 of file selfuncs.c.

7230{
7231 IndexOptInfo *index = path->indexinfo;
7232 GenericCosts costs = {0};
7233 VariableStatData vardata = {0};
7234 double numIndexTuples;
7235 Cost descentCost;
7236 List *indexBoundQuals;
7237 List *indexSkipQuals;
7238 int indexcol;
7239 bool eqQualHere;
7240 bool found_row_compare;
7241 bool found_array;
7242 bool found_is_null_op;
7243 bool have_correlation = false;
7244 double num_sa_scans;
7245 double correlation = 0.0;
7246 ListCell *lc;
7247
7248 /*
7249 * For a btree scan, only leading '=' quals plus inequality quals for the
7250 * immediately next attribute contribute to index selectivity (these are
7251 * the "boundary quals" that determine the starting and stopping points of
7252 * the index scan). Additional quals can suppress visits to the heap, so
7253 * it's OK to count them in indexSelectivity, but they should not count
7254 * for estimating numIndexTuples. So we must examine the given indexquals
7255 * to find out which ones count as boundary quals. We rely on the
7256 * knowledge that they are given in index column order. Note that nbtree
7257 * preprocessing can add skip arrays that act as leading '=' quals in the
7258 * absence of ordinary input '=' quals, so in practice _most_ input quals
7259 * are able to act as index bound quals (which we take into account here).
7260 *
7261 * For a RowCompareExpr, we consider only the first column, just as
7262 * rowcomparesel() does.
7263 *
7264 * If there's a SAOP or skip array in the quals, we'll actually perform up
7265 * to N index descents (not just one), but the underlying array key's
7266 * operator can be considered to act the same as it normally does.
7267 */
7268 indexBoundQuals = NIL;
7269 indexSkipQuals = NIL;
7270 indexcol = 0;
7271 eqQualHere = false;
7272 found_row_compare = false;
7273 found_array = false;
7274 found_is_null_op = false;
7275 num_sa_scans = 1;
7276 foreach(lc, path->indexclauses)
7277 {
7278 IndexClause *iclause = lfirst_node(IndexClause, lc);
7279 ListCell *lc2;
7280
7281 if (indexcol < iclause->indexcol)
7282 {
7283 double num_sa_scans_prev_cols = num_sa_scans;
7284
7285 /*
7286 * Beginning of a new column's quals.
7287 *
7288 * Skip scans use skip arrays, which are ScalarArrayOp style
7289 * arrays that generate their elements procedurally and on demand.
7290 * Given a multi-column index on "(a, b)", and an SQL WHERE clause
7291 * "WHERE b = 42", a skip scan will effectively use an indexqual
7292 * "WHERE a = ANY('{every col a value}') AND b = 42". (Obviously,
7293 * the array on "a" must also return "IS NULL" matches, since our
7294 * WHERE clause used no strict operator on "a").
7295 *
7296 * Here we consider how nbtree will backfill skip arrays for any
7297 * index columns that lacked an '=' qual. This maintains our
7298 * num_sa_scans estimate, and determines if this new column (the
7299 * "iclause->indexcol" column, not the prior "indexcol" column)
7300 * can have its RestrictInfos/quals added to indexBoundQuals.
7301 *
7302 * We'll need to handle columns that have inequality quals, where
7303 * the skip array generates values from a range constrained by the
7304 * quals (not every possible value). We've been maintaining
7305 * indexSkipQuals to help with this; it will now contain all of
7306 * the prior column's quals (that is, indexcol's quals) when they
7307 * might be used for this.
7308 */
7309 if (found_row_compare)
7310 {
7311 /*
7312 * Skip arrays can't be added after a RowCompare input qual
7313 * due to limitations in nbtree
7314 */
7315 break;
7316 }
7317 if (eqQualHere)
7318 {
7319 /*
7320 * Don't need to add a skip array for an indexcol that already
7321 * has an '=' qual/equality constraint
7322 */
7323 indexcol++;
7324 indexSkipQuals = NIL;
7325 }
7326 eqQualHere = false;
7327
7328 while (indexcol < iclause->indexcol)
7329 {
7330 double ndistinct;
7331 bool isdefault = true;
7332
7333 found_array = true;
7334
7335 /*
7336 * A skipped attribute's ndistinct forms the basis of our
7337 * estimate of the total number of "array elements" used by
7338 * its skip array at runtime. Look that up first.
7339 */
7340 examine_indexcol_variable(root, index, indexcol, &vardata);
7341 ndistinct = get_variable_numdistinct(&vardata, &isdefault);
7342
7343 if (indexcol == 0)
7344 {
7345 /*
7346 * Get an estimate of the leading column's correlation in
7347 * passing (avoids rereading variable stats below)
7348 */
7349 if (HeapTupleIsValid(vardata.statsTuple))
7350 correlation = btcost_correlation(index, &vardata);
7351 have_correlation = true;
7352 }
7353
7354 ReleaseVariableStats(vardata);
7355
7356 /*
7357 * If ndistinct is a default estimate, conservatively assume
7358 * that no skipping will happen at runtime
7359 */
7360 if (isdefault)
7361 {
7362 num_sa_scans = num_sa_scans_prev_cols;
7363 break; /* done building indexBoundQuals */
7364 }
7365
7366 /*
7367 * Apply indexcol's indexSkipQuals selectivity to ndistinct
7368 */
7369 if (indexSkipQuals != NIL)
7370 {
7371 List *partialSkipQuals;
7372 Selectivity ndistinctfrac;
7373
7374 /*
7375 * If the index is partial, AND the index predicate with
7376 * the index-bound quals to produce a more accurate idea
7377 * of the number of distinct values for prior indexcol
7378 */
7379 partialSkipQuals = add_predicate_to_index_quals(index,
7380 indexSkipQuals);
7381
7382 ndistinctfrac = clauselist_selectivity(root, partialSkipQuals,
7383 index->rel->relid,
7384 JOIN_INNER,
7385 NULL);
7386
7387 /*
7388 * If ndistinctfrac is selective (on its own), the scan is
7389 * unlikely to benefit from repositioning itself using
7390 * later quals. Do not allow iclause->indexcol's quals to
7391 * be added to indexBoundQuals (it would increase descent
7392 * costs, without lowering numIndexTuples costs by much).
7393 */
7394 if (ndistinctfrac < DEFAULT_RANGE_INEQ_SEL)
7395 {
7396 num_sa_scans = num_sa_scans_prev_cols;
7397 break; /* done building indexBoundQuals */
7398 }
7399
7400 /* Adjust ndistinct downward */
7401 ndistinct = rint(ndistinct * ndistinctfrac);
7402 ndistinct = Max(ndistinct, 1);
7403 }
7404
7405 /*
7406 * When there's no inequality quals, account for the need to
7407 * find an initial value by counting -inf/+inf as a value.
7408 *
7409 * We don't charge anything extra for possible next/prior key
7410 * index probes, which are sometimes used to find the next
7411 * valid skip array element (ahead of using the located
7412 * element value to relocate the scan to the next position
7413 * that might contain matching tuples). It seems hard to do
7414 * better here. Use of the skip support infrastructure often
7415 * avoids most next/prior key probes. But even when it can't,
7416 * there's a decent chance that most individual next/prior key
7417 * probes will locate a leaf page whose key space overlaps all
7418 * of the scan's keys (even the lower-order keys) -- which
7419 * also avoids the need for a separate, extra index descent.
7420 * Note also that these probes are much cheaper than non-probe
7421 * primitive index scans: they're reliably very selective.
7422 */
7423 if (indexSkipQuals == NIL)
7424 ndistinct += 1;
7425
7426 /*
7427 * Update num_sa_scans estimate by multiplying by ndistinct.
7428 *
7429 * We make the pessimistic assumption that there is no
7430 * naturally occurring cross-column correlation. This is
7431 * often wrong, but it seems best to err on the side of not
7432 * expecting skipping to be helpful...
7433 */
7434 num_sa_scans *= ndistinct;
7435
7436 /*
7437 * ...but back out of adding this latest group of 1 or more
7438 * skip arrays when num_sa_scans exceeds the total number of
7439 * index pages (revert to num_sa_scans from before indexcol).
7440 * This causes a sharp discontinuity in cost (as a function of
7441 * the indexcol's ndistinct), but that is representative of
7442 * actual runtime costs.
7443 *
7444 * Note that skipping is helpful when each primitive index
7445 * scan only manages to skip over 1 or 2 irrelevant leaf pages
7446 * on average. Skip arrays bring savings in CPU costs due to
7447 * the scan not needing to evaluate indexquals against every
7448 * tuple, which can greatly exceed any savings in I/O costs.
7449 * This test is a test of whether num_sa_scans implies that
7450 * we're past the point where the ability to skip ceases to
7451 * lower the scan's costs (even qual evaluation CPU costs).
7452 */
7453 if (index->pages < num_sa_scans)
7454 {
7455 num_sa_scans = num_sa_scans_prev_cols;
7456 break; /* done building indexBoundQuals */
7457 }
7458
7459 indexcol++;
7460 indexSkipQuals = NIL;
7461 }
7462
7463 /*
7464 * Finished considering the need to add skip arrays to bridge an
7465 * initial eqQualHere gap between the old and new index columns
7466 * (or there was no initial eqQualHere gap in the first place).
7467 *
7468 * If an initial gap could not be bridged, then new column's quals
7469 * (i.e. iclause->indexcol's quals) won't go into indexBoundQuals,
7470 * and so won't affect our final numIndexTuples estimate.
7471 */
7472 if (indexcol != iclause->indexcol)
7473 break; /* done building indexBoundQuals */
7474 }
7475
7476 Assert(indexcol == iclause->indexcol);
7477
7478 /* Examine each indexqual associated with this index clause */
7479 foreach(lc2, iclause->indexquals)
7480 {
7481 RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
7482 Expr *clause = rinfo->clause;
7483 Oid clause_op = InvalidOid;
7484 int op_strategy;
7485
7486 if (IsA(clause, OpExpr))
7487 {
7488 OpExpr *op = (OpExpr *) clause;
7489
7490 clause_op = op->opno;
7491 }
7492 else if (IsA(clause, RowCompareExpr))
7493 {
7494 RowCompareExpr *rc = (RowCompareExpr *) clause;
7495
7496 clause_op = linitial_oid(rc->opnos);
7497 found_row_compare = true;
7498 }
7499 else if (IsA(clause, ScalarArrayOpExpr))
7500 {
7501 ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
7502 Node *other_operand = (Node *) lsecond(saop->args);
7503 double alength = estimate_array_length(root, other_operand);
7504
7505 clause_op = saop->opno;
7506 found_array = true;
7507 /* estimate SA descents by indexBoundQuals only */
7508 if (alength > 1)
7509 num_sa_scans *= alength;
7510 }
7511 else if (IsA(clause, NullTest))
7512 {
7513 NullTest *nt = (NullTest *) clause;
7514
7515 if (nt->nulltesttype == IS_NULL)
7516 {
7517 found_is_null_op = true;
7518 /* IS NULL is like = for selectivity/skip scan purposes */
7519 eqQualHere = true;
7520 }
7521 }
7522 else
7523 elog(ERROR, "unsupported indexqual type: %d",
7524 (int) nodeTag(clause));
7525
7526 /* check for equality operator */
7527 if (OidIsValid(clause_op))
7528 {
7529 op_strategy = get_op_opfamily_strategy(clause_op,
7530 index->opfamily[indexcol]);
7531 Assert(op_strategy != 0); /* not a member of opfamily?? */
7532 if (op_strategy == BTEqualStrategyNumber)
7533 eqQualHere = true;
7534 }
7535
7536 indexBoundQuals = lappend(indexBoundQuals, rinfo);
7537
7538 /*
7539 * We apply inequality selectivities to estimate index descent
7540 * costs with scans that use skip arrays. Save this indexcol's
7541 * RestrictInfos if it looks like they'll be needed for that.
7542 */
7543 if (!eqQualHere && !found_row_compare &&
7544 indexcol < index->nkeycolumns - 1)
7545 indexSkipQuals = lappend(indexSkipQuals, rinfo);
7546 }
7547 }
7548
7549 /*
7550 * If index is unique and we found an '=' clause for each column, we can
7551 * just assume numIndexTuples = 1 and skip the expensive
7552 * clauselist_selectivity calculations. However, an array or NullTest
7553 * always invalidates that theory (even when eqQualHere has been set).
7554 */
7555 if (index->unique &&
7556 indexcol == index->nkeycolumns - 1 &&
7557 eqQualHere &&
7558 !found_array &&
7559 !found_is_null_op)
7560 numIndexTuples = 1.0;
7561 else
7562 {
7563 List *selectivityQuals;
7564 Selectivity btreeSelectivity;
7565
7566 /*
7567 * If the index is partial, AND the index predicate with the
7568 * index-bound quals to produce a more accurate idea of the number of
7569 * rows covered by the bound conditions.
7570 */
7571 selectivityQuals = add_predicate_to_index_quals(index, indexBoundQuals);
7572
7573 btreeSelectivity = clauselist_selectivity(root, selectivityQuals,
7574 index->rel->relid,
7575 JOIN_INNER,
7576 NULL);
7577 numIndexTuples = btreeSelectivity * index->rel->tuples;
7578
7579 /*
7580 * btree automatically combines individual array element primitive
7581 * index scans whenever the tuples covered by the next set of array
7582 * keys are close to tuples covered by the current set. That puts a
7583 * natural ceiling on the worst case number of descents -- there
7584 * cannot possibly be more than one descent per leaf page scanned.
7585 *
7586 * Clamp the number of descents to at most 1/3 the number of index
7587 * pages. This avoids implausibly high estimates with low selectivity
7588 * paths, where scans usually require only one or two descents. This
7589 * is most likely to help when there are several SAOP clauses, where
7590 * naively accepting the total number of distinct combinations of
7591 * array elements as the number of descents would frequently lead to
7592 * wild overestimates.
7593 *
7594 * We somewhat arbitrarily don't just make the cutoff the total number
7595 * of leaf pages (we make it 1/3 the total number of pages instead) to
7596 * give the btree code credit for its ability to continue on the leaf
7597 * level with low selectivity scans.
7598 *
7599 * Note: num_sa_scans includes both ScalarArrayOp array elements and
7600 * skip array elements whose qual affects our numIndexTuples estimate.
7601 */
7602 num_sa_scans = Min(num_sa_scans, ceil(index->pages * 0.3333333));
7603 num_sa_scans = Max(num_sa_scans, 1);
7604
7605 /*
7606 * As in genericcostestimate(), we have to adjust for any array quals
7607 * included in indexBoundQuals, and then round to integer.
7608 *
7609 * It is tempting to make genericcostestimate behave as if array
7610 * clauses work in almost the same way as scalar operators during
7611 * btree scans, making the top-level scan look like a continuous scan
7612 * (as opposed to num_sa_scans-many primitive index scans). After
7613 * all, btree scans mostly work like that at runtime. However, such a
7614 * scheme would badly bias genericcostestimate's simplistic approach
7615 * to calculating numIndexPages through prorating.
7616 *
7617 * Stick with the approach taken by non-native SAOP scans for now.
7618 * genericcostestimate will use the Mackert-Lohman formula to
7619 * compensate for repeat page fetches, even though that definitely
7620 * won't happen during btree scans (not for leaf pages, at least).
7621 * We're usually very pessimistic about the number of primitive index
7622 * scans that will be required, but it's not clear how to do better.
7623 */
7624 numIndexTuples = rint(numIndexTuples / num_sa_scans);
7625 }
7626
7627 /*
7628 * Now do generic index cost estimation.
7629 */
7630 costs.numIndexTuples = numIndexTuples;
7631 costs.num_sa_scans = num_sa_scans;
7632
7633 genericcostestimate(root, path, loop_count, &costs);
7634
7635 /*
7636 * Add a CPU-cost component to represent the costs of initial btree
7637 * descent. We don't charge any I/O cost for touching upper btree levels,
7638 * since they tend to stay in cache, but we still have to do about log2(N)
7639 * comparisons to descend a btree of N leaf tuples. We charge one
7640 * cpu_operator_cost per comparison.
7641 *
7642 * If there are SAOP or skip array keys, charge this once per estimated
7643 * index descent. The ones after the first one are not startup cost so
7644 * far as the overall plan goes, so just add them to "total" cost.
7645 */
7646 if (index->tuples > 1) /* avoid computing log(0) */
7647 {
7648 descentCost = ceil(log(index->tuples) / log(2.0)) * cpu_operator_cost;
7649 costs.indexStartupCost += descentCost;
7650 costs.indexTotalCost += costs.num_sa_scans * descentCost;
7651 }
7652
7653 /*
7654 * Even though we're not charging I/O cost for touching upper btree pages,
7655 * it's still reasonable to charge some CPU cost per page descended
7656 * through. Moreover, if we had no such charge at all, bloated indexes
7657 * would appear to have the same search cost as unbloated ones, at least
7658 * in cases where only a single leaf page is expected to be visited. This
7659 * cost is somewhat arbitrarily set at 50x cpu_operator_cost per page
7660 * touched. The number of such pages is btree tree height plus one (ie,
7661 * we charge for the leaf page too). As above, charge once per estimated
7662 * SAOP/skip array descent.
7663 */
7664 descentCost = (index->tree_height + 1) * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
7665 costs.indexStartupCost += descentCost;
7666 costs.indexTotalCost += costs.num_sa_scans * descentCost;
7667
7668 if (!have_correlation)
7669 {
7670 examine_indexcol_variable(root, index, 0, &vardata);
7671 if (HeapTupleIsValid(vardata.statsTuple))
7672 costs.indexCorrelation = btcost_correlation(index, &vardata);
7673 ReleaseVariableStats(vardata);
7674 }
7675 else
7676 {
7677 /* btcost_correlation already called earlier on */
7678 costs.indexCorrelation = correlation;
7679 }
7680
7681 *indexStartupCost = costs.indexStartupCost;
7682 *indexTotalCost = costs.indexTotalCost;
7683 *indexSelectivity = costs.indexSelectivity;
7684 *indexCorrelation = costs.indexCorrelation;
7685 *indexPages = costs.numIndexPages;
7686}
#define OidIsValid(objectId)
Definition: c.h:746
List * lappend(List *list, void *datum)
Definition: list.c:339
int get_op_opfamily_strategy(Oid opno, Oid opfamily)
Definition: lsyscache.c:84
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
#define nodeTag(nodeptr)
Definition: nodes.h:139
double Selectivity
Definition: nodes.h:256
#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:30
@ IS_NULL
Definition: primnodes.h:1957
List * add_predicate_to_index_quals(IndexOptInfo *index, List *indexQuals)
Definition: selfuncs.c:7158
#define DEFAULT_PAGE_CPU_MULTIPLIER
Definition: selfuncs.c:145
double estimate_array_length(PlannerInfo *root, Node *arrayexpr)
Definition: selfuncs.c:2144
void genericcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, GenericCosts *costs)
Definition: selfuncs.c:6935
static void examine_indexcol_variable(PlannerInfo *root, IndexOptInfo *index, int indexcol, VariableStatData *vardata)
Definition: selfuncs.c:6048
static double btcost_correlation(IndexOptInfo *index, VariableStatData *vardata)
Definition: selfuncs.c:7189
double get_variable_numdistinct(VariableStatData *vardata, bool *isdefault)
Definition: selfuncs.c:6149
#define DEFAULT_RANGE_INEQ_SEL
Definition: selfuncs.h:40
#define BTEqualStrategyNumber
Definition: stratnum.h:31
Selectivity indexSelectivity
Definition: selfuncs.h:128
Cost indexStartupCost
Definition: selfuncs.h:126
double indexCorrelation
Definition: selfuncs.h:129
double num_sa_scans
Definition: selfuncs.h:135
Cost indexTotalCost
Definition: selfuncs.h:127
double numIndexPages
Definition: selfuncs.h:132
double numIndexTuples
Definition: selfuncs.h:133
List * indexquals
Definition: pathnodes.h:1896
Definition: nodes.h:135
NullTestType nulltesttype
Definition: primnodes.h:1964
Oid opno
Definition: primnodes.h:835
Expr * clause
Definition: pathnodes.h:2700

References add_predicate_to_index_quals(), ScalarArrayOpExpr::args, Assert(), btcost_correlation(), BTEqualStrategyNumber, RestrictInfo::clause, clauselist_selectivity(), cpu_operator_cost, DEFAULT_PAGE_CPU_MULTIPLIER, DEFAULT_RANGE_INEQ_SEL, elog, ERROR, estimate_array_length(), examine_indexcol_variable(), genericcostestimate(), get_op_opfamily_strategy(), get_variable_numdistinct(), HeapTupleIsValid, IndexPath::indexclauses, IndexClause::indexcol, GenericCosts::indexCorrelation, IndexPath::indexinfo, IndexClause::indexquals, GenericCosts::indexSelectivity, GenericCosts::indexStartupCost, GenericCosts::indexTotalCost, InvalidOid, IS_NULL, IsA, JOIN_INNER, lappend(), lfirst_node, linitial_oid, lsecond, Max, Min, NIL, nodeTag, NullTest::nulltesttype, GenericCosts::num_sa_scans, GenericCosts::numIndexPages, GenericCosts::numIndexTuples, OidIsValid, OpExpr::opno, ScalarArrayOpExpr::opno, ReleaseVariableStats, root, 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 8141 of file selfuncs.c.

8145{
8146 IndexOptInfo *index = path->indexinfo;
8147 List *indexQuals = get_quals_from_indexclauses(path->indexclauses);
8148 List *selectivityQuals;
8149 double numPages = index->pages,
8150 numTuples = index->tuples;
8151 double numEntryPages,
8152 numDataPages,
8153 numPendingPages,
8154 numEntries;
8155 GinQualCounts counts;
8156 bool matchPossible;
8157 bool fullIndexScan;
8158 double partialScale;
8159 double entryPagesFetched,
8160 dataPagesFetched,
8161 dataPagesFetchedBySel;
8162 double qual_op_cost,
8163 qual_arg_cost,
8164 spc_random_page_cost,
8165 outer_scans;
8166 Cost descentCost;
8167 Relation indexRel;
8168 GinStatsData ginStats;
8169 ListCell *lc;
8170 int i;
8171
8172 /*
8173 * Obtain statistical information from the meta page, if possible. Else
8174 * set ginStats to zeroes, and we'll cope below.
8175 */
8176 if (!index->hypothetical)
8177 {
8178 /* Lock should have already been obtained in plancat.c */
8179 indexRel = index_open(index->indexoid, NoLock);
8180 ginGetStats(indexRel, &ginStats);
8181 index_close(indexRel, NoLock);
8182 }
8183 else
8184 {
8185 memset(&ginStats, 0, sizeof(ginStats));
8186 }
8187
8188 /*
8189 * Assuming we got valid (nonzero) stats at all, nPendingPages can be
8190 * trusted, but the other fields are data as of the last VACUUM. We can
8191 * scale them up to account for growth since then, but that method only
8192 * goes so far; in the worst case, the stats might be for a completely
8193 * empty index, and scaling them will produce pretty bogus numbers.
8194 * Somewhat arbitrarily, set the cutoff for doing scaling at 4X growth; if
8195 * it's grown more than that, fall back to estimating things only from the
8196 * assumed-accurate index size. But we'll trust nPendingPages in any case
8197 * so long as it's not clearly insane, ie, more than the index size.
8198 */
8199 if (ginStats.nPendingPages < numPages)
8200 numPendingPages = ginStats.nPendingPages;
8201 else
8202 numPendingPages = 0;
8203
8204 if (numPages > 0 && ginStats.nTotalPages <= numPages &&
8205 ginStats.nTotalPages > numPages / 4 &&
8206 ginStats.nEntryPages > 0 && ginStats.nEntries > 0)
8207 {
8208 /*
8209 * OK, the stats seem close enough to sane to be trusted. But we
8210 * still need to scale them by the ratio numPages / nTotalPages to
8211 * account for growth since the last VACUUM.
8212 */
8213 double scale = numPages / ginStats.nTotalPages;
8214
8215 numEntryPages = ceil(ginStats.nEntryPages * scale);
8216 numDataPages = ceil(ginStats.nDataPages * scale);
8217 numEntries = ceil(ginStats.nEntries * scale);
8218 /* ensure we didn't round up too much */
8219 numEntryPages = Min(numEntryPages, numPages - numPendingPages);
8220 numDataPages = Min(numDataPages,
8221 numPages - numPendingPages - numEntryPages);
8222 }
8223 else
8224 {
8225 /*
8226 * We might get here because it's a hypothetical index, or an index
8227 * created pre-9.1 and never vacuumed since upgrading (in which case
8228 * its stats would read as zeroes), or just because it's grown too
8229 * much since the last VACUUM for us to put our faith in scaling.
8230 *
8231 * Invent some plausible internal statistics based on the index page
8232 * count (and clamp that to at least 10 pages, just in case). We
8233 * estimate that 90% of the index is entry pages, and the rest is data
8234 * pages. Estimate 100 entries per entry page; this is rather bogus
8235 * since it'll depend on the size of the keys, but it's more robust
8236 * than trying to predict the number of entries per heap tuple.
8237 */
8238 numPages = Max(numPages, 10);
8239 numEntryPages = floor((numPages - numPendingPages) * 0.90);
8240 numDataPages = numPages - numPendingPages - numEntryPages;
8241 numEntries = floor(numEntryPages * 100);
8242 }
8243
8244 /* In an empty index, numEntries could be zero. Avoid divide-by-zero */
8245 if (numEntries < 1)
8246 numEntries = 1;
8247
8248 /*
8249 * If the index is partial, AND the index predicate with the index-bound
8250 * quals to produce a more accurate idea of the number of rows covered by
8251 * the bound conditions.
8252 */
8253 selectivityQuals = add_predicate_to_index_quals(index, indexQuals);
8254
8255 /* Estimate the fraction of main-table tuples that will be visited */
8256 *indexSelectivity = clauselist_selectivity(root, selectivityQuals,
8257 index->rel->relid,
8258 JOIN_INNER,
8259 NULL);
8260
8261 /* fetch estimated page cost for tablespace containing index */
8262 get_tablespace_page_costs(index->reltablespace,
8263 &spc_random_page_cost,
8264 NULL);
8265
8266 /*
8267 * Generic assumption about index correlation: there isn't any.
8268 */
8269 *indexCorrelation = 0.0;
8270
8271 /*
8272 * Examine quals to estimate number of search entries & partial matches
8273 */
8274 memset(&counts, 0, sizeof(counts));
8275 counts.arrayScans = 1;
8276 matchPossible = true;
8277
8278 foreach(lc, path->indexclauses)
8279 {
8280 IndexClause *iclause = lfirst_node(IndexClause, lc);
8281 ListCell *lc2;
8282
8283 foreach(lc2, iclause->indexquals)
8284 {
8285 RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
8286 Expr *clause = rinfo->clause;
8287
8288 if (IsA(clause, OpExpr))
8289 {
8290 matchPossible = gincost_opexpr(root,
8291 index,
8292 iclause->indexcol,
8293 (OpExpr *) clause,
8294 &counts);
8295 if (!matchPossible)
8296 break;
8297 }
8298 else if (IsA(clause, ScalarArrayOpExpr))
8299 {
8300 matchPossible = gincost_scalararrayopexpr(root,
8301 index,
8302 iclause->indexcol,
8303 (ScalarArrayOpExpr *) clause,
8304 numEntries,
8305 &counts);
8306 if (!matchPossible)
8307 break;
8308 }
8309 else
8310 {
8311 /* shouldn't be anything else for a GIN index */
8312 elog(ERROR, "unsupported GIN indexqual type: %d",
8313 (int) nodeTag(clause));
8314 }
8315 }
8316 }
8317
8318 /* Fall out if there were any provably-unsatisfiable quals */
8319 if (!matchPossible)
8320 {
8321 *indexStartupCost = 0;
8322 *indexTotalCost = 0;
8323 *indexSelectivity = 0;
8324 return;
8325 }
8326
8327 /*
8328 * If attribute has a full scan and at the same time doesn't have normal
8329 * scan, then we'll have to scan all non-null entries of that attribute.
8330 * Currently, we don't have per-attribute statistics for GIN. Thus, we
8331 * must assume the whole GIN index has to be scanned in this case.
8332 */
8333 fullIndexScan = false;
8334 for (i = 0; i < index->nkeycolumns; i++)
8335 {
8336 if (counts.attHasFullScan[i] && !counts.attHasNormalScan[i])
8337 {
8338 fullIndexScan = true;
8339 break;
8340 }
8341 }
8342
8343 if (fullIndexScan || indexQuals == NIL)
8344 {
8345 /*
8346 * Full index scan will be required. We treat this as if every key in
8347 * the index had been listed in the query; is that reasonable?
8348 */
8349 counts.partialEntries = 0;
8350 counts.exactEntries = numEntries;
8351 counts.searchEntries = numEntries;
8352 }
8353
8354 /* Will we have more than one iteration of a nestloop scan? */
8355 outer_scans = loop_count;
8356
8357 /*
8358 * Compute cost to begin scan, first of all, pay attention to pending
8359 * list.
8360 */
8361 entryPagesFetched = numPendingPages;
8362
8363 /*
8364 * Estimate number of entry pages read. We need to do
8365 * counts.searchEntries searches. Use a power function as it should be,
8366 * but tuples on leaf pages usually is much greater. Here we include all
8367 * searches in entry tree, including search of first entry in partial
8368 * match algorithm
8369 */
8370 entryPagesFetched += ceil(counts.searchEntries * rint(pow(numEntryPages, 0.15)));
8371
8372 /*
8373 * Add an estimate of entry pages read by partial match algorithm. It's a
8374 * scan over leaf pages in entry tree. We haven't any useful stats here,
8375 * so estimate it as proportion. Because counts.partialEntries is really
8376 * pretty bogus (see code above), it's possible that it is more than
8377 * numEntries; clamp the proportion to ensure sanity.
8378 */
8379 partialScale = counts.partialEntries / numEntries;
8380 partialScale = Min(partialScale, 1.0);
8381
8382 entryPagesFetched += ceil(numEntryPages * partialScale);
8383
8384 /*
8385 * Partial match algorithm reads all data pages before doing actual scan,
8386 * so it's a startup cost. Again, we haven't any useful stats here, so
8387 * estimate it as proportion.
8388 */
8389 dataPagesFetched = ceil(numDataPages * partialScale);
8390
8391 *indexStartupCost = 0;
8392 *indexTotalCost = 0;
8393
8394 /*
8395 * Add a CPU-cost component to represent the costs of initial entry btree
8396 * descent. We don't charge any I/O cost for touching upper btree levels,
8397 * since they tend to stay in cache, but we still have to do about log2(N)
8398 * comparisons to descend a btree of N leaf tuples. We charge one
8399 * cpu_operator_cost per comparison.
8400 *
8401 * If there are ScalarArrayOpExprs, charge this once per SA scan. The
8402 * ones after the first one are not startup cost so far as the overall
8403 * plan is concerned, so add them only to "total" cost.
8404 */
8405 if (numEntries > 1) /* avoid computing log(0) */
8406 {
8407 descentCost = ceil(log(numEntries) / log(2.0)) * cpu_operator_cost;
8408 *indexStartupCost += descentCost * counts.searchEntries;
8409 *indexTotalCost += counts.arrayScans * descentCost * counts.searchEntries;
8410 }
8411
8412 /*
8413 * Add a cpu cost per entry-page fetched. This is not amortized over a
8414 * loop.
8415 */
8416 *indexStartupCost += entryPagesFetched * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
8417 *indexTotalCost += entryPagesFetched * counts.arrayScans * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
8418
8419 /*
8420 * Add a cpu cost per data-page fetched. This is also not amortized over a
8421 * loop. Since those are the data pages from the partial match algorithm,
8422 * charge them as startup cost.
8423 */
8424 *indexStartupCost += DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost * dataPagesFetched;
8425
8426 /*
8427 * Since we add the startup cost to the total cost later on, remove the
8428 * initial arrayscan from the total.
8429 */
8430 *indexTotalCost += dataPagesFetched * (counts.arrayScans - 1) * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
8431
8432 /*
8433 * Calculate cache effects if more than one scan due to nestloops or array
8434 * quals. The result is pro-rated per nestloop scan, but the array qual
8435 * factor shouldn't be pro-rated (compare genericcostestimate).
8436 */
8437 if (outer_scans > 1 || counts.arrayScans > 1)
8438 {
8439 entryPagesFetched *= outer_scans * counts.arrayScans;
8440 entryPagesFetched = index_pages_fetched(entryPagesFetched,
8441 (BlockNumber) numEntryPages,
8442 numEntryPages, root);
8443 entryPagesFetched /= outer_scans;
8444 dataPagesFetched *= outer_scans * counts.arrayScans;
8445 dataPagesFetched = index_pages_fetched(dataPagesFetched,
8446 (BlockNumber) numDataPages,
8447 numDataPages, root);
8448 dataPagesFetched /= outer_scans;
8449 }
8450
8451 /*
8452 * Here we use random page cost because logically-close pages could be far
8453 * apart on disk.
8454 */
8455 *indexStartupCost += (entryPagesFetched + dataPagesFetched) * spc_random_page_cost;
8456
8457 /*
8458 * Now compute the number of data pages fetched during the scan.
8459 *
8460 * We assume every entry to have the same number of items, and that there
8461 * is no overlap between them. (XXX: tsvector and array opclasses collect
8462 * statistics on the frequency of individual keys; it would be nice to use
8463 * those here.)
8464 */
8465 dataPagesFetched = ceil(numDataPages * counts.exactEntries / numEntries);
8466
8467 /*
8468 * If there is a lot of overlap among the entries, in particular if one of
8469 * the entries is very frequent, the above calculation can grossly
8470 * under-estimate. As a simple cross-check, calculate a lower bound based
8471 * on the overall selectivity of the quals. At a minimum, we must read
8472 * one item pointer for each matching entry.
8473 *
8474 * The width of each item pointer varies, based on the level of
8475 * compression. We don't have statistics on that, but an average of
8476 * around 3 bytes per item is fairly typical.
8477 */
8478 dataPagesFetchedBySel = ceil(*indexSelectivity *
8479 (numTuples / (BLCKSZ / 3)));
8480 if (dataPagesFetchedBySel > dataPagesFetched)
8481 dataPagesFetched = dataPagesFetchedBySel;
8482
8483 /* Add one page cpu-cost to the startup cost */
8484 *indexStartupCost += DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost * counts.searchEntries;
8485
8486 /*
8487 * Add once again a CPU-cost for those data pages, before amortizing for
8488 * cache.
8489 */
8490 *indexTotalCost += dataPagesFetched * counts.arrayScans * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
8491
8492 /* Account for cache effects, the same as above */
8493 if (outer_scans > 1 || counts.arrayScans > 1)
8494 {
8495 dataPagesFetched *= outer_scans * counts.arrayScans;
8496 dataPagesFetched = index_pages_fetched(dataPagesFetched,
8497 (BlockNumber) numDataPages,
8498 numDataPages, root);
8499 dataPagesFetched /= outer_scans;
8500 }
8501
8502 /* And apply random_page_cost as the cost per page */
8503 *indexTotalCost += *indexStartupCost +
8504 dataPagesFetched * spc_random_page_cost;
8505
8506 /*
8507 * Add on index qual eval costs, much as in genericcostestimate. We charge
8508 * cpu but we can disregard indexorderbys, since GIN doesn't support
8509 * those.
8510 */
8511 qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
8512 qual_op_cost = cpu_operator_cost * list_length(indexQuals);
8513
8514 *indexStartupCost += qual_arg_cost;
8515 *indexTotalCost += qual_arg_cost;
8516
8517 /*
8518 * Add a cpu cost per search entry, corresponding to the actual visited
8519 * entries.
8520 */
8521 *indexTotalCost += (counts.searchEntries * counts.arrayScans) * (qual_op_cost);
8522 /* Now add a cpu cost per tuple in the posting lists / trees */
8523 *indexTotalCost += (numTuples * *indexSelectivity) * (cpu_index_tuple_cost);
8524 *indexPages = dataPagesFetched;
8525}
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:628
int i
Definition: isn.c:77
static int list_length(const List *l)
Definition: pg_list.h:152
static int scale
Definition: pgbench.c:182
static bool gincost_scalararrayopexpr(PlannerInfo *root, IndexOptInfo *index, int indexcol, ScalarArrayOpExpr *clause, double numIndexEntries, GinQualCounts *counts)
Definition: selfuncs.c:8025
static bool gincost_opexpr(PlannerInfo *root, IndexOptInfo *index, int indexcol, OpExpr *clause, GinQualCounts *counts)
Definition: selfuncs.c:7975
bool attHasNormalScan[INDEX_MAX_KEYS]
Definition: selfuncs.c:7848
double exactEntries
Definition: selfuncs.c:7850
double arrayScans
Definition: selfuncs.c:7852
double partialEntries
Definition: selfuncs.c:7849
bool attHasFullScan[INDEX_MAX_KEYS]
Definition: selfuncs.c:7847
double searchEntries
Definition: selfuncs.c:7851
BlockNumber nDataPages
Definition: gin.h:60
BlockNumber nPendingPages
Definition: gin.h:57
BlockNumber nEntryPages
Definition: gin.h:59
int64 nEntries
Definition: gin.h:61
BlockNumber nTotalPages
Definition: gin.h:58

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 7731 of file selfuncs.c.

7735{
7736 IndexOptInfo *index = path->indexinfo;
7737 GenericCosts costs = {0};
7738 Cost descentCost;
7739
7740 genericcostestimate(root, path, loop_count, &costs);
7741
7742 /*
7743 * We model index descent costs similarly to those for btree, but to do
7744 * that we first need an idea of the tree height. We somewhat arbitrarily
7745 * assume that the fanout is 100, meaning the tree height is at most
7746 * log100(index->pages).
7747 *
7748 * Although this computation isn't really expensive enough to require
7749 * caching, we might as well use index->tree_height to cache it.
7750 */
7751 if (index->tree_height < 0) /* unknown? */
7752 {
7753 if (index->pages > 1) /* avoid computing log(0) */
7754 index->tree_height = (int) (log(index->pages) / log(100.0));
7755 else
7756 index->tree_height = 0;
7757 }
7758
7759 /*
7760 * Add a CPU-cost component to represent the costs of initial descent. We
7761 * just use log(N) here not log2(N) since the branching factor isn't
7762 * necessarily two anyway. As for btree, charge once per SA scan.
7763 */
7764 if (index->tuples > 1) /* avoid computing log(0) */
7765 {
7766 descentCost = ceil(log(index->tuples)) * cpu_operator_cost;
7767 costs.indexStartupCost += descentCost;
7768 costs.indexTotalCost += costs.num_sa_scans * descentCost;
7769 }
7770
7771 /*
7772 * Likewise add a per-page charge, calculated the same as for btrees.
7773 */
7774 descentCost = (index->tree_height + 1) * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
7775 costs.indexStartupCost += descentCost;
7776 costs.indexTotalCost += costs.num_sa_scans * descentCost;
7777
7778 *indexStartupCost = costs.indexStartupCost;
7779 *indexTotalCost = costs.indexTotalCost;
7780 *indexSelectivity = costs.indexSelectivity;
7781 *indexCorrelation = costs.indexCorrelation;
7782 *indexPages = costs.numIndexPages;
7783}

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 7689 of file selfuncs.c.

7693{
7694 GenericCosts costs = {0};
7695
7696 genericcostestimate(root, path, loop_count, &costs);
7697
7698 /*
7699 * A hash index has no descent costs as such, since the index AM can go
7700 * directly to the target bucket after computing the hash value. There
7701 * are a couple of other hash-specific costs that we could conceivably add
7702 * here, though:
7703 *
7704 * Ideally we'd charge spc_random_page_cost for each page in the target
7705 * bucket, not just the numIndexPages pages that genericcostestimate
7706 * thought we'd visit. However in most cases we don't know which bucket
7707 * that will be. There's no point in considering the average bucket size
7708 * because the hash AM makes sure that's always one page.
7709 *
7710 * Likewise, we could consider charging some CPU for each index tuple in
7711 * the bucket, if we knew how many there were. But the per-tuple cost is
7712 * just a hash value comparison, not a general datatype-dependent
7713 * comparison, so any such charge ought to be quite a bit less than
7714 * cpu_operator_cost; which makes it probably not worth worrying about.
7715 *
7716 * A bigger issue is that chance hash-value collisions will result in
7717 * wasted probes into the heap. We don't currently attempt to model this
7718 * cost on the grounds that it's rare, but maybe it's not rare enough.
7719 * (Any fix for this ought to consider the generic lossy-operator problem,
7720 * though; it's not entirely hash-specific.)
7721 */
7722
7723 *indexStartupCost = costs.indexStartupCost;
7724 *indexTotalCost = costs.indexTotalCost;
7725 *indexSelectivity = costs.indexSelectivity;
7726 *indexCorrelation = costs.indexCorrelation;
7727 *indexPages = costs.numIndexPages;
7728}

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 7786 of file selfuncs.c.

7790{
7791 IndexOptInfo *index = path->indexinfo;
7792 GenericCosts costs = {0};
7793 Cost descentCost;
7794
7795 genericcostestimate(root, path, loop_count, &costs);
7796
7797 /*
7798 * We model index descent costs similarly to those for btree, but to do
7799 * that we first need an idea of the tree height. We somewhat arbitrarily
7800 * assume that the fanout is 100, meaning the tree height is at most
7801 * log100(index->pages).
7802 *
7803 * Although this computation isn't really expensive enough to require
7804 * caching, we might as well use index->tree_height to cache it.
7805 */
7806 if (index->tree_height < 0) /* unknown? */
7807 {
7808 if (index->pages > 1) /* avoid computing log(0) */
7809 index->tree_height = (int) (log(index->pages) / log(100.0));
7810 else
7811 index->tree_height = 0;
7812 }
7813
7814 /*
7815 * Add a CPU-cost component to represent the costs of initial descent. We
7816 * just use log(N) here not log2(N) since the branching factor isn't
7817 * necessarily two anyway. As for btree, charge once per SA scan.
7818 */
7819 if (index->tuples > 1) /* avoid computing log(0) */
7820 {
7821 descentCost = ceil(log(index->tuples)) * cpu_operator_cost;
7822 costs.indexStartupCost += descentCost;
7823 costs.indexTotalCost += costs.num_sa_scans * descentCost;
7824 }
7825
7826 /*
7827 * Likewise add a per-page charge, calculated the same as for btrees.
7828 */
7829 descentCost = (index->tree_height + 1) * DEFAULT_PAGE_CPU_MULTIPLIER * cpu_operator_cost;
7830 costs.indexStartupCost += descentCost;
7831 costs.indexTotalCost += costs.num_sa_scans * descentCost;
7832
7833 *indexStartupCost = costs.indexStartupCost;
7834 *indexTotalCost = costs.indexTotalCost;
7835 *indexSelectivity = costs.indexSelectivity;
7836 *indexCorrelation = costs.indexCorrelation;
7837 *indexPages = costs.numIndexPages;
7838}

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().