mirror of
https://github.com/php/php-src.git
synced 2024-12-05 07:46:06 +08:00
Fix for
Bug #52891 Wrong data inserted with mysqli/mysqlnd when using bind_param,value>LONG_MAX
This commit is contained in:
parent
c0528261d7
commit
fbaed5476c
@ -482,10 +482,17 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s TSRMLS_DC)
|
||||
/* close the statement here, the connection has been closed */
|
||||
}
|
||||
stmt->state = MYSQLND_STMT_PREPARED;
|
||||
stmt->send_types_to_server = 1;
|
||||
} else {
|
||||
/*
|
||||
stmt->send_types_to_server has already been set to 0 in
|
||||
mysqlnd_stmt_execute_generate_request / mysqlnd_stmt_execute_store_params
|
||||
In case there is a situation in which binding was done for integer and the
|
||||
value is > LONG_MAX or < LONG_MIN, there is string conversion and we have
|
||||
to resend the types. Next execution will also need to resend the type.
|
||||
*/
|
||||
SET_EMPTY_ERROR(stmt->error_info);
|
||||
SET_EMPTY_ERROR(stmt->conn->error_info);
|
||||
stmt->send_types_to_server = 0;
|
||||
stmt->upsert_status = conn->upsert_status;
|
||||
stmt->state = MYSQLND_STMT_EXECUTED;
|
||||
if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
|
||||
|
@ -608,12 +608,46 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
|
||||
size_t data_size = 0;
|
||||
zval **copies = NULL;/* if there are different types */
|
||||
enum_func_status ret = FAIL;
|
||||
int resend_types_next_time = 0;
|
||||
|
||||
DBG_ENTER("mysqlnd_stmt_execute_store_params");
|
||||
|
||||
/* 1. Store type information */
|
||||
if (stmt->send_types_to_server) {
|
||||
/*
|
||||
check if need to send the types even if stmt->send_types_to_server is 0. This is because
|
||||
if we send "i" (42) then the type will be int and the server will expect int. However, if next
|
||||
time we try to send > LONG_MAX, the conversion to string will send a string and the server
|
||||
won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
|
||||
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))
|
||||
{
|
||||
/* always copy the var, because we do many conversions */
|
||||
if (PASS != mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC)) {
|
||||
SET_OOM_ERROR(stmt->error_info);
|
||||
goto end;
|
||||
}
|
||||
/*
|
||||
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
|
||||
*/
|
||||
{
|
||||
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) {
|
||||
stmt->send_types_to_server = resend_types_next_time = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int1store(*p, stmt->send_types_to_server);
|
||||
(*p)++;
|
||||
|
||||
if (stmt->send_types_to_server) {
|
||||
/* 2 bytes per type, and leave 20 bytes for future use */
|
||||
if (left < ((stmt->param_count * 2) + 20)) {
|
||||
unsigned int offset = *p - *buf;
|
||||
@ -631,16 +665,40 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
|
||||
*p = *buf + offset;
|
||||
}
|
||||
for (i = 0; i < stmt->param_count; i++) {
|
||||
short current_type = stmt->param_bind[i].type;
|
||||
/* our types are not unsigned */
|
||||
#if SIZEOF_LONG==8
|
||||
if (stmt->param_bind[i].type == MYSQL_TYPE_LONG) {
|
||||
stmt->param_bind[i].type = MYSQL_TYPE_LONGLONG;
|
||||
if (current_type == MYSQL_TYPE_LONG) {
|
||||
current_type = MYSQL_TYPE_LONGLONG;
|
||||
}
|
||||
#endif
|
||||
int2store(*p, 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)) {
|
||||
/*
|
||||
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
|
||||
*/
|
||||
{
|
||||
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);
|
||||
current_type = MYSQL_TYPE_VAR_STRING;
|
||||
/*
|
||||
don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
|
||||
we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
int2store(*p, current_type);
|
||||
*p+= 2;
|
||||
}
|
||||
}
|
||||
stmt->send_types_to_server = resend_types_next_time;
|
||||
|
||||
/* 2. Store data */
|
||||
/* 2.1 Calculate how much space we need */
|
||||
@ -654,9 +712,11 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
|
||||
for (j = i + 1; j < stmt->param_count; j++) {
|
||||
if (stmt->param_bind[j].zv == the_var) {
|
||||
/* Double binding of the same zval, make a copy */
|
||||
if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
|
||||
SET_OOM_ERROR(stmt->error_info);
|
||||
goto end;
|
||||
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);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -674,23 +734,25 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
|
||||
}
|
||||
}
|
||||
break;
|
||||
#if SIZEOF_LONG==8
|
||||
case MYSQL_TYPE_LONGLONG:
|
||||
data_size += 8;
|
||||
#elif SIZEOF_LONG==4
|
||||
case MYSQL_TYPE_LONG:
|
||||
data_size += 4;
|
||||
#else
|
||||
#error "Should not happen"
|
||||
#endif
|
||||
if (Z_TYPE_P(the_var) != IS_LONG) {
|
||||
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);
|
||||
goto end;
|
||||
}
|
||||
{
|
||||
zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
|
||||
if (Z_TYPE_P(tmp_data) == IS_STRING) {
|
||||
goto use_string;
|
||||
}
|
||||
convert_to_long_ex(&tmp_data);
|
||||
}
|
||||
data_size += 8;
|
||||
break;
|
||||
case MYSQL_TYPE_LONG:
|
||||
{
|
||||
zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
|
||||
if (Z_TYPE_P(tmp_data) == IS_STRING) {
|
||||
goto use_string;
|
||||
}
|
||||
convert_to_long_ex(&tmp_data);
|
||||
}
|
||||
data_size += 4;
|
||||
break;
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
|
||||
@ -703,6 +765,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
|
||||
}
|
||||
break;
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
use_string:
|
||||
data_size += 8; /* max 8 bytes for size */
|
||||
#if MYSQLND_UNICODE
|
||||
if (Z_TYPE_P(the_var) != IS_STRING || Z_TYPE_P(the_var) == IS_UNICODE)
|
||||
@ -757,7 +820,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
|
||||
|
||||
/* 2.3 Store the actual data */
|
||||
for (i = 0; i < stmt->param_count; i++) {
|
||||
zval *data = copies && copies[i]? copies[i]: stmt->param_bind[i].zv;
|
||||
zval *data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
|
||||
/* Handle long data */
|
||||
if (stmt->param_bind[i].zv && Z_TYPE_P(data) == IS_NULL) {
|
||||
(*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
|
||||
@ -768,21 +831,22 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
|
||||
float8store(*p, Z_DVAL_P(data));
|
||||
(*p) += 8;
|
||||
break;
|
||||
#if SIZEOF_LONG==8
|
||||
case MYSQL_TYPE_LONGLONG:
|
||||
convert_to_long_ex(&data);
|
||||
if (Z_TYPE_P(data) == IS_STRING) {
|
||||
goto send_string;
|
||||
}
|
||||
/* data has alreade been converted to long */
|
||||
int8store(*p, Z_LVAL_P(data));
|
||||
(*p) += 8;
|
||||
break;
|
||||
#elif SIZEOF_LONG==4
|
||||
case MYSQL_TYPE_LONG:
|
||||
convert_to_long_ex(&data);
|
||||
if (Z_TYPE_P(data) == IS_STRING) {
|
||||
goto send_string;
|
||||
}
|
||||
/* data has alreade been converted to long */
|
||||
int4store(*p, Z_LVAL_P(data));
|
||||
(*p) += 4;
|
||||
break;
|
||||
#else
|
||||
#error "Should not happen"
|
||||
#endif
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
|
||||
stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
|
||||
@ -791,7 +855,9 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
|
||||
*p = php_mysqlnd_net_store_length(*p, 0);
|
||||
}
|
||||
break;
|
||||
case MYSQL_TYPE_VAR_STRING:{
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
send_string:
|
||||
{
|
||||
unsigned int len = Z_STRLEN_P(data);
|
||||
/* to is after p. The latter hasn't been moved */
|
||||
*p = php_mysqlnd_net_store_length(*p, len);
|
||||
@ -855,10 +921,6 @@ mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** requ
|
||||
memset(p, 0, null_count);
|
||||
p += null_count;
|
||||
|
||||
|
||||
int1store(p, stmt->send_types_to_server);
|
||||
p++;
|
||||
|
||||
ret = mysqlnd_stmt_execute_store_params(s, &cmd_buffer, &p, &cmd_buffer_length, null_byte_offset TSRMLS_CC);
|
||||
|
||||
*free_buffer = (cmd_buffer != stmt->execute_cmd_buffer.buffer);
|
||||
|
Loading…
Reference in New Issue
Block a user