From 415409579af68ee2e3c55d5294d7bb5e6397b03f Mon Sep 17 00:00:00 2001
From: Monty <monty@mariadb.org>
Date: Mon, 1 Mar 2021 14:44:18 +0200
Subject: [PATCH] MDEV-24958 Server crashes in my_strtod ... with DEFAULT(blob)

Fixes also:
MDEV-24942 Server crashes in _ma_rec_pack... with DEFAULT() on BLOB

This was caused by two different bugs, both related to that the default
value for the blob was not calculated before it was used:
- There where now Item_default_value::..result() wrappers, which is
  needed as item in HAVING uses these.  This causes crashes when
  using a reference to a DEFAULT(blob_field) in HAVING. It also
  caused wrong results when used with other fields with default value
  expressions that are not constants.
- create_tmp_field() did not take into account that blob fields with
  default expressions are not yet initialized. Fixed by treating
  Item_default_value(blob) like a normal item expression.
---
 mysql-test/main/having.result | 33 ++++++++++++++++++++++++
 mysql-test/main/having.test   | 20 +++++++++++++++
 sql/item.cc                   | 47 ++++++++++++++++++++++++++++++++---
 sql/item.h                    | 10 ++++++++
 sql/sql_select.cc             | 24 ++++++++++++++++--
 5 files changed, 129 insertions(+), 5 deletions(-)

diff --git a/mysql-test/main/having.result b/mysql-test/main/having.result
index f37cc48772e..233843de36f 100644
--- a/mysql-test/main/having.result
+++ b/mysql-test/main/having.result
@@ -846,3 +846,36 @@ t	r
 DROP TABLE t1;
 DROP FUNCTION next_seq_value;
 DROP TABLE series;
+#
+# MDEV-24958 Server crashes in my_strtod /
+# Value_source::Converter_strntod::Converter_strntod with DEFAULT(blob)
+#
+# MDEV-24942 Server crashes in _ma_rec_pack / _ma_write_blob_record with
+# DEFAULT() on BLOB
+#
+CREATE TABLE t1 (id INT, f MEDIUMTEXT NOT NULL DEFAULT 'A');
+INSERT INTO t1 VALUES (1,'foo'),(2,'bar');
+SELECT f FROM t1 GROUP BY id ORDER BY DEFAULT(f);
+f
+foo
+bar
+SELECT DEFAULT(f) AS h FROM t1 HAVING h > 5;
+h
+Warnings:
+Warning	1292	Truncated incorrect DOUBLE value: 'A'
+SELECT DEFAULT(f) AS h FROM t1 HAVING h >= 0;
+h
+A
+A
+Warnings:
+Warning	1292	Truncated incorrect DOUBLE value: 'A'
+SELECT DEFAULT(f) AS h FROM t1 HAVING h >= 'A';
+h
+A
+A
+alter table t1 add column b int default (rand()+1+3);
+select default(b) AS h FROM t1 HAVING h > "2";
+h
+#
+#
+drop table t1;
diff --git a/mysql-test/main/having.test b/mysql-test/main/having.test
index 179af14559f..8b0cc244f51 100644
--- a/mysql-test/main/having.test
+++ b/mysql-test/main/having.test
@@ -890,3 +890,23 @@ SELECT t, next_seq_value() r FROM t1 FORCE INDEX(t)
 DROP TABLE t1;
 DROP FUNCTION next_seq_value;
 DROP TABLE series;
+
+--echo #
+--echo # MDEV-24958 Server crashes in my_strtod /
+--echo # Value_source::Converter_strntod::Converter_strntod with DEFAULT(blob)
+--echo #
+--echo # MDEV-24942 Server crashes in _ma_rec_pack / _ma_write_blob_record with
+--echo # DEFAULT() on BLOB
+--echo #
+
+CREATE TABLE t1 (id INT, f MEDIUMTEXT NOT NULL DEFAULT 'A');
+INSERT INTO t1 VALUES (1,'foo'),(2,'bar');
+SELECT f FROM t1 GROUP BY id ORDER BY DEFAULT(f);
+SELECT DEFAULT(f) AS h FROM t1 HAVING h > 5;
+SELECT DEFAULT(f) AS h FROM t1 HAVING h >= 0;
+SELECT DEFAULT(f) AS h FROM t1 HAVING h >= 'A';
+
+alter table t1 add column b int default (rand()+1+3);
+--replace_column 1 #
+select default(b) AS h FROM t1 HAVING h > "2";
+drop table t1;
diff --git a/sql/item.cc b/sql/item.cc
index 522fef57699..7b15c7ddb43 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -9341,7 +9341,6 @@ bool Item_default_value::fix_fields(THD *thd, Item **items)
   }
   thd->column_usage= save_column_usage;
 
