From b6f04ed5d62ecd8d1c81fd7defaad8636eca8108 Mon Sep 17 00:00:00 2001
From: unknown <monty@mysql.com>
Date: Sat, 15 Jan 2005 12:28:38 +0200
Subject: [PATCH] Changed interface for my_strntod() to make it more general
 and more portable

BUILD/compile-solaris-sparc-purify:
  Cleanup (Changes from Kent)
include/m_string.h:
  New interface for my_strtod()
mysql-test/mysql-test-run.sh:
  Added option --use-old-data to allow one to run a test case on an existing table
  (Good for debugging)
mysql-test/r/strict.result:
  Updated results
mysql-test/r/type_float.result:
  More tests
mysql-test/t/strict.test:
  Safety fix
mysql-test/t/type_float.test:
  More tests
mysys/mf_iocache.c:
  Change flush_io_cache() to my_b_flush_io_cache()
  More debugging
mysys/thr_lock.c:
  Added comment
sql/field.cc:
  Use new my_strntod()
sql/filesort.cc:
  Indentation fixes
sql/item.cc:
  Use new my_strntod()
sql/item_strfunc.cc:
  Use new my_strntod()
sql/item_sum.cc:
  Use new my_strntod()
strings/ctype-cp932.c:
  strnncollsp was missing one argument
strings/ctype-simple.c:
  Use new my_strntod()
strings/ctype-ucs2.c:
  Use new my_strntod()
strings/strtod.c:
  Changed interface:
  - Force user to supply pointer to end of string (eliminates the need for an end \0)
  - More strict error checking (depend less off if INF is set), which makes this more portable
  - Better handling of numbers of type 0.000000....E+...
  - Return pointer to + in case of '+.'

  The above should fix a that strict.test failed on Solaris-sparc.
---
 BUILD/compile-solaris-sparc-purify |  23 ++--
 include/m_string.h                 |   2 +-
 mysql-test/mysql-test-run.sh       |  16 ++-
 mysql-test/r/strict.result         |   2 +-
 mysql-test/r/type_float.result     |   5 +-
 mysql-test/t/strict.test           |   2 +-
 mysql-test/t/type_float.test       |   3 +-
 mysys/mf_iocache.c                 |  13 +--
 mysys/thr_lock.c                   |   6 +-
 sql/field.cc                       |  29 ++++--
 sql/filesort.cc                    |   5 +-
 sql/item.cc                        |  24 +++--
 sql/item_strfunc.cc                |  10 +-
 sql/item_sum.cc                    |   9 +-
 strings/ctype-cp932.c              |   6 +-
 strings/ctype-simple.c             |  27 +----
 strings/ctype-ucs2.c               |  18 ++--
 strings/strtod.c                   | 162 ++++++++++++++++++++---------
 18 files changed, 219 insertions(+), 143 deletions(-)

diff --git a/BUILD/compile-solaris-sparc-purify b/BUILD/compile-solaris-sparc-purify
index 71a60e45cb0..c895d99c2cf 100755
--- a/BUILD/compile-solaris-sparc-purify
+++ b/BUILD/compile-solaris-sparc-purify
@@ -3,28 +3,27 @@
 while test $# -gt 0
 do
   case "$1" in
