]> granicus.if.org Git - php/commitdiff
Backport refactoring from php-src
authorAndrey Hristov <andrey@php.net>
Fri, 24 Jan 2014 09:39:24 +0000 (11:39 +0200)
committerAndrey Hristov <andrey@php.net>
Fri, 24 Jan 2014 09:39:24 +0000 (11:39 +0200)
ext/mysqlnd/mysqlnd_ps_codec.c
ext/mysqlnd/mysqlnd_wireprotocol.h

index d0e44fa27589d879322a9c055def1dd7cdc64dab..724feea07f6da7ff419dbed7d647e1d15e723955 100644 (file)
@@ -516,68 +516,70 @@ 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++) {
+                       if (copies[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++) {
                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)) {
+                       zval ** copies;
                        /* 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))
+                               PASS != mysqlnd_stmt_copy_it(copies_param, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC))
                        {
                                SET_OOM_ERROR(*stmt->error_info);
                                goto end;
                        }
+                       copies = *copies_param;
                        /*
                          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
@@ -602,7 +604,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.
                                */
                                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);
                                } else {
                                        convert_to_long_ex(&tmp_data);
@@ -612,68 +614,61 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
                        }
                }
        }
+       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;
-               }
-               for (i = 0; i < stmt->param_count; i++) {
-                       short current_type = stmt->param_bind[i].type;
-                       /* our types are not unsigned */
+/* {{{ 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;
+               /* our types are not unsigned */
 #if SIZEOF_LONG==8  
-                       if (current_type == MYSQL_TYPE_LONG) {
-                               current_type = MYSQL_TYPE_LONGLONG;
-                       }
+               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)) {
+               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
+                       */
+                       if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
+                               const zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
                                /*
-                                 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;
+                               if (Z_TYPE_P(tmp_data) == IS_STRING) {
+                                       current_type = MYSQL_TYPE_VAR_STRING;
                                        /*
-                                         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.
+                                         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.
                                        */
-                                       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 int j;
                zval *the_var = stmt->param_bind[i].zv;
@@ -684,8 +679,8 @@ 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 (!copies || !copies[i]) {
-                                       if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
+                               if (!*copies_param || !(*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;
                                        }
@@ -696,10 +691,10 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
 
                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 || !(*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;
                                                }
@@ -708,23 +703,23 @@ 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;
+                                       zval *tmp_data = (*copies_param && (*copies_param)[i])? (*copies_param)[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;
+                               *data_size += 8;
                                break;
                        case MYSQL_TYPE_LONG:
                                {
-                                       zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+                                       zval *tmp_data = (*copies_param && (*copies_param)[i])? (*copies_param)[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;
+                               *data_size += 4;
                                break;
                        case MYSQL_TYPE_LONG_BLOB:
                                if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
@@ -733,58 +728,43 @@ 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 || !(*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);
+                               *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 = (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));
+                       (buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
                } else {
                        switch (stmt->param_bind[i].type) {
                                case MYSQL_TYPE_DOUBLE:
@@ -819,7 +799,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);
@@ -828,22 +808,86 @@ 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;
+       unsigned int i = 0;
+       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);
 }
index 4bd33592bf83374261789a567c2a955e73cfe1ed..be71d065082824453bdc091f3dbb721431c1b829 100644 (file)
@@ -302,6 +302,7 @@ PHPAPI void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * c
 
 unsigned long  php_mysqlnd_net_field_length(zend_uchar **packet);
 zend_uchar *   php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length);
+size_t                 php_mysqlnd_net_store_length_size(uint64_t length);
 
 PHPAPI const extern char * const mysqlnd_empty_string;