-
   real_arg= arg->real_item();
   if (real_arg->type() != FIELD_ITEM)
   {
@@ -9364,7 +9363,7 @@ bool Item_default_value::fix_fields(THD *thd, Item **items)
   def_field->reset_fields();
   // If non-constant default value expression or a blob
   if (def_field->default_value &&
-      (def_field->default_value->flags || def_field->flags & BLOB_FLAG))
+      (def_field->default_value->flags || (def_field->flags & BLOB_FLAG)))
   {
     uchar *newptr= (uchar*) thd->alloc(1+def_field->pack_length());
     if (!newptr)
@@ -9461,11 +9460,53 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
   return Item_field::save_in_field(field_arg, no_conversions);
 }
 
+double Item_default_value::val_result()
+{
+  calculate();
+  return Item_field::val_result();
+}
+
+longlong Item_default_value::val_int_result()
+{
+  calculate();
+  return Item_field::val_int_result();
+}
+
+String *Item_default_value::str_result(String* tmp)
+{
+  calculate();
+  return Item_field::str_result(tmp);
+}
+
+bool Item_default_value::val_bool_result()
+{
+  calculate();
+  return Item_field::val_bool_result();
+}
+
+bool Item_default_value::is_null_result()
+{
+  calculate();
+  return Item_field::is_null_result();
+}
+
+my_decimal *Item_default_value::val_decimal_result(my_decimal *decimal_value)
+{
+  calculate();
+  return Item_field::val_decimal_result(decimal_value);
+}
+
+bool Item_default_value::get_date_result(MYSQL_TIME *ltime,ulonglong fuzzydate)
+{
+  calculate();
+  return Item_field::get_date_result(ltime, fuzzydate);
+}
+
 table_map Item_default_value::used_tables() const
 {
   if (!field || !field->default_value)
     return static_cast<table_map>(0);
-  if (!field->default_value->expr)                      // not fully parsed field
+  if (!field->default_value->expr)           // not fully parsed field
     return static_cast<table_map>(RAND_TABLE_BIT);
   return field->default_value->expr->used_tables();
 }
diff --git a/sql/item.h b/sql/item.h
index 9200dd80ee8..9fa384f0947 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -5859,6 +5859,16 @@ class Item_default_value : public Item_field
   longlong val_int();
   my_decimal *val_decimal(my_decimal *decimal_value);
   bool get_date(MYSQL_TIME *ltime,ulonglong fuzzydate);
+
+  /* Result variants */
+  double val_result();
+  longlong val_int_result();
+  String *str_result(String* tmp);
+  my_decimal *val_decimal_result(my_decimal *val);
+  bool val_bool_result();
+  bool is_null_result();
+  bool get_date_result(MYSQL_TIME *ltime,ulonglong fuzzydate);
+
   bool send(Protocol *protocol, st_value *buffer);
   int save_in_field(Field *field_arg, bool no_conversions);
   bool save_in_param(THD *thd, Item_param *param)
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 7658d843d8b..ce820946908 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -17224,7 +17224,13 @@ Field *Item::create_field_for_schema(THD *thd, TABLE *table)
                        the record in the original table.
                        If modify_item is 0 then fill_record() will update
                        the temporary table
-
+  @param table_cant_handle_bit_fields
+                       Set to 1 if the temporary table cannot handle bit
+                       fields. Only set for heap tables when the bit field
+                       is part of an index.
+  @param make_copy_field
+                       Set when using with rollup when we want to have
+                       an exact copy of the field.
   @retval
     0			on error
   @retval
@@ -17261,8 +17267,22 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
       my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
     return result;
   }
-  case Item::FIELD_ITEM:
   case Item::DEFAULT_VALUE_ITEM:
+  {
+    Field *field= ((Item_default_value*) item)->field;
+    if (field->default_value && (field->flags & BLOB_FLAG))
+    {
+      /*
+        We have to use a copy function when using a blob with default value
+        as the we have to calcuate the default value before we can use it.
+      */
+      return create_tmp_field_from_item(thd, item, table,
+                                        (make_copy_field ? 0 : copy_func),
+                                        modify_item);
+    }
+  }
+  /* Fall through */
+  case Item::FIELD_ITEM:
   case Item::CONTEXTUALLY_TYPED_VALUE_ITEM:
   case Item::INSERT_VALUE_ITEM:
   case Item::TRIGGER_FIELD_ITEM:
-- 
2.30.9