diff options
author | Varun Gupta <varun.gupta@mariadb.com> | 2020-05-22 21:15:17 +0530 |
---|---|---|
committer | Varun Gupta <varun.gupta@mariadb.com> | 2020-06-18 20:15:06 +0530 |
commit | 4c3cbe23928029288ee1e6fd2fdb957f78a3240f (patch) | |
tree | 607a5725ecb117f0841f42172250c9cecb337bd7 | |
parent | 205b0ce6ad21dbafe8def505307b4922398db5b2 (diff) | |
download | mariadb-git-4c3cbe23928029288ee1e6fd2fdb957f78a3240f.tar.gz |
MDEV-22665: Print ranges in the optimizer trace created for non-indexed columns when optimizer_use_condition_selectivity >2
Now the optimizer trace shows the ranges constructed while getting estimates from EITS
-rw-r--r-- | mysql-test/main/opt_trace.result | 96 | ||||
-rw-r--r-- | mysql-test/main/opt_trace.test | 18 | ||||
-rw-r--r-- | sql/field.cc | 40 | ||||
-rw-r--r-- | sql/field.h | 1 | ||||
-rw-r--r-- | sql/opt_range.cc | 123 |
5 files changed, 240 insertions, 38 deletions
diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result index be358e69c47..6da22802cca 100644 --- a/mysql-test/main/opt_trace.result +++ b/mysql-test/main/opt_trace.result @@ -108,6 +108,7 @@ select * from v1 { "selectivity_for_columns": [ { "column_name": "a", + "ranges": ["1 <= a <= 1"], "selectivity_from_histogram": 0.5 } ], @@ -253,6 +254,7 @@ select * from (select * from t1 where t1.a=1)q { "selectivity_for_columns": [ { "column_name": "a", + "ranges": ["1 <= a <= 1"], "selectivity_from_histogram": 0.5 } ], @@ -403,6 +405,7 @@ select * from v2 { "selectivity_for_columns": [ { "column_name": "a", + "ranges": ["1 <= a <= 1"], "selectivity_from_histogram": 0.5 } ], @@ -1416,10 +1419,12 @@ EXPLAIN SELECT MIN(d) FROM t1 where b=2 and c=3 group by a { "selectivity_for_columns": [ { "column_name": "b", + "ranges": ["2 <= b <= 2"], "selectivity_from_histogram": 0.2891 }, { "column_name": "c", + "ranges": ["3 <= c <= 3"], "selectivity_from_histogram": 0.2891 } ], @@ -2091,10 +2096,12 @@ explain select * from t1 where a=1 and b=2 order by c limit 1 { "selectivity_for_columns": [ { "column_name": "a", + "ranges": ["1 <= a <= 1"], "selectivity_from_histogram": 0.1797 }, { "column_name": "b", + "ranges": ["2 <= b <= 2"], "selectivity_from_histogram": 0.0156 } ], @@ -3310,10 +3317,12 @@ explain select * from t1 where pk = 2 and a=5 and b=1 { "selectivity_for_columns": [ { "column_name": "a", + "ranges": ["5 <= a <= 5"], "selectivity_from_histogram": 0.1 }, { "column_name": "b", + "ranges": ["1 <= b <= 1"], "selectivity_from_histogram": 0.1 } ], @@ -8465,5 +8474,90 @@ JSON_DETAILED(JSON_EXTRACT(trace, '$**.considered_execution_plans')) ] ] DROP TABLE t1,t2; -# End of 10.4 tests +# +# MDEV-22665: Print ranges in the optimizer trace created for non-indexed columns when +# optimizer_use_condition_selectivity >2 +# +CREATE TABLE t1(a INT, b INT); +INSERT INTO t1 SELECT seq, seq from seq_1_to_100; +SET optimizer_trace=1; +ANALYZE TABLE t1 PERSISTENT FOR ALL; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +EXPLAIN EXTENDED SELECT * from t1 WHERE a between 1 and 5 and b <= 5; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 0.22 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where `test`.`t1`.`a` between 1 and 5 and `test`.`t1`.`b` <= 5 +select JSON_DETAILED(JSON_EXTRACT(trace, '$**.selectivity_for_columns')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE; +JSON_DETAILED(JSON_EXTRACT(trace, '$**.selectivity_for_columns')) +[ + + [ + + { + "column_name": "a", + "ranges": + [ + "1 <= a <= 5" + ], + "selectivity_from_histogram": 0.0469 + }, + + { + "column_name": "b", + "ranges": + [ + "NULL < b <= 5" + ], + "selectivity_from_histogram": 0.0469 + } + ] +] +EXPLAIN EXTENDED SELECT * from t1 WHERE a != 5; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where `test`.`t1`.`a` <> 5 +select JSON_DETAILED(JSON_EXTRACT(trace, '$**.selectivity_for_columns')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE; +JSON_DETAILED(JSON_EXTRACT(trace, '$**.selectivity_for_columns')) +[ + + [ + + { + "column_name": "a", + "ranges": + [ + "NULL < a < 5", + "5 < a" + ], + "selectivity_from_histogram": 1 + } + ] +] +EXPLAIN EXTENDED SELECT * from t1 WHERE b >= 10 and b < 25; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 15.62 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where `test`.`t1`.`b` >= 10 and `test`.`t1`.`b` < 25 +select JSON_DETAILED(JSON_EXTRACT(trace, '$**.selectivity_for_columns')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE; +JSON_DETAILED(JSON_EXTRACT(trace, '$**.selectivity_for_columns')) +[ + + [ + + { + "column_name": "b", + "ranges": + [ + "10 <= b < 25" + ], + "selectivity_from_histogram": 0.1562 + } + ] +] +drop table t1; set optimizer_trace='enabled=off'; +# End of 10.4 tests diff --git a/mysql-test/main/opt_trace.test b/mysql-test/main/opt_trace.test index d1a8fedc635..8633ed5b020 100644 --- a/mysql-test/main/opt_trace.test +++ b/mysql-test/main/opt_trace.test @@ -594,6 +594,22 @@ EXPLAIN SELECT * FROM t1, t2 WHERE t1.a=t2.a ORDER BY t2.b; select JSON_DETAILED(JSON_EXTRACT(trace, '$**.considered_execution_plans')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE; DROP TABLE t1,t2; ---echo # End of 10.4 tests +--echo # +--echo # MDEV-22665: Print ranges in the optimizer trace created for non-indexed columns when +--echo # optimizer_use_condition_selectivity >2 +--echo # + +CREATE TABLE t1(a INT, b INT); +INSERT INTO t1 SELECT seq, seq from seq_1_to_100; +SET optimizer_trace=1; +ANALYZE TABLE t1 PERSISTENT FOR ALL; +EXPLAIN EXTENDED SELECT * from t1 WHERE a between 1 and 5 and b <= 5; +select JSON_DETAILED(JSON_EXTRACT(trace, '$**.selectivity_for_columns')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE; +EXPLAIN EXTENDED SELECT * from t1 WHERE a != 5; +select JSON_DETAILED(JSON_EXTRACT(trace, '$**.selectivity_for_columns')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE; +EXPLAIN EXTENDED SELECT * from t1 WHERE b >= 10 and b < 25; +select JSON_DETAILED(JSON_EXTRACT(trace, '$**.selectivity_for_columns')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE; +drop table t1; set optimizer_trace='enabled=off'; +--echo # End of 10.4 tests diff --git a/sql/field.cc b/sql/field.cc index 3f4e1e73029..21145cdf32a 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -11400,6 +11400,46 @@ void Field_blob::print_key_value(String *out, uint32 length) } +/* + @brief Print value of the key part + + @param + out Output string + key value of the key + length Length of field in bytes, + excluding NULL flag and length bytes +*/ + + +void +Field::print_key_part_value(String *out, const uchar* key, uint32 length) +{ + StringBuffer<128> tmp(system_charset_info); + uint null_byte= 0; + if (real_maybe_null()) + { + /* + Byte 0 of key is the null-byte. If set, key is NULL. + Otherwise, print the key value starting immediately after the + null-byte + */ + if (*key) + { + out->append(STRING_WITH_LEN("NULL")); + return; + } + null_byte++; // Skip null byte + } + + set_key_image(key + null_byte, length); + print_key_value(&tmp, length); + if (charset() == &my_charset_bin) + out->append(tmp.ptr(), tmp.length(), tmp.charset()); + else + tmp.print(out, system_charset_info); +} + + void Field::print_key_value_binary(String *out, const uchar* key, uint32 length) { out->append_semi_hex((const char*)key, length, charset()); diff --git a/sql/field.h b/sql/field.h index 7393def96b1..1d421abe880 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1410,6 +1410,7 @@ public: bool set_warning(Sql_condition::enum_warning_level, unsigned int code, int cuted_increment, ulong current_row=0) const; virtual void print_key_value(String *out, uint32 length); + void print_key_part_value(String *out, const uchar *key, uint32 length); void print_key_value_binary(String *out, const uchar* key, uint32 length); protected: bool set_warning(unsigned int code, int cuted_increment) const diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 690657616b8..ecbf5d6afcb 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -442,6 +442,13 @@ static void print_range(String *out, const KEY_PART_INFO *key_part, KEY_MULTI_RANGE *range, uint n_key_parts); +static +void print_range_for_non_indexed_field(String *out, Field *field, + KEY_MULTI_RANGE *range); + +static void print_min_range_operator(String *out, const ha_rkey_function flag); +static void print_max_range_operator(String *out, const ha_rkey_function flag); + /* SEL_IMERGE is a list of possible ways to do index merge, i.e. it is @@ -3174,6 +3181,7 @@ static double records_in_column_ranges(PARAM *param, uint idx, SEL_ARG *tree) { + THD *thd= param->thd; SEL_ARG_RANGE_SEQ seq; KEY_MULTI_RANGE range; range_seq_t seq_it; @@ -3200,6 +3208,8 @@ double records_in_column_ranges(PARAM *param, uint idx, seq_it= seq_if.init((void *) &seq, 0, flags); + Json_writer_array range_trace(thd, "ranges"); + while (!seq_if.next(seq_it, &range)) { key_range *min_endp, *max_endp; @@ -3216,6 +3226,13 @@ double records_in_column_ranges(PARAM *param, uint idx, if (range.start_key.flag == HA_READ_BEFORE_KEY) range_flag |= NEAR_MAX; + if (unlikely(thd->trace_started())) + { + StringBuffer<128> range_info(system_charset_info); + print_range_for_non_indexed_field(&range_info, field, &range); + range_trace.add(range_info.c_ptr_safe(), range_info.length()); + } + rows= get_column_range_cardinality(field, min_endp, max_endp, range_flag); if (DBL_MAX == rows) { @@ -15804,6 +15821,37 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose) #endif /* !DBUG_OFF */ + +/* + @brief Print the comparison operator for the min range +*/ + +static void print_min_range_operator(String *out, const ha_rkey_function flag) +{ + if (flag == HA_READ_AFTER_KEY) + out->append(STRING_WITH_LEN(" < ")); + else if (flag == HA_READ_KEY_EXACT || flag == HA_READ_KEY_OR_NEXT) + out->append(STRING_WITH_LEN(" <= ")); + else + out->append(STRING_WITH_LEN(" ? ")); +} + + +/* + @brief Print the comparison operator for the max range +*/ + +static void print_max_range_operator(String *out, const ha_rkey_function flag) +{ + if (flag == HA_READ_BEFORE_KEY) + out->append(STRING_WITH_LEN(" < ")); + else if (flag == HA_READ_AFTER_KEY) + out->append(STRING_WITH_LEN(" <= ")); + else + out->append(STRING_WITH_LEN(" ? ")); +} + + static void print_range(String *out, const KEY_PART_INFO *key_part, KEY_MULTI_RANGE *range, uint n_key_parts) @@ -15832,30 +15880,55 @@ void print_range(String *out, const KEY_PART_INFO *key_part, { print_key_value(out, key_part, range->start_key.key, range->start_key.length); - if (range->start_key.flag == HA_READ_AFTER_KEY) - out->append(STRING_WITH_LEN(" < ")); - else if (range->start_key.flag == HA_READ_KEY_EXACT || - range->start_key.flag == HA_READ_KEY_OR_NEXT) - out->append(STRING_WITH_LEN(" <= ")); - else - out->append(STRING_WITH_LEN(" ? ")); + print_min_range_operator(out, range->start_key.flag); } print_keyparts_name(out, key_part, n_key_parts, keypart_map); if (range->end_key.length) { - if (range->end_key.flag == HA_READ_BEFORE_KEY) - out->append(STRING_WITH_LEN(" < ")); - else if (range->end_key.flag == HA_READ_AFTER_KEY) - out->append(STRING_WITH_LEN(" <= ")); - else - out->append(STRING_WITH_LEN(" ? ")); + print_max_range_operator(out, range->end_key.flag); print_key_value(out, key_part, range->end_key.key, range->end_key.length); } } + +/* + @brief Print range created for non-indexed columns + + @param + out output string + field field for which the range is printed + range range for the field +*/ + +static +void print_range_for_non_indexed_field(String *out, Field *field, + KEY_MULTI_RANGE *range) +{ + TABLE *table= field->table; + my_bitmap_map *old_sets[2]; + dbug_tmp_use_all_columns(table, old_sets, table->read_set, table->write_set); + + if (range->start_key.length) + { + field->print_key_part_value(out, range->start_key.key, field->key_length()); + print_min_range_operator(out, range->start_key.flag); + } + + out->append(field->field_name); + + if (range->end_key.length) + { + print_max_range_operator(out, range->end_key.flag); + field->print_key_part_value(out, range->end_key.key, field->key_length()); + } + dbug_tmp_restore_column_maps(table->read_set, table->write_set, old_sets); +} + + + /* Add ranges to the trace @@ -15927,30 +16000,8 @@ static void print_key_value(String *out, const KEY_PART_INFO *key_part, field= key_part->field; store_length= key_part->store_length; - if (field->real_maybe_null()) - { - /* - Byte 0 of key is the null-byte. If set, key is NULL. - Otherwise, print the key value starting immediately after the - null-byte - */ - if (*key) - { - out->append(STRING_WITH_LEN("NULL")); - goto next; - } - key++; // Skip null byte - store_length--; - } - - field->set_key_image(key, key_part->length); - field->print_key_value(&tmp, key_part->length); - if (field->charset() == &my_charset_bin) - out->append(tmp.ptr(), tmp.length(), tmp.charset()); - else - tmp.print(out, system_charset_info); + field->print_key_part_value(out, key, key_part->length); - next: if (key + store_length < key_end) out->append(STRING_WITH_LEN(",")); } |