--- embedaddon/php/ext/mysqlnd/mysqlnd_ps_codec.c 2012/02/21 23:47:58 1.1 +++ embedaddon/php/ext/mysqlnd/mysqlnd_ps_codec.c 2014/06/15 20:03:52 1.1.1.5 @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 2006-2012 The PHP Group | + | Copyright (c) 2006-2014 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -12,13 +12,13 @@ | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Authors: Georg Richter | - | Andrey Hristov | + | Authors: Andrey Hristov | | Ulf Wendel | + | Georg Richter | +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_ps_codec.c,v 1.1 2012/02/21 23:47:58 misho Exp $ */ +/* $Id: mysqlnd_ps_codec.c,v 1.1.1.5 2014/06/15 20:03:52 misho Exp $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -258,18 +258,14 @@ void ps_fetch_time(zval *zv, const MYSQLND_FIELD * con t.time_type = MYSQLND_TIMESTAMP_TIME; } - /* - QQ : How to make this unicode without copying two times the buffer - - Unicode equivalent of spprintf? - */ - length = spprintf(&value, 0, "%s%02u:%02u:%02u", (t.neg ? "-" : ""), t.hour, t.minute, t.second); + length = mnd_sprintf(&value, 0, "%s%02u:%02u:%02u", (t.neg ? "-" : ""), t.hour, t.minute, t.second); DBG_INF_FMT("%s", value); #if MYSQLND_UNICODE if (!as_unicode) { #endif ZVAL_STRINGL(zv, value, length, 1); - efree(value); /* allocated by spprintf */ + mnd_sprintf_free(value); #if MYSQLND_UNICODE } else { ZVAL_UTF8_STRINGL(zv, value, length, ZSTR_AUTOFREE); @@ -309,18 +305,14 @@ void ps_fetch_date(zval *zv, const MYSQLND_FIELD * con t.time_type = MYSQLND_TIMESTAMP_DATE; } - /* - QQ : How to make this unicode without copying two times the buffer - - Unicode equivalent of spprintf? - */ - length = spprintf(&value, 0, "%04u-%02u-%02u", t.year, t.month, t.day); + length = mnd_sprintf(&value, 0, "%04u-%02u-%02u", t.year, t.month, t.day); DBG_INF_FMT("%s", value); #if MYSQLND_UNICODE if (!as_unicode) { #endif ZVAL_STRINGL(zv, value, length, 1); - efree(value); /* allocated by spprintf */ + mnd_sprintf_free(value); #if MYSQLND_UNICODE } else { ZVAL_UTF8_STRINGL(zv, value, length, ZSTR_AUTOFREE); @@ -367,19 +359,14 @@ void ps_fetch_datetime(zval *zv, const MYSQLND_FIELD * t.time_type = MYSQLND_TIMESTAMP_DATETIME; } - /* - QQ : How to make this unicode without copying two times the buffer - - Unicode equivalent of spprintf? - */ - length = spprintf(&value, 0, "%04u-%02u-%02u %02u:%02u:%02u", - t.year, t.month, t.day, t.hour, t.minute, t.second); + 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); #if MYSQLND_UNICODE if (!as_unicode) { #endif ZVAL_STRINGL(zv, value, length, 1); - efree(value); /* allocated by spprintf */ + mnd_sprintf_free(value); #if MYSQLND_UNICODE } else { ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE); @@ -621,7 +608,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, ze *buf_len = offset + null_count + 20; tmp_buf = mnd_emalloc(*buf_len); if (!tmp_buf) { - SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(*stmt->error_info); goto end; } memcpy(tmp_buf, *buf, offset); @@ -639,6 +626,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, ze *p += null_count; } + left = (*buf_len - (*p - *buf)); /* 1. Store type information */ /* check if need to send the types even if stmt->send_types_to_server is 0. This is because @@ -648,14 +636,13 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, ze occur, and force resend for the next execution. */ for (i = 0; i < stmt->param_count; i++) { - if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && - (stmt->param_bind[i].type == MYSQL_TYPE_LONG || stmt->param_bind[i].type == MYSQL_TYPE_LONGLONG)) - { + short current_type = stmt->param_bind[i].type; + if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) { /* always copy the var, because we do many conversions */ if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG && PASS != mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC)) { - SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(*stmt->error_info); goto end; } /* @@ -664,10 +651,31 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, ze */ if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) { zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv; - convert_to_double_ex(&tmp_data); - if (Z_DVAL_P(tmp_data) > LONG_MAX || Z_DVAL_P(tmp_data) < LONG_MIN) { + /* + Because converting to double and back to long can lead + to losing precision we need second variable. Conversion to double is to see if + value is too big for a long. As said, precision could be lost. + */ + zval *tmp_data_copy; + MAKE_STD_ZVAL(tmp_data_copy); + *tmp_data_copy = *tmp_data; + Z_SET_REFCOUNT_P(tmp_data_copy, 1); + zval_copy_ctor(tmp_data_copy); + convert_to_double_ex(&tmp_data_copy); + + /* + if it doesn't fit in a long send it as a string. + Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX + We do transformation here, which will be used later when sending types. The code later relies on this. + */ + if (Z_DVAL_P(tmp_data_copy) > LONG_MAX || Z_DVAL_P(tmp_data_copy) < LONG_MIN) { stmt->send_types_to_server = resend_types_next_time = 1; + convert_to_string_ex(&tmp_data); + } else { + convert_to_long_ex(&tmp_data); } + + zval_ptr_dtor(&tmp_data_copy); } } } @@ -683,7 +691,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, ze *buf_len = offset + stmt->param_count * 2 + 20; tmp_buf = mnd_emalloc(*buf_len); if (!tmp_buf) { - SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(*stmt->error_info); goto end; } memcpy(tmp_buf, *buf, offset); @@ -710,10 +718,11 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, ze */ if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) { zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv; - - convert_to_double_ex(&tmp_data); - if (Z_DVAL_P(tmp_data) > LONG_MAX || Z_DVAL_P(tmp_data) < LONG_MIN) { - convert_to_string_ex(&tmp_data); + /* + In case of IS_LONG we do nothing, it is ok, in case of string, we just need to set current_type. + The actual transformation has been performed several dozens line above. + */ + if (Z_TYPE_P(tmp_data) == IS_STRING) { current_type = MYSQL_TYPE_VAR_STRING; /* don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING @@ -721,8 +730,6 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, ze if the type is however not long, then we will do a goto in the next switch. We want to preserve the original bind type given by the user. Thus, we do these hacks. */ - } else { - convert_to_long_ex(&tmp_data); } } } @@ -746,7 +753,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, ze /* Double binding of the same zval, make a copy */ if (!copies || !copies[i]) { if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) { - SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(*stmt->error_info); goto end; } } @@ -760,7 +767,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, ze if (Z_TYPE_P(the_var) != IS_DOUBLE) { if (!copies || !copies[i]) { if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) { - SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(*stmt->error_info); goto end; } } @@ -807,7 +814,7 @@ use_string: { if (!copies || !copies[i]) { if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) { - SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(*stmt->error_info); goto end; } } @@ -832,7 +839,7 @@ use_string: *buf_len = offset + data_size + 10; /* Allocate + 10 for safety */ tmp_buf = mnd_emalloc(*buf_len); if (!tmp_buf) { - SET_OOM_ERROR(stmt->error_info); + SET_OOM_ERROR(*stmt->error_info); goto end; } memcpy(tmp_buf, *buf, offset);