-   --debug) EXTRA_CONFIG_FLAGS=--with-debug; shift ;;
-  -h | --help ) cat <<EOF; exit 0 ;;
-Usage: $0 [-h|-n] [configure-options]
-  --debug		Compile with DBUG enabled
-EOF
-   *)  echo "No such option '$1'" ; exit ;;
+    --debug) EXTRA_CONFIG_FLAGS=--with-debug; shift ;;
+    -h | --help )
+      echo "Usage: $0 [-h|-n] [configure-options]"
+      echo "  --debug		Compile with DBUG enabled"
+      exit 0 ;;
+    *)  echo "No such option '$1'" ; exit ;;
   esac
 done
 
 gmake -k clean || true 
 /bin/rm -f */.deps/*.P config.cache
 aclocal && autoheader && aclocal && automake && autoconf
-(cd bdb/dist && sh s_all)
+# (cd bdb/dist && sh s_all)
 (cd innobase && aclocal && autoheader && aclocal && automake && autoconf)
 
-CFLAGS="-g -Wimplicit -Wreturn-type -Wswitch -Wtrigraphs -Wcomment -W -Wchar-subscripts -Wformat -Wimplicit-int -Wparentheses -Wsign-compare -Wwrite-strings -Wunused  -DHAVE_purify -DEXTRA_DEBUG -O2" CXX=gcc CXXLD=g++ CXXFLAGS="-g -Wimplicit -Wreturn-type -Wswitch -Wtrigraphs -Wcomment -W -Wchar-subscripts -Wformat -Wparentheses -Wsign-compare -Wwrite-strings -Woverloaded-virtual -Wsign-promo -Wreorder -Wctor-dtor-privacy -Wnon-virtual-dtor -felide-constructors -fno-exceptions -fno-rtti  -DHAVE_purify -DEXTRA_DEBUG -O2" ./configure --prefix=/usr/local/mysql --enable-assembler --with-extra-charsets=complex --enable-thread-safe-client --with-berkeley-db --with-innodb $EXTRA_CONFIG_FLAGS
+CFLAGS="-g -Wimplicit -Wreturn-type -Wswitch -Wtrigraphs -Wcomment -W -Wchar-subscripts -Wformat -Wimplicit-int -Wparentheses -Wsign-compare -Wwrite-strings -Wunused  -DHAVE_purify -DEXTRA_DEBUG -O2" CXX=gcc CXXLD=g++ CXXFLAGS="-g -Wimplicit -Wreturn-type -Wswitch -Wtrigraphs -Wcomment -W -Wchar-subscripts -Wformat -Wparentheses -Wsign-compare -Wwrite-strings -Woverloaded-virtual -Wsign-promo -Wreorder -Wctor-dtor-privacy -Wnon-virtual-dtor -felide-constructors -fno-exceptions -fno-rtti  -DHAVE_purify -DEXTRA_DEBUG -O2" ./configure --prefix=/usr/local/mysql --enable-assembler --with-extra-charsets=complex --enable-thread-safe-client --without-berkeley-db --with-embedded-server --with-innodb $EXTRA_CONFIG_FLAGS
 
 gmake -j 4
 
 cd sql ; mv mysqld mysqld-org ;
-make CXXLD="purify -best-effort g++"  mysqld ; mv mysqld mysqld-purify
-make CXXLD="quantify -best-effort g++"  mysqld ; mv mysqld mysqld-quantify
-make CXXLD="purecov -best-effort g++"  mysqld ; mv mysqld mysqld-purecov
+gmake CXXLD="purify -best-effort g++"  mysqld ; mv mysqld mysqld-purify
+gmake CXXLD="quantify -best-effort g++"  mysqld ; mv mysqld mysqld-quantify
+gmake CXXLD="purecov -best-effort g++"  mysqld ; mv mysqld mysqld-purecov
 mv mysqld-org mysqld
-
diff --git a/include/m_string.h b/include/m_string.h
index 97d34421537..d3465363beb 100644
--- a/include/m_string.h
+++ b/include/m_string.h
@@ -215,7 +215,7 @@ extern char *strstr(const char *, const char *);
 extern int is_prefix(const char *, const char *);
 
 /* Conversion routines */
-double my_strtod(const char *str, char **end);
+double my_strtod(const char *str, char **end, int *error);
 double my_atof(const char *nptr);
 
 extern char *llstr(longlong value,char *buff);
diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh
index 274f91550d4..ea41e847c2c 100644
--- a/mysql-test/mysql-test-run.sh
+++ b/mysql-test/mysql-test-run.sh
@@ -427,6 +427,9 @@ while test $# -gt 0; do
     --fast)
       FAST_START=1
       ;;
+    --use-old-data)
+      USE_OLD_DATA=1;
+      ;;
     -- )  shift; break ;;
     --* ) $ECHO "Unrecognized option: $1"; exit 1 ;;
     * ) break ;;
