mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
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
This commit is contained in:
@@ -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
|
||||
|
@@ -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
|
||||
|
40
sql/field.cc
40
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());
|
||||
|
@@ -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
|
||||
|
123
sql/opt_range.cc
123
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->print_key_part_value(out, key, key_part->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);
|
||||
|
||||
next:
|
||||
if (key + store_length < key_end)
|
||||
out->append(STRING_WITH_LEN(","));
|
||||
}
|
||||
|
Reference in New Issue
Block a user