summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2017-01-12 15:16:45 +0400
committerAlexander Barkov <bar@mariadb.org>2017-01-12 16:37:58 +0400
commitebb8c9fb26f86cff8c0d81bd2415f415cef952bb (patch)
tree412a9f09133ef8280b55f4e689637380cc8451d6
parent2dc5d8bb7e087a340edb989c045070b808e0da57 (diff)
downloadmariadb-git-ebb8c9fb26f86cff8c0d81bd2415f415cef952bb.tar.gz
MDEV-11030 Assertion `precision > 0' failed in decimal_bin_size
Fixing Item::decimal_precision() to return at least one 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));
-rw-r--r--mysql-test/r/cast.result73
-rw-r--r--mysql-test/t/cast.test39
-rw-r--r--sql/item.cc9
-rw-r--r--sql/item_func.h11
4 files changed, 128 insertions, 4 deletions
diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result
index 4c9f257fdf2..ab784b5afc2 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 58f91571ebb..5316c12c857 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.cc b/sql/item.cc
index 08599f1d486..015f5591c5d 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -533,7 +533,14 @@ uint Item::decimal_precision() const
unsigned_flag);
return MY_MIN(prec, DECIMAL_MAX_PRECISION);
}
- return MY_MIN(max_char_length(), DECIMAL_MAX_PRECISION);
+ uint res= max_char_length();
+ /*
+ Return at least one decimal digit, even if Item::max_char_length()
+ returned 0. This is important to avoid attempts to create fields of types
+ INT(0) or DECIMAL(0,0) when converting NULL or empty strings to INT/DECIMAL:
+ CREATE TABLE t1 AS SELECT CONVERT(NULL,SIGNED) AS a;
+ */
+ return res ? MY_MIN(res, DECIMAL_MAX_PRECISION) : 1;
}
diff --git a/sql/item_func.h b/sql/item_func.h
index 4a55bd68453..56d7c190181 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -626,8 +626,15 @@ public:
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(); }