Bug #63112 | Truncated incorrect DOUBLE value | ||
---|---|---|---|
Submitted: | 5 Nov 2011 17:17 | Modified: | 6 Nov 2011 14:07 |
Reporter: | Yurii Korotia | Email Updates: | |
Status: | Not a Bug | Impact on me: | |
Category: | MySQL Server: Parser | Severity: | S3 (Non-critical) |
Version: | 5.5.12 MySQL Community Server | OS: | Windows (x64 vista sp2) |
Assigned to: | CPU Architecture: | Any |
[5 Nov 2011 17:17]
Yurii Korotia
[6 Nov 2011 8:43]
Peter Laursen
You are probably running 'strict mode'? In non-strict mode I get warnings but the UPDATEs actually do perform. I also noticed same behaviour on 5.1 and 5.5 server. With 5.0 there is one more strange observaation. Also user variables are not required to reproduce this: -- simplified test case with no user variable SET SQL_MODE = ''; CREATE DATABASE d; USE d; DROP TABLE IF EXISTS t; CREATE TABLE t (id CHAR(36), id2 VARCHAR(36) NULL); INSERT t VALUES (1,NULL),(2,NULL),(3,1),(4,2); UPDATE t SET id = UUID() WHERE id = 1; UPDATE t SET id = UUID() WHERE id = 2; -- same with any number replacing 2 SHOW WARNINGS; /* returns the warning ONCE on 5.1 and 5.5 but TWICE on 5.0 Warning Code : 1292 Truncated incorrect DOUBLE value: 'e71a018f-0854-11e1-a13f-7d6ca803186e */ SELECT * FROM t; -- see that UPDATE actually did happen UPDATE t SET id = UUID(); -- success with no WHERE clause -- test case with one row in column DROP TABLE IF EXISTS t; CREATE TABLE t (id CHAR(36) NULL); INSERT t VALUES (1),(2),(3),(4); UPDATE t SET id = UUID() WHERE id = 1; UPDATE t SET id = UUID() WHERE id = 2; SHOW WARNINGS; /* returns the warning ONCE Warning Code : 1292 Truncated incorrect DOUBLE value: 'e71a018f-0854-11e1-a13f-7d6ca803186e */ SELECT * FROM t: -- see that UPDATE actually did happen UPDATE t SET id = UUID(); -- succes So it is the comparison in the WHERE clause that causes this. An integer is being compared with a string and both are converted to double for the comparison. I think this is expected. But why no error/warning on the first UPDATE? And why *TWO* warnings on the two-column table in MySQL 5.0 as the WHERE condition is on a single column. I don't have the answer. Just added my observations! Peter (not a MySQL person)
[6 Nov 2011 9:02]
Peter Laursen
UUID() function also not required. This is probably most simple test case: SET SQL_MODE = ''; DROP TABLE IF EXISTS t; CREATE TABLE t (id CHAR(36) NULL); INSERT t VALUES (1),(2),(3),(4); UPDATE t SET id = 'a' WHERE id = 1; SHOW WARNINGS -- empty set UPDATE t SET id = 'b' WHERE id = 2; SHOW WARNINGS; /* returns Warning Code : 1292 Truncated incorrect DOUBLE value: 'a */ SELECT * FROM t; /* returns id ------ a b 3 4 */ Obviously quoting the numbers in WHERE clause a strings will solve this: SET SQL_MODE = ''; DROP TABLE IF EXISTS t; CREATE TABLE t (id CHAR(36) NULL); INSERT t VALUES (1),(2),(3),(4); UPDATE t SET id = 'a' WHERE id = '1'; SHOW WARNINGS; -- empty set UPDATE t SET id = 'b' WHERE id = '2'; SHOW WARNINGS; -- empty set Here "WHERE id = '2'" will compare two strings and no internal casting occurs. But I still do not understand why there is no error/warning on the first update when not quoting.
[6 Nov 2011 9:17]
Peter Laursen
Not a bug! See this: SET SQL_MODE = ''; DROP TABLE IF EXISTS t; CREATE TABLE t (id CHAR(36) NULL); INSERT t VALUES (1),(2),(3),(4); UPDATE t SET id = 'a' WHERE id = 1; SHOW WARNINGS; -- empty set UPDATE t SET id = 'b' WHERE id = 2; SHOW WARNINGS; /* ONE warning Warning Code : 1292 Truncated incorrect DOUBLE value: 'a */ UPDATE t SET id = 'c' WHERE id = 3; SHOW WARNINGS; /* TWO warnings Warning Code : 1292 Truncated incorrect DOUBLE value: 'a */ SELECT * FROM t; /* returns id ------ a b 3 4 */ The WHERE clause will compare with `id`column for every row in the table. First UPDATE finds only numbers --> no warning Second UPDATE finds 3 numbers and the string 'a' --> one warning Second UPDATE finds 2 numbers and the strings 'a' and 'b' --> two warnings .. So it seems that with integers 1,2,3,4 stored in a string column the internal cast to DOUBLE does not truncate and thus no warning/error The correct way to write the WHERE clause is WHERE id = 'string' .. because the column is a string column.
[6 Nov 2011 9:22]
Peter Laursen
and one more detail: INSERT t VALUES (1),(2),(3),(4); could be written as INSERT t VALUES ('1'),('2'),('3'),('4'); .. what would be logically more correct when inserting to a string column. But MySQL does not care about that. Other RDBMS would!
[6 Nov 2011 10:13]
Yurii Korotia
uuid was used as I used it to convert ids to guids, so it was left as it was when this error appeared. thanks Peter for answer. I thought mysql should convert number to string and make comparition. As you may see, when we use @some_variable we cannot quote it as string. Or can we? I don't know. About insert values('1','2') not values(1,2). Mysql has inserted it well both ways. As for me, it is a bug. But as previous my report was called as mysql specific, this probably will be called same way. What was expected when we use UPDATE t SET id = 'c' WHERE id = 3; 1. mysql looks for type of column. sees char(36) 2. converts 3 to '3 ...'. IN FACT, it was converted to '3' which is wrong, or not? 3. compare strings 'c' == '3' 4. update id As new question appeared, is it correct that char(36) contains only '3' without 35 trailing spaces?
[6 Nov 2011 10:16]
Yurii Korotia
3. compare strings 'c' == '3' compare id == '3'
[6 Nov 2011 12:48]
Peter Laursen
SELECT orange > banana; Is this true or false? Well .. most often a banana is longer than an orange. So if lenght is the criteria the above statement is false. But most often an orange is heavier than a banana, so if weight is the criteria the above statement is true. When comparing different types (orange versus banana, string versus integer) some 'criteria' or 'rule' will have to be used. When MySQL compares different datatypes they are bost cast to DOUBLE internally before comparison. The integer "1" casts to the double "1" - the string 'a' or 'UUID()' casts to the double "0". I was confused by your report as your original test case used user variables and the UUID() function - but the issue here is not related to any of those. I had to work out a simplied test case eliminating those to understand the basics of this. To avoid casting to DOUBLE internally ensure that the values compared are same datatype. In your case the `id` columns are strings and the value you compare with should also be strings (not 1 but '1' - not 2 but '2' etc.) or use cast()/convert() in your statement as you also figured out yourself.
[6 Nov 2011 13:30]
Peter Laursen
http://dev.mysql.com/doc/refman/5.5/en/char.html "The CHAR and VARCHAR types .. differ .. in whether trailing spaces are retained. When CHAR (not VARCHAR - my comment) values are stored, they are right-padded with spaces to the specified length. When CHAR (not varchar - my comment) values are retrieved, trailing spaces are removed unless the PAD_CHAR_TO_FULL_LENGTH SQL mode is enabled." SET sql_mode = '' USE test; DROP TABLE IF EXISTS trailingblanks; CREATE TABLE trailingblanks (id INT, str1 CHAR (2), str2 VARCHAR(2)); INSERT INTO trailingblanks VALUES (1, 'a', 'a'); INSERT INTO trailingblanks VALUES (2, CONCAT('a', X'20'), CONCAT('a', X'20')); -- space character INSERT INTO trailingblanks VALUES (3, CONCAT('a', X'00'), CONCAT('a', X'20')); -- null character SELECT * FROM trailingblanks; /* returns id str1 str2 ------ ------ ------ 1 a a 2 a a 3 a a */ SELECT id, HEX(str1), HEX(str2) FROM trailingblanks; /* returns id hex(str1) hex(str2) ------ --------- --------- 1 61 61 2 61 6120 3 6100 6120 */ SET sql_mode = 'pad_char_to_full_length'; USE test; DROP TABLE IF EXISTS trailingblanks; CREATE TABLE trailingblanks (id INT, str1 CHAR (2), str2 VARCHAR(2)); INSERT INTO trailingblanks VALUES (1, 'a', 'a'); INSERT INTO trailingblanks VALUES (2, CONCAT('a', X'20'), CONCAT('a', X'20')); -- space character INSERT INTO trailingblanks VALUES (3, CONCAT('a', X'00'), CONCAT('a', X'20')); -- null character SELECT * FROM trailingblanks; /* returns id str1 str2 ------ ------ ------ 1 a a 2 a a 3 a a */ SELECT id, HEX(str1), HEX(str2) FROM trailingblanks; /* returns id HEX(str1) HEX(str2) ------ --------- --------- 1 6120 61 2 6120 6120 3 6100 6120 */
[6 Nov 2011 14:07]
Yurii Korotia
i got it
[6 Nov 2011 14:08]
Peter Laursen
something messed up wiht my last post! sorry! :-( This should be enough 8and also adding TEXT type to the case): SET sql_mode = ''; USE test; DROP TABLE IF EXISTS trailingblanks; CREATE TABLE trailingblanks (id INT, str1 CHAR (2), str2 VARCHAR(2), str3 TEXT); INSERT INTO trailingblanks VALUES (1, 'a', 'a', 'a'); INSERT INTO trailingblanks VALUES (2, CONCAT('a', X'20'), CONCAT('a', X'20'), CONCAT('a', X'20')); SELECT id, HEX(str1), HEX(str2), HEX(str3) FROM trailingblanks; /* returns id hex(str1) hex(str2) hex(str3) ------ --------- --------- --------- 1 61 61 61 2 61 6120 6120 */ SET sql_mode = 'pad_char_to_full_length'; USE test; DROP TABLE IF EXISTS trailingblanks; CREATE TABLE trailingblanks (id INT, str1 CHAR (2), str2 VARCHAR(2), str3 TEXT); INSERT INTO trailingblanks VALUES (1, 'a', 'a', 'a'); INSERT INTO trailingblanks VALUES (2, CONCAT('a', X'20'), CONCAT('a', X'20'), CONCAT('a', X'20')); SELECT id, HEX(str1), HEX(str2), HEX(str3) FROM trailingblanks; /* returns id HEX(str1) HEX(str2) hex(str3) ------ --------- --------- --------- 1 6120 61 61 2 6120 6120 6120 */ Note that HEX() on the CHAR column shows that the trailing space is stored if (and only if) the 'pad_char_to_full length is SET - and that is true no matter if you INSERT that trailing space or not. VARCHAR and TEXT columns will INSERT (only) what trailing spaces you specify.