|
101 | 101 | #include<float.h>
|
102 | 102 | #include<math.h>
|
103 | 103 |
|
| 104 | +#include"access/brin.h" |
104 | 105 | #include"access/gin.h"
|
105 | 106 | #include"access/htup_details.h"
|
106 | 107 | #include"access/sysattr.h"
|
@@ -7721,56 +7722,200 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
7721 | 7722 | {
|
7722 | 7723 | IndexOptInfo*index=path->indexinfo;
|
7723 | 7724 | List*indexQuals=path->indexquals;
|
7724 |
| -List*indexOrderBys=path->indexorderbys; |
7725 | 7725 | doublenumPages=index->pages;
|
7726 |
| -doublenumTuples=index->tuples; |
| 7726 | +RelOptInfo*baserel=index->rel; |
| 7727 | +RangeTblEntry*rte=planner_rt_fetch(baserel->relid,root); |
7727 | 7728 | List*qinfos;
|
7728 | 7729 | Costspc_seq_page_cost;
|
7729 | 7730 | Costspc_random_page_cost;
|
7730 |
| -doublequal_op_cost; |
7731 | 7731 | doublequal_arg_cost;
|
| 7732 | +doublequalSelectivity; |
| 7733 | +BrinStatsDatastatsData; |
| 7734 | +doubleindexRanges; |
| 7735 | +doubleminimalRanges; |
| 7736 | +doubleestimatedRanges; |
| 7737 | +doubleselec; |
| 7738 | +RelationindexRel; |
| 7739 | +ListCell*l; |
| 7740 | +VariableStatDatavardata; |
7732 | 7741 |
|
7733 |
| -/* Do preliminary analysis of indexquals */ |
7734 |
| -qinfos=deconstruct_indexquals(path); |
| 7742 | +Assert(rte->rtekind==RTE_RELATION); |
7735 | 7743 |
|
7736 |
| -/* fetch estimated page cost for tablespace containing index */ |
| 7744 | +/* fetch estimated page cost forthetablespace containing the index */ |
7737 | 7745 | get_tablespace_page_costs(index->reltablespace,
|
7738 | 7746 | &spc_random_page_cost,
|
7739 | 7747 | &spc_seq_page_cost);
|
7740 | 7748 |
|
7741 | 7749 | /*
|
7742 |
| - * BRIN indexes are always read in full; use that as startup cost. |
| 7750 | + * Obtain some data from the index itself. |
| 7751 | + */ |
| 7752 | +indexRel=index_open(index->indexoid,AccessShareLock); |
| 7753 | +brinGetStats(indexRel,&statsData); |
| 7754 | +index_close(indexRel,AccessShareLock); |
| 7755 | + |
| 7756 | +/* |
| 7757 | + * Compute index correlation |
7743 | 7758 | *
|
7744 |
| - * XXX maybe only include revmap pages here? |
| 7759 | + * Because we can use all index quals equally when scanning, we can use |
| 7760 | + * the largest correlation (in absolute value) among columns used by the |
| 7761 | + * query. Start at zero, the worst possible case. If we cannot find |
| 7762 | + * any correlation statistics, we will keep it as 0. |
| 7763 | + */ |
| 7764 | +*indexCorrelation=0; |
| 7765 | + |
| 7766 | +qinfos=deconstruct_indexquals(path); |
| 7767 | +foreach(l,qinfos) |
| 7768 | +{ |
| 7769 | +IndexQualInfo*qinfo= (IndexQualInfo*)lfirst(l); |
| 7770 | +AttrNumberattnum=index->indexkeys[qinfo->indexcol]; |
| 7771 | + |
| 7772 | +/* attempt to lookup stats in relation for this index column */ |
| 7773 | +if (attnum!=0) |
| 7774 | +{ |
| 7775 | +/* Simple variable -- look to stats for the underlying table */ |
| 7776 | +if (get_relation_stats_hook&& |
| 7777 | +(*get_relation_stats_hook) (root,rte,attnum,&vardata)) |
| 7778 | +{ |
| 7779 | +/* |
| 7780 | + * The hook took control of acquiring a stats tuple. If it |
| 7781 | + * did supply a tuple, it'd better have supplied a freefunc. |
| 7782 | + */ |
| 7783 | +if (HeapTupleIsValid(vardata.statsTuple)&& !vardata.freefunc) |
| 7784 | +elog(ERROR, |
| 7785 | +"no function provided to release variable stats with"); |
| 7786 | +} |
| 7787 | +else |
| 7788 | +{ |
| 7789 | +vardata.statsTuple= |
| 7790 | +SearchSysCache3(STATRELATTINH, |
| 7791 | +ObjectIdGetDatum(rte->relid), |
| 7792 | +Int16GetDatum(attnum), |
| 7793 | +BoolGetDatum(false)); |
| 7794 | +vardata.freefunc=ReleaseSysCache; |
| 7795 | +} |
| 7796 | +} |
| 7797 | +else |
| 7798 | +{ |
| 7799 | +/* |
| 7800 | + * Looks like we've found an expression column in the index. Let's |
| 7801 | + * see if there's any stats for it. |
| 7802 | + */ |
| 7803 | + |
| 7804 | +/* get the attnum from the 0-based index. */ |
| 7805 | +attnum=qinfo->indexcol+1; |
| 7806 | + |
| 7807 | +if (get_index_stats_hook&& |
| 7808 | +(*get_index_stats_hook) (root,index->indexoid,attnum,&vardata)) |
| 7809 | +{ |
| 7810 | +/* |
| 7811 | + * The hook took control of acquiring a stats tuple. If it did |
| 7812 | + * supply a tuple, it'd better have supplied a freefunc. |
| 7813 | + */ |
| 7814 | +if (HeapTupleIsValid(vardata.statsTuple)&& |
| 7815 | +!vardata.freefunc) |
| 7816 | +elog(ERROR,"no function provided to release variable stats with"); |
| 7817 | +} |
| 7818 | +else |
| 7819 | +{ |
| 7820 | +vardata.statsTuple=SearchSysCache3(STATRELATTINH, |
| 7821 | +ObjectIdGetDatum(index->indexoid), |
| 7822 | +Int16GetDatum(attnum), |
| 7823 | +BoolGetDatum(false)); |
| 7824 | +vardata.freefunc=ReleaseSysCache; |
| 7825 | +} |
| 7826 | +} |
| 7827 | + |
| 7828 | +if (HeapTupleIsValid(vardata.statsTuple)) |
| 7829 | +{ |
| 7830 | +float4*numbers; |
| 7831 | +intnnumbers; |
| 7832 | + |
| 7833 | +if (get_attstatsslot(vardata.statsTuple,InvalidOid,0, |
| 7834 | +STATISTIC_KIND_CORRELATION, |
| 7835 | +InvalidOid, |
| 7836 | +NULL, |
| 7837 | +NULL,NULL, |
| 7838 | +&numbers,&nnumbers)) |
| 7839 | +{ |
| 7840 | +doublevarCorrelation=0.0; |
| 7841 | + |
| 7842 | +if (nnumbers>0) |
| 7843 | +varCorrelation=Abs(numbers[0]); |
| 7844 | + |
| 7845 | +if (varCorrelation>*indexCorrelation) |
| 7846 | +*indexCorrelation=varCorrelation; |
| 7847 | + |
| 7848 | +free_attstatsslot(InvalidOid,NULL,0,numbers,nnumbers); |
| 7849 | +} |
| 7850 | +} |
| 7851 | + |
| 7852 | +ReleaseVariableStats(vardata); |
| 7853 | +} |
| 7854 | + |
| 7855 | +qualSelectivity=clauselist_selectivity(root,indexQuals, |
| 7856 | +baserel->relid, |
| 7857 | +JOIN_INNER,NULL, |
| 7858 | +baserel); |
| 7859 | + |
| 7860 | +/* work out the actual number of ranges in the index */ |
| 7861 | +indexRanges=Max(ceil((double)baserel->pages /statsData.pagesPerRange), |
| 7862 | +1.0); |
| 7863 | + |
| 7864 | +/* |
| 7865 | + * Now calculate the minimum possible ranges we could match with if all of |
| 7866 | + * the rows were in the perfect order in the table's heap. |
7745 | 7867 | */
|
7746 |
| -*indexStartupCost=spc_seq_page_cost*numPages*loop_count; |
| 7868 | +minimalRanges=ceil(indexRanges*qualSelectivity); |
7747 | 7869 |
|
7748 | 7870 | /*
|
7749 |
| - *To read a BRIN index there might be a bit of back and forth over |
7750 |
| - *regular pages, as revmap might point tothem out of sequential order; |
7751 |
| - *calculate this as readingthewhole index in random order. |
| 7871 | + *Now estimate the number of ranges that we'll touch by using the |
| 7872 | + *indexCorrelation from the stats. Careful not todivide by zero |
| 7873 | + *(note we're usingtheabsolute value of the correlation). |
7752 | 7874 | */
|
7753 |
| -*indexTotalCost=spc_random_page_cost*numPages*loop_count; |
| 7875 | +if (*indexCorrelation<1.0e-10) |
| 7876 | +estimatedRanges=indexRanges; |
| 7877 | +else |
| 7878 | +estimatedRanges=Min(minimalRanges /*indexCorrelation,indexRanges); |
7754 | 7879 |
|
7755 |
| -*indexSelectivity= |
7756 |
| -clauselist_selectivity(root,indexQuals, |
7757 |
| -path->indexinfo->rel->relid, |
7758 |
| -JOIN_INNER,NULL, |
7759 |
| -path->indexinfo->rel); |
7760 |
| -*indexCorrelation=1; |
| 7880 | +/* we expect to visit this portion of the table */ |
| 7881 | +selec=estimatedRanges /indexRanges; |
| 7882 | + |
| 7883 | +CLAMP_PROBABILITY(selec); |
| 7884 | + |
| 7885 | +*indexSelectivity=selec; |
7761 | 7886 |
|
7762 | 7887 | /*
|
7763 |
| - * Add on index qual eval costs, much as in genericcostestimate. |
| 7888 | + * Compute the index qual costs, much as in genericcostestimate, to add |
| 7889 | + * to the index costs. |
7764 | 7890 | */
|
7765 | 7891 | qual_arg_cost=other_operands_eval_cost(root,qinfos)+
|
7766 | 7892 | orderby_operands_eval_cost(root,path);
|
7767 |
| -qual_op_cost=cpu_operator_cost* |
7768 |
| -(list_length(indexQuals)+list_length(indexOrderBys)); |
7769 | 7893 |
|
| 7894 | +/* |
| 7895 | + * Compute the startup cost as the cost to read the whole revmap |
| 7896 | + * sequentially, including the cost to execute the index quals. |
| 7897 | + */ |
| 7898 | +*indexStartupCost= |
| 7899 | +spc_seq_page_cost*statsData.revmapNumPages*loop_count; |
7770 | 7900 | *indexStartupCost+=qual_arg_cost;
|
7771 |
| -*indexTotalCost+=qual_arg_cost; |
7772 |
| -*indexTotalCost+= (numTuples**indexSelectivity)* (cpu_index_tuple_cost+qual_op_cost); |
7773 |
| -*indexPages=index->pages; |
7774 | 7901 |
|
7775 |
| -/* XXX what about pages_per_range? */ |
| 7902 | +/* |
| 7903 | + * To read a BRIN index there might be a bit of back and forth over |
| 7904 | + * regular pages, as revmap might point to them out of sequential order; |
| 7905 | + * calculate the total cost as reading the whole index in random order. |
| 7906 | + */ |
| 7907 | +*indexTotalCost=*indexStartupCost+ |
| 7908 | +spc_random_page_cost* (numPages-statsData.revmapNumPages)*loop_count; |
| 7909 | + |
| 7910 | +/* |
| 7911 | + * Charge a small amount per range tuple which we expect to match to. This |
| 7912 | + * is meant to reflect the costs of manipulating the bitmap. The BRIN scan |
| 7913 | + * will set a bit for each page in the range when we find a matching |
| 7914 | + * range, so we must multiply the charge by the number of pages in the |
| 7915 | + * range. |
| 7916 | + */ |
| 7917 | +*indexTotalCost+=0.1*cpu_operator_cost*estimatedRanges* |
| 7918 | +statsData.pagesPerRange; |
| 7919 | + |
| 7920 | +*indexPages=index->pages; |
7776 | 7921 | }
|