summaryrefslogtreecommitdiff
path: root/ext/mysqlnd/mysqlnd_ps_codec.c
diff options
context:
space:
mode:
authorStanley Sufficool <ssufficool@php.net>2014-10-20 21:33:32 -0700
committerStanley Sufficool <ssufficool@php.net>2014-10-20 21:33:32 -0700
commit8defcb855ab01d9c8ab4759cb793d80149b55a8c (patch)
treeed51eb30a2cbc92b102557498fb3e4113da1bb07 /ext/mysqlnd/mysqlnd_ps_codec.c
parent9c7dbb0487f5991fde03873ea8f5e66d6688415f (diff)
parentbaddb1c73a170ef1d2c31bd54cddbc6e1ab596b9 (diff)
downloadphp-git-8defcb855ab01d9c8ab4759cb793d80149b55a8c.tar.gz
Merge branch 'master' of https://git.php.net/push/php-src
* 'master' of https://git.php.net/push/php-src: (6215 commits) Extra comma Moved proxy object support in ASSIGN_ADD (and family) from VM to slow paths of corresponding operators Simplification zend_get_property_info_quick() cleanup and optimization initialize lineno before calling compile file file in phar Use ADDREF instead of DUP, it must be enough. Removed old irrelevant comment fixed compilation error Fix bug #68262: Broken reference across cloned objects export functions needed for phpdbg Fixed compilation Optimized property access handlers. Removed EG(std_property_info). Fixed bug #68199 (PDO::pgsqlGetNotify doesn't support NOTIFY payloads) Don't make difference between undefined and unaccessible properies when call __get() and family Don't make useless CSE array_pop/array_shift optimization check for zlib headers as well as lib for mysqlnd a realpath cache key can be int or float, catching this News entry for new curl constants News entry for new curl constants ...
Diffstat (limited to 'ext/mysqlnd/mysqlnd_ps_codec.c')
-rw-r--r--ext/mysqlnd/mysqlnd_ps_codec.c525
1 files changed, 317 insertions, 208 deletions
diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c
index d8ad06c608..e98d1c3a52 100644
--- a/ext/mysqlnd/mysqlnd_ps_codec.c
+++ b/ext/mysqlnd/mysqlnd_ps_codec.c
@@ -1,8 +1,8 @@
/*
+----------------------------------------------------------------------+
- | PHP Version 5 |
+ | PHP Version 7 |
+----------------------------------------------------------------------+
- | Copyright (c) 2006-2013 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 |
@@ -41,7 +41,7 @@ enum mysqlnd_timestamp_type
struct st_mysqlnd_time
{
unsigned int year, month, day, hour, minute, second;
- unsigned long second_part;
+ zend_ulong second_part;
zend_bool neg;
enum mysqlnd_timestamp_type time_type;
};
@@ -76,7 +76,7 @@ ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, unsigne
case 1:uval = (uint64_t) uint1korr(*row);break;
}
-#if SIZEOF_LONG==4
+#if SIZEOF_ZEND_LONG==4
if (uval > INT_MAX) {
DBG_INF("stringify");
tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval);
@@ -84,7 +84,7 @@ ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, unsigne
#endif /* #if SIZEOF_LONG==4 */
{
if (byte_count < 8 || uval <= L64(9223372036854775807)) {
- ZVAL_LONG(zv, (long) uval); /* the cast is safe, we are in the range */
+ ZVAL_LONG(zv, (zend_long) uval); /* the cast is safe, we are in the range */
} else {
DBG_INF("stringify");
tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval);
@@ -105,19 +105,19 @@ ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, unsigne
case 1:lval = (int64_t) *(int8_t*)*row;break;
}
-#if SIZEOF_LONG==4
+#if SIZEOF_ZEND_LONG==4
if ((L64(2147483647) < (int64_t) lval) || (L64(-2147483648) > (int64_t) lval)) {
DBG_INF("stringify");
tmp_len = sprintf((char *)&tmp, MYSQLND_LL_SPEC, lval);
} else
#endif /* SIZEOF */
{
- ZVAL_LONG(zv, (long) lval); /* the cast is safe, we are in the range */
+ ZVAL_LONG(zv, (zend_long) lval); /* the cast is safe, we are in the range */
}
}
if (tmp_len) {
- ZVAL_STRINGL(zv, tmp, tmp_len, 1);
+ ZVAL_STRINGL(zv, tmp, tmp_len);
}
(*row)+= byte_count;
DBG_VOID_RETURN;
@@ -174,12 +174,63 @@ ps_fetch_int64(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_l
static void
ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
{
- float value;
+ float fval;
+ double dval;
DBG_ENTER("ps_fetch_float");
- float4get(value, *row);
- ZVAL_DOUBLE(zv, value);
+ float4get(fval, *row);
(*row)+= 4;
- DBG_INF_FMT("value=%f", value);
+ DBG_INF_FMT("value=%f", fval);
+
+ /*
+ * The following is needed to correctly support 4-byte floats.
+ * Otherwise, a value of 9.99 in a FLOAT column comes out of mysqli
+ * as 9.9998998641968.
+ *
+ * For GCC, we use the built-in decimal support to "up-convert" a
+ * 4-byte float to a 8-byte double.
+ * When that is not available, we fall back to converting the float
+ * to a string and then converting the string to a double. This mimics
+ * what MySQL does.
+ */
+#ifdef HAVE_DECIMAL_FP_SUPPORT
+ {
+ typedef float dec32 __attribute__((mode(SD)));
+ dec32 d32val = fval;
+
+ /* The following cast is guaranteed to do the right thing */
+ dval = (double) d32val;
+ }
+#elif defined(PHP_WIN32)
+ {
+ /* float datatype on Winows is already 4 byte but has a precision of 7 digits */
+ char num_buf[2048];
+ (void)_gcvt_s(num_buf, 2048, fval, field->decimals >= 31 ? 7 : field->decimals);
+ dval = zend_strtod(num_buf, NULL);
+ }
+#else
+ {
+ char num_buf[2048]; /* Over allocated */
+ char *s;
+
+#ifndef FLT_DIG
+# define FLT_DIG 6
+#endif
+ /* Convert to string. Ignoring localization, etc.
+ * Following MySQL's rules. If precision is undefined (NOT_FIXED_DEC i.e. 31)
+ * or larger than 31, the value is limited to 6 (FLT_DIG).
+ */
+ s = php_gcvt(fval,
+ field->decimals >= 31 ? FLT_DIG : field->decimals,
+ '.',
+ 'e',
+ num_buf);
+
+ /* And now convert back to double */
+ dval = zend_strtod(s, NULL);
+ }
+#endif
+
+ ZVAL_DOUBLE(zv, dval);
DBG_VOID_RETURN;
}
/* }}} */
@@ -205,7 +256,7 @@ static void
ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
{
struct st_mysqlnd_time t;
- unsigned long length; /* First byte encodes the length*/
+ zend_ulong length; /* First byte encodes the length*/
char * value;
DBG_ENTER("ps_fetch_time");
@@ -215,11 +266,11 @@ ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
t.time_type = MYSQLND_TIMESTAMP_TIME;
t.neg = (zend_bool) to[0];
- t.day = (unsigned long) sint4korr(to+1);
+ t.day = (zend_ulong) sint4korr(to+1);
t.hour = (unsigned int) to[5];
t.minute = (unsigned int) to[6];
t.second = (unsigned int) to[7];
- t.second_part = (length > 8) ? (unsigned long) sint4korr(to+8) : 0;
+ t.second_part = (length > 8) ? (zend_ulong) sint4korr(to+8) : 0;
t.year = t.month= 0;
if (t.day) {
/* Convert days to hours at once */
@@ -236,7 +287,7 @@ ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
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, 1);
+ ZVAL_STRINGL(zv, value, length);
mnd_sprintf_free(value);
DBG_VOID_RETURN;
}
@@ -248,7 +299,7 @@ static void
ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
{
struct st_mysqlnd_time t = {0};
- unsigned long length; /* First byte encodes the length*/
+ zend_ulong length; /* First byte encodes the length*/
char * value;
DBG_ENTER("ps_fetch_date");
@@ -273,7 +324,7 @@ ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_le
length = mnd_sprintf(&value, 0, "%04u-%02u-%02u", t.year, t.month, t.day);
DBG_INF_FMT("%s", value);
- ZVAL_STRINGL(zv, value, length, 1);
+ ZVAL_STRINGL(zv, value, length);
mnd_sprintf_free(value);
DBG_VOID_RETURN;
}
@@ -285,7 +336,7 @@ static void
ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
{
struct st_mysqlnd_time t;
- unsigned long length; /* First byte encodes the length*/
+ zend_ulong length; /* First byte encodes the length*/
char * value;
DBG_ENTER("ps_fetch_datetime");
@@ -306,7 +357,7 @@ ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, unsigned int pac
} else {
t.hour = t.minute = t.second= 0;
}
- t.second_part = (length > 7) ? (unsigned long) sint4korr(to+7) : 0;
+ t.second_part = (length > 7) ? (zend_ulong) sint4korr(to+7) : 0;
(*row)+= length;
} else {
@@ -317,7 +368,7 @@ ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, unsigned int pac
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, 1);
+ ZVAL_STRINGL(zv, value, length);
mnd_sprintf_free(value);
DBG_VOID_RETURN;
}
@@ -332,11 +383,11 @@ ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_
For now just copy, before we make it possible
to write \0 to the row buffer
*/
- const unsigned long length = php_mysqlnd_net_field_length(row);
+ const zend_ulong length = php_mysqlnd_net_field_length(row);
DBG_ENTER("ps_fetch_string");
DBG_INF_FMT("len = %lu", length);
DBG_INF("copying from the row buffer");
- ZVAL_STRINGL(zv, (char *)*row, length, 1);
+ ZVAL_STRINGL(zv, (char *)*row, length);
(*row) += length;
DBG_VOID_RETURN;
@@ -348,7 +399,7 @@ ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_
static void
ps_fetch_bit(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
{
- unsigned long length = php_mysqlnd_net_field_length(row);
+ zend_ulong length = php_mysqlnd_net_field_length(row);
ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, length TSRMLS_CC);
}
/* }}} */
@@ -499,16 +550,13 @@ void _mysqlnd_init_ps_fetch_subsystem()
/* {{{ mysqlnd_stmt_copy_it */
static enum_func_status
-mysqlnd_stmt_copy_it(zval *** copies, zval * original, unsigned int param_count, unsigned int current TSRMLS_DC)
+mysqlnd_stmt_copy_it(zval ** copies, zval * original, unsigned int param_count, unsigned int current TSRMLS_DC)
{
if (!*copies) {
- *copies = mnd_ecalloc(param_count, sizeof(zval *));
+ *copies = mnd_ecalloc(param_count, sizeof(zval));
}
if (*copies) {
- MAKE_STD_ZVAL((*copies)[current]);
- *(*copies)[current] = *original;
- Z_SET_REFCOUNT_P((*copies)[current], 1);
- zval_copy_ctor((*copies)[current]);
+ ZVAL_COPY(&(*copies)[current], original);
return PASS;
}
return FAIL;
@@ -516,65 +564,64 @@ mysqlnd_stmt_copy_it(zval *** copies, zval * original, unsigned int param_count,
/* }}} */
-/* {{{ 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_free_copies */
+static void
+mysqlnd_stmt_free_copies(MYSQLND_STMT_DATA * stmt, zval *copies TSRMLS_DC)
{
- MYSQLND_STMT_DATA * stmt = s->data;
- unsigned int i = 0;
- zend_uchar * provided_buffer = *buf;
- size_t left = (*buf_len - (*p - *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;
+ if (copies) {
+ unsigned int i;
+ for (i = 0; i < stmt->param_count; i++) {
+ zval_ptr_dtor(&copies[i]);
+ }
+ mnd_efree(copies);
+ }
+}
+/* }}} */
- DBG_ENTER("mysqlnd_stmt_execute_store_params");
- {
- unsigned int null_count = (stmt->param_count + 7) / 8;
- /* give it some reserved space - 20 bytes */
- if (left < (null_count + 20)) {
- unsigned int offset = *p - *buf;
- zend_uchar *tmp_buf;
- *buf_len = offset + null_count + 20;
- tmp_buf = mnd_emalloc(*buf_len);
- if (!tmp_buf) {
- SET_OOM_ERROR(*stmt->error_info);
- goto end;
- }
- memcpy(tmp_buf, *buf, offset);
- if (*buf != provided_buffer) {
- mnd_efree(*buf);
- }
- *buf = tmp_buf;
+/* {{{ mysqlnd_stmt_execute_check_n_enlarge_buffer */
+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)
+{
+ const size_t overalloc = 5;
+ size_t left = (*buf_len - (*p - *buf));
- /* Update our pos pointer */
- *p = *buf + offset;
+ if (left < (needed_bytes + overalloc)) {
+ size_t offset = *p - *buf;
+ zend_uchar *tmp_buf;
+ *buf_len = offset + needed_bytes + overalloc;
+ tmp_buf = mnd_emalloc(*buf_len);
+ if (!tmp_buf) {
+ return FAIL;
}
- /* put `null` bytes */
- null_byte_offset = *p - *buf;
- memset(*p, 0, null_count);
- *p += null_count;
+ memcpy(tmp_buf, *buf, offset);
+ if (*buf != provided_buffer) {
+ mnd_efree(*buf);
+ }
+ *buf = tmp_buf;
+ /* Update our pos pointer */
+ *p = *buf + offset;
}
+ return PASS;
+}
+/* }}} */
- 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
- 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.
- */
+
+/* {{{ mysqlnd_stmt_execute_prepare_param_types */
+static enum_func_status
+mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval ** copies_param, int * resend_types_next_time TSRMLS_DC)
+{
+ unsigned int i;
+ DBG_ENTER("mysqlnd_stmt_execute_prepare_param_types");
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;
+ zval *parameter = &stmt->param_bind[i].zv;
+
+ ZVAL_DEREF(parameter);
+ if (!Z_ISNULL_P(parameter) && (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))
+ if (Z_TYPE_P(parameter) != IS_LONG &&
+ PASS != mysqlnd_stmt_copy_it(copies_param, parameter, stmt->param_count, i TSRMLS_CC))
{
SET_OOM_ERROR(*stmt->error_info);
goto end;
@@ -583,104 +630,122 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
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
*/
- 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) {
- stmt->send_types_to_server = resend_types_next_time = 1;
+ if (Z_TYPE_P(parameter) != IS_LONG) {
+ zval *tmp_data = (*copies_param && !Z_ISUNDEF((*copies_param)[i]))? &(*copies_param)[i]: parameter;
+ /*
+ 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;
+ ZVAL_COPY(&tmp_data_copy, tmp_data);
+ 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(tmp_data_copy) > ZEND_LONG_MAX || Z_DVAL(tmp_data_copy) < ZEND_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);
}
}
}
+ DBG_RETURN(PASS);
+end:
+ DBG_RETURN(FAIL);
+}
+/* }}} */
- 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;
- zend_uchar *tmp_buf;
- *buf_len = offset + stmt->param_count * 2 + 20;
- tmp_buf = mnd_emalloc(*buf_len);
- if (!tmp_buf) {
- SET_OOM_ERROR(*stmt->error_info);
- goto end;
- }
- memcpy(tmp_buf, *buf, offset);
- if (*buf != provided_buffer) {
- mnd_efree(*buf);
- }
- *buf = tmp_buf;
-
- /* Update our pos pointer */
- *p = *buf + offset;
+/* {{{ 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++) {
+ short current_type = stmt->param_bind[i].type;
+ zval *parameter = &stmt->param_bind[i].zv;
+ /* our types are not unsigned */
+#if SIZEOF_ZEND_LONG==8
+ if (current_type == MYSQL_TYPE_LONG) {
+ current_type = MYSQL_TYPE_LONGLONG;
}
- 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 (current_type == MYSQL_TYPE_LONG) {
- current_type = MYSQL_TYPE_LONGLONG;
- }
#endif
- if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
+ ZVAL_DEREF(parameter);
+ if (!Z_ISNULL_P(parameter) && (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
+ */
+ if (Z_TYPE_P(parameter) != IS_LONG) {
+ const zval *tmp_data = (copies && !Z_ISUNDEF(copies[i]))? &copies[i] : parameter;
/*
- 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
+ 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(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);
- 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);
- }
+ 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
+ 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.
+ */
}
}
- int2store(*p, current_type);
- *p+= 2;
}
+ 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 */
+
+/* {{{ 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");
for (i = 0; i < stmt->param_count; i++) {
+ unsigned short is_longlong = 0;
unsigned int j;
- zval *the_var = stmt->param_bind[i].zv;
+ zval *bind_var, *the_var = &stmt->param_bind[i].zv;
- if (!the_var || (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB && Z_TYPE_P(the_var) == IS_NULL)) {
+ bind_var = the_var;
+ ZVAL_DEREF(the_var);
+ if ((stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB && Z_TYPE_P(the_var) == IS_NULL)) {
continue;
}
- 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 (!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;
+
+ if (Z_ISREF_P(bind_var)) {
+ for (j = i + 1; j < stmt->param_count; j++) {
+ if (Z_ISREF(stmt->param_bind[j].zv) && Z_REFVAL(stmt->param_bind[j].zv) == the_var) {
+ /* Double binding of the same zval, make a copy */
+ if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
+ if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i TSRMLS_CC)) {
+ SET_OOM_ERROR(*stmt->error_info);
+ goto end;
+ }
}
+ break;
}
- break;
}
}
switch (stmt->param_bind[i].type) {
case MYSQL_TYPE_DOUBLE:
- data_size += 8;
+ *data_size += 8;
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)) {
+ if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
+ if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info);
goto end;
}
@@ -688,24 +753,17 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
}
break;
case MYSQL_TYPE_LONGLONG:
- {
- 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;
+ is_longlong = 4;
+ /* fall-through */
case MYSQL_TYPE_LONG:
{
- zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ zval *tmp_data = (*copies_param && !Z_ISUNDEF((*copies_param)[i]))? &(*copies_param)[i]: the_var;
if (Z_TYPE_P(tmp_data) == IS_STRING) {
goto use_string;
}
- convert_to_long_ex(&tmp_data);
+ convert_to_long_ex(tmp_data);
}
- data_size += 4;
+ *data_size += 4 + is_longlong;
break;
case MYSQL_TYPE_LONG_BLOB:
if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
@@ -714,62 +772,50 @@ 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
data will follows after it.
*/
- data_size++;
+ (*data_size)++;
}
break;
case MYSQL_TYPE_VAR_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 (!copies || !copies[i]) {
- if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
+ if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
+ if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info);
goto end;
}
}
- the_var = copies[i];
+ the_var = &((*copies_param)[i]);
}
- convert_to_string_ex(&the_var);
- data_size += Z_STRLEN_P(the_var);
+ convert_to_string_ex(the_var);
+ *data_size += Z_STRLEN_P(the_var);
break;
}
}
+ DBG_RETURN(PASS);
+end:
+ DBG_RETURN(FAIL);
+}
+/* }}} */
- /* 2.2 Enlarge the buffer, if needed */
- left = (*buf_len - (*p - *buf));
- if (left < data_size) {
- unsigned int offset = *p - *buf;
- zend_uchar *tmp_buf;
- *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);
- goto end;
- }
- memcpy(tmp_buf, *buf, offset);
- /*
- When too many columns the buffer provided to the function might not be sufficient.
- In this case new buffer has been allocated above. When we allocate a buffer and then
- allocate a bigger one here, we should free the first one.
- */
- if (*buf != provided_buffer) {
- mnd_efree(*buf);
- }
- *buf = tmp_buf;
- /* Update our pos pointer */
- *p = *buf + offset;
- }
- /* 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++) {
- zval *data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ zval *data, *parameter = &stmt->param_bind[i].zv;
+
+ ZVAL_DEREF(parameter);
+ data = (copies && !Z_ISUNDEF(copies[i]))? &copies[i]: parameter;
/* 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));
+ if (!Z_ISUNDEF_P(parameter) && Z_TYPE_P(data) == IS_NULL) {
+ (buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
} else {
switch (stmt->param_bind[i].type) {
case MYSQL_TYPE_DOUBLE:
- convert_to_double_ex(&data);
+ convert_to_double_ex(data);
float8store(*p, Z_DVAL_P(data));
(*p) += 8;
break;
@@ -800,7 +846,7 @@ use_string:
case MYSQL_TYPE_VAR_STRING:
send_string:
{
- unsigned int len = Z_STRLEN_P(data);
+ size_t len = Z_STRLEN_P(data);
/* to is after p. The latter hasn't been moved */
*p = php_mysqlnd_net_store_length(*p, len);
memcpy(*p, Z_STRVAL_P(data), len);
@@ -809,22 +855,85 @@ send_string:
break;
default:
/* Won't happen, but set to NULL */
- (*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
+ (buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
break;
}
}
}
- ret = PASS;
-end:
- if (copies) {
- for (i = 0; i < stmt->param_count; i++) {
- if (copies[i]) {
- zval_ptr_dtor(&copies[i]);
- }
+}
+/* }}} */
+
+
+/* {{{ 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;
}
- mnd_efree(copies);
+ /* 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 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;
+end:
+ mysqlnd_stmt_free_copies(stmt, copies TSRMLS_CC);
+
DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
DBG_RETURN(ret);
}