← Back to team overview

maria-developers team mailing list archive

MDEV-11030 Assertion `precision > 0' failed in decimal_bin_size

 

Hello Sanja,

Can you please review a patch for MDEV-11030?

Thanks!
commit f50bf07ed91a8a0c5db4628ed051070699b8563a
Author: Alexander Barkov <bar@xxxxxxxxxxx>
Date:   Thu Jan 12 13:04:50 2017 +0400

    MDEV-11030 Assertion `precision > 0' failed in decimal_bin_size
    
    Fixing Item_func_signed::decimal_precision() to return at least 1 digit.
    This fixes the problem reported in MDEV.
    
    Also, fixing Item_func_signed::fix_length_and_dec() to reserve
    space for at least one digit (plus one character for an optional sign).
    This is needed to have CONVERT(expr,SIGNED) and CONVERT(expr,UNSIGNED)
    create correct string fields when they appear in string context, e.g.:
      CREATE TABLE t1 AS SELECT CONCAT(CONVERT('',SIGNED));

diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result
index 4c9f257..ab784b5 100644
--- a/mysql-test/r/cast.result
+++ b/mysql-test/r/cast.result
@@ -583,7 +583,7 @@ show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
   `cast(1 as unsigned)` int(1) unsigned NOT NULL,
-  `cast(1 as signed)` int(1) NOT NULL,
+  `cast(1 as signed)` int(2) NOT NULL,
   `cast(1 as double(5,2))` double(5,2) DEFAULT NULL,
   `cast(1 as decimal(5,3))` decimal(5,3) NOT NULL,
   `cast("A" as binary)` varbinary(1) NOT NULL,
@@ -819,3 +819,74 @@ utf8_bin
 select collation(cast("a" as char(10) binary ascii));
 collation(cast("a" as char(10) binary ascii))
 latin1_bin
+#
+# MDEV-11030 Assertion `precision > 0' failed in decimal_bin_size
+#
+SELECT * FROM (SELECT IFNULL(CONVERT(NULL, UNSIGNED), NULL)) sq;
+IFNULL(CONVERT(NULL, UNSIGNED), NULL)
+NULL
+CREATE TABLE t1 AS SELECT IFNULL(CONVERT(NULL, UNSIGNED), NULL);
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `IFNULL(CONVERT(NULL, UNSIGNED), NULL)` decimal(1,0) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT COALESCE(CONVERT(NULL, UNSIGNED), NULL);
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `COALESCE(CONVERT(NULL, UNSIGNED), NULL)` decimal(1,0) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT CASE WHEN TRUE THEN CONVERT(NULL, UNSIGNED) ELSE NULL END;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `CASE WHEN TRUE THEN CONVERT(NULL, UNSIGNED) ELSE NULL END` decimal(1,0) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT IFNULL(CONVERT(NULL,SIGNED),CONVERT(NULL,UNSIGNED)) AS a;
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` decimal(1,0) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+-1,
+CONVERT(NULL,SIGNED),
+CONCAT(CONVERT(NULL,SIGNED)),
+1,
+CONVERT(NULL,UNSIGNED),
+CONCAT(CONVERT(NULL,UNSIGNED));
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `-1` int(2) NOT NULL,
+  `CONVERT(NULL,SIGNED)` int(2) DEFAULT NULL,
+  `CONCAT(CONVERT(NULL,SIGNED))` varchar(2) DEFAULT NULL,
+  `1` int(1) NOT NULL,
+  `CONVERT(NULL,UNSIGNED)` int(1) unsigned DEFAULT NULL,
+  `CONCAT(CONVERT(NULL,UNSIGNED))` varchar(1) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+CONVERT('',SIGNED),
+CONCAT(CONVERT('',SIGNED)),
+CONVERT('',UNSIGNED),
+CONCAT(CONVERT('',UNSIGNED));
+Warnings:
+Warning	1292	Truncated incorrect INTEGER value: ''
+Warning	1292	Truncated incorrect INTEGER value: ''
+Warning	1292	Truncated incorrect INTEGER value: ''
+Warning	1292	Truncated incorrect INTEGER value: ''
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `CONVERT('',SIGNED)` int(2) NOT NULL,
+  `CONCAT(CONVERT('',SIGNED))` varchar(2) NOT NULL,
+  `CONVERT('',UNSIGNED)` int(1) unsigned NOT NULL,
+  `CONCAT(CONVERT('',UNSIGNED))` varchar(1) NOT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
diff --git a/mysql-test/t/cast.test b/mysql-test/t/cast.test
index 58f9157..5316c12 100644
--- a/mysql-test/t/cast.test
+++ b/mysql-test/t/cast.test
@@ -472,3 +472,42 @@ select collation(cast("a" as char(10) ascii binary));
 select collation(cast("a" as char(10) binary charset utf8));
 select collation(cast("a" as char(10) binary ascii));
 
+--echo #
+--echo # MDEV-11030 Assertion `precision > 0' failed in decimal_bin_size
+--echo #
+
+SELECT * FROM (SELECT IFNULL(CONVERT(NULL, UNSIGNED), NULL)) sq;
+
+CREATE TABLE t1 AS SELECT IFNULL(CONVERT(NULL, UNSIGNED), NULL);
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 AS SELECT COALESCE(CONVERT(NULL, UNSIGNED), NULL);
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 AS SELECT CASE WHEN TRUE THEN CONVERT(NULL, UNSIGNED) ELSE NULL END;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 AS SELECT IFNULL(CONVERT(NULL,SIGNED),CONVERT(NULL,UNSIGNED)) AS a;
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 AS SELECT
+  -1,
+  CONVERT(NULL,SIGNED),
+  CONCAT(CONVERT(NULL,SIGNED)),
+  1,
+  CONVERT(NULL,UNSIGNED),
+  CONCAT(CONVERT(NULL,UNSIGNED));
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+
+CREATE TABLE t1 AS SELECT
+  CONVERT('',SIGNED),
+  CONCAT(CONVERT('',SIGNED)),
+  CONVERT('',UNSIGNED),
+  CONCAT(CONVERT('',UNSIGNED));
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
diff --git a/sql/item_func.h b/sql/item_func.h
index 4a55bd6..0addfde 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -626,11 +626,26 @@ class Item_func_signed :public Item_int_func
   longlong val_int_from_str(int *error);
   void fix_length_and_dec()
   {
-    fix_char_length(MY_MIN(args[0]->max_char_length(),
-                           MY_INT64_NUM_DECIMAL_DIGITS));
+    uint32 char_length= MY_MIN(args[0]->max_char_length(),
+                               MY_INT64_NUM_DECIMAL_DIGITS);
+    /*
+      args[0]->max_char_length() can return 0.
+      Reserve max_length to fit at least one character for one digit,
+      plus one character for the sign (if signed).
+    */
+    set_if_bigger(char_length, 1 + (unsigned_flag ? 0 : 1));
+    fix_char_length(char_length);
   }
   virtual void print(String *str, enum_query_type query_type);
-  uint decimal_precision() const { return args[0]->decimal_precision(); }
+  uint decimal_precision() const
+  {
+    /*
+      Some items (e.g. Item_null) can return 0 in decimal_precision().
+      We want CONVERT(NULL, SIGNED) to report at least one digit.
+    */
+    uint res= args[0]->decimal_precision();
+    return MY_MAX(res, 1);
+  }
 };
 
 

Follow ups