@@ -768,12 +771,14 @@ report_stats () {
 
 mysql_install_db () {
     $ECHO "Removing Stale Files"
-    $RM -rf $MASTER_MYDDIR $MASTER_MYDDIR"1" $SLAVE_MYDDIR $MY_LOG_DIR/* 
-    $ECHO "Installing Master Databases"
-    $INSTALL_DB
-    if [ $? != 0 ]; then
+    if [ -z "$USE_OLD_DATA" ]; then
+      $RM -rf $MASTER_MYDDIR $MASTER_MYDDIR"1"
+      $ECHO "Installing Master Databases"
+      $INSTALL_DB
+      if [ $? != 0 ]; then
 	error "Could not install master test DBs"
-	exit 1
+        exit 1
+      fi
     fi
     if [ ! -z "$USE_NDBCLUSTER" ]
     then
@@ -785,6 +790,7 @@ mysql_install_db () {
       fi
     fi
     $ECHO "Installing Slave Databases"
+    $RM -rf $SLAVE_MYDDIR $MY_LOG_DIR/* 
     $INSTALL_DB -slave
     if [ $? != 0 ]; then
 	error "Could not install slave test DBs"
diff --git a/mysql-test/r/strict.result b/mysql-test/r/strict.result
index fb228d37da3..f28317ce947 100644
--- a/mysql-test/r/strict.result
+++ b/mysql-test/r/strict.result
@@ -768,7 +768,7 @@ INSERT INTO t1 VALUES (-2.2E-307,0),(+1.7E+308,+1.7E+308);
 INSERT INTO t1 VALUES ('-2.2E-307',0),('+1.7E+308','+1.7E+308');
 INSERT INTO t1 (col1) VALUES (-2.2E-330);
 INSERT INTO t1 (col1) VALUES (+1.7E+309);
-ERROR 22007: Illegal double '1.7E+309' value found during parsing
+Got one of the listed errors
 INSERT INTO t1 (col2) VALUES (-1.1E-3);
 ERROR 22003: Out of range value adjusted for column 'col2' at row 1
 INSERT INTO t1 (col1) VALUES ('+1.8E+309');
diff --git a/mysql-test/r/type_float.result b/mysql-test/r/type_float.result
index 62aae177f6a..26cfbdeffd0 100644
--- a/mysql-test/r/type_float.result
+++ b/mysql-test/r/type_float.result
@@ -1,4 +1,4 @@
-drop table if exists t1;
+drop table if exists t1,t2;
 SELECT 10,10.0,10.,.1e+2,100.0e-1;
 10	10.0	10.	.1e+2	100.0e-1
 10	10.0	10	10	10
@@ -8,6 +8,9 @@ SELECT 6e-05, -6e-05, --6e-05, -6e-05+1.000000;
 SELECT 1e1,1.e1,1.0e1,1e+1,1.e+1,1.0e+1,1e-1,1.e-1,1.0e-1;
 1e1	1.e1	1.0e1	1e+1	1.e+1	1.0e+1	1e-1	1.e-1	1.0e-1
 10	10	10	10	10	10	0.1	0.1	0.1
+SELECT 0.001e+1,0.001e-1, -0.001e+01,-0.001e-01;
+0.001e+1	0.001e-1	-0.001e+01	-0.001e-01
+0.01	0.0001	-0.01	-0.0001
 create table t1 (f1 float(24),f2 float(52));
 show full columns from t1;
 Field	Type	Collation	Null	Key	Default	Extra	Privileges	Comment
diff --git a/mysql-test/t/strict.test b/mysql-test/t/strict.test
index a0cfc0c60f4..2ccc3e672c7 100644
--- a/mysql-test/t/strict.test
+++ b/mysql-test/t/strict.test
@@ -531,7 +531,7 @@ INSERT INTO t1 VALUES (-2.2E-307,0),(+1.7E+308,+1.7E+308);
 INSERT INTO t1 VALUES ('-2.2E-307',0),('+1.7E+308','+1.7E+308');
 # We don't give warnings for underflow
 INSERT INTO t1 (col1) VALUES (-2.2E-330);
---error 1367
+--error 1367,1264
 INSERT INTO t1 (col1) VALUES (+1.7E+309);
 --error 1264
 INSERT INTO t1 (col2) VALUES (-1.1E-3);
diff --git a/mysql-test/t/type_float.test b/mysql-test/t/type_float.test
index 3fe3afa3fac..913034dac0e 100644
--- a/mysql-test/t/type_float.test
+++ b/mysql-test/t/type_float.test
@@ -3,7 +3,7 @@
 # Numeric floating point.
 
 --disable_warnings
-drop table if exists t1;
+drop table if exists t1,t2;
 --enable_warnings
 
 --replace_result e-0 e- e+0 e+
@@ -11,6 +11,7 @@ SELECT 10,10.0,10.,.1e+2,100.0e-1;
 --replace_result e-00 e-0 
 SELECT 6e-05, -6e-05, --6e-05, -6e-05+1.000000;
 SELECT 1e1,1.e1,1.0e1,1e+1,1.e+1,1.0e+1,1e-1,1.e-1,1.0e-1;
+SELECT 0.001e+1,0.001e-1, -0.001e+01,-0.001e-01;
 
 create table t1 (f1 float(24),f2 float(52));
 show full columns from t1;
diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c
index f109df912f1..7466ae24675 100644
--- a/mysys/mf_iocache.c
+++ b/mysys/mf_iocache.c
@@ -27,7 +27,7 @@
   also info->read_pos is set to info->read_end.
   If called through open_cached_file(), then the temporary file will
   only be created if a write exeeds the file buffer or if one calls
-  flush_io_cache().
+  my_b_flush_io_cache().
 
   If one uses SEQ_READ_APPEND, then two buffers are allocated, one for
   reading and another for writing.  Reads are first done from disk and
@@ -43,7 +43,7 @@
   each time the write buffer gets full and it's written to disk, we will
   always do a disk read to read a part of the buffer from disk to the
   read buffer.
-  This should be fixed so that when we do a flush_io_cache() and
+  This should be fixed so that when we do a my_b_flush_io_cache() and
   we have been reading the write buffer, we should transfer the rest of the
   write buffer to the read buffer before we start to reuse it.
 */
@@ -339,7 +339,7 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type,
     if (info->type == WRITE_CACHE && type == READ_CACHE)
       info->end_of_file=my_b_tell(info);
     /* flush cache if we want to reuse it */
-    if (!clear_cache && flush_io_cache(info))
+    if (!clear_cache && my_b_flush_io_cache(info,1))
       DBUG_RETURN(1);
     info->pos_in_file=seek_offset;
     /* Better to do always do a seek */
@@ -948,7 +948,7 @@ int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count)
   Buffer+=rest_length;
   Count-=rest_length;
   info->write_pos+=rest_length;
-  if (flush_io_cache(info))
+  if (my_b_flush_io_cache(info,1))
     return 1;
   if (Count >= IO_SIZE)
   {					/* Fill first intern buffer */
@@ -1191,6 +1191,7 @@ int end_io_cache(IO_CACHE *info)
   int error=0;
   IO_CACHE_CALLBACK pre_close;
   DBUG_ENTER("end_io_cache");
+  DBUG_PRINT("enter",("cache: 0x%lx", (ulong) info));
 
 #ifdef THREAD
   /*
@@ -1200,7 +1201,7 @@ int end_io_cache(IO_CACHE *info)
   */
   if (info->share)
   {
-    pthread_cond_destroy (&info->share->cond);
+    pthread_cond_destroy(&info->share->cond);
     pthread_mutex_destroy(&info->share->mutex);
     info->share=0;
   }
@@ -1215,7 +1216,7 @@ int end_io_cache(IO_CACHE *info)
   {
     info->alloced_buffer=0;
     if (info->file != -1)			/* File doesn't exist */
-      error=flush_io_cache(info);
+      error= my_b_flush_io_cache(info,1);
     my_free((gptr) info->buffer,MYF(MY_WME));
     info->buffer=info->read_pos=(byte*) 0;
   }
diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c
index d47ca8de183..9ce058f90fc 100644
--- a/mysys/thr_lock.c
+++ b/mysys/thr_lock.c
@@ -523,8 +523,10 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type)
 	data->prev=lock->write_wait.last;
 	lock->write_wait.last= &data->next;
 	data->cond=get_cond();
-	if (lock->get_status)
-	  (*lock->get_status)(data->status_param);
+        /*
+          We don't have to do get_status here as we will do it when we change
+          the delayed lock to a real write lock
+        */
 	statistic_increment(locks_immediate,&THR_LOCK_lock);
 	goto end;
       }
