========================================
Core:
- . The ext_skel utility has been completely redesigned with new options and
- some old options removed. This is now written in PHP and has no external
+ . The ext_skel utility has been completely redesigned with new options and
+ some old options removed. This is now written in PHP and has no external
dependencies.
. Support for BeOS has been dropped.
. Exceptions thrown due to automatic conversion of warnings into exceptions
. bcmul() and bcpow() now return numbers with the requested scale. Formerly,
the returned numbers may have omitted trailing decimal zeroes.
+mysqli:
+ . Prepared statements now properly report the fractional seconds for DATETIME/
+ TIME/TIMESTAMP columns with decimals specifier (e.g. TIMESTAMP(6) when using
+ microseconds). Formerly, the fractional seconds part was simply omitted from
+ the returned values.
+
+PDO/MySQL:
+ . Prepared statements now properly report the fractional seconds for DATETIME/
+ TIME/TIMESTAMP columns with decimals specifier (e.g. TIMESTAMP(6) when using
+ microseconds). Formerly, the fractional seconds part was simply omitted from
+ the returned values.
+ Please note that this only affects the usage of PDO_MYSQL with emulated
+ prepares turned off (e.g. using the native preparation functionality).
+ Statements using connections having PDO::ATTR_EMULATE_PREPARES=true (which
+ is the default) were not affected by the bug fixed and have already been
+ getting the proper fractional seconds values from the engine.
+
Reflection:
. Reflection export to string now uses `int` and `bool` instead of `integer`
and `boolean`.
MBString:
. The configuration option --with-libmbfl is no longer available.
- ODBC:
+ ODBC:
. Support for ODBCRouter has been removed.
. Support for Birdstep has been removed.
--- /dev/null
+--TEST--
+Prepared Statement formatter truncates fractional seconds from date/time column (bug #76386)
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifemb.inc');
+require_once('skipifconnectfailure.inc');
+?>
+--FILE--
+<?php
+require_once('connect.inc');
+// test part 1 - TIMESTAMP(n)
+$link = new mysqli($host, $user, $passwd, $db, $port, $socket);
+$link->query('DROP TABLE IF EXISTS ts_test;');
+$link->query(
+ 'CREATE TABLE ts_test (id bigint unsigned auto_increment primary key, ' .
+ 'ts timestamp default current_timestamp(), ts2 timestamp(2) default ' .
+ 'current_timestamp(2), ts2b timestamp(2) default "2018-01-01 03:04:05.06", ' .
+ 'ts4 timestamp(4) default current_timestamp(4), ts4b timestamp(4) default ' .
+ '"2018-01-01 03:04:05.0070", ts6 timestamp(6) default current_timestamp(6), ts6b ' .
+ 'timestamp(6) default "2018-01-01 03:04:05.008000") character set utf8 collate ' .
+ 'utf8_general_ci;'
+);
+$link->query(
+ 'INSERT INTO ts_test (ts, ts2, ts4, ts6) VALUES ("2018-01-01 11:22:33", ' .
+ '"2018-02-02 11:22:33.77", "2018-03-03 11:22:33.8888", ' .
+ '"2018-04-04 11:22:33.999999");'
+);
+$stmt = $link->prepare('SELECT * FROM ts_test;'); // must be statement
+if ($stmt) {
+ $stmt->execute();
+ $tsid = $ts = $ts2 = $ts2b = $ts4 = $ts4b = $ts6 = $ts6b = null;
+ $stmt->bind_result($tsid, $ts, $ts2, $ts2b, $ts4, $ts4b, $ts6, $ts6b);
+ $stmt->fetch();
+ var_dump($ts, $ts2, $ts2b, $ts4, $ts4b, $ts6, $ts6b);
+ $stmt->free_result();
+} else {
+ echo('[FIRST][FAIL] mysqli::prepare returned false: ' . $link->error . PHP_EOL);
+}
+$link->close();
+
+// test part 2 - TIME(n)
+$link = new mysqli($host, $user, $passwd, $db, $port, $socket);
+$link->query('DROP TABLE IF EXISTS t_test;');
+$link->query(
+ 'CREATE TABLE t_test (id bigint unsigned auto_increment primary key, ' .
+ 't time default "11:00:00", t2 time(2) default "11:00:00.22", t4 ' .
+ 'time(4) default "11:00:00.4444", t6 time(6) default "11:00:00.006054") ' .
+ 'character set utf8 collate utf8_general_ci;'
+);
+$link->query(
+ 'INSERT INTO t_test (t, t2, t4, t6) VALUES ("21:22:33", "21:22:33.44", ' .
+ '"21:22:33.5555", "21:22:33.676767");'
+);
+$link->query('INSERT INTO t_test VALUES ();');
+
+$stmt = $link->prepare('SELECT * FROM t_test;');
+if ($stmt) {
+ $stmt->execute();
+ $tid = $t = $t2 = $t3 = $t4 = null;
+ $stmt->bind_result($tid, $t, $t2, $t4, $t6);
+ while ($stmt->fetch()) {
+ var_dump($t, $t2, $t4, $t6);
+ }
+ $stmt->free_result();
+} else {
+ echo('[SECOND][FAIL] mysqli::prepare returned false: ' . $link->error . PHP_EOL);
+}
+$link->close();
+?>
+--EXPECTF--
+string(19) "2018-01-01 11:22:33"
+string(22) "2018-02-02 11:22:33.77"
+string(22) "2018-01-01 03:04:05.06"
+string(24) "2018-03-03 11:22:33.8888"
+string(24) "2018-01-01 03:04:05.0070"
+string(26) "2018-04-04 11:22:33.999999"
+string(26) "2018-01-01 03:04:05.008000"
+string(8) "21:22:33"
+string(11) "21:22:33.44"
+string(13) "21:22:33.5555"
+string(15) "21:22:33.676767"
+string(8) "11:00:00"
+string(11) "11:00:00.22"
+string(13) "11:00:00.4444"
+string(15) "11:00:00.006054"
+--CLEAN--
+<?php
+require_once('connect.inc');
+$link = new mysqli($host, $user, $passwd, $db, $port, $socket);
+$link->query('DROP TABLE ts_test;');
+$link->query('DROP TABLE t_test;');
+$link->close();
+?>
+----------------------------------------------------------------------+
*/
+#include <math.h>
#include "php.h"
#include "mysqlnd.h"
#include "mysqlnd_wireprotocol.h"
t.time_type = MYSQLND_TIMESTAMP_TIME;
}
- length = mnd_sprintf(&value, 0, "%s%02u:%02u:%02u", (t.neg ? "-" : ""), t.hour, t.minute, t.second);
+ if (field->decimals > 0 && field->decimals < 7) {
+ length = mnd_sprintf(
+ &value,
+ 0,
+ "%s%02u:%02u:%02u.%0*u",
+ (t.neg ? "-" : ""),
+ t.hour,
+ t.minute,
+ t.second,
+ field->decimals,
+ (uint32_t) (t.second_part / pow(10, 6 - field->decimals))
+ );
+ } else {
+ length = mnd_sprintf(&value, 0, "%s%02u:%02u:%02u", (t.neg ? "-" : ""), t.hour, t.minute, t.second);
+ }
DBG_INF_FMT("%s", value);
ZVAL_STRINGL(zv, value, length);
t.time_type = MYSQLND_TIMESTAMP_DATETIME;
}
- length = mnd_sprintf(&value, 0, "%04u-%02u-%02u %02u:%02u:%02u", t.year, t.month, t.day, t.hour, t.minute, t.second);
+ if (field->decimals > 0 && field->decimals < 7) {
+ length = mnd_sprintf(
+ &value,
+ 0,
+ "%04u-%02u-%02u %02u:%02u:%02u.%0*u",
+ t.year,
+ t.month,
+ t.day,
+ t.hour,
+ t.minute,
+ t.second,
+ field->decimals,
+ (uint32_t) (t.second_part / pow(10, 6 - field->decimals))
+ );
+ } else {
+ length = mnd_sprintf(&value, 0, "%04u-%02u-%02u %02u:%02u:%02u", t.year, t.month, t.day, t.hour, t.minute, t.second);
+ }
DBG_INF_FMT("%s", value);
ZVAL_STRINGL(zv, value, length);