[Commits] b73e7b0bc1e: MDEV-18899: Server crashes in Field::set_warning_truncated_wrong_value
revision-id: b73e7b0bc1e6c7a9c62e1c22a026244fd58e1833 (mariadb-10.2.22-91-gb73e7b0bc1e) parent(s): 50a8fc52988d13a5164a1a542b9d7a85e3ecc1c1 author: Varun Gupta committer: Varun Gupta timestamp: 2019-03-26 14:36:58 +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 | 39 ++++++++++++++++++++++++++++++++++ mysql-test/r/stat_tables_innodb.result | 39 ++++++++++++++++++++++++++++++++++ mysql-test/t/stat_tables.test | 31 +++++++++++++++++++++++++++ sql/field.cc | 7 +++--- sql/sql_statistics.cc | 34 +++++++++++++++++++++++++++-- sql/sql_statistics.h | 2 ++ 6 files changed, 146 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/stat_tables.result b/mysql-test/r/stat_tables.result index 3ebc3b47833..4ab69c4c3c9 100644 --- a/mysql-test/r/stat_tables.result +++ b/mysql-test/r/stat_tables.result @@ -624,4 +624,43 @@ 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 @save_optimizer_use_condition_selectivity=@@optimizer_use_condition_selectivity; +set optimizer_use_condition_selectivity=4; +set use_stat_tables=preferably; +set @save_histogram_size= @@histogram_size; +set histogram_size=255; +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) +A7 254 +select HEX(RIGHT(max_value, 1)), length(max_value) from mysql.column_stats; +HEX(RIGHT(max_value, 1)) length(max_value) +A5 254 +analyze +select * from t1 where a >= 'ӥ'; +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 2.00 50.00 50.00 Using where +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 +analyze +select * from t1 where a >= 'ӥ'; +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 2.00 50.00 50.00 Using where +set names latin1; +set @@sql_mode= @save_sql_mode; set use_stat_tables=@save_use_stat_tables; +set @@histogram_size= @save_histogram_size; +set @@optimizer_use_condition_selectivity=@save_optimizer_use_condition_selectivity; +drop table t1; diff --git a/mysql-test/r/stat_tables_innodb.result b/mysql-test/r/stat_tables_innodb.result index a6c5525a0d3..f4801019508 100644 --- a/mysql-test/r/stat_tables_innodb.result +++ b/mysql-test/r/stat_tables_innodb.result @@ -651,6 +651,45 @@ 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 @save_optimizer_use_condition_selectivity=@@optimizer_use_condition_selectivity; +set optimizer_use_condition_selectivity=4; +set use_stat_tables=preferably; +set @save_histogram_size= @@histogram_size; +set histogram_size=255; +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) +A7 254 +select HEX(RIGHT(max_value, 1)), length(max_value) from mysql.column_stats; +HEX(RIGHT(max_value, 1)) length(max_value) +A5 254 +analyze +select * from t1 where a >= 'ӥ'; +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 2.00 50.00 50.00 Using where +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 +analyze +select * from t1 where a >= 'ӥ'; +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 2.00 50.00 50.00 Using where +set names latin1; +set @@sql_mode= @save_sql_mode; set use_stat_tables=@save_use_stat_tables; +set @@histogram_size= @save_histogram_size; +set @@optimizer_use_condition_selectivity=@save_optimizer_use_condition_selectivity; +drop table t1; 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..dee4e911623 100644 --- a/mysql-test/t/stat_tables.test +++ b/mysql-test/t/stat_tables.test @@ -401,4 +401,35 @@ 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 @save_optimizer_use_condition_selectivity=@@optimizer_use_condition_selectivity; +set optimizer_use_condition_selectivity=4; +set use_stat_tables=preferably; +set @save_histogram_size= @@histogram_size; +set histogram_size=255; + +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; +select HEX(RIGHT(max_value, 1)), length(max_value) from mysql.column_stats; +analyze +select * from t1 where a >= 'ӥ'; + +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= 'ӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥӥ'; + +analyze +select * from t1 where a >= 'ӥ'; + +set names latin1; +set @@sql_mode= @save_sql_mode; set use_stat_tables=@save_use_stat_tables; +set @@histogram_size= @save_histogram_size; +set @@optimizer_use_condition_selectivity=@save_optimizer_use_condition_selectivity; +drop table t1; diff --git a/sql/field.cc b/sql/field.cc index 080cf34c76d..118710410df 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2219,7 +2219,6 @@ bool Field_str::can_be_substituted_to_equal_item(const Context &ctx, return false; } - void Field_num::make_field(Send_field *field) { Field::make_field(field); @@ -4332,7 +4331,7 @@ longlong Field_long::val_int(void) ASSERT_COLUMN_MARKED_FOR_READ; int32 j; /* See the comment in Field_long::store(long long) */ - DBUG_ASSERT(!table || table->in_use == current_thd); + DBUG_ASSERT(table->in_use == current_thd); j=sint4korr(ptr); return unsigned_flag ? (longlong) (uint32) j : (longlong) j; } @@ -7087,7 +7086,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) String_copier copier; /* See the comment for Field_long::store(long long) */ - DBUG_ASSERT(!table || table->in_use == current_thd); + DBUG_ASSERT(table->in_use == current_thd); copy_length= copier.well_formed_copy(field_charset, (char*) ptr, field_length, @@ -7273,7 +7272,7 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)), { ASSERT_COLUMN_MARKED_FOR_READ; /* See the comment for Field_long::store(long long) */ - DBUG_ASSERT(!table || table->in_use == current_thd); + DBUG_ASSERT(table->in_use == current_thd); uint length; if (get_thd()->variables.sql_mode & MODE_PAD_CHAR_TO_FULL_LENGTH) diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index b5811c683e8..dd9070e5bc0 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,9 @@ 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= Well_formed_prefix(val.charset(), val.ptr(), + MY_MIN(val.length(), stat_field->field_length)).length(); + stat_field->store(val.ptr(), length, &my_charset_bin); } break; case COLUMN_STAT_MAX_VALUE: @@ -1069,7 +1072,9 @@ 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= Well_formed_prefix(val.charset(), val.ptr(), + MY_MIN(val.length(), stat_field->field_length)).length(); + stat_field->store(val.ptr(), length, &my_charset_bin); } break; case COLUMN_STAT_NULLS_RATIO: @@ -2935,6 +2940,25 @@ 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; +} + +void check_stat_tables_owner(THD *thd, TABLE_LIST *table_list) +{ + for ( ; table_list ; table_list= table_list->next_global) + { + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, + table_list->db, + table_list->table_name, + MDL_SHARED_READ)); + } +} + /** @brief Read statistics for a table from the persistent statistical tables @@ -2978,8 +3002,10 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables) KEY *key_info, *key_info_end; TABLE_SHARE *table_share= table->s; Table_statistics *read_stats= table_share->stats_cb.table_stats; + TABLE_LIST *table_list= stat_tables; DBUG_ENTER("read_statistics_for_table"); + check_stat_tables_owner(thd, stat_tables); /* Read statistics from the statistical table table_stats */ stat_table= stat_tables[TABLE_STAT].table; @@ -2994,6 +3020,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(); @@ -3059,6 +3086,9 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables) } } } + + for (field_ptr= table_share->field; *field_ptr; field_ptr++) + set_min_max_fields_table(table_field, NULL); table->stats_is_read= TRUE; diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index 8600db8890d..0912d89b004 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -113,6 +113,8 @@ double get_column_range_cardinality(Field *field, uint range_flag); bool is_stat_table(const char *db, const char *table); bool is_eits_usable(Field* field); +void set_min_max_fields_table(Field* field); +void check_stat_tables_owner(THD *thd, TABLE_LIST *table_list); class Histogram {
participants (1)
-
Varun