diff --git a/sql/field.cc b/sql/field.cc
index 175ca09df37..29125d9d655 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1035,7 +1035,9 @@ int Field_decimal::store(longlong nr)
 double Field_decimal::val_real(void)
 {
   int not_used;
-  return my_strntod(&my_charset_bin, ptr, field_length, NULL, &not_used);
+  char *end_not_used;
+  return my_strntod(&my_charset_bin, ptr, field_length, &end_not_used,
+                    &not_used);
 }
 
 longlong Field_decimal::val_int(void)
@@ -4425,16 +4427,18 @@ int Field_string::store(longlong nr)
 double Field_string::val_real(void)
 {
   int not_used;
-  CHARSET_INFO *cs=charset();
-  return my_strntod(cs,ptr,field_length,(char**)0,&not_used);
+  char *end_not_used;
+  CHARSET_INFO *cs= charset();
+  return my_strntod(cs,ptr,field_length,&end_not_used,&not_used);
 }
 
 
 longlong Field_string::val_int(void)
 {
   int not_used;
+  char *end_not_used;
   CHARSET_INFO *cs=charset();
-  return my_strntoll(cs,ptr,field_length,10,NULL,&not_used);
+  return my_strntoll(cs,ptr,field_length,10,&end_not_used,&not_used);
 }
 
 
