mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Backport my_strntod() from 5.0
Change string->float conversion to delay division as long as possible. This gives us more exact integer->float conversion for numbers of type '123.45E+02' (Bug #7740) client/mysql.cc: Fix wront usage of charset (found during review of pushed code) include/m_string.h: Backported my_strtod() from 5.0 mysql-test/mysql-test-run.sh: Run also mysql_client_test with --debug mysql-test/r/ps_1general.result: Safety fix (if mysql_client_test.test fails) mysql-test/r/type_float.result: More test mysql-test/t/mysql_client_test.test: Comments for what to do if this test fails mysql-test/t/ps_1general.test: Safety fix (if mysql_client_test.test fails) mysql-test/t/type_float.test: More test to better test new strtod() function Test also bug #7740 (wrong comparsion between integer and float-in-integer-range) sql/field.cc: Backport my_strntod() from 5.0 sql/item.cc: Backport my_strntod() from 5.0 sql/item.h: Backport my_strntod() from 5.0 sql/item_func.h: Backport my_strntod() from 5.0 sql/item_strfunc.cc: Backport my_strntod() from 5.0 sql/item_sum.cc: Backport my_strntod() from 5.0 sql/item_sum.h: Backport my_strntod() from 5.0 sql/procedure.h: Backport my_strntod() from 5.0 strings/ctype-simple.c: Backport my_strntod() from 5.0 strings/ctype-ucs2.c: Backport my_strntod() from 5.0 strings/strtod.c: Backport my_strntod() from 5.0 Change conversion to delay division as long as possible. This gives us more exact integer-> float conversion for numbers of type '123.45E+02'
This commit is contained in:
@ -2914,9 +2914,9 @@ com_status(String *buffer __attribute__((unused)),
|
|||||||
MYSQL_ROW cur=mysql_fetch_row(result);
|
MYSQL_ROW cur=mysql_fetch_row(result);
|
||||||
if (cur)
|
if (cur)
|
||||||
{
|
{
|
||||||
tee_fprintf(stdout, "Server characterset:\t%s\n", cur[0] ? cur[2] : "");
|
tee_fprintf(stdout, "Server characterset:\t%s\n", cur[2] ? cur[2] : "");
|
||||||
tee_fprintf(stdout, "Db characterset:\t%s\n", cur[3] ? cur[3] : "");
|
tee_fprintf(stdout, "Db characterset:\t%s\n", cur[3] ? cur[3] : "");
|
||||||
tee_fprintf(stdout, "Client characterset:\t%s\n", cur[2] ? cur[0] : "");
|
tee_fprintf(stdout, "Client characterset:\t%s\n", cur[0] ? cur[0] : "");
|
||||||
tee_fprintf(stdout, "Conn. characterset:\t%s\n", cur[1] ? cur[1] : "");
|
tee_fprintf(stdout, "Conn. characterset:\t%s\n", cur[1] ? cur[1] : "");
|
||||||
}
|
}
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
|
@ -215,7 +215,7 @@ extern char *strstr(const char *, const char *);
|
|||||||
extern int is_prefix(const char *, const char *);
|
extern int is_prefix(const char *, const char *);
|
||||||
|
|
||||||
/* Conversion routines */
|
/* 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);
|
double my_atof(const char *nptr);
|
||||||
|
|
||||||
extern char *llstr(longlong value,char *buff);
|
extern char *llstr(longlong value,char *buff);
|
||||||
|
@ -444,6 +444,7 @@ while test $# -gt 0; do
|
|||||||
--debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysqldump.trace"
|
--debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysqldump.trace"
|
||||||
EXTRA_MYSQLBINLOG_OPT="$EXTRA_MYSQLBINLOG_OPT \
|
EXTRA_MYSQLBINLOG_OPT="$EXTRA_MYSQLBINLOG_OPT \
|
||||||
--debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysqlbinlog.trace"
|
--debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysqlbinlog.trace"
|
||||||
|
EXTRA_MYSQL_CLIENT_TEST_OPT="--debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysql_client_test.trace"
|
||||||
;;
|
;;
|
||||||
--fast)
|
--fast)
|
||||||
FAST_START=1
|
FAST_START=1
|
||||||
@ -681,7 +682,7 @@ then
|
|||||||
EXTRA_SLAVE_MYSQLD_OPT="$EXTRA_SLAVE_MYSQLD_OPT --user=root"
|
EXTRA_SLAVE_MYSQLD_OPT="$EXTRA_SLAVE_MYSQLD_OPT --user=root"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
MYSQL_CLIENT_TEST="$MYSQL_CLIENT_TEST --no-defaults --testcase --user=root --socket=$MASTER_MYSOCK --port=$MYSQL_TCP_PORT --silent"
|
MYSQL_CLIENT_TEST="$MYSQL_CLIENT_TEST --no-defaults --testcase --user=root --socket=$MASTER_MYSOCK --port=$MYSQL_TCP_PORT --silent $EXTRA_MYSQL_CLIENT_TEST_OPT"
|
||||||
MYSQL_DUMP="$MYSQL_DUMP --no-defaults -uroot --socket=$MASTER_MYSOCK --password=$DBPASSWD $EXTRA_MYSQLDUMP_OPT"
|
MYSQL_DUMP="$MYSQL_DUMP --no-defaults -uroot --socket=$MASTER_MYSOCK --password=$DBPASSWD $EXTRA_MYSQLDUMP_OPT"
|
||||||
MYSQL_BINLOG="$MYSQL_BINLOG --no-defaults --local-load=$MYSQL_TMP_DIR $EXTRA_MYSQLBINLOG_OPT"
|
MYSQL_BINLOG="$MYSQL_BINLOG --no-defaults --local-load=$MYSQL_TMP_DIR $EXTRA_MYSQLBINLOG_OPT"
|
||||||
MYSQL_FIX_SYSTEM_TABLES="$MYSQL_FIX_SYSTEM_TABLES --no-defaults --host=localhost --port=$MASTER_MYPORT --socket=$MASTER_MYSOCK --user=root --password=$DBPASSWD --basedir=$BASEDIR --bindir=$CLIENT_BINDIR --verbose"
|
MYSQL_FIX_SYSTEM_TABLES="$MYSQL_FIX_SYSTEM_TABLES --no-defaults --host=localhost --port=$MASTER_MYPORT --socket=$MASTER_MYSOCK --user=root --password=$DBPASSWD --basedir=$BASEDIR --bindir=$CLIENT_BINDIR --verbose"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
drop table if exists t5, t6, t7, t8;
|
drop table if exists t5, t6, t7, t8;
|
||||||
drop database if exists mysqltest ;
|
drop database if exists mysqltest ;
|
||||||
|
drop database if exists client_test_db;
|
||||||
test_sequence
|
test_sequence
|
||||||
------ basic tests ------
|
------ basic tests ------
|
||||||
drop table if exists t1, t9 ;
|
drop table if exists t1, t9 ;
|
||||||
|
@ -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;
|
SELECT 10,10.0,10.,.1e+2,100.0e-1;
|
||||||
10 10.0 10. .1e+2 100.0e-1
|
10 10.0 10. .1e+2 100.0e-1
|
||||||
10 10.0 10 10 10
|
10 10.0 10 10 10
|
||||||
@ -8,6 +8,15 @@ 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 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
|
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
|
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
|
||||||
|
SELECT 123.23E+02,-123.23E-02,"123.23E+02"+0.0,"-123.23E-02"+0.0;
|
||||||
|
123.23E+02 -123.23E-02 "123.23E+02"+0.0 "-123.23E-02"+0.0
|
||||||
|
12323 -1.2323 12323 -1.2323
|
||||||
|
SELECT 2147483647E+02,21474836.47E+06;
|
||||||
|
2147483647E+02 21474836.47E+06
|
||||||
|
214748364700 21474836470000
|
||||||
create table t1 (f1 float(24),f2 float(52));
|
create table t1 (f1 float(24),f2 float(52));
|
||||||
show full columns from t1;
|
show full columns from t1;
|
||||||
Field Type Collation Null Key Default Extra Privileges Comment
|
Field Type Collation Null Key Default Extra Privileges Comment
|
||||||
@ -139,6 +148,9 @@ create table t1 (c20 char);
|
|||||||
insert into t1 values (5000.0);
|
insert into t1 values (5000.0);
|
||||||
Warnings:
|
Warnings:
|
||||||
Warning 1265 Data truncated for column 'c20' at row 1
|
Warning 1265 Data truncated for column 'c20' at row 1
|
||||||
|
insert into t1 values (0.5e4);
|
||||||
|
Warnings:
|
||||||
|
Warning 1265 Data truncated for column 'c20' at row 1
|
||||||
drop table t1;
|
drop table t1;
|
||||||
create table t1 (f float(54));
|
create table t1 (f float(54));
|
||||||
ERROR 42000: Incorrect column specifier for column 'f'
|
ERROR 42000: Incorrect column specifier for column 'f'
|
||||||
@ -203,3 +215,23 @@ c
|
|||||||
0.0002
|
0.0002
|
||||||
2e-05
|
2e-05
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
CREATE TABLE t1 (
|
||||||
|
reckey int unsigned NOT NULL,
|
||||||
|
recdesc varchar(50) NOT NULL,
|
||||||
|
PRIMARY KEY (reckey)
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
|
||||||
|
INSERT INTO t1 VALUES (108, 'Has 108 as key');
|
||||||
|
INSERT INTO t1 VALUES (109, 'Has 109 as key');
|
||||||
|
select * from t1 where reckey=108;
|
||||||
|
reckey recdesc
|
||||||
|
108 Has 108 as key
|
||||||
|
select * from t1 where reckey=1.08E2;
|
||||||
|
reckey recdesc
|
||||||
|
108 Has 108 as key
|
||||||
|
select * from t1 where reckey=109;
|
||||||
|
reckey recdesc
|
||||||
|
109 Has 109 as key
|
||||||
|
select * from t1 where reckey=1.09E2;
|
||||||
|
reckey recdesc
|
||||||
|
109 Has 109 as key
|
||||||
|
drop table t1;
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
# We run with different binaries for normal and --embedded-server
|
# We run with different binaries for normal and --embedded-server
|
||||||
|
#
|
||||||
|
# If this test fails with "command "$MYSQL_CLIENT_TEST" failed",
|
||||||
|
# you should either run mysql_client_test separartely against a running
|
||||||
|
# server or run mysql-test-run --debug mysql_client_test and check
|
||||||
|
# var/log/mysql_client_test.trace
|
||||||
|
|
||||||
--disable_result_log
|
--disable_result_log
|
||||||
|
--exec echo $MYSQL_CLIENT_TEST
|
||||||
--exec $MYSQL_CLIENT_TEST
|
--exec $MYSQL_CLIENT_TEST
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
--disable_warnings
|
--disable_warnings
|
||||||
drop table if exists t5, t6, t7, t8;
|
drop table if exists t5, t6, t7, t8;
|
||||||
drop database if exists mysqltest ;
|
drop database if exists mysqltest ;
|
||||||
|
drop database if exists client_test_db;
|
||||||
--enable_warnings
|
--enable_warnings
|
||||||
|
|
||||||
--disable_query_log
|
--disable_query_log
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# Numeric floating point.
|
# Numeric floating point.
|
||||||
|
|
||||||
--disable_warnings
|
--disable_warnings
|
||||||
drop table if exists t1;
|
drop table if exists t1,t2;
|
||||||
--enable_warnings
|
--enable_warnings
|
||||||
|
|
||||||
--replace_result e-0 e- e+0 e+
|
--replace_result e-0 e- e+0 e+
|
||||||
@ -11,6 +11,9 @@ SELECT 10,10.0,10.,.1e+2,100.0e-1;
|
|||||||
--replace_result e-00 e-0
|
--replace_result e-00 e-0
|
||||||
SELECT 6e-05, -6e-05, --6e-05, -6e-05+1.000000;
|
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 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;
|
||||||
|
SELECT 123.23E+02,-123.23E-02,"123.23E+02"+0.0,"-123.23E-02"+0.0;
|
||||||
|
SELECT 2147483647E+02,21474836.47E+06;
|
||||||
|
|
||||||
create table t1 (f1 float(24),f2 float(52));
|
create table t1 (f1 float(24),f2 float(52));
|
||||||
show full columns from t1;
|
show full columns from t1;
|
||||||
@ -83,6 +86,7 @@ drop table t1;
|
|||||||
#
|
#
|
||||||
create table t1 (c20 char);
|
create table t1 (c20 char);
|
||||||
insert into t1 values (5000.0);
|
insert into t1 values (5000.0);
|
||||||
|
insert into t1 values (0.5e4);
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
# Errors
|
# Errors
|
||||||
@ -120,3 +124,23 @@ create table t1 (c char(6));
|
|||||||
insert into t1 values (2e5),(2e6),(2e-4),(2e-5);
|
insert into t1 values (2e5),(2e6),(2e-4),(2e-5);
|
||||||
select * from t1;
|
select * from t1;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test of comparison of integer with float-in-range (Bug #7840)
|
||||||
|
# This is needed because some ODBC applications (like Foxpro) uses
|
||||||
|
# floats for everything.
|
||||||
|
#
|
||||||
|
|
||||||
|
CREATE TABLE t1 (
|
||||||
|
reckey int unsigned NOT NULL,
|
||||||
|
recdesc varchar(50) NOT NULL,
|
||||||
|
PRIMARY KEY (reckey)
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
|
||||||
|
|
||||||
|
INSERT INTO t1 VALUES (108, 'Has 108 as key');
|
||||||
|
INSERT INTO t1 VALUES (109, 'Has 109 as key');
|
||||||
|
select * from t1 where reckey=108;
|
||||||
|
select * from t1 where reckey=1.08E2;
|
||||||
|
select * from t1 where reckey=109;
|
||||||
|
select * from t1 where reckey=1.09E2;
|
||||||
|
drop table t1;
|
||||||
|
14
sql/field.cc
14
sql/field.cc
@ -968,7 +968,9 @@ int Field_decimal::store(longlong nr)
|
|||||||
double Field_decimal::val_real(void)
|
double Field_decimal::val_real(void)
|
||||||
{
|
{
|
||||||
int not_used;
|
int not_used;
|
||||||
return my_strntod(&my_charset_bin, ptr, field_length, NULL, ¬_used);
|
char *end_not_used;
|
||||||
|
return my_strntod(&my_charset_bin, ptr, field_length, &end_not_used,
|
||||||
|
¬_used);
|
||||||
}
|
}
|
||||||
|
|
||||||
longlong Field_decimal::val_int(void)
|
longlong Field_decimal::val_int(void)
|
||||||
@ -4360,8 +4362,9 @@ int Field_string::store(longlong nr)
|
|||||||
double Field_string::val_real(void)
|
double Field_string::val_real(void)
|
||||||
{
|
{
|
||||||
int not_used;
|
int not_used;
|
||||||
|
char *end_not_used;
|
||||||
CHARSET_INFO *cs=charset();
|
CHARSET_INFO *cs=charset();
|
||||||
return my_strntod(cs,ptr,field_length,(char**)0,¬_used);
|
return my_strntod(cs, ptr, field_length, &end_not_used, ¬_used);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4577,7 +4580,9 @@ double Field_varstring::val_real(void)
|
|||||||
int not_used;
|
int not_used;
|
||||||
uint length=uint2korr(ptr)+HA_KEY_BLOB_LENGTH;
|
uint length=uint2korr(ptr)+HA_KEY_BLOB_LENGTH;
|
||||||
CHARSET_INFO *cs=charset();
|
CHARSET_INFO *cs=charset();
|
||||||
return my_strntod(cs, ptr+HA_KEY_BLOB_LENGTH, length, (char**)0, ¬_used);
|
char *end_not_used;
|
||||||
|
return my_strntod(cs, ptr+HA_KEY_BLOB_LENGTH, length, &end_not_used,
|
||||||
|
¬_used);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4955,12 +4960,13 @@ double Field_blob::val_real(void)
|
|||||||
{
|
{
|
||||||
int not_used;
|
int not_used;
|
||||||
char *blob;
|
char *blob;
|
||||||
|
char *end_not_used;
|
||||||
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
|
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
|
||||||
if (!blob)
|
if (!blob)
|
||||||
return 0.0;
|
return 0.0;
|
||||||
uint32 length=get_length(ptr);
|
uint32 length=get_length(ptr);
|
||||||
CHARSET_INFO *cs=charset();
|
CHARSET_INFO *cs=charset();
|
||||||
return my_strntod(cs,blob,length,(char**)0, ¬_used);
|
return my_strntod(cs,blob,length, &end_not_used, ¬_used);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
11
sql/item.cc
11
sql/item.cc
@ -1140,8 +1140,9 @@ double Item_param::val()
|
|||||||
case LONG_DATA_VALUE:
|
case LONG_DATA_VALUE:
|
||||||
{
|
{
|
||||||
int dummy_err;
|
int dummy_err;
|
||||||
|
char *end_not_used;
|
||||||
return my_strntod(str_value.charset(), (char*) str_value.ptr(),
|
return my_strntod(str_value.charset(), (char*) str_value.ptr(),
|
||||||
str_value.length(), (char**) 0, &dummy_err);
|
str_value.length(), &end_not_used, &dummy_err);
|
||||||
}
|
}
|
||||||
case TIME_VALUE:
|
case TIME_VALUE:
|
||||||
/*
|
/*
|
||||||
@ -2585,10 +2586,12 @@ double Item_cache_str::val()
|
|||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
int err;
|
int err;
|
||||||
if (value)
|
if (value)
|
||||||
|
{
|
||||||
|
char *end_not_used;
|
||||||
return my_strntod(value->charset(), (char*) value->ptr(),
|
return my_strntod(value->charset(), (char*) value->ptr(),
|
||||||
value->length(), (char**) 0, &err);
|
value->length(), &end_not_used, &err);
|
||||||
else
|
}
|
||||||
return (double)0;
|
return (double)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -719,8 +719,9 @@ public:
|
|||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
int err;
|
int err;
|
||||||
|
char *end_not_used;
|
||||||
return my_strntod(str_value.charset(), (char*) str_value.ptr(),
|
return my_strntod(str_value.charset(), (char*) str_value.ptr(),
|
||||||
str_value.length(), (char**) 0, &err);
|
str_value.length(), &end_not_used, &err);
|
||||||
}
|
}
|
||||||
longlong val_int()
|
longlong val_int()
|
||||||
{
|
{
|
||||||
@ -1044,9 +1045,10 @@ public:
|
|||||||
double val()
|
double val()
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
char *end_not_used;
|
||||||
return (null_value ? 0.0 :
|
return (null_value ? 0.0 :
|
||||||
my_strntod(str_value.charset(), (char*) str_value.ptr(),
|
my_strntod(str_value.charset(), (char*) str_value.ptr(),
|
||||||
str_value.length(),NULL,&err));
|
str_value.length(), &end_not_used, &err));
|
||||||
}
|
}
|
||||||
longlong val_int()
|
longlong val_int()
|
||||||
{
|
{
|
||||||
|
@ -828,8 +828,11 @@ public:
|
|||||||
double val()
|
double val()
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
String *res; res=val_str(&str_value);
|
String *res;
|
||||||
return res ? my_strntod(res->charset(),(char*) res->ptr(),res->length(),0,&err) : 0.0;
|
char *end_not_used;
|
||||||
|
res=val_str(&str_value);
|
||||||
|
return res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(),
|
||||||
|
&end_not_used, &err) : 0.0;
|
||||||
}
|
}
|
||||||
longlong val_int()
|
longlong val_int()
|
||||||
{
|
{
|
||||||
|
@ -63,10 +63,11 @@ double Item_str_func::val()
|
|||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
int err;
|
int err;
|
||||||
char buff[64];
|
char buff[64];
|
||||||
|
char *end_not_used;
|
||||||
String *res, tmp(buff,sizeof(buff), &my_charset_bin);
|
String *res, tmp(buff,sizeof(buff), &my_charset_bin);
|
||||||
res= val_str(&tmp);
|
res= val_str(&tmp);
|
||||||
return res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(),
|
return res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(),
|
||||||
NULL, &err) : 0.0;
|
&end_not_used, &err) : 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
longlong Item_str_func::val_int()
|
longlong Item_str_func::val_int()
|
||||||
|
@ -471,13 +471,14 @@ double Item_sum_hybrid::val()
|
|||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
int err;
|
int err;
|
||||||
|
char *end_not_used;
|
||||||
if (null_value)
|
if (null_value)
|
||||||
return 0.0;
|
return 0.0;
|
||||||
switch (hybrid_type) {
|
switch (hybrid_type) {
|
||||||
case STRING_RESULT:
|
case STRING_RESULT:
|
||||||
String *res; res=val_str(&str_value);
|
String *res; res=val_str(&str_value);
|
||||||
return (res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(),
|
return (res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(),
|
||||||
(char**) 0, &err) : 0.0);
|
&end_not_used, &err) : 0.0);
|
||||||
case INT_RESULT:
|
case INT_RESULT:
|
||||||
if (unsigned_flag)
|
if (unsigned_flag)
|
||||||
return ulonglong2double(sum_int);
|
return ulonglong2double(sum_int);
|
||||||
|
@ -600,9 +600,11 @@ public:
|
|||||||
double val()
|
double val()
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
String *res; res=val_str(&str_value);
|
char *end_not_used;
|
||||||
|
String *res;
|
||||||
|
res=val_str(&str_value);
|
||||||
return res ? my_strntod(res->charset(),(char*) res->ptr(),res->length(),
|
return res ? my_strntod(res->charset(),(char*) res->ptr(),res->length(),
|
||||||
(char**) 0, &err) : 0.0;
|
&end_not_used, &err) : 0.0;
|
||||||
}
|
}
|
||||||
longlong val_int()
|
longlong val_int()
|
||||||
{
|
{
|
||||||
|
@ -59,7 +59,11 @@ public:
|
|||||||
void set(double nr) { value=nr; }
|
void set(double nr) { value=nr; }
|
||||||
void set(longlong nr) { value=(double) nr; }
|
void set(longlong nr) { value=(double) nr; }
|
||||||
void set(const char *str,uint length,CHARSET_INFO *cs)
|
void set(const char *str,uint length,CHARSET_INFO *cs)
|
||||||
{ int err; value=my_strntod(cs,(char*) str,length,(char**)0,&err); }
|
{
|
||||||
|
int err;
|
||||||
|
char *end_not_used;
|
||||||
|
value= my_strntod(cs, (char*) str, length, &end_not_used, &err);
|
||||||
|
}
|
||||||
double val() { return value; }
|
double val() { return value; }
|
||||||
longlong val_int() { return (longlong) value; }
|
longlong val_int() { return (longlong) value; }
|
||||||
String *val_str(String *s) { s->set(value,decimals,default_charset()); return s; }
|
String *val_str(String *s) { s->set(value,decimals,default_charset()); return s; }
|
||||||
@ -99,9 +103,10 @@ public:
|
|||||||
double val()
|
double val()
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
CHARSET_INFO *cs=str_value.charset();
|
CHARSET_INFO *cs= str_value.charset();
|
||||||
|
char *end_not_used;
|
||||||
return my_strntod(cs, (char*) str_value.ptr(), str_value.length(),
|
return my_strntod(cs, (char*) str_value.ptr(), str_value.length(),
|
||||||
(char**) 0, &err);
|
&end_not_used, &err);
|
||||||
}
|
}
|
||||||
longlong val_int()
|
longlong val_int()
|
||||||
{
|
{
|
||||||
|
@ -773,31 +773,10 @@ double my_strntod_8bit(CHARSET_INFO *cs __attribute__((unused)),
|
|||||||
char *str, uint length,
|
char *str, uint length,
|
||||||
char **end, int *err)
|
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)
|
if (length == INT_MAX32)
|
||||||
#else
|
length= 65535; /* Should be big enough */
|
||||||
if (length == INT_MAX32 || str[length] == 0)
|
*end= str + length;
|
||||||
#endif
|
return my_strtod(str, end, err);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -946,13 +946,10 @@ double my_strntod_ucs2(CHARSET_INFO *cs __attribute__((unused)),
|
|||||||
break; /* Can't be part of double */
|
break; /* Can't be part of double */
|
||||||
*b++= (char) wc;
|
*b++= (char) wc;
|
||||||
}
|
}
|
||||||
*b= 0;
|
|
||||||
|
|
||||||
errno= 0;
|
*endptr= b;
|
||||||
res=my_strtod(buf, endptr);
|
res= my_strtod(buf, endptr, err);
|
||||||
*err= errno;
|
*endptr= nptr + (uint) (*endptr- buf);
|
||||||
if (endptr)
|
|
||||||
*endptr=(char*) (*endptr-buf+nptr);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
175
strings/strtod.c
175
strings/strtod.c
@ -2,7 +2,7 @@
|
|||||||
An alternative implementation of "strtod()" that is both
|
An alternative implementation of "strtod()" that is both
|
||||||
simplier, and thread-safe.
|
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
|
SQL:2003 specifies a number as
|
||||||
|
|
||||||
@ -29,6 +29,8 @@
|
|||||||
#include "my_base.h" /* Includes errno.h */
|
#include "my_base.h" /* Includes errno.h */
|
||||||
#include "m_ctype.h"
|
#include "m_ctype.h"
|
||||||
|
|
||||||
|
#define MAX_DBL_EXP 308
|
||||||
|
#define MAX_RESULT_FOR_MAX_EXP 1.79769313486232
|
||||||
static double scaler10[] = {
|
static double scaler10[] = {
|
||||||
1.0, 1e10, 1e20, 1e30, 1e40, 1e50, 1e60, 1e70, 1e80, 1e90
|
1.0, 1e10, 1e20, 1e30, 1e40, 1e50, 1e60, 1e70, 1e80, 1e90
|
||||||
};
|
};
|
||||||
@ -37,89 +39,157 @@ 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;
|
double result= 0.0;
|
||||||
int negative, ndigits;
|
uint negative= 0, ndigits, dec_digits= 0, neg_exp= 0;
|
||||||
const char *old_str;
|
int exp= 0, digits_after_dec_point= 0;
|
||||||
|
const char *old_str, *end= *end_ptr, *start_of_number;
|
||||||
|
char next_char;
|
||||||
my_bool overflow=0;
|
my_bool overflow=0;
|
||||||
|
|
||||||
while (my_isspace(&my_charset_latin1, *str))
|
*error= 0;
|
||||||
str++;
|
if (str >= end)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
while (my_isspace(&my_charset_latin1, *str))
|
||||||
|
{
|
||||||
|
if (++str == end)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_of_number= str;
|
||||||
if ((negative= (*str == '-')) || *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;
|
old_str= str;
|
||||||
while (my_isdigit (&my_charset_latin1, *str))
|
while ((next_char= *str) >= '0' && next_char <= '9')
|
||||||
{
|
{
|
||||||
result= result*10.0 + (*str - '0');
|
result= result*10.0 + (next_char - '0');
|
||||||
str++;
|
if (++str == end)
|
||||||
}
|
|
||||||
ndigits= str-old_str;
|
|
||||||
|
|
||||||
if (*str == '.')
|
|
||||||
{
|
|
||||||
double p10=10;
|
|
||||||
str++;
|
|
||||||
old_str= str;
|
|
||||||
while (my_isdigit (&my_charset_latin1, *str))
|
|
||||||
{
|
{
|
||||||
result+= (*str++ - '0')/p10;
|
next_char= 0; /* Found end of string */
|
||||||
p10*=10;
|
break;
|
||||||
}
|
}
|
||||||
ndigits+= str-old_str;
|
start_of_number= 0; /* Found digit */
|
||||||
if (!ndigits) str--;
|
|
||||||
}
|
}
|
||||||
if (ndigits && (*str=='e' || *str=='E'))
|
ndigits= (uint) (str-old_str);
|
||||||
|
|
||||||
|
if (next_char == '.' && str < end-1)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Continue to add numbers after decimal point to the result, as if there
|
||||||
|
was no decimal point. We will later (in the exponent handling) shift
|
||||||
|
the number down with the required number of fractions. We do it this
|
||||||
|
way to be able to get maximum precision for numbers like 123.45E+02,
|
||||||
|
which are normal for some ODBC applications.
|
||||||
|
*/
|
||||||
|
old_str= ++str;
|
||||||
|
while (my_isdigit(&my_charset_latin1, (next_char= *str)))
|
||||||
|
{
|
||||||
|
result= result*10.0 + (next_char - '0');
|
||||||
|
digits_after_dec_point++;
|
||||||
|
if (++str == end)
|
||||||
|
{
|
||||||
|
next_char= 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 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 ((next_char == 'e' || next_char == 'E') &&
|
||||||
|
dec_digits + ndigits != 0 && str < end-1)
|
||||||
{
|
{
|
||||||
int exp= 0;
|
|
||||||
int neg= 0;
|
|
||||||
const char *old_str= str++;
|
const char *old_str= str++;
|
||||||
|
|
||||||
if ((neg= (*str == '-')) || *str == '+')
|
if ((neg_exp= (*str == '-')) || *str == '+')
|
||||||
str++;
|
str++;
|
||||||
|
|
||||||
if (!my_isdigit (&my_charset_latin1, *str))
|
if (str == end || !my_isdigit(&my_charset_latin1, *str))
|
||||||
str= old_str;
|
str= old_str;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
double scaler= 1.0;
|
do
|
||||||
while (my_isdigit (&my_charset_latin1, *str))
|
|
||||||
{
|
{
|
||||||
if (exp < 9999) /* protection against exp overflow */
|
if (exp < 9999) /* prot. against exp overfl. */
|
||||||
exp= exp*10 + *str - '0';
|
exp= exp*10 + (*str - '0');
|
||||||
str++;
|
str++;
|
||||||
}
|
} while (str < end && my_isdigit(&my_charset_latin1, *str));
|
||||||
if (exp >= 1000)
|
}
|
||||||
|
}
|
||||||
|
if ((exp= (neg_exp ? exp + digits_after_dec_point :
|
||||||
|
exp - digits_after_dec_point)))
|
||||||
|
{
|
||||||
|
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)
|
if (neg_exp)
|
||||||
result= 0.0;
|
result= 0.0;
|
||||||
else
|
else
|
||||||
overflow= 1;
|
overflow= 1;
|
||||||
goto done;
|
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:
|
done:
|
||||||
if (end)
|
*end_ptr= (char*) str; /* end of number */
|
||||||
*end = (char *)str;
|
|
||||||
|
|
||||||
if (overflow || isinf(result))
|
if (overflow || isinf(result))
|
||||||
{
|
{
|
||||||
result= DBL_MAX;
|
result= DBL_MAX;
|
||||||
errno= EOVERFLOW;
|
*error= EOVERFLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
return negative ? -result : result;
|
return negative ? -result : result;
|
||||||
@ -127,6 +197,7 @@ done:
|
|||||||
|
|
||||||
double my_atof(const char *nptr)
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user