← Back to team overview

maria-developers team mailing list archive

Re: [Commits] e0fa1993692: MDEV-18899: Server crashes in Field::set_warning_truncated_wrong_value

 

Hi Varun,

First, Some input on the test:

On Wed, Mar 27, 2019 at 01:55:16PM +0530, Varun wrote:
> revision-id: e0fa1993692a3552feddae56cbee3078d4da2121 (mariadb-10.2.22-91-ge0fa1993692)
> parent(s): 50a8fc52988d13a5164a1a542b9d7a85e3ecc1c1
> author: Varun Gupta
> committer: Varun Gupta
> timestamp: 2019-03-27 13:48:44 +0530
> message:
> 
> MDEV-18899: Server crashes in Field::set_warning_truncated_wrong_value
> 
> To fix the crash there we need to make sure that the
> server while storing the statistical values in statistical tables should do it
> in a multi-byte safe way.
> Also there is no need to throw warnings if there is truncation while storing
> values from statistical fields.
> 
...
> --- a/mysql-test/r/stat_tables_innodb.result
> +++ b/mysql-test/r/stat_tables_innodb.result
> @@ -651,6 +651,63 @@ 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 (REPEAT('ӥ',255)), (REPEAT('ç',255));
> +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= REPEAT('ӥ',255);

This looks scary, what if there were some other data in these tables? 
Can you change add a WHERE db_name=database() and table_name='t1' ? 

> +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
> +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;
> +drop table t1;
> +CREATE TABLE t1 (col1 date);
> +INSERT INTO t1 VALUES('2004-01-01'),('2004-02-29');
> +INSERT INTO t1 VALUES('0000-10-31');
> +analyze table t1;
> +Table	Op	Msg_type	Msg_text
> +test.t1	analyze	status	Engine-independent statistics collected
> +test.t1	analyze	status	OK
> +update mysql.column_stats set min_value='2004-0-31123';

The same as above.

> +select min_value from mysql.column_stats;
> +min_value
> +2004-0-31123

This case also makes one wonder whether not producing a warning was a good idea
after all.

A varchar string that is 255-byte long, and after 255 bytes it gets truncated
in the middle of a character is a rare case.

An incorrect datetime value... not as rare?

> +select * from t1;

Is the above supposed to read the EITS data? It doesn't as the query doesn't
use the WHERE clause.
> +col1
> +2004-01-01
> +2004-02-29
> +0000-10-31
> +drop table t1;
> +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;
>  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..114c5c97a7e 100644
> --- a/mysql-test/t/stat_tables.test
> +++ b/mysql-test/t/stat_tables.test
> @@ -401,4 +401,44 @@ 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 (REPEAT('ӥ',255)), (REPEAT('ç',255));
> +
> +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= REPEAT('ӥ',255);
> +select HEX(RIGHT(min_value, 1)), length(min_value) from mysql.column_stats;
> +analyze select * from t1 where a  >= 'ӥ';
> +
> +set names latin1;
> +drop table t1;
> +
> +CREATE TABLE t1 (col1 date);
> +INSERT INTO t1 VALUES('2004-01-01'),('2004-02-29');
> +INSERT INTO t1 VALUES('0000-10-31');
> +analyze table t1;
> +update mysql.column_stats set min_value='2004-0-31123';
> +select min_value from mysql.column_stats;
> +select * from t1;
> +drop table t1;
> +
> +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;
> diff --git a/sql/field.cc b/sql/field.cc
> index 080cf34c76d..c26195a3650 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);
> @@ -7023,12 +7022,16 @@ Field_longstr::check_string_copy_error(const String_copier *copier,
>  {
>    const char *pos;
>    char tmp[32];
> +  THD *thd= get_thd();
>  
>    if (!(pos= copier->most_important_error_pos()))
>      return FALSE;
>  
> -  convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
> -  set_warning_truncated_wrong_value("string", tmp);
> +  if (thd->count_cuted_fields)
> +  {
> +    convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
> +    set_warning_truncated_wrong_value("string", tmp);
> +  }
>    return TRUE;
>  }
>  
> diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
> index b5811c683e8..0960a9a1ec5 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:
> @@ -2934,7 +2939,6 @@ int update_statistics_for_table(THD *thd, TABLE *table)
>    DBUG_RETURN(rc);
>  }
>  
> -
>  /**
>    @brief
>    Read statistics for a table from the persistent statistical tables
> @@ -2980,6 +2984,7 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables)
>    Table_statistics *read_stats= table_share->stats_cb.table_stats;
>  
>    DBUG_ENTER("read_statistics_for_table");
> +  DBUG_ASSERT(thd->count_cuted_fields == CHECK_FIELD_IGNORE);
>  
>    /* Read statistics from the statistical table table_stats */
>    stat_table= stat_tables[TABLE_STAT].table;
> @@ -3059,7 +3064,7 @@ int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables)
>        }
>      }
>    }
> -      
> +
>    table->stats_is_read= TRUE;
>  
>    DBUG_RETURN(0);

BR
 Sergei
-- 
Sergei Petrunia, Software Developer
MariaDB Corporation | Skype: sergefp | Blog: http://s.petrunia.net/blog