More refactoring of the big function generating execute requests

This commit is contained in:
Andrey Hristov 2014-01-14 01:31:16 +02:00
parent cf9966671c
commit 73008bb5ce

View file

@ -516,6 +516,23 @@ mysqlnd_stmt_copy_it(zval *** copies, zval * original, unsigned int param_count,
/* }}} */ /* }}} */
/* {{{ mysqlnd_stmt_free_copies */
static void
mysqlnd_stmt_free_copies(MYSQLND_STMT_DATA * stmt, zval ** copies TSRMLS_DC)
{
if (copies) {
unsigned int i;
for (i = 0; i < stmt->param_count; i++) {
if (copies[i]) {
zval_ptr_dtor(&copies[i]);
}
}
mnd_efree(copies);
}
}
/* }}} */
/* {{{ mysqlnd_stmt_execute_check_n_enlarge_buffer */ /* {{{ mysqlnd_stmt_execute_check_n_enlarge_buffer */
static enum_func_status static enum_func_status
mysqlnd_stmt_execute_check_n_enlarge_buffer(zend_uchar **buf, zend_uchar **p, size_t * buf_len, zend_uchar * const provided_buffer, size_t needed_bytes TSRMLS_DC) mysqlnd_stmt_execute_check_n_enlarge_buffer(zend_uchar **buf, zend_uchar **p, size_t * buf_len, zend_uchar * const provided_buffer, size_t needed_bytes TSRMLS_DC)
@ -544,51 +561,24 @@ mysqlnd_stmt_execute_check_n_enlarge_buffer(zend_uchar **buf, zend_uchar **p, si
/* }}} */ /* }}} */
/* {{{ mysqlnd_stmt_execute_store_params */ /* {{{ mysqlnd_stmt_execute_prepare_param_types */
static enum_func_status static enum_func_status
mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t * buf_len TSRMLS_DC) mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval *** copies_param, int * resend_types_next_time TSRMLS_DC)
{ {
MYSQLND_STMT_DATA * stmt = s->data; unsigned int i;
unsigned int i = 0; DBG_ENTER("mysqlnd_stmt_execute_prepare_param_types");
zend_uchar * provided_buffer = *buf;
size_t data_size = 0;
zval **copies = NULL;/* if there are different types */
enum_func_status ret = FAIL;
int resend_types_next_time = 0;
size_t null_byte_offset;
DBG_ENTER("mysqlnd_stmt_execute_store_params");
{
unsigned int null_count = (stmt->param_count + 7) / 8;
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, null_count TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info);
goto end;
}
/* put `null` bytes */
null_byte_offset = *p - *buf;
memset(*p, 0, null_count);
*p += null_count;
}
/* 1. Store type information */
/*
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++) { for (i = 0; i < stmt->param_count; i++) {
short current_type = stmt->param_bind[i].type; 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)) { if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
zval ** copies;
/* always copy the var, because we do many conversions */ /* always copy the var, because we do many conversions */
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG && 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)) PASS != mysqlnd_stmt_copy_it(copies_param, 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; goto end;
} }
copies = *copies_param;
/* /*
if it doesn't fit in a long send it as a string. 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 Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
@ -613,7 +603,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
We do transformation here, which will be used later when sending types. The code later relies on this. 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) { 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; stmt->send_types_to_server = *resend_types_next_time = 1;
convert_to_string_ex(&tmp_data); convert_to_string_ex(&tmp_data);
} else { } else {
convert_to_long_ex(&tmp_data); convert_to_long_ex(&tmp_data);
@ -623,15 +613,18 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
} }
} }
} }
DBG_RETURN(PASS);
int1store(*p, stmt->send_types_to_server); end:
(*p)++; DBG_RETURN(FAIL);
if (stmt->send_types_to_server) {
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, stmt->param_count * 2 TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info);
goto end;
} }
/* }}} */
/* {{{ mysqlnd_stmt_execute_store_types */
static void
mysqlnd_stmt_execute_store_types(MYSQLND_STMT_DATA * stmt, zval ** copies, zend_uchar ** p)
{
unsigned int i;
for (i = 0; i < stmt->param_count; i++) { for (i = 0; i < stmt->param_count; i++) {
short current_type = stmt->param_bind[i].type; short current_type = stmt->param_bind[i].type;
/* our types are not unsigned */ /* our types are not unsigned */
@ -646,7 +639,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
*/ */
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) { if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv; const zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
/* /*
In case of IS_LONG we do nothing, it is ok, in case of string, we just need to set current_type. 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. The actual transformation has been performed several dozens line above.
@ -666,11 +659,18 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
*p+= 2; *p+= 2;
} }
} }
stmt->send_types_to_server = resend_types_next_time; /* }}} */
/* {{{ mysqlnd_stmt_execute_calculate_param_values_size */
static enum_func_status
mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt, zval *** copies_param, size_t * data_size TSRMLS_DC)
{
unsigned int i;
DBG_ENTER("mysqlnd_stmt_execute_calculate_param_values_size");
/* 2. Store data */
/* 2.1 Calculate how much space we need */
for (i = 0; i < stmt->param_count; i++) { for (i = 0; i < stmt->param_count; i++) {
zval ** copies;
unsigned int j; unsigned int j;
zval *the_var = stmt->param_bind[i].zv; zval *the_var = stmt->param_bind[i].zv;
@ -681,7 +681,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
if (stmt->param_bind[j].zv == the_var) { if (stmt->param_bind[j].zv == the_var) {
/* Double binding of the same zval, make a copy */ /* Double binding of the same zval, make a copy */
if (!copies || !copies[i]) { if (!copies || !copies[i]) {
if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) { if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info); SET_OOM_ERROR(*stmt->error_info);
goto end; goto end;
} }
@ -690,12 +690,14 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
} }
} }
copies = *copies_param;
switch (stmt->param_bind[i].type) { switch (stmt->param_bind[i].type) {
case MYSQL_TYPE_DOUBLE: case MYSQL_TYPE_DOUBLE:
data_size += 8; *data_size += 8;
if (Z_TYPE_P(the_var) != IS_DOUBLE) { if (Z_TYPE_P(the_var) != IS_DOUBLE) {
if (!copies || !copies[i]) { if (!copies || !copies[i]) {
if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) { if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info); SET_OOM_ERROR(*stmt->error_info);
goto end; goto end;
} }
@ -710,7 +712,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
} }
convert_to_long_ex(&tmp_data); convert_to_long_ex(&tmp_data);
} }
data_size += 8; *data_size += 8;
break; break;
case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONG:
{ {
@ -720,7 +722,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
} }
convert_to_long_ex(&tmp_data); convert_to_long_ex(&tmp_data);
} }
data_size += 4; *data_size += 4;
break; break;
case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_LONG_BLOB:
if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) { if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
@ -729,33 +731,39 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
Empty string has length of 0, encoded in 1 byte. No real Empty string has length of 0, encoded in 1 byte. No real
data will follows after it. data will follows after it.
*/ */
data_size++; (*data_size)++;
} }
break; break;
case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VAR_STRING:
use_string: use_string:
data_size += 8; /* max 8 bytes for size */ *data_size += 8; /* max 8 bytes for size */
if (Z_TYPE_P(the_var) != IS_STRING) { if (Z_TYPE_P(the_var) != IS_STRING) {
if (!copies || !copies[i]) { if (!copies || !copies[i]) {
if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) { if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info); SET_OOM_ERROR(*stmt->error_info);
goto end; goto end;
} }
} }
copies = *copies_param;
the_var = copies[i]; the_var = copies[i];
} }
convert_to_string_ex(&the_var); convert_to_string_ex(&the_var);
data_size += Z_STRLEN_P(the_var); *data_size += Z_STRLEN_P(the_var);
break; break;
} }
} }
/* 2.2 Enlarge the buffer, if needed */ DBG_RETURN(PASS);
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, data_size TSRMLS_CC)) { end:
SET_OOM_ERROR(*stmt->error_info); DBG_RETURN(FAIL);
goto end;
} }
/* }}} */
/* 2.3 Store the actual data */
/* {{{ mysqlnd_stmt_execute_store_param_values */
static void
mysqlnd_stmt_execute_store_param_values(MYSQLND_STMT_DATA * stmt, zval ** copies, zend_uchar ** buf, zend_uchar ** p, size_t null_byte_offset)
{
unsigned int i;
for (i = 0; i < stmt->param_count; i++) { 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 */ /* Handle long data */
@ -809,16 +817,78 @@ send_string:
} }
} }
} }
}
/* }}} */
/* {{{ mysqlnd_stmt_execute_store_params */
static enum_func_status
mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar ** buf, zend_uchar ** p, size_t * buf_len TSRMLS_DC)
{
MYSQLND_STMT_DATA * stmt = s->data;
zend_uchar * provided_buffer = *buf;
size_t data_size = 0;
zval ** copies = NULL;/* if there are different types */
enum_func_status ret = FAIL;
int resend_types_next_time = 0;
size_t null_byte_offset;
DBG_ENTER("mysqlnd_stmt_execute_store_params");
{
unsigned int null_count = (stmt->param_count + 7) / 8;
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, null_count TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info);
goto end;
}
/* put `null` bytes */
null_byte_offset = *p - *buf;
memset(*p, 0, null_count);
*p += null_count;
}
/* 1. Store type information */
/*
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.
*/
if (FAIL == mysqlnd_stmt_execute_prepare_param_types(stmt, &copies, &resend_types_next_time TSRMLS_CC)) {
goto end;
}
int1store(*p, stmt->send_types_to_server);
(*p)++;
if (stmt->send_types_to_server) {
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, stmt->param_count * 2 TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info);
goto end;
}
mysqlnd_stmt_execute_store_types(stmt, copies, p);
}
stmt->send_types_to_server = resend_types_next_time;
/* 2. Store actual data */
/* 2.1 Calculate how much space we need */
if (FAIL == mysqlnd_stmt_execute_calculate_param_values_size(stmt, &copies, &data_size TSRMLS_CC)) {
goto end;
}
/* 2.2 Enlarge the buffer, if needed */
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, data_size TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info);
goto end;
}
/* 2.3 Store the actual data */
mysqlnd_stmt_execute_store_param_values(stmt, copies, buf, p, null_byte_offset);
ret = PASS; ret = PASS;
end: end:
if (copies) { mysqlnd_stmt_free_copies(stmt, copies TSRMLS_CC);
for (i = 0; i < stmt->param_count; i++) {
if (copies[i]) {
zval_ptr_dtor(&copies[i]);
}
}
mnd_efree(copies);
}
DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL"); DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
DBG_RETURN(ret); DBG_RETURN(ret);