Skip to content

Commit d9f8537

Browse files
committed
Fix for bugs #68114 (Build fails on OS X due to undefined symbols)
and #68657 (Reading 4 byte floats with Mysqli and libmysqlclient has rounding errors). The patch removes support for Decimal floating point numbers and now defaults to using similar logic as what libmysqlclient does: convert a 4 byte floating point number into a string, and then the string into a double. The quirks of MySQL are maintained as seen in Field_Float::val_str()
1 parent aa192f5 commit d9f8537

File tree

9 files changed

+141
-91
lines changed

9 files changed

+141
-91
lines changed

ext/mysqli/mysqli_api.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "ext/standard/php_smart_str.h"
3434
#include "php_mysqli_structs.h"
3535
#include "mysqli_priv.h"
36+
#include "ext/standard/float_to_double.h"
3637

3738

3839
#if !defined(MYSQLI_USE_MYSQLND)
@@ -413,8 +414,18 @@ mysqli_stmt_bind_result_do_bind(MY_STMT *stmt, zval ***args, unsigned int argc,
413414
col_type = (stmt->stmt->fields) ? stmt->stmt->fields[ofs].type : MYSQL_TYPE_STRING;
414415

415416
switch (col_type) {
416-
case MYSQL_TYPE_DOUBLE:
417417
case MYSQL_TYPE_FLOAT:
418+
convert_to_double_ex(args[i]);
419+
stmt->result.buf[ofs].type = IS_DOUBLE;
420+
stmt->result.buf[ofs].buflen = sizeof(float);
421+
422+
stmt->result.buf[ofs].val = (char *)emalloc(sizeof(float));
423+
bind[ofs].buffer_type = MYSQL_TYPE_FLOAT;
424+
bind[ofs].buffer = stmt->result.buf[ofs].val;
425+
bind[ofs].is_null = &stmt->result.is_null[ofs];
426+
break;
427+
428+
case MYSQL_TYPE_DOUBLE:
418429
convert_to_double_ex(args[i]);
419430
stmt->result.buf[ofs].type = IS_DOUBLE;
420431
stmt->result.buf[ofs].buflen = sizeof(double);
@@ -1045,8 +1056,22 @@ void mysqli_stmt_fetch_libmysql(INTERNAL_FUNCTION_PARAMETERS)
10451056
}
10461057
break;
10471058
case IS_DOUBLE:
1048-
ZVAL_DOUBLE(stmt->result.vars[i], *(double *)stmt->result.buf[i].val);
1059+
{
1060+
double dval;
1061+
if (stmt->stmt->bind[i].buffer_type == MYSQL_TYPE_FLOAT) {
1062+
#ifndef NOT_FIXED_DEC
1063+
# define NOT_FIXED_DEC 31
1064+
#endif
1065+
dval = float_to_double(*(float *)stmt->result.buf[i].val,
1066+
(stmt->stmt->fields[i].decimals >= NOT_FIXED_DEC) ? -1 :
1067+
stmt->stmt->fields[i].decimals);
1068+
} else {
1069+
dval = *((double *)stmt->result.buf[i].val);
1070+
}
1071+
1072+
ZVAL_DOUBLE(stmt->result.vars[i], dval);
10491073
break;
1074+
}
10501075
case IS_STRING:
10511076
if (stmt->stmt->bind[i].buffer_type == MYSQL_TYPE_LONGLONG
10521077
#if MYSQL_VERSION_ID > 50002

ext/mysqli/tests/010.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,18 @@ mysqli_close($link);
6262
--EXPECT--
6363
array(7) {
6464
[0]=>
65-
float(3.141593)
65+
float(3.14159)
6666
[1]=>
6767
float(-1.0E-6)
6868
[2]=>
6969
float(0)
7070
[3]=>
7171
float(1.0E+12)
7272
[4]=>
73-
float(0.5646425)
73+
float(0.564642)
7474
[5]=>
7575
float(1)
7676
[6]=>
77-
float(8.888889E+14)
77+
float(8.88889E+14)
7878
}
7979
done!

ext/mysqli/tests/bug67839.phpt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,27 @@ mysqli_float_handling - ensure 4 byte float is handled correctly
3636
die();
3737
}
3838

39-
if (!mysqli_stmt_execute($stmt)) {
39+
$id = null;
40+
$fp4 = null;
41+
$fp8 = null;
42+
43+
if (!mysqli_stmt_bind_result($stmt, $id, $fp4, $fp8)) {
4044
printf("[006] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
4145
die();
4246
}
4347

44-
45-
if (!($result = mysqli_stmt_get_result($stmt))) {
48+
if (!mysqli_stmt_execute($stmt)) {
4649
printf("[007] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
4750
die();
4851
}
4952

50-
$data = mysqli_fetch_assoc($result);
51-
print $data['id'] . ": " . $data['fp4'] . ": " . $data['fp8'] . "\n";
53+
54+
if (!(mysqli_stmt_fetch($stmt))) {
55+
printf("[008] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
56+
die();
57+
}
58+
59+
print $id . ": " . $fp4 . ": " . $fp8 . "\n";
5260
?>
5361
--CLEAN--
5462
<?php

ext/mysqlnd/config9.m4

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -61,34 +61,3 @@ if test "$PHP_MYSQLND" != "no" || test "$PHP_MYSQLND_ENABLED" = "yes" || test "$
6161
#endif
6262
])
6363
fi
64-
65-
dnl
66-
dnl Check if the compiler supports Decimal32/64/128 types from the IEEE-754 2008 version
67-
dnl References: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1657.pdf
68-
dnl http://speleotrove.com/decimal/
69-
dnl
70-
AC_CACHE_CHECK([whether whether compiler supports Decimal32/64/128 types], ac_cv_decimal_fp_supported,[
71-
AC_TRY_RUN( [
72-
#include <stdio.h>
73-
#include <string.h>
74-
75-
int main(int argc, char **argv) {
76-
typedef float dec32 __attribute__((mode(SD)));
77-
dec32 k = 99.49f;
78-
double d2 = (double)k;
79-
const char *check_str = "99.49";
80-
char print_str[32];
81-
82-
snprintf(print_str, 32, "%f", d2);
83-
return memcmp(print_str, check_str, 5);
84-
}
85-
],[
86-
ac_cv_decimal_fp_supported=yes
87-
],[
88-
ac_cv_decimal_fp_supported=no
89-
],[
90-
ac_cv_decimal_fp_supported=no
91-
])])
92-
if test "$ac_cv_decimal_fp_supported" = "yes"; then
93-
AC_DEFINE(HAVE_DECIMAL_FP_SUPPORT, 1, [Define if the compiler supports Decimal32/64/128 types.])
94-
fi

ext/mysqlnd/mysqlnd_ps_codec.c

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "mysqlnd_wireprotocol.h"
2525
#include "mysqlnd_priv.h"
2626
#include "mysqlnd_debug.h"
27+
#include "ext/standard/float_to_double.h"
2728

2829
#define MYSQLND_SILENT
2930

@@ -181,56 +182,12 @@ ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_l
181182
(*row)+= 4;
182183
DBG_INF_FMT("value=%f", fval);
183184

184-
/*
185-
* The following is needed to correctly support 4-byte floats.
186-
* Otherwise, a value of 9.99 in a FLOAT column comes out of mysqli
187-
* as 9.9998998641968.
188-
*
189-
* For GCC, we use the built-in decimal support to "up-convert" a
190-
* 4-byte float to a 8-byte double.
191-
* When that is not available, we fall back to converting the float
192-
* to a string and then converting the string to a double. This mimics
193-
* what MySQL does.
194-
*/
195-
#ifdef HAVE_DECIMAL_FP_SUPPORT
196-
{
197-
typedef float dec32 __attribute__((mode(SD)));
198-
/* volatile so the compiler will not optimize away the conversion */
199-
volatile dec32 d32val = fval;
200-
201-
/* The following cast is guaranteed to do the right thing */
202-
dval = (double) d32val;
203-
}
204-
#elif defined(PHP_WIN32)
205-
{
206-
/* float datatype on Winows is already 4 byte but has a precision of 7 digits */
207-
char num_buf[2048];
208-
(void)_gcvt_s(num_buf, 2048, fval, field->decimals >= 31 ? 7 : field->decimals);
209-
dval = zend_strtod(num_buf, NULL);
210-
}
211-
#else
212-
{
213-
char num_buf[2048]; /* Over allocated */
214-
char *s;
215-
216-
#ifndef FLT_DIG
217-
# define FLT_DIG 6
218-
#endif
219-
/* Convert to string. Ignoring localization, etc.
220-
* Following MySQL's rules. If precision is undefined (NOT_FIXED_DEC i.e. 31)
221-
* or larger than 31, the value is limited to 6 (FLT_DIG).
222-
*/
223-
s = php_gcvt(fval,
224-
field->decimals >= 31 ? FLT_DIG : field->decimals,
225-
'.',
226-
'e',
227-
num_buf);
228-
229-
/* And now convert back to double */
230-
dval = zend_strtod(s, NULL);
231-
}
185+
#ifndef NOT_FIXED_DEC
186+
# define NOT_FIXED_DEC 31
232187
#endif
233188

189+
dval = float_to_double(fval, (field->decimals >= NOT_FIXED_DEC) ? -1 : field->decimals);
190+
234191
ZVAL_DOUBLE(zv, dval);
235192
DBG_VOID_RETURN;
236193
}

ext/standard/config.m4

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,8 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32.
603603
incomplete_class.c url_scanner_ex.c ftp_fopen_wrapper.c \
604604
http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \
605605
var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \
606-
filters.c proc_open.c streamsfuncs.c http.c password.c)
606+
filters.c proc_open.c streamsfuncs.c http.c password.c \
607+
float_to_double.c)
607608

608609
PHP_ADD_MAKEFILE_FRAGMENT
609610
PHP_INSTALL_HEADERS([ext/standard/])

ext/standard/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \
2020
url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \
2121
php_fopen_wrapper.c credits.c css.c var_unserializer.c ftok.c sha1.c \
2222
user_filters.c uuencode.c filters.c proc_open.c password.c \
23-
streamsfuncs.c http.c flock_compat.c", false /* never shared */);
23+
streamsfuncs.c http.c flock_compat.c float_to_double.c", false /* never shared */);
2424
PHP_INSTALL_HEADERS("", "ext/standard");
2525
if (PHP_MBREGEX != "no") {
2626
CHECK_HEADER_ADD_INCLUDE("oniguruma.h", "CFLAGS_STANDARD", PHP_MBREGEX + ";ext\\mbstring\\oniguruma")

ext/standard/float_to_double.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| PHP Version 5 |
4+
+----------------------------------------------------------------------+
5+
| Copyright (c) 2006-2014 The PHP Group |
6+
+----------------------------------------------------------------------+
7+
| This source file is subject to version 3.01 of the PHP license, |
8+
| that is bundled with this package in the file LICENSE, and is |
9+
| available through the world-wide-web at the following url: |
10+
| http://www.php.net/license/3_01.txt |
11+
| If you did not receive a copy of the PHP license and are unable to |
12+
| obtain it through the world-wide-web, please send a note to |
13+
| license@php.net so we can mail you a copy immediately. |
14+
+----------------------------------------------------------------------+
15+
| Authors: Keyur Govande <kgovande@gmail.com> |
16+
+----------------------------------------------------------------------+
17+
*/
18+
19+
#include <float.h>
20+
#include "float_to_double.h"
21+
#include "main/snprintf.h"
22+
23+
#define MAX_BUF_LEN 255
24+
25+
#ifndef FLT_DIG
26+
# define FLT_DIG 6
27+
#endif
28+
29+
/*
30+
* Convert from a 4-byte float to a 8-byte decimal by first converting
31+
* the float to a string, and then the string to a double.
32+
* The decimals argument specifies the precision of the output. If decimals
33+
* is less than zero, then a gcvt(3) like logic is used with the significant
34+
* digits set to FLT_DIG i.e. 6.
35+
*/
36+
double float_to_double(float fp4, int decimals) {
37+
char num_buf[MAX_BUF_LEN]; /* Over allocated */
38+
39+
if (decimals < 0) {
40+
php_gcvt(fp4, FLT_DIG, '.', 'e', num_buf);
41+
} else {
42+
php_sprintf(num_buf, "%.*f", decimals, fp4);
43+
}
44+
45+
return zend_strtod(num_buf, NULL);
46+
}
47+
48+
/*
49+
* Local variables:
50+
* tab-width: 4
51+
* c-basic-offset: 4
52+
* End:
53+
* vim600: noet sw=4 ts=4 fdm=marker
54+
* vim<600: noet sw=4 ts=4
55+
*/

ext/standard/float_to_double.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| PHP Version 5 |
4+
+----------------------------------------------------------------------+
5+
| Copyright (c) 2006-2014 The PHP Group |
6+
+----------------------------------------------------------------------+
7+
| This source file is subject to version 3.01 of the PHP license, |
8+
| that is bundled with this package in the file LICENSE, and is |
9+
| available through the world-wide-web at the following url: |
10+
| http://www.php.net/license/3_01.txt |
11+
| If you did not receive a copy of the PHP license and are unable to |
12+
| obtain it through the world-wide-web, please send a note to |
13+
| license@php.net so we can mail you a copy immediately. |
14+
+----------------------------------------------------------------------+
15+
| Authors: Keyur Govande <kgovande@gmail.com> |
16+
+----------------------------------------------------------------------+
17+
*/
18+
19+
#ifndef FLOAT_TO_DOUBLE_H
20+
#define FLOAT_TO_DOUBLE_H
21+
22+
#include "main/php.h"
23+
24+
PHPAPI double float_to_double(float fp4, int decimals);
25+
26+
#endif /* FLOAT_TO_DOUBLE_H */
27+
28+
/*
29+
* Local variables:
30+
* tab-width: 4
31+
* c-basic-offset: 4
32+
* End:
33+
* vim600: noet sw=4 ts=4 fdm=marker
34+
* vim<600: noet sw=4 ts=4
35+
*/

0 commit comments

Comments
 (0)