@@ -4734,8 +4738,9 @@ int Field_varstring::store(longlong nr)
 double Field_varstring::val_real(void)
 {
   int not_used;
+  char *end_not_used;
   uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
-  return my_strntod(field_charset, ptr+length_bytes, length, (char**) 0,
+  return my_strntod(field_charset, ptr+length_bytes, length, &end_not_used,
                     &not_used);
 }
 
@@ -4743,9 +4748,10 @@ double Field_varstring::val_real(void)
 longlong Field_varstring::val_int(void)
 {
   int not_used;
+  char *end_not_used;
   uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
-  return my_strntoll(field_charset, ptr+length_bytes, length, 10, NULL,
-                     &not_used);
+  return my_strntoll(field_charset, ptr+length_bytes, length, 10,
+                     &end_not_used, &not_used);
 }
 
 
@@ -5339,12 +5345,15 @@ int Field_blob::store(longlong nr)
 double Field_blob::val_real(void)
 {
   int not_used;
-  char *blob;
+  char *end_not_used, *blob;
+  uint32 length;
+  CHARSET_INFO *cs;
+
   memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
   if (!blob)
     return 0.0;
-  uint32 length=get_length(ptr);
-  CHARSET_INFO *cs=charset();
+  length= get_length(ptr);
+  cs= charset();
   return my_strntod(cs,blob,length,(char**)0, &not_used);
 }
 
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 0a4e747a136..b79ea6515c3 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -567,10 +567,10 @@ write_keys(SORTPARAM *param, register uchar **sort_keys, uint count,
   if (!my_b_inited(tempfile) &&
       open_cached_file(tempfile, mysql_tmpdir, TEMP_PREFIX, DISK_BUFFER_SIZE,
                        MYF(MY_WME)))
-    goto err;                                        /* purecov: inspected */
+    goto err;                                   /* purecov: inspected */
   buffpek.file_pos= my_b_tell(tempfile);
   if ((ha_rows) count > param->max_rows)
-    count=(uint) param->max_rows;                /* purecov: inspected */
+    count=(uint) param->max_rows;               /* purecov: inspected */
   buffpek.count=(ha_rows) count;
   for (end=sort_keys+count ; sort_keys != end ; sort_keys++)
     if (my_b_write(tempfile, (byte*) *sort_keys, (uint) rec_length))
@@ -844,6 +844,7 @@ int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
     if (flush_io_cache(to_file))
       break;					/* purecov: inspected */
     temp=from_file; from_file=to_file; to_file=temp;
+    
     *maxbuffer= (uint) (lastbuff-buffpek)-1;
   }
   close_cached_file(to_file);			// This holds old result
diff --git a/sql/item.cc b/sql/item.cc
index c84496f8eb7..47dccf5b8da 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1290,11 +1290,12 @@ double Item_param::val_real()
     return (double) value.integer;
   case STRING_VALUE:
   case LONG_DATA_VALUE:
-    {
-      int dummy_err;
-      return my_strntod(str_value.charset(), (char*) str_value.ptr(),
-                        str_value.length(), (char**) 0, &dummy_err);
-    }
+  {
+    int dummy_err;
+    char *end_not_used;
+    return my_strntod(str_value.charset(), (char*) str_value.ptr(),
+                      str_value.length(), &end_not_used, &dummy_err);
+  }
   case TIME_VALUE:
     /*
       This works for example when user says SELECT ?+0.0 and supplies
@@ -2545,8 +2546,9 @@ Item_num *Item_uint::neg()
 Item_real::Item_real(const char *str_arg, uint length)
 {
   int error;
-  char *end;
-  value= my_strntod(&my_charset_bin, (char*) str_arg, length, &end, &error);
+  char *end_not_used;
+  value= my_strntod(&my_charset_bin, (char*) str_arg, length, &end_not_used,
+                    &error);
   if (error)
   {
     /*
@@ -3522,12 +3524,12 @@ void Item_cache_str::store(Item *item)
 double Item_cache_str::val_real()
 {
   DBUG_ASSERT(fixed == 1);
-  int err;
+  int err_not_used;
+  char *end_not_used;
   if (value)
     return my_strntod(value->charset(), (char*) value->ptr(),
-		      value->length(), (char**) 0, &err);
-  else
-    return (double)0;
+		      value->length(), &end_not_used, &err_not_used);
+  return (double) 0;
 }
 
 
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 9e28acdd091..09b6d9cc35d 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -58,17 +58,19 @@ uint nr_of_decimals(const char *str)
   return 0;
 }
 
+
 double Item_str_func::val_real()
 {
   DBUG_ASSERT(fixed == 1);
-  int err;
-  char buff[64];
+  int err_not_used;
+  char *end_not_used, buff[64];
   String *res, tmp(buff,sizeof(buff), &my_charset_bin);
   res= val_str(&tmp);
-  return res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(),
-			  NULL, &err) : 0.0;
+  return res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(),
+			  &end_not_used, &err_not_used) : 0.0;
 }
 
+
 longlong Item_str_func::val_int()
 {
   DBUG_ASSERT(fixed == 1);
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 168c68ad706..be89aa3f86d 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -591,14 +591,17 @@ void Item_sum_hybrid::clear()
 double Item_sum_hybrid::val_real()
 {
   DBUG_ASSERT(fixed == 1);
-  int err;
   if (null_value)
     return 0.0;
   switch (hybrid_type) {
   case STRING_RESULT:
+  {
+    char *end_not_used;
+    int err_not_used;
     String *res;  res=val_str(&str_value);
-    return (res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(),
-			     (char**) 0, &err) : 0.0);
+    return (res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(),
+			     &end_not_used, &err_not_used) : 0.0);
+  }
   case INT_RESULT:
     if (unsigned_flag)
       return ulonglong2double(sum_int);
diff --git a/strings/ctype-cp932.c b/strings/ctype-cp932.c
index 218fc72ad17..e5429148970 100644
--- a/strings/ctype-cp932.c
+++ b/strings/ctype-cp932.c
@@ -243,8 +243,10 @@ static int my_strnncoll_cp932(CHARSET_INFO *cs __attribute__((unused)),
 
 
 static int my_strnncollsp_cp932(CHARSET_INFO *cs __attribute__((unused)),
-			       const uchar *a, uint a_length, 
-			       const uchar *b, uint b_length)
+                                const uchar *a, uint a_length, 
+                                const uchar *b, uint b_length,
+                                my_bool diff_if_only_endspace_difference
+                                __attribute__((unused)))
 {
   const uchar *a_end= a + a_length;
   const uchar *b_end= b + b_length;
diff --git a/strings/ctype-simple.c b/strings/ctype-simple.c
index 0659cb5d387..fdfe72864b2 100644
--- a/strings/ctype-simple.c
+++ b/strings/ctype-simple.c
@@ -750,31 +750,10 @@ double my_strntod_8bit(CHARSET_INFO *cs __attribute__((unused)),
 		       char *str, uint length,
 		       char **end, int *err)
 {
-  char end_char;
-  double result;
-
-  errno= 0;					/* Safety */
-
-  /*
-    The following define is to avoid warnings from valgrind as str[length]
-    may not be defined (which is not fatal in real life)
-  */
-
-#ifdef HAVE_purify
   if (length == INT_MAX32)
-#else
-  if (length == INT_MAX32 || str[length] == 0)
-#endif
-    result= my_strtod(str, end);
-  else
-  {
-    end_char= str[length];
-    str[length]= 0;
-    result= my_strtod(str, end);
-    str[length]= end_char;			/* Restore end char */
-  }
-  *err= errno;
-  return result;
+    length= 65535;                          /* Should be big enough */
+  *end= str + length;
+  return my_strtod(str, end, err);
 }
 
 
