diff options
Diffstat (limited to 'ext/mysqlnd/mysqlnd_ps.c')
-rw-r--r-- | ext/mysqlnd/mysqlnd_ps.c | 169 |
1 files changed, 73 insertions, 96 deletions
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index 4588165f05..c47da0262a 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -38,6 +38,7 @@ const char * const mysqlnd_stmt_not_prepared = "Statement not prepared"; /* Exported by mysqlnd_ps_codec.c */ enum_func_status mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC); +enum_func_status mysqlnd_stmt_execute_batch_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC); static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC); static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC); @@ -109,12 +110,12 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC) DBG_RETURN(NULL); } /* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */ - set->data = mnd_emalloc((size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval *))); + set->data = mnd_emalloc((size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval))); if (!set->data) { SET_OOM_ERROR(*conn->error_info); DBG_RETURN(NULL); } - memset(set->data, 0, (size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval *))); + memset(set->data, 0, (size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval)));; } /* Position at the first row */ set->data_cursor = set->data; @@ -642,11 +643,13 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s TSRMLS_DC) fetch(); <-- no binding, but that's not a problem bind_result(); execute(); <-- here we will leak because we separate without need - */ + */ unsigned int i; for (i = 0; i < stmt->field_count; i++) { if (stmt->result_bind[i].bound == TRUE) { - zval_copy_ctor(stmt->result_bind[i].zv); + zval *result = &stmt->result_bind[i].zv; + ZVAL_DEREF(result); + Z_TRY_ADDREF_P(result); } } } @@ -680,7 +683,7 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s TSRMLS_DC) DBG_RETURN(FAIL); } for (i = 0; i < stmt->param_count; i++) { - if (stmt->param_bind[i].zv == NULL) { + if (Z_ISUNDEF(stmt->param_bind[i].zv)) { not_bound++; } } @@ -751,9 +754,9 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int /* The user could have skipped binding - don't crash*/ if (stmt->result_bind) { unsigned int i; - zval **current_row = set->data_cursor; + zval *current_row = set->data_cursor; - if (NULL == current_row[0]) { + if (Z_ISUNDEF(current_row[0])) { uint64_t row_num = (set->data_cursor - set->data) / field_count; enum_func_status rc = result->stored_data->m.row_decoder(result->stored_data->row_buffers[row_num], current_row, @@ -772,8 +775,8 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int String of zero size, definitely can't be the next max_length. Thus for NULL and zero-length we are quite efficient. */ - if (Z_TYPE_P(current_row[i]) >= IS_STRING) { - unsigned long len = Z_STRLEN_P(current_row[i]); + if (Z_TYPE(current_row[i]) == IS_STRING) { + php_uint_t len = Z_STRSIZE(current_row[i]); if (meta->fields[i].max_length < len) { meta->fields[i].max_length = len; } @@ -783,16 +786,17 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int } for (i = 0; i < result->field_count; i++) { + zval *result = &stmt->result_bind[i].zv; + + ZVAL_DEREF(result); /* Clean what we copied last time */ #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF - if (stmt->result_bind[i].zv) { - zval_dtor(stmt->result_bind[i].zv); - } + zval_dtor(result); #endif /* copy the type */ if (stmt->result_bind[i].bound == TRUE) { - DBG_INF_FMT("i=%u type=%u", i, Z_TYPE_P(current_row[i])); - if (Z_TYPE_P(current_row[i]) != IS_NULL) { + DBG_INF_FMT("i=%u type=%u", i, Z_TYPE(current_row[i])); + if (Z_TYPE(current_row[i]) != IS_NULL) { /* Copy the value. Pre-condition is that the zvals in the result_bind buffer @@ -801,13 +805,12 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int counting the user can't delete the strings the variables point to. */ - Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(current_row[i]); - stmt->result_bind[i].zv->value = current_row[i]->value; + ZVAL_COPY_VALUE(result, ¤t_row[i]); #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF - zval_copy_ctor(stmt->result_bind[i].zv); + Z_TRY_ADDREF_P(result); #endif } else { - ZVAL_NULL(stmt->result_bind[i].zv); + ZVAL_NULL(result); } } } @@ -889,19 +892,23 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i for (i = 0; i < field_count; i++) { if (stmt->result_bind[i].bound == TRUE) { - zval *data = result->unbuf->last_row_data[i]; + zval *data = &result->unbuf->last_row_data[i]; + zval *result = &stmt->result_bind[i].zv; + + ZVAL_DEREF(result); /* stmt->result_bind[i].zv has been already destructed in result->unbuf->m.free_last_data() */ #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF - zval_dtor(stmt->result_bind[i].zv); + zval_dtor(result); #endif - if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) { - if ((Z_TYPE_P(data) == IS_STRING) && (meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data))) { - meta->fields[i].max_length = Z_STRLEN_P(data); + if (!Z_ISNULL_P(data)) { + if ((Z_TYPE_P(data) == IS_STRING) && + (meta->fields[i].max_length < (unsigned long) Z_STRSIZE_P(data))) { + meta->fields[i].max_length = Z_STRSIZE_P(data); } - stmt->result_bind[i].zv->value = data->value; + ZVAL_COPY_VALUE(result, data); /* copied data, thus also the ownership. Thus null data */ ZVAL_NULL(data); } @@ -1069,21 +1076,27 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int f /* If no result bind, do nothing. We consumed the data */ for (i = 0; i < field_count; i++) { if (stmt->result_bind[i].bound == TRUE) { - zval *data = result->unbuf->last_row_data[i]; + zval *data = &result->unbuf->last_row_data[i]; + zval *result = &stmt->result_bind[i].zv; + + ZVAL_DEREF(result); /* stmt->result_bind[i].zv has been already destructed in result->unbuf->m.free_last_data() */ #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF - zval_dtor(stmt->result_bind[i].zv); + zval_dtor(result); #endif - DBG_INF_FMT("i=%u bound_var=%p type=%u refc=%u", i, stmt->result_bind[i].zv, - Z_TYPE_P(data), Z_REFCOUNT_P(stmt->result_bind[i].zv)); - if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data))) { - if ((Z_TYPE_P(data) == IS_STRING) && (meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data))) { - meta->fields[i].max_length = Z_STRLEN_P(data); + DBG_INF_FMT("i=%u bound_var=%p type=%u refc=%u", i, &stmt->result_bind[i].zv, + Z_TYPE_P(data), Z_REFCOUNTED(stmt->result_bind[i].zv)? + Z_REFCOUNT(stmt->result_bind[i].zv) : 0); + + if (!Z_ISNULL_P(data)) { + if ((Z_TYPE_P(data) == IS_STRING) && + (meta->fields[i].max_length < (unsigned long) Z_STRSIZE_P(data))) { + meta->fields[i].max_length = Z_STRSIZE_P(data); } - stmt->result_bind[i].zv->value = data->value; + ZVAL_COPY_VALUE(result, data); /* copied data, thus also the ownership. Thus null data */ ZVAL_NULL(data); } @@ -1167,7 +1180,7 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fe SET_EMPTY_ERROR(*stmt->error_info); SET_EMPTY_ERROR(*stmt->conn->error_info); - DBG_INF_FMT("result_bind=%p separated_once=%u", stmt->result_bind, stmt->result_zvals_separated_once); + DBG_INF_FMT("result_bind=%p separated_once=%u", &stmt->result_bind, stmt->result_zvals_separated_once); /* The user might have not bound any variables for result. Do the binding once she does it. @@ -1180,8 +1193,10 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fe */ for (i = 0; i < stmt->result->field_count; i++) { if (stmt->result_bind[i].bound == TRUE) { - zval_dtor(stmt->result_bind[i].zv); - ZVAL_NULL(stmt->result_bind[i].zv); + zval *result = &stmt->result_bind[i].zv; + ZVAL_DEREF(result); + zval_dtor(result); + ZVAL_NULL(result); } } stmt->result_zvals_separated_once = TRUE; @@ -1287,7 +1302,7 @@ MYSQLND_METHOD(mysqlnd_stmt, flush)(MYSQLND_STMT * const s TSRMLS_DC) /* {{{ mysqlnd_stmt::send_long_data */ static enum_func_status MYSQLND_METHOD(mysqlnd_stmt, send_long_data)(MYSQLND_STMT * const s, unsigned int param_no, - const char * const data, unsigned long length TSRMLS_DC) + const char * const data, php_uint_t length TSRMLS_DC) { MYSQLND_STMT_DATA * stmt = s? s->data:NULL; enum_func_status ret = FAIL; @@ -1437,9 +1452,7 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PA We may have the last reference, then call zval_ptr_dtor() or we may leak memory. Switching from bind_one_parameter to bind_parameters may result in zv being NULL */ - if (stmt->param_bind[i].zv) { - zval_ptr_dtor(&stmt->param_bind[i].zv); - } + zval_ptr_dtor(&stmt->param_bind[i].zv); } if (stmt->param_bind != param_bind) { s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC); @@ -1452,7 +1465,7 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_parameters)(MYSQLND_STMT * const s, MYSQLND_PA DBG_INF_FMT("%u is of type %u", i, stmt->param_bind[i].type); /* Prevent from freeing */ /* Don't update is_ref, or we will leak during conversion */ - Z_ADDREF_P(stmt->param_bind[i].zv); + Z_TRY_ADDREF(stmt->param_bind[i].zv); stmt->param_bind[i].flags = 0; if (stmt->param_bind[i].type == MYSQL_TYPE_LONG_BLOB) { stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED; @@ -1502,17 +1515,15 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigne /* Prevent from freeing */ /* Don't update is_ref, or we will leak during conversion */ - Z_ADDREF_P(zv); + Z_TRY_ADDREF_P(zv); DBG_INF("Binding"); /* Release what we had, if we had */ - if (stmt->param_bind[param_no].zv) { - zval_ptr_dtor(&stmt->param_bind[param_no].zv); - } + zval_ptr_dtor(&stmt->param_bind[param_no].zv); if (type == MYSQL_TYPE_LONG_BLOB) { /* The client will use stmt_send_long_data */ stmt->param_bind[param_no].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED; } - stmt->param_bind[param_no].zv = zv; + ZVAL_COPY_VALUE(&stmt->param_bind[param_no].zv, zv); stmt->param_bind[param_no].type = type; stmt->send_types_to_server = 1; @@ -1588,8 +1599,10 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_result)(MYSQLND_STMT * const s, stmt->result_bind = result_bind; for (i = 0; i < stmt->field_count; i++) { /* Prevent from freeing */ - Z_ADDREF_P(stmt->result_bind[i].zv); - DBG_INF_FMT("ref of %p = %u", stmt->result_bind[i].zv, Z_REFCOUNT_P(stmt->result_bind[i].zv)); + Z_TRY_ADDREF(stmt->result_bind[i].zv); + + DBG_INF_FMT("ref of %p = %u", &stmt->result_bind[i].zv, + Z_REFCOUNTED(stmt->result_bind[i].zv)? Z_REFCOUNT(stmt->result_bind[i].zv) : 0); /* Don't update is_ref !!! it's not our job Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt @@ -1643,7 +1656,7 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_result)(MYSQLND_STMT * const s, unsigned i if (!stmt->result_bind) { DBG_RETURN(FAIL); } - ALLOC_INIT_ZVAL(stmt->result_bind[param_no].zv); + ZVAL_NULL(&stmt->result_bind[param_no].zv); /* Don't update is_ref !!! it's not our job Otherwise either 009.phpt or mysqli_stmt_bind_result.phpt @@ -1866,7 +1879,7 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_set)(MYSQLND_STMT * const s, } case STMT_ATTR_CURSOR_TYPE: { unsigned int ival = *(unsigned int *) value; - if (ival > (unsigned long) CURSOR_TYPE_READ_ONLY) { + if (ival > (php_uint_t) CURSOR_TYPE_READ_ONLY) { SET_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "Not implemented"); DBG_INF("FAIL"); DBG_RETURN(FAIL); @@ -1914,10 +1927,10 @@ MYSQLND_METHOD(mysqlnd_stmt, attr_get)(const MYSQLND_STMT * const s, *(zend_bool *) value= stmt->update_max_length; break; case STMT_ATTR_CURSOR_TYPE: - *(unsigned long *) value= stmt->flags; + *(php_uint_t *) value= stmt->flags; break; case STMT_ATTR_PREFETCH_ROWS: - *(unsigned long *) value= stmt->prefetch_rows; + *(php_uint_t *) value= stmt->prefetch_rows; break; default: DBG_RETURN(FAIL); @@ -2008,29 +2021,12 @@ mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const s TSRMLS_DC) for (i = 0; i < stmt->field_count; i++) { /* Let's try with no cache */ if (stmt->result_bind[i].bound == TRUE) { - DBG_INF_FMT("%u has refcount=%u", i, Z_REFCOUNT_P(stmt->result_bind[i].zv)); - /* - We have to separate the actual zval value of the bound - variable from our allocated zvals or we will face double-free - */ - if (Z_REFCOUNT_P(stmt->result_bind[i].zv) > 1) { -#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF - zval_copy_ctor(stmt->result_bind[i].zv); -#endif - zval_ptr_dtor(&stmt->result_bind[i].zv); - } else { - /* - If it is a string, what is pointed will be freed - later in free_result(). We need to remove the variable to - which the user has lost reference. - */ -#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF - ZVAL_NULL(stmt->result_bind[i].zv); -#endif - zval_ptr_dtor(&stmt->result_bind[i].zv); - } + DBG_INF_FMT("%u has refcount=%u", i, + Z_REFCOUNTED(stmt->result_bind[i].zv)? Z_REFCOUNT(stmt->result_bind[i].zv) : 0); + zval_ptr_dtor(&stmt->result_bind[i].zv); } } + s->m->free_result_bind(s, stmt->result_bind TSRMLS_CC); stmt->result_bind = NULL; @@ -2061,27 +2057,10 @@ mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const s, unsigned int param */ /* Let's try with no cache */ if (stmt->result_bind[param_no].bound == TRUE) { - DBG_INF_FMT("%u has refcount=%u", param_no, Z_REFCOUNT_P(stmt->result_bind[param_no].zv)); - /* - We have to separate the actual zval value of the bound - variable from our allocated zvals or we will face double-free - */ - if (Z_REFCOUNT_P(stmt->result_bind[param_no].zv) > 1) { -#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF - zval_copy_ctor(stmt->result_bind[param_no].zv); -#endif - zval_ptr_dtor(&stmt->result_bind[param_no].zv); - } else { - /* - If it is a string, what is pointed will be freed - later in free_result(). We need to remove the variable to - which the user has lost reference. - */ -#ifdef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF - ZVAL_NULL(stmt->result_bind[param_no].zv); -#endif - zval_ptr_dtor(&stmt->result_bind[param_no].zv); - } + DBG_INF_FMT("%u has refcount=%u", param_no, + Z_REFCOUNTED(stmt->result_bind[param_no].zv)? + Z_REFCOUNT(stmt->result_bind[param_no].zv) : 0); + zval_ptr_dtor(&stmt->result_bind[param_no].zv); } DBG_VOID_RETURN; @@ -2144,9 +2123,7 @@ MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s TSRMLS_DC If bind_one_parameter was used, but not everything was bound and nothing was fetched, then some `zv` could be NULL */ - if (stmt->param_bind[i].zv) { - zval_ptr_dtor(&stmt->param_bind[i].zv); - } + zval_ptr_dtor(&stmt->param_bind[i].zv); } s->m->free_parameter_bind(s, stmt->param_bind TSRMLS_CC); stmt->param_bind = NULL; |