diff --git a/sql/handler.cc b/sql/handler.cc index f6972876732..1f9847a8089 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6405,18 +6405,18 @@ ha_rows handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, case. Also note: ranges of the form "x IS NULL" may have more than 1 matching row so records_in_range() is called for these. 2) SKIP_RECORDS_IN_RANGE is set. - There are supportive statistics and the user requested to use - statistics instead of records_in_range(); or the user is not - interested in cost calculation at all. + A user request to skip records_in_range() for this range, + either by using FORCE INDEX or by setting use_index_statistics, + was accepted. */ int keyparts_used = 0; if ((range.range_flag & UNIQUE_RANGE) && // 1) !(range.range_flag & NULL_RANGE)) rows = 1; /* there can be at most one row */ - else if (range.range_flag & SKIP_RECORDS_IN_RANGE) { // 2) - if ((range.range_flag & EQ_RANGE) && - !(range.range_flag & NULL_RANGE) && - (keyparts_used = std::popcount(range.start_key.keypart_map))) { + else if (range.range_flag & SKIP_RECORDS_IN_RANGE) { // 2) + if (can_use_index_statistics(table, keyno, range.range_flag, + range.start_key.keypart_map, + &keyparts_used)) { rows = static_cast( table->key_info[keyno].records_per_key(keyparts_used - 1)); } else { @@ -9080,6 +9080,16 @@ bool is_index_access_error(int error) { return (error != HA_ERR_END_OF_FILE && error != HA_ERR_KEY_NOT_FOUND); } +bool can_use_index_statistics(const TABLE *table, uint keyno, uint range_flag, + key_part_map keypart_map, int *keyparts_used) { + *keyparts_used = std::popcount(keypart_map); + return (range_flag & EQ_RANGE) && // 1) Equality range + !(range_flag & NULL_RANGE) && // 2) No NULL parts + *keyparts_used > 0 && // 3a) At least one keypart + table->key_info[keyno].has_records_per_key( + *keyparts_used - 1); // 3b) Statistics available +} + Xa_state_list::Xa_state_list(Xa_state_list::list &populated_by_tc) : m_underlying{populated_by_tc} {} diff --git a/sql/handler.h b/sql/handler.h index 810d2995256..faee58ea59e 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -7792,6 +7793,30 @@ std::pair commit_owned_gtids(THD *thd, bool all); bool set_tx_isolation(THD *thd, enum_tx_isolation tx_isolation, bool one_shot); bool is_index_access_error(int error); +/** + Check if index statistics can be used for an equality range. + + Index statistics are suitable for equality ranges when: + 1) It is an equality range (EQ_RANGE flag set) + 2) It contains no NULL parts (NULL_RANGE flag not set) + 3) Index statistics are available for the required keyparts + + Ranges of the form "x IS NULL" will not use index statistics because + the number of rows with NULL values are likely to be very different + than the values in the index statistics. + + @param table The table structure + @param keyno The index number + @param range_flag The range flags (EQ_RANGE, NULL_RANGE, etc.) + @param keypart_map Bitmap of keyparts used in the range + @param[out] keyparts_used Number of keyparts used (output parameter) + + @retval true Index statistics can be used for this range + @retval false Index statistics cannot be used for this range +*/ +bool can_use_index_statistics(const TABLE *table, uint keyno, uint range_flag, + key_part_map keypart_map, int *keyparts_used); + /* This class is used by INFORMATION_SCHEMA.FILES to read SE specific tablespace dynamic metadata. Some member like m_type and id, is not diff --git a/sql/range_optimizer/index_range_scan_plan.cc b/sql/range_optimizer/index_range_scan_plan.cc index 891eb13bbbb..fda7c42ff84 100644 --- a/sql/range_optimizer/index_range_scan_plan.cc +++ b/sql/range_optimizer/index_range_scan_plan.cc @@ -555,24 +555,23 @@ static uint sel_arg_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range) { /* Use statistics instead of index dives for estimates of rows in - this range if: + this range if the user has requested it (use_index_statistics) and + the range is suitable for index statistics. - 1) The user has requested it. - 2) Index statistics is supportive for the range, which means - a) It is an equality range. - b) It contains no null part. - 3) Index statistics is available. + The can_use_index_statistics() function checks whether index statistics + are supportive for the range, which requires: + a) It is an equality range. + b) It contains no null part. + c) Index statistics are available. Ranges of the form "x IS NULL" will not use index statistics - because the number of rows with this value are likely to be + because the number of rows with NULL values are likely to be very different than the values in the index statistics. */ int keyparts_used = 0; - if (param->use_index_statistics && // 1) - (range->range_flag & EQ_RANGE) && // 2a) - !(range->range_flag & NULL_RANGE) && // 2b) - (keyparts_used = std::popcount(range->start_key.keypart_map)) && // 3) - param->table->key_info[seq->keyno].has_records_per_key(keyparts_used - 1)) + if (param->use_index_statistics && + can_use_index_statistics(param->table, seq->keyno, range->range_flag, + range->start_key.keypart_map, &keyparts_used)) range->range_flag |= SKIP_RECORDS_IN_RANGE; if (seq->skip_records_in_range) range->range_flag |= SKIP_RECORDS_IN_RANGE;