diff --git a/mysql-test/r/range_estimator.result b/mysql-test/r/range_estimator.result index f0f2df3433f..bd5c8bb4aba 100644 --- a/mysql-test/r/range_estimator.result +++ b/mysql-test/r/range_estimator.result @@ -800,4 +800,41 @@ Note 1003 /* select#1 */ select `test`.`tbl_int`.`col1` AS `col1` from `test`.`t set range_estimation_adjust_threshold = @save_threshold; # col NULL, for D8 and D9 DROP TABLE tbl_int; +# +# postfix keyno should be real_keyno +# +CREATE TABLE tbl_int (col1 INT primary key, col2 INT, key index_col2(col2)); +INSERT INTO tbl_int VALUES (1,1), (2,2), (3,3), (4,4), (5,5), (6,6), (7,7), (8,8), (9,NULL), (10,NULL); +ANALYZE TABLE tbl_int; +Table Op Msg_type Msg_text +test.tbl_int analyze status OK +ANALYZE TABLE tbl_int UPDATE HISTOGRAM ON col2 WITH 10 BUCKETS; +Table Op Msg_type Msg_text +test.tbl_int histogram status Histogram statistics created for column 'col2'. +# Expect "8" in column "rows" +EXPLAIN SELECT * FROM tbl_int WHERE col2 > 0; +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE tbl_int NULL range index_col2 index_col2 5 NULL 8 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`tbl_int`.`col1` AS `col1`,`test`.`tbl_int`.`col2` AS `col2` from `test`.`tbl_int` where (`test`.`tbl_int`.`col2` > 0) +DROP TABLE tbl_int; +# +# postfix range flag with next keypart +# +CREATE TABLE tbl_int (col1 INT, col2 INT, key(col1,col2)); +INSERT INTO tbl_int VALUES (1,1), (1,1), (3,3), (4,4), (5,5), (6,6), (7,7), (8,8), (NULL,NULL), (NULL,NULL); +ANALYZE TABLE tbl_int; +Table Op Msg_type Msg_text +test.tbl_int analyze status OK +ANALYZE TABLE tbl_int UPDATE HISTOGRAM ON col1 WITH 10 BUCKETS; +Table Op Msg_type Msg_text +test.tbl_int histogram status Histogram statistics created for column 'col1'. +set range_estimation = USE_STATISTICS_ONLY; +# Expect "3" in column "rows" (10 * 0.8 * 0.33 = 2.64 = 3) +EXPLAIN SELECT * FROM tbl_int WHERE col1 >= 1 and col2 = 2; +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE tbl_int NULL range col1 col1 10 NULL 3 10.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`tbl_int`.`col1` AS `col1`,`test`.`tbl_int`.`col2` AS `col2` from `test`.`tbl_int` where ((`test`.`tbl_int`.`col2` = 2) and (`test`.`tbl_int`.`col1` >= 1)) +DROP TABLE tbl_int; SET SESSION range_estimation = @savmode; diff --git a/mysql-test/t/range_estimator.test b/mysql-test/t/range_estimator.test index d9ea39d7779..4a4647ee6f0 100644 --- a/mysql-test/t/range_estimator.test +++ b/mysql-test/t/range_estimator.test @@ -317,4 +317,34 @@ set range_estimation_adjust_threshold = @save_threshold; --echo # col NULL, for D8 and D9 DROP TABLE tbl_int; +--echo # +--echo # postfix keyno should be real_keyno +--echo # + +CREATE TABLE tbl_int (col1 INT primary key, col2 INT, key index_col2(col2)); +INSERT INTO tbl_int VALUES (1,1), (2,2), (3,3), (4,4), (5,5), (6,6), (7,7), (8,8), (9,NULL), (10,NULL); +ANALYZE TABLE tbl_int; +ANALYZE TABLE tbl_int UPDATE HISTOGRAM ON col2 WITH 10 BUCKETS; + +--echo # Expect "8" in column "rows" +EXPLAIN SELECT * FROM tbl_int WHERE col2 > 0; + +DROP TABLE tbl_int; + +--echo # +--echo # postfix range flag with next keypart +--echo # + +CREATE TABLE tbl_int (col1 INT, col2 INT, key(col1,col2)); +INSERT INTO tbl_int VALUES (1,1), (1,1), (3,3), (4,4), (5,5), (6,6), (7,7), (8,8), (NULL,NULL), (NULL,NULL); +ANALYZE TABLE tbl_int; +ANALYZE TABLE tbl_int UPDATE HISTOGRAM ON col1 WITH 10 BUCKETS; + +set range_estimation = USE_STATISTICS_ONLY; + +--echo # Expect "3" in column "rows" (10 * 0.8 * 0.33 = 2.64 = 3) +EXPLAIN SELECT * FROM tbl_int WHERE col1 >= 1 and col2 = 2; + +DROP TABLE tbl_int; + SET SESSION range_estimation = @savmode; diff --git a/sql/handler.cc b/sql/handler.cc index 2e7c65b2766..29b15c56981 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6407,11 +6407,10 @@ ha_rows handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, /* Can't scan one range => can't do MRR scan at all */ return HA_POS_ERROR; } - const ha_rows rows_limit = - table->file->stats.records * - table->in_use->variables.range_estimation_adjust_threshold; - if (rows_limit > 0 && rows > rows_limit && - table->range_estimator.use_statistics_adjust()) { + if (table->in_use->variables.range_estimation_adjust_threshold > 0 && + table->range_estimator.use_statistics_adjust() && + rows > (table->in_use->variables.range_estimation_adjust_threshold * + table->file->stats.records)) { ha_rows adjusted_rows = table->range_estimator.records_in_range(range); if (adjusted_rows != HA_POS_ERROR) rows = adjusted_rows; } diff --git a/sql/range_optimizer/index_range_scan_plan.cc b/sql/range_optimizer/index_range_scan_plan.cc index bef070acf95..bf978b17f53 100644 --- a/sql/range_optimizer/index_range_scan_plan.cc +++ b/sql/range_optimizer/index_range_scan_plan.cc @@ -219,7 +219,7 @@ static range_seq_t sel_arg_range_seq_init(void *init_param, uint, uint) { Sel_arg_range_sequence *seq = static_cast(init_param); seq->reset(); - seq->param->table->range_estimator.use_index(seq->keyno); + seq->param->table->range_estimator.use_index(seq->real_keyno); return init_param; } diff --git a/sql/range_optimizer/range_estimator.cc b/sql/range_optimizer/range_estimator.cc index 2758d87f359..455415b40cf 100644 --- a/sql/range_optimizer/range_estimator.cc +++ b/sql/range_optimizer/range_estimator.cc @@ -175,14 +175,35 @@ bool Range_estimator::check_histogram_statistics(KEY_MULTI_RANGE &range) { !histogram_array[0]); // 2) } +// Contains min part +#define KEY_MIN(range, part) \ + ((range).start_key.keypart_map & (1 << (part))) +// Contains min part as a strict prefix +#define KEY_MIN_P(range, part) \ + ((range).start_key.keypart_map & (1 << ((part) + 1))) +// Contains min part as the last, inclusive +#define KEY_MIN_E(range, part) \ + ((range).start_key.keypart_map & (1 << (part)) && \ + !((range).start_key.keypart_map & (1 << ((part) + 1))) && \ + !((range).range_flag & NEAR_MIN)) +// Contains max part +#define KEY_MAX(range, part) \ + ((range).end_key.keypart_map & (1 << (part))) +// Contains max part as a strict prefix +#define KEY_MAX_P(range, part) \ + ((range).end_key.keypart_map & (1 << ((part) + 1))) +// Contains max part as the last, inclusive +#define KEY_MAX_E(range, part) \ + ((range).end_key.keypart_map & (1 << (part)) && \ + !((range).end_key.keypart_map & (1 << ((part) + 1))) && \ + !((range).range_flag & NEAR_MAX)) + bool Range_estimator::get_range_selectivity(const KEY_MULTI_RANGE &range, double &selectivity) { assert(!(range.range_flag & (GEOM_FLAG | SKIP_RANGE))); selectivity = 1.0; uint fld_offset = 0; - key_part_map part_map = - (range.start_key.keypart_map | range.end_key.keypart_map); const KEY *key_info = &table->key_info[keyno]; bool prev_is_null = false; @@ -195,21 +216,44 @@ bool Range_estimator::get_range_selectivity(const KEY_MULTI_RANGE &range, */ fld = down_cast(fld)->get_conv_field(); } - if (!(part_map & (1 << part))) break; + + if (!(KEY_MIN(range, part) || KEY_MAX(range, part))) { + break; + } /* For a range supported by a given key, - (1) All parts except for the last are equal parts, - (2) The combined range flag effectively reflects only the last part, - which can be either equal or range. + - All parts except for the last are equal parts, + - The combined range flag effectively reflects only the last part, + which can be either equal or range. + As a result, a field range is an equality range if + (1) The field is part of the strict prefix of effective key parts of + both ends. + (2) The field is the last part of one end, it must be both end inclusive + and containing same value. */ - uint flag = - (part_map & (1 << (part + 1))) ? EQ_RANGE : range.range_flag; + uint flag; + if ((KEY_MIN_P(range, part) && KEY_MAX_P(range, part)) || // (1) + (((KEY_MIN_P(range, part) && KEY_MAX_E(range, part)) || // (2) + (KEY_MIN_E(range, part) && KEY_MAX_P(range, part)) || + (KEY_MIN_E(range, part) && KEY_MAX_E(range, part))) && + !memcmp(range.start_key.key + fld_offset, + range.end_key.key + fld_offset, + key_info->key_part[part].store_length))) { + flag = EQ_RANGE; + } else { + flag = range.range_flag; + } + /* + When it is an equality field range, the flag must be both end present, + inclusive, and same. + */ assert(!(flag & EQ_RANGE) || !(flag & (NO_MIN_RANGE | NO_MAX_RANGE | NEAR_MIN | NEAR_MAX)) && !memcmp(range.start_key.key + fld_offset, range.end_key.key + fld_offset, key_info->key_part[part].store_length)); + if ((flag & EQ_RANGE) && fld->is_nullable() && *range.start_key.key) flag |= NULL_RANGE; @@ -280,3 +324,9 @@ bool Range_estimator::get_range_selectivity(const KEY_MULTI_RANGE &range, return false; } +#undef KEY_MIN +#undef KEY_MIN_P +#undef KEY_MIN_E +#undef KEY_MAX +#undef KEY_MAX_P +#undef KEY_MAX_E