[Commits] 9329e97db7e: MDEV-18899: Server crashes in Field::set_warning_truncated_wrong_value
revision-id: 9329e97db7ede719c917ab4a1a9932d03c29fd12 (mariadb-10.2.22-91-g9329e97db7e) parent(s): 50a8fc52988d13a5164a1a542b9d7a85e3ecc1c1 author: Varun Gupta committer: Varun Gupta timestamp: 2019-03-23 14:26:21 +0530 message: MDEV-18899: Server crashes in Field::set_warning_truncated_wrong_value To fix the crash there were 2 steps: 1) Set table_field->table to a non-null pointer for min and max fields which are created in function create_min_max_statistical_fields_for_table_share() 2) Fix writing code to write only full multi-byte sequences --- mysql-test/r/stat_tables.result | 25 +++++++++++++++++++++++++ mysql-test/r/stat_tables_innodb.result | 23 +++++++++++++++++++++++ mysql-test/t/stat_tables.test | 21 +++++++++++++++++++++ sql/field.cc | 16 ++++++++++++++++ sql/field.h | 5 +++++ sql/sql_statistics.cc | 16 ++++++++++++++-- 6 files changed, 104 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/stat_tables.result b/mysql-test/r/stat_tables.result index 3ebc3b47833..f147fe79b84 100644 --- a/mysql-test/r/stat_tables.result +++ b/mysql-test/r/stat_tables.result @@ -624,4 +624,29 @@ SELECT MAX(pk) FROM t1; MAX(pk) NULL DROP TABLE t1; +# +# MDEV-18899: Server crashes in Field::set_warning_truncated_wrong_value +# +set names utf8; +set use_stat_tables=complementary; +create table t1 ( a varchar(255) character set utf8); +insert into t1 values ('ӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥ'); +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +select HEX(RIGHT(min_value, 1)) , length(min_value) from mysql.column_stats; +HEX(RIGHT(min_value, 1)) length(min_value) +A5 254 +set @save_sql_mode= @@sql_mode; +set sql_mode='ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; +update mysql.column_stats set min_value= 'ӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥ'; +Warnings: +Warning 1265 Data truncated for column 'min_value' at row 1 +select HEX(RIGHT(min_value, 1)), length(min_value) from mysql.column_stats; +HEX(RIGHT(min_value, 1)) length(min_value) +D3 255 +drop table t1; +set names latin1; +set @@sql_mode= @save_sql_mode; set use_stat_tables=@save_use_stat_tables; diff --git a/mysql-test/r/stat_tables_innodb.result b/mysql-test/r/stat_tables_innodb.result index a6c5525a0d3..986e01981dd 100644 --- a/mysql-test/r/stat_tables_innodb.result +++ b/mysql-test/r/stat_tables_innodb.result @@ -651,6 +651,29 @@ SELECT MAX(pk) FROM t1; MAX(pk) NULL DROP TABLE t1; +# +# MDEV-18899: Server crashes in Field::set_warning_truncated_wrong_value +# +set names utf8; +set use_stat_tables=complementary; +create table t1 ( a varchar(255) character set utf8); +insert into t1 values ('ӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥ'); +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +select HEX(RIGHT(min_value, 1)) , length(min_value) from mysql.column_stats; +HEX(RIGHT(min_value, 1)) length(min_value) +A5 254 +set sql_mode=''; +update mysql.column_stats set min_value= 'ӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥ'; +Warnings: +Warning 1265 Data truncated for column 'min_value' at row 1 +select HEX(RIGHT(min_value, 1)), length(min_value) from mysql.column_stats; +HEX(RIGHT(min_value, 1)) length(min_value) +D3 255 +drop table t1; +set names latin1; set use_stat_tables=@save_use_stat_tables; set optimizer_switch=@save_optimizer_switch_for_stat_tables_test; SET SESSION STORAGE_ENGINE=DEFAULT; diff --git a/mysql-test/t/stat_tables.test b/mysql-test/t/stat_tables.test index b89ab2bbd2d..0c264c289e4 100644 --- a/mysql-test/t/stat_tables.test +++ b/mysql-test/t/stat_tables.test @@ -401,4 +401,25 @@ SELECT MAX(pk) FROM t1; DROP TABLE t1; +--echo # +--echo # MDEV-18899: Server crashes in Field::set_warning_truncated_wrong_value +--echo # + +set names utf8; +set use_stat_tables=complementary; + +create table t1 ( a varchar(255) character set utf8); +insert into t1 values ('ӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥ'); + +analyze table t1; +select HEX(RIGHT(min_value, 1)) , length(min_value) from mysql.column_stats; + +set @save_sql_mode= @@sql_mode; +set sql_mode='ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; +update mysql.column_stats set min_value= 'ӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥ'; +select HEX(RIGHT(min_value, 1)), length(min_value) from mysql.column_stats; + +drop table t1; +set names latin1; +set @@sql_mode= @save_sql_mode; set use_stat_tables=@save_use_stat_tables; diff --git a/sql/field.cc b/sql/field.cc index 080cf34c76d..652a1ad296e 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2219,6 +2219,22 @@ bool Field_str::can_be_substituted_to_equal_item(const Context &ctx, return false; } +uint32 Field_str::actual_length(const char *str, uint32 length, CHARSET_INFO *cs) +{ + uint mb_len; + set_if_smaller(length, field_length); + const char *from= str; + const char *end= str + length; + while (from < end) + { + mb_len= my_charlen(cs, from, end); + if (mb_len <= 0 || from + mb_len > end) + break; + from= from + mb_len; + } + return from - str; +} + void Field_num::make_field(Send_field *field) { diff --git a/sql/field.h b/sql/field.h index 5a1ec2df8d0..c34dbd3a6f5 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1537,6 +1537,10 @@ class Field: public Value_source /* Mark field in read map. Updates also virtual fields */ void register_field_in_read_map(); + virtual uint32 actual_length(const char* str, uint32 length, CHARSET_INFO *cs) + { + return length; + } friend int cre_myisam(char * name, TABLE *form, uint options, ulonglong auto_increment_value); @@ -1752,6 +1756,7 @@ class Field_str :public Field { return pos_in_interval_val_str(min, max, length_size()); } bool test_if_equality_guarantees_uniqueness(const Item *const_item) const; + uint32 actual_length(const char* str, uint32 length, CHARSET_INFO *cs); }; /* base class for Field_string, Field_varstring and Field_blob */ diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index b5811c683e8..f7dd587aa1f 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -1044,6 +1044,7 @@ class Column_stat: public Stat_table { char buff[MAX_FIELD_WIDTH]; String val(buff, sizeof(buff), &my_charset_bin); + uint32 length= 0; for (uint i= COLUMN_STAT_MIN_VALUE; i <= COLUMN_STAT_HISTOGRAM; i++) { @@ -1060,7 +1061,8 @@ class Column_stat: public Stat_table else { table_field->collected_stats->min_value->val_str(&val); - stat_field->store(val.ptr(), val.length(), &my_charset_bin); + length= stat_field->actual_length(val.ptr(), val.length(), val.charset()); + stat_field->store(val.ptr(), length, &my_charset_bin); } break; case COLUMN_STAT_MAX_VALUE: @@ -1069,7 +1071,8 @@ class Column_stat: public Stat_table else { table_field->collected_stats->max_value->val_str(&val); - stat_field->store(val.ptr(), val.length(), &my_charset_bin); + length= stat_field->actual_length(val.ptr(), val.length(), val.charset()); + stat_field->store(val.ptr(), length, &my_charset_bin); } break; case COLUMN_STAT_NULLS_RATIO: @@ -2935,6 +2938,14 @@ int update_statistics_for_table(THD *thd, TABLE *table) } +void set_min_max_fields_table(Field* field, TABLE *table) +{ + if (field->read_stats->min_value) + field->read_stats->min_value->table= table; + if (field->read_stats->max_value) + field->read_stats->max_value->table= table; +} + /** @brief Read statistics for a table from the persistent statistical tables @@ -2994,6 +3005,7 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables) for (field_ptr= table_share->field; *field_ptr; field_ptr++) { table_field= *field_ptr; + set_min_max_fields_table(table_field, table); column_stat.set_key_fields(table_field); column_stat.get_stat_values(); total_hist_size+= table_field->read_stats->histogram.get_size();
participants (1)
-
Varun