Skip to content

Commit 223ab78

Browse files
committed
Fix for Issue #16910
Prevent truncation of fractional seconds in binary protocol if server sends AUTO_SEC_PART_DIGITS (value=39) in decimals value of metadata for TIME or DATETIME types.
1 parent e00079d commit 223ab78

File tree

2 files changed

+51
-4
lines changed

2 files changed

+51
-4
lines changed

ext/mysqli/tests/gh16910.phpt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
GH-xxxxx (AUTO_SEC_PART_DIGITS (field->decimal value=39) ignored in binary protocol)
3+
--EXTENSIONS--
4+
mysqli
5+
--SKIPIF--
6+
<?php
7+
require_once 'skipifconnectfailure.inc';
8+
?>
9+
--FILE--
10+
<?php
11+
require_once 'connect.inc';
12+
13+
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
14+
printf("Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
15+
$host, $user, $db, $port, $socket);
16+
exit(1);
17+
}
18+
19+
// Values from binary and text protocol should be the same
20+
$stmt = $link->prepare("select FROM_UNIXTIME('1992.1'), FROM_UNIXTIME('1992.0')");
21+
$stmt->execute();
22+
$stmt->bind_result($binval[0],$binval[1]);
23+
$stmt->fetch();
24+
$stmt->close();
25+
26+
$result = $link->query("select FROM_UNIXTIME('1992.1'), FROM_UNIXTIME('1992.0')");
27+
$txtval = $result->fetch_row();
28+
$result->close();
29+
$err_cnt= 0;
30+
31+
for ($i=0; $i < 2; $i++)
32+
if ($txtval[$i] != $binval[$i])
33+
printf("[%03d] Expected '%s' (text protocol), got '%s' (binary protocol)",
34+
$err_cnt++, $txtval[$i], $binval[$i]);
35+
36+
if ($err_cnt == 0)
37+
print "done!";
38+
?>
39+
--EXPECT--
40+
done!

ext/mysqlnd/mysqlnd_ps_codec.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
5050
#define MYSQLND_PS_SKIP_RESULT_W_LEN -1
5151
#define MYSQLND_PS_SKIP_RESULT_STR -2
5252

53+
#define MYSQLND_SEC_PART_DIGITS 6
54+
#define MYSQLND_AUTO_SEC_PART_DIGITS 39
55+
5356
/* {{{ ps_fetch_from_1_to_8_bytes */
5457
void
5558
ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len,
@@ -237,7 +240,9 @@ ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, const unsigned int p
237240
t.time_type = MYSQLND_TIMESTAMP_TIME;
238241
}
239242

240-
if (field->decimals > 0 && field->decimals < 7) {
243+
if (field->decimals > 0 && (field->decimals <= MYSQLND_SEC_PART_DIGITS ||
244+
(field->decimals == MYSQLND_AUTO_SEC_PART_DIGITS && t.second_part))) {
245+
uint8_t decimals= (field->decimals == MYSQLND_AUTO_SEC_PART_DIGITS) ? MYSQLND_SEC_PART_DIGITS : field->decimals;
241246
ZVAL_STR(zv, zend_strpprintf(0, "%s%02u:%02u:%02u.%0*u",
242247
(t.neg ? "-" : ""), t.hour, t.minute, t.second, field->decimals,
243248
(uint32_t) (t.second_part / pow(10, 6 - field->decimals))));
@@ -315,10 +320,12 @@ ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, const unsigned i
315320
t.time_type = MYSQLND_TIMESTAMP_DATETIME;
316321
}
317322

318-
if (field->decimals > 0 && field->decimals < 7) {
323+
if (field->decimals > 0 && (field->decimals <= MYSQLND_SEC_PART_DIGITS ||
324+
(field->decimals == MYSQLND_AUTO_SEC_PART_DIGITS && t.second_part))) {
325+
uint8_t decimals= (field->decimals == MYSQLND_AUTO_SEC_PART_DIGITS) ? MYSQLND_SEC_PART_DIGITS : field->decimals;
319326
ZVAL_STR(zv, zend_strpprintf(0, "%04u-%02u-%02u %02u:%02u:%02u.%0*u",
320-
t.year, t.month, t.day, t.hour, t.minute, t.second, field->decimals,
321-
(uint32_t) (t.second_part / pow(10, 6 - field->decimals))));
327+
t.year, t.month, t.day, t.hour, t.minute, t.second, decimals,
328+
(uint32_t) (t.second_part / pow(10, 6 - decimals))));
322329
} else {
323330
ZVAL_STR(zv, zend_strpprintf(0, "%04u-%02u-%02u %02u:%02u:%02u",
324331
t.year, t.month, t.day, t.hour, t.minute, t.second));

0 commit comments

Comments
 (0)