diff --git a/strings/ctype-ucs2.c b/strings/ctype-ucs2.c
index d21b340e768..7b9a7ab3020 100644
--- a/strings/ctype-ucs2.c
+++ b/strings/ctype-ucs2.c
@@ -925,15 +925,16 @@ ulonglong  my_strntoull_ucs2(CHARSET_INFO *cs,
   return (negative ? -((longlong) res) : (longlong) res);
 }
 
-double      my_strntod_ucs2(CHARSET_INFO *cs __attribute__((unused)),
-			   char *nptr, uint length, 
-			   char **endptr, int *err)
+
+double my_strntod_ucs2(CHARSET_INFO *cs __attribute__((unused)),
+                       char *nptr, uint length, 
+                       char **endptr, int *err)
 {
   char     buf[256];
   double   res;
   register char *b=buf;
   register const uchar *s= (const uchar*) nptr;
-  register const uchar *end;
+  const uchar *end;
   my_wc_t  wc;
   int      cnv;
 
@@ -950,13 +951,10 @@ double      my_strntod_ucs2(CHARSET_INFO *cs __attribute__((unused)),
       break;					/* Can't be part of double */
     *b++= (char) wc;
   }
-  *b= 0;
 
-  errno= 0;
-  res=my_strtod(buf, endptr);
-  *err= errno;
-  if (endptr)
-    *endptr=(char*) (*endptr-buf+nptr);
+  *endptr= b;
+  res= my_strtod(buf, endptr, err);
+  *endptr= nptr + (uint) (*endptr- buf);
   return res;
 }
 
