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
double numPages = index -> pages ;
7726
- double numTuples = index -> tuples ;
7726
+ RelOptInfo * baserel = index -> rel ;
7727
+ RangeTblEntry * rte = planner_rt_fetch (baserel -> relid , root );
7727
7728
List * qinfos ;
7728
7729
Cost spc_seq_page_cost ;
7729
7730
Cost spc_random_page_cost ;
7730
- double qual_op_cost ;
7731
7731
double qual_arg_cost ;
7732
+ double qualSelectivity ;
7733
+ BrinStatsData statsData ;
7734
+ double indexRanges ;
7735
+ double minimalRanges ;
7736
+ double estimatedRanges ;
7737
+ double selec ;
7738
+ Relation indexRel ;
7739
+ ListCell * l ;
7740
+ VariableStatData vardata ;
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 for the tablespace 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
+ AttrNumber attnum = 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
+ int nnumbers ;
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
+ double varCorrelation = 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 to them out of sequential order;
7751
- * calculate this as reading the whole 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 to divide by zero
7873
+ * (note we're using the absolute 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
}
0 commit comments