summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVarun Gupta <varun.gupta@mariadb.com>2020-05-28 18:33:10 +0530
committerVarun Gupta <varun.gupta@mariadb.com>2020-05-28 18:33:10 +0530
commit847df79f51bc21811071bbd05c21c5392a898e7c (patch)
tree3ff73ea3ceca2e3fa740134ec5d651025d9016ba
parent5d85bc08c6412d067a69d2c1354a10f9a803b332 (diff)
downloadmariadb-git-847df79f51bc21811071bbd05c21c5392a898e7c.tar.gz
MDEV-22509: Server crashes in Field_inet6::store_inet6_null_with_warn / Field::maybe_null
For field with type INET, during EITS collection the min and max values are store in text representation in the statistical table. While retrieving the value from the statistical table, the value is stored back in the original field using binary form instead of text and this was resulting in the crash. Introduced 2 functions in the Field structure: 1) store_to_statistical_minmax_field 2) store_from_statistical_minmax_field
-rw-r--r--plugin/type_inet/mysql-test/type_inet/type_inet6_stat_tables.result14
-rw-r--r--plugin/type_inet/mysql-test/type_inet/type_inet6_stat_tables.test10
-rw-r--r--sql/field.cc31
-rw-r--r--sql/field.h22
-rw-r--r--sql/sql_statistics.cc44
5 files changed, 95 insertions, 26 deletions
diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6_stat_tables.result b/plugin/type_inet/mysql-test/type_inet/type_inet6_stat_tables.result
index 6d84d105c99..1cbedad1c3c 100644
--- a/plugin/type_inet/mysql-test/type_inet/type_inet6_stat_tables.result
+++ b/plugin/type_inet/mysql-test/type_inet/type_inet6_stat_tables.result
@@ -13,5 +13,19 @@ test.t1 analyze status OK
INSERT INTO t1 VALUES ('3::3');
DROP TABLE t1;
#
+# MDEV-22509: Server crashes in Field_inet6::store_inet6_null_with_warn / Field::maybe_null
+#
+CREATE TABLE t1 (a INT, b INET6 NOT NULL);
+INSERT INTO t1 VALUES (1,'::'),(2,'::');
+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
+SELECT t1.a from t1;
+a
+1
+2
+DROP TABLE t1;
+#
# End of 10.5 tests
#
diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6_stat_tables.test b/plugin/type_inet/mysql-test/type_inet/type_inet6_stat_tables.test
index fb092db6bc4..063581b12f5 100644
--- a/plugin/type_inet/mysql-test/type_inet/type_inet6_stat_tables.test
+++ b/plugin/type_inet/mysql-test/type_inet/type_inet6_stat_tables.test
@@ -15,5 +15,15 @@ INSERT INTO t1 VALUES ('3::3');
DROP TABLE t1;
--echo #
+--echo # MDEV-22509: Server crashes in Field_inet6::store_inet6_null_with_warn / Field::maybe_null
+--echo #
+
+CREATE TABLE t1 (a INT, b INET6 NOT NULL);
+INSERT INTO t1 VALUES (1,'::'),(2,'::');
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+SELECT t1.a from t1;
+DROP TABLE t1;
+
+--echo #
--echo # End of 10.5 tests
--echo #
diff --git a/sql/field.cc b/sql/field.cc
index 6234de43ec8..cfa67b0c5dc 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1967,6 +1967,37 @@ int Field::store_timestamp_dec(const timeval &ts, uint dec)
return store_time_dec(Datetime(get_thd(), ts).get_mysql_time(), dec);
}
+
+int Field::store_to_statistical_minmax_field(Field *field, String *val)
+{
+ val_str(val);
+ size_t length= Well_formed_prefix(val->charset(), val->ptr(),
+ MY_MIN(val->length(), field->field_length)).length();
+ return field->store(val->ptr(), length, &my_charset_bin);
+}
+
+
+int Field::store_from_statistical_minmax_field(Field *stat_field, String *str)
+{
+ stat_field->val_str(str);
+ return store_text(str->ptr(), str->length(), &my_charset_bin);
+}
+
+
+int Field_bit::store_to_statistical_minmax_field(Field *field, String *str)
+{
+ longlong nr= val_int();
+ return field->store(nr, TRUE);
+}
+
+
+int Field_bit::store_from_statistical_minmax_field(Field *stat_field,
+ String *str __attribute__((unused)))
+{
+ return store(stat_field->val_int(), TRUE);
+}
+
+
/**
Pack the field into a format suitable for storage and transfer.
diff --git a/sql/field.h b/sql/field.h
index d709a84e0fe..358eb3939dc 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -977,6 +977,26 @@ public:
DBUG_ASSERT(ls.length < UINT_MAX32);
return store(ls.str, (uint) ls.length, cs);
}
+
+ /*
+ @brief
+ Store minimum/maximum value of a column in the statistics table.
+ @param
+ field statistical table field
+ str value buffer
+ */
+ virtual int store_to_statistical_minmax_field(Field *field, String *str);
+
+ /*
+ @brief
+ Store minimum/maximum value of a column from the statistical table.
+
+ @param
+ field statistical table field
+ str value buffer
+ */
+ virtual int store_from_statistical_minmax_field(Field *field, String *str);
+
virtual double val_real()=0;
virtual longlong val_int()=0;
/*
@@ -4946,6 +4966,8 @@ public:
static_cast<uint16>((field_length & 7) |
((field_length / 8) << 8)), 2);
}
+ int store_to_statistical_minmax_field(Field *fld, String *str) override;
+ int store_from_statistical_minmax_field(Field *fld, String *str) override;
private:
size_t do_last_null_byte() const override;
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index 55e8e52c052..2b171cba560 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -1037,27 +1037,17 @@ public:
stat_field->set_notnull();
switch (i) {
case COLUMN_STAT_MIN_VALUE:
- if (table_field->type() == MYSQL_TYPE_BIT)
- stat_field->store(table_field->collected_stats->min_value->val_int(),true);
- else
- {
- table_field->collected_stats->min_value->val_str(&val);
- size_t 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);
- }
+ {
+ Field *field= table_field->collected_stats->min_value;
+ field->store_to_statistical_minmax_field(stat_field, &val);
break;
+ }
case COLUMN_STAT_MAX_VALUE:
- if (table_field->type() == MYSQL_TYPE_BIT)
- stat_field->store(table_field->collected_stats->max_value->val_int(),true);
- else
- {
- table_field->collected_stats->max_value->val_str(&val);
- size_t 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);
- }
+ {
+ Field *field= table_field->collected_stats->max_value;
+ field->store_to_statistical_minmax_field(stat_field, &val);
break;
+ }
case COLUMN_STAT_NULLS_RATIO:
stat_field->store(table_field->collected_stats->get_nulls_ratio());
break;
@@ -1134,17 +1124,19 @@ public:
switch (i) {
case COLUMN_STAT_MIN_VALUE:
- table_field->read_stats->min_value->set_notnull();
- stat_field->val_str(&val);
- table_field->read_stats->min_value->store(val.ptr(), val.length(),
- &my_charset_bin);
+ {
+ Field *field= table_field->read_stats->min_value;
+ field->set_notnull();
+ field->store_from_statistical_minmax_field(stat_field, &val);
break;
+ }
case COLUMN_STAT_MAX_VALUE:
- table_field->read_stats->max_value->set_notnull();
- stat_field->val_str(&val);
- table_field->read_stats->max_value->store(val.ptr(), val.length(),
- &my_charset_bin);
+ {
+ Field *field= table_field->read_stats->max_value;
+ field->set_notnull();
+ field->store_from_statistical_minmax_field(stat_field, &val);
break;
+ }
case COLUMN_STAT_NULLS_RATIO:
table_field->read_stats->set_nulls_ratio(stat_field->val_real());
break;