diff --git a/strings/strtod.c b/strings/strtod.c
index bc8105b8040..92d93612dd0 100644
--- a/strings/strtod.c
+++ b/strings/strtod.c
@@ -2,7 +2,7 @@
   An alternative implementation of "strtod()" that is both
   simplier, and thread-safe.
 
-  From mit-threads as bundled with MySQL 3.23
+  Original code from mit-threads as bundled with MySQL 3.23
 
   SQL:2003 specifies a number as
 
@@ -29,6 +29,8 @@
 #include "my_base.h"				/* Includes errno.h */
 #include "m_ctype.h"
 
+#define MAX_DBL_EXP	308
+#define MAX_RESULT_FOR_MAX_EXP 1.79769313486232
 static double scaler10[] = {
   1.0, 1e10, 1e20, 1e30, 1e40, 1e50, 1e60, 1e70, 1e80, 1e90
 };
@@ -37,89 +39,154 @@ static double scaler1[] = {
 };
 
 
-double my_strtod(const char *str, char **end)
+/*
+  Convert string to double (string doesn't have to be null terminated)
+
+  SYNOPSIS
+    my_strtod()
+    str		String to convert
+    end_ptr	Pointer to pointer that points to end of string
+		Will be updated to point to end of double.
+    error	Will contain error number in case of error (else 0)
+
+  RETURN
+    value of str as double
+*/
+
+double my_strtod(const char *str, char **end_ptr, int *error)
 {
   double result= 0.0;
-  int negative, ndigits;
-  const char *old_str;
+  uint negative= 0, ndigits, dec_digits= 0, pre_zero, neg_exp= 0;
+  int exp= 0;
+  const char *old_str, *end= *end_ptr, *start_of_number;
+  char next_char;
   my_bool overflow=0;
 
+  *error= 0;
+  if (str >= end)
+    goto done;
+
   while (my_isspace(&my_charset_latin1, *str))
-    str++;
+  {
+    if (++str == end)
+      goto done;
+  }
 
+  start_of_number= str;
   if ((negative= (*str == '-')) || *str=='+')
-    str++;
+  {
+    if (++str == end)
+      goto done;                                /* Could be changed to error */
+  }
+
+  /* Skip pre-zero for easier calculation of overflows */
+  while (*str == '0')
+  {
+    if (++str == end)
+      goto done;
+    start_of_number= 0;                         /* Found digit */
+  }
 
   old_str= str;
-  while (my_isdigit (&my_charset_latin1, *str))
+  while ((next_char= *str) >= '0' && next_char <= '9')
   {
-    result= result*10.0 + (*str - '0');
-    str++;
+    result= result*10.0 + (next_char - '0');
+    if (++str == end)
+    {
+      next_char= 0;                             /* Found end of string */
+      break;
+    }
+    start_of_number= 0;                         /* Found digit */
   }
-  ndigits= str-old_str;
+  ndigits= (uint) (str-old_str);
 
-  if (*str == '.')
+  pre_zero= 0;
+  if (next_char == '.' && str < end-1)
   {
-    double p10=10;
-    str++;
-    old_str= str;
-    while (my_isdigit (&my_charset_latin1, *str))
+    double p10= 10;
+    old_str= ++str;
+    while (my_isdigit(&my_charset_latin1, (next_char= *str)))
     {
-      result+= (*str++ - '0')/p10;
-      p10*=10;
+      result+= (next_char - '0')/p10;
+      if (!result)
+        pre_zero++;
+      else
+        p10*= 10;
+      if (++str == end)
+      {
+        next_char= 0;
+        break;
+      }
     }
-    ndigits+= str-old_str;
-    if (!ndigits) str--;
+    /* If we found just '+.' or '.' then point at first character */
+    if (!(dec_digits= (uint) (str-old_str)) && start_of_number)
+      str= start_of_number;                     /* Point at '+' or '.' */
   }
-  if (ndigits && (*str=='e' || *str=='E'))
+  if ((next_char == 'e' || next_char == 'E') &&
+      dec_digits + ndigits != 0 && str < end-1)
   {
-    int exp= 0;
-    int neg= 0;
     const char *old_str= str++;
 
-    if ((neg= (*str == '-')) || *str == '+')
+    if ((neg_exp= (*str == '-')) || *str == '+')
       str++;
 
-    if (!my_isdigit (&my_charset_latin1, *str))
+    if (str == end || !my_isdigit(&my_charset_latin1, *str))
       str= old_str;
     else
     {
-      double scaler= 1.0;
-      while (my_isdigit (&my_charset_latin1, *str))
+      do
       {
-        if (exp < 9999) /* protection against exp overflow */
+        if (exp < 9999)                         /* protec against exp overfl. */
           exp= exp*10 + *str - '0';
         str++;
-      }
-      if (exp >= 1000)
+      } while (str < end && my_isdigit(&my_charset_latin1, *str));
+    }
+  }
+  if ((exp= neg_exp ? exp + pre_zero : exp - pre_zero))
+  {
+    double scaler;
+    if (exp < 0)
+    {
+      exp= -exp;
+      neg_exp= 1;                               /* neg_exp was 0 before */
+    }
+    if (exp + ndigits >= MAX_DBL_EXP + 1 && result)
+    {
+      /*
+        This is not 100 % as we actually will give an owerflow for
+        17E307 but not for 1.7E308 but lets cut some corners to make life
+        simpler
+      */
+      if (exp + ndigits > MAX_DBL_EXP + 1 ||
+          result >= MAX_RESULT_FOR_MAX_EXP)
       {
-	if (neg)
-	  result= 0.0;
-	else
+        if (neg_exp)
+          result= 0.0;
+        else
           overflow= 1;
         goto done;
       }
-      while (exp >= 100)
-      {
-        scaler*= 1.0e100;
-        exp-= 100;
-      }
-      scaler*= scaler10[exp/10]*scaler1[exp%10];
-      if (neg)
-        result/= scaler;
-      else
-        result*= scaler;
     }
+    scaler= 1.0;
+    while (exp >= 100)
+    {
+      scaler*= 1.0e100;
+      exp-= 100;
+    }
+    scaler*= scaler10[exp/10]*scaler1[exp%10];
+    if (neg_exp)
+      result/= scaler;
+    else
+      result*= scaler;
   }
 
 done:
-  if (end)
-    *end = (char *)str;
+  *end_ptr= (char*) str;                        /* end of number */
 
   if (overflow || isinf(result))
   {
     result= DBL_MAX;
-    errno= EOVERFLOW;
+    *error= EOVERFLOW;
   }
 
   return negative ? -result : result;
@@ -127,6 +194,7 @@ double my_strtod(const char *str, char **end)
 
 double my_atof(const char *nptr)
 {
-  return (my_strtod(nptr, 0));
+  int error;
+  const char *end= nptr+65535;                  /* Should be enough */
+  return (my_strtod(nptr, (char**) &end, &error));
 }
-
-- 
2.30.9