[MDEV-8924] mysqldump restores a negative zero as a positive zero Created: 2015-10-09  Updated: 2017-01-09

Status: Confirmed
Project: MariaDB Server
Component/s: Scripts & Clients
Affects Version/s: 5.1.67, 5.2.14, 5.3.12, 5.5, 10.0, 10.1
Fix Version/s: 10.1

Type: Bug Priority: Minor
Reporter: Peter (Stig) Edwards Assignee: Alexander Barkov
Resolution: Unresolved Votes: 1
Labels: upstream


 Description   

Hello and thank you for MariaDB,

While importing a database using mysqldump, I noticed table checksums were not the same, a diff on the mysqldump showed that negative zero values in a double field were represented as -0 in the mysqldump which was being imported as positive zero when the mysqldump was run using the mysql client.

If I add the following to mysql-test/t/mysqldump_restore.test , notice to obtain a negative zero I enclose it in quotes:

--echo #
--echo # Negative zero
--echo #
CREATE TABLE t1 (a double);
INSERT INTO t1 VALUES ('-0');
--exec $MYSQL_DUMP --compact test t1 > $mysqldumpfile
let $table_name = test.t1;
--source include/mysqldump.inc

and then run:

./mysql-test-run mysqldump_restore
...
# Recreate table from mysqldump output
# Compare original and recreated tables
# Recreated table: test.t1
# Original table: test.t1_orig
include/diff_tables.inc [test.t1, test.t1_orig]
--- mysql-test/var/mysqld.1/data//diff_table--test-t1   2015-10-09 07:51:27.890186982 -0400
+++ mysql-test/var/mysqld.1/data//diff_table--test-t1_orig      2015-10-09 07:51:27.918591757 -0400
@@ -1 +1 @@
-0
+-0

I suspect in the case of the data being worked on that the negative zero was written as a string in error, and had it been numeric then it would have been stored as positive zero and we would have not seen a difference between the original and restored data.

I am not sure if test_if_special_chars in client/mysqldump.c could return 1 if passed "-0", or if INSERT -0 should be expected to store a negative zero.

Thank you.



 Comments   
Comment by Elena Stepanova [ 2015-10-21 ]

Found here another way to get the same problem, which does not involve representing numbers as strings:

CREATE TABLE t1 (a double);
insert into t1 values (0.001), (-0.001);
update t1 set a = round(a);
select * from t1;
+------+
| a    |
+------+
|    0 |
|   -0 |
+------+

The rest is the same: mysqldump converts it into INSERT INTO `t1` VALUES (0),(-0) and after restore we get positive zeros.

Comment by Elena Stepanova [ 2015-10-21 ]

It is reproducible with upstream versions, including MySQL 5.7. I found a somewhat related bug report Bug #67529 GROUP BY treats +0 and -0 as different values, but nothing closer than that.

Comment by Elena Stepanova [ 2015-10-21 ]

bar, could you please take a look at it, decide what's the best course of actions here, and re-report it to MySQL if needed (unless thatsafunnyname has already done it or is planning to do)?

Comment by Peter (Stig) Edwards [ 2015-10-21 ]

I have not reported the bug to MySQL. I was wondering if because of https://dev.mysql.com/doc/refman/5.5/en/problems-with-float.html :

"A floating-point value as written in an SQL statement may not be the same as the value represented internally. Attempts to treat floating-point values as exact in comparisons may lead to problems. They are also subject to platform or implementation dependencies. The FLOAT and DOUBLE data types are subject to these issues."

it would be deemed "not a bug", similar to https://bugs.mysql.com/bug.php?id=43262 - "Float values lose precision when dumped out and reloaded" and https://bugs.launchpad.net/maria/+bug/714419 - "Replication failures with slave provisioning via mysqldump and tables with no PK"

One could argue the documentation for mysqldump could mention such potential problems.

From a pragmatic point of view, the behaviour alerted us to -0 DOUBLE values, which were changed to 0. If the -0 values had been preserved between dump and load, it would have saved me some time from looking into why the table checksums differed. I am guessing the universe of usage of DOUBLE -0 and mysqldump and load and table checksums is very small, and within that universe the deliberate usage of DOUBLE -0 is even smaller. I am thinking it would be desirable for any fix for this
to have close to zero (or negative zero ) overhead.

UPDATE. Also see:

.) The --lossless-fp option added by Justin Tolmer (at the time of Google) to mysqldump at https://code.google.com/p/google-mysql , along with the IEEE754_TO_STRING function, this contains tests for -0e0 and +0e0 being inserted into a DOUBLE.
.) https://github.com/google/mysql-tools/wiki/Lossless-FLOAT-dump-(historical-document)
.) https://code.google.com/p/google-mysql/source/detail?name=mysql-5.1.61/03-fixes-google-1&r=b22ae2a057e0c967dac089e689275b0377c966d0
.) https://code.google.com/r/lsylny-mysql/source/browse/mysql-test/t/ieee754_to_string.test?name=mysql-5.1.61/03-fixes-google-1
.) The worklog that resulted in dtoa.c being used:
.) https://bugs.mysql.com/bug.php?id=36829
.) http://dev.mysql.com/worklog/task/?id=2934

I do not see a DOUBLE -0 test in server/mysql-test/t/type_float.test
I have not dug into the mysql* code. I did notice fixes to dtoa.c since it was included in MySQL.

http://www.exploringbinary.com/gays-strtod-returns-zero-for-inputs-just-above-2-1075/

20131122
dtoa.c: fix a possible glitch with strtod in deciding whether a
decimal value less in absolute value than the smallest denormal should
be rounded to zero. (The fix is to force use of bigcomp() when at the
bottom of the exponent range and we otherwise would have returned 0.)
Thanks to Rick Regan <exploringbinary@gmail.com> for sending an
example that illustrated the bug.

Comment by Hartmut Holzgraefe [ 2017-01-09 ]

So we have two different questions here:

  • should INSERT ... VALUES (-0) insert a negative zero value the same way as '-0' or -0e0 do?
  • should mysqldump write out a quoted '-0' or the extended -0e0 notation when seeing a negative zero value?

The implementation of "let mysqldump quote negative zeros" would be simple:

diff --git a/client/mysqldump.c b/client/mysqldump.c
index f257686..44c7aee 100644
--- a/client/mysqldump.c
+++ b/client/mysqldump.c
@@ -3889,7 +3889,8 @@ static void dump_table(char *table, char *db)
                   dynstr_append_checked(&extended_row, "NULL");
                 else
                 {
-                  if (field->type == MYSQL_TYPE_DECIMAL)
+                  if ((field->type == MYSQL_TYPE_DECIMAL) ||
+                      ((field->type == MYSQL_TYPE_FLOAT) || (field->type == MYSQL_TYPE_DOUBLE) && !strcmp(ptr, "-0")))
                   {
                     /* add " signs around */
                     dynstr_append_checked(&extended_row, "'");

Comment by Hartmut Holzgraefe [ 2017-01-09 ]

Side note: "What would PostgreSQL do?"

PostgreSQL also interprets -0 as 0, but the string form '-0' as -0

Also pg_dump restores correctly in default mode that uses COPY, but fails the same way as mysqldump when being forced to use INSERT statements by the --column-inserts option.

Generated at Thu Feb 08 07:30:49 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.