diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index b4d610731bb..36c16bdabe0 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -680,6 +680,7 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f unsigned int field_count = result->meta->field_count; DBG_ENTER("mysqlnd_fetch_stmt_row_buffered"); + *fetched_anything = FALSE; DBG_INF_FMT("stmt=%lu", stmt->stmt_id); /* If we haven't read everything */ @@ -694,15 +695,18 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f if (NULL == current_row[0]) { uint64_t row_num = (set->data_cursor - set->data) / field_count; + enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num], + current_row, + meta->field_count, + meta->fields, + result->stored_data->persistent, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (PASS != rc) { + DBG_RETURN(FAIL); + } set->initialized_rows++; - result->m.row_decoder(set->row_buffers[row_num], - current_row, - meta->field_count, - meta->fields, - result->stored_data->persistent, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); if (stmt->update_max_length) { for (i = 0; i < result->field_count; i++) { /* @@ -757,7 +761,6 @@ mysqlnd_fetch_stmt_row_buffered(MYSQLND_RES *result, void *param, unsigned int f DBG_INF("row fetched"); } else { set->data_cursor = NULL; - *fetched_anything = FALSE; DBG_INF("no more data"); } DBG_INF("PASS"); @@ -777,9 +780,10 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered"); + *fetched_anything = FALSE; + if (result->unbuf->eof_reached) { /* No more rows obviously */ - *fetched_anything = FALSE; DBG_INF("eof reached"); DBG_RETURN(PASS); } @@ -798,8 +802,6 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int */ if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { unsigned int i, field_count = result->field_count; - result->unbuf->row_count++; - *fetched_anything = TRUE; if (!row_packet->skip_extraction) { result->m.unbuffered_free_last_data(result TSRMLS_CC); @@ -810,14 +812,17 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int row_packet->fields = NULL; row_packet->row_buffer = NULL; - result->m.row_decoder(result->unbuf->last_row_buffer, - result->unbuf->last_row_data, - row_packet->field_count, - row_packet->fields_metadata, - FALSE, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); + if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer, + result->unbuf->last_row_data, + row_packet->field_count, + row_packet->fields_metadata, + FALSE, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC)) + { + DBG_RETURN(FAIL); + } for (i = 0; i < field_count; i++) { if (stmt->result_bind[i].bound == TRUE) { @@ -858,8 +863,10 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int row_packet->row_buffer->free_chunk(row_packet->row_buffer TSRMLS_CC); row_packet->row_buffer = NULL; } + + result->unbuf->row_count++; + *fetched_anything = TRUE; } else if (ret == FAIL) { - *fetched_anything = FALSE; if (row_packet->error_info.error_no) { stmt->conn->error_info = row_packet->error_info; stmt->error_info = row_packet->error_info; @@ -867,7 +874,6 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int CONN_SET_STATE(result->conn, CONN_READY); result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ } else if (row_packet->eof) { - *fetched_anything = FALSE; DBG_INF("EOF"); /* Mark the connection as usable again */ result->unbuf->eof_reached = TRUE; @@ -975,8 +981,6 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { unsigned int i, field_count = result->field_count; - result->unbuf->row_count++; - *fetched_anything = TRUE; DBG_INF_FMT("skip_extraction=%d", row_packet->skip_extraction); if (!row_packet->skip_extraction) { @@ -988,14 +992,17 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla row_packet->fields = NULL; row_packet->row_buffer = NULL; - result->m.row_decoder(result->unbuf->last_row_buffer, - result->unbuf->last_row_data, - row_packet->field_count, - row_packet->fields_metadata, - FALSE, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); + if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer, + result->unbuf->last_row_data, + row_packet->field_count, + row_packet->fields_metadata, + FALSE, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC)) + { + DBG_RETURN(FAIL); + } /* If no result bind, do nothing. We consumed the data */ for (i = 0; i < field_count; i++) { @@ -1044,6 +1051,9 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla row_packet->row_buffer = NULL; } MYSQLND_INC_CONN_STATISTIC(stmt->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_CURSOR); + + result->unbuf->row_count++; + *fetched_anything = TRUE; } else { *fetched_anything = FALSE; diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index 8cf8abb1e84..7769018d515 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -35,31 +35,36 @@ /* {{{ mysqlnd_res::initialize_result_set_rest */ -static void +static enum_func_status MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest)(MYSQLND_RES * const result TSRMLS_DC) { unsigned int i; - zval **data_cursor = result->stored_data->data; - zval **data_begin = result->stored_data->data; - unsigned int field_count = result->meta->field_count; - unsigned int row_count = result->stored_data->row_count; + zval **data_cursor = result->stored_data? result->stored_data->data:NULL; + zval **data_begin = result->stored_data? result->stored_data->data:NULL; + unsigned int field_count = result->meta? result->meta->field_count : 0; + unsigned int row_count = result->stored_data? result->stored_data->row_count:0; + enum_func_status ret = PASS; DBG_ENTER("mysqlnd_res::initialize_result_set_rest"); if (!data_cursor || row_count == result->stored_data->initialized_rows) { - DBG_VOID_RETURN; + DBG_RETURN(ret); } while ((data_cursor - data_begin) < (row_count * field_count)) { if (NULL == data_cursor[0]) { + enum_func_status rc = result->m.row_decoder( + result->stored_data->row_buffers[(data_cursor - data_begin) / field_count], + data_cursor, + result->meta->field_count, + result->meta->fields, + result->stored_data->persistent, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (rc != PASS) { + ret = FAIL; + break; + } result->stored_data->initialized_rows++; - result->m.row_decoder( - result->stored_data->row_buffers[(data_cursor - data_begin) / field_count], - data_cursor, - result->meta->field_count, - result->meta->fields, - result->stored_data->persistent, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); for (i = 0; i < result->field_count; i++) { /* NULL fields are 0 length, 0 is not more than 0 @@ -76,7 +81,7 @@ MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest)(MYSQLND_RES * const resu } data_cursor += field_count; } - DBG_VOID_RETURN; + DBG_RETURN(ret); } /* }}} */ @@ -633,6 +638,10 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC) UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); DBG_RETURN(retrow); } + if (!row_packet) { + /* Not fully initialized object that is being cleaned up */ + DBG_RETURN(retrow); + } /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */ row_packet->skip_extraction = FALSE; @@ -656,14 +665,17 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC) MYSQLND_FIELD *field = result->meta->fields; struct mysqlnd_field_hash_key *zend_hash_key = result->meta->zend_hash_keys; - result->m.row_decoder(result->unbuf->last_row_buffer, - result->unbuf->last_row_data, - row_packet->field_count, - row_packet->fields_metadata, - FALSE, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); + enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer, + result->unbuf->last_row_data, + row_packet->field_count, + row_packet->fields_metadata, + FALSE, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (PASS != rc) { + DBG_RETURN(retrow); + } retrow = mnd_malloc(result->field_count * sizeof(char *)); if (retrow) { @@ -733,15 +745,19 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla DBG_ENTER("mysqlnd_fetch_row_unbuffered"); DBG_INF_FMT("flags=%d", flags); + *fetched_anything = FALSE; if (result->unbuf->eof_reached) { /* No more rows obviously */ - *fetched_anything = FALSE; DBG_RETURN(PASS); } if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) { SET_CLIENT_ERROR(result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync); DBG_RETURN(FAIL); } + if (!row_packet) { + /* Not fully initialized object that is being cleaned up */ + DBG_RETURN(FAIL); + } /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */ row_packet->skip_extraction = row? FALSE:TRUE; @@ -750,9 +766,6 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla result->m.unbuffered_free_last_data() before it. The function returns always true. */ if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { - result->unbuf->row_count++; - *fetched_anything = TRUE; - result->m.unbuffered_free_last_data(result TSRMLS_CC); result->unbuf->last_row_data = row_packet->fields; @@ -760,7 +773,6 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla row_packet->fields = NULL; row_packet->row_buffer = NULL; - MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF); if (!row_packet->skip_extraction) { @@ -770,15 +782,17 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla unsigned int i, field_count = result->field_count; unsigned long *lengths = result->lengths; - result->m.row_decoder(result->unbuf->last_row_buffer, - result->unbuf->last_row_data, - field_count, - row_packet->fields_metadata, - FALSE, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); - + enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer, + result->unbuf->last_row_data, + field_count, + row_packet->fields_metadata, + FALSE, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (PASS != rc) { + DBG_RETURN(FAIL); + } for (i = 0; i < field_count; i++, field++, zend_hash_key++) { zval *data = result->unbuf->last_row_data[i]; unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); @@ -825,12 +839,13 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla } } } + *fetched_anything = TRUE; + result->unbuf->row_count++; } else if (ret == FAIL) { if (row_packet->error_info.error_no) { result->conn->error_info = row_packet->error_info; DBG_ERR_FMT("errorno=%d error=%s", row_packet->error_info.error_no, row_packet->error_info.error); } - *fetched_anything = FALSE; CONN_SET_STATE(result->conn, CONN_READY); result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */ } else if (row_packet->eof) { @@ -849,7 +864,6 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla CONN_SET_STATE(result->conn, CONN_READY); } result->m.unbuffered_free_last_data(result TSRMLS_CC); - *fetched_anything = FALSE; } DBG_INF_FMT("ret=%s fetched=%d", ret == PASS? "PASS":"FAIL", *fetched_anything); @@ -867,11 +881,6 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps SET_EMPTY_ERROR(result->conn->error_info); - result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC); - result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); - if (!result->result_set_memory_pool || !result->unbuf) { - goto oom; - } if (ps == FALSE) { result->type = MYSQLND_RES_NORMAL; @@ -884,12 +893,19 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps } } else { result->type = MYSQLND_RES_PS_UNBUF; + result->m.fetch_row = NULL; /* result->m.fetch_row() will be set in mysqlnd_ps.c */ result->m.fetch_lengths = NULL; /* makes no sense */ result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; result->lengths = NULL; } + result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC); + result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + if (!result->result_set_memory_pool || !result->unbuf) { + goto oom; + } + /* Will be freed in the mysqlnd_internal_free_result_contents() called by the resource destructor. mysqlnd_fetch_row_unbuffered() expects @@ -935,15 +951,18 @@ mysqlnd_fetch_row_buffered_c(MYSQLND_RES * result TSRMLS_DC) if (NULL == current_row[0]) { uint64_t row_num = (set->data_cursor - set->data) / result->meta->field_count; + enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num], + current_row, + result->meta->field_count, + result->meta->fields, + FALSE, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (rc != PASS) { + DBG_RETURN(ret); + } set->initialized_rows++; - result->m.row_decoder(set->row_buffers[row_num], - current_row, - result->meta->field_count, - result->meta->fields, - FALSE, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); for (i = 0; i < result->field_count; i++) { /* NULL fields are 0 length, 0 is not more than 0 @@ -992,6 +1011,7 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags unsigned int i; zval *row = (zval *) param; MYSQLND_RES_BUFFERED *set = result->stored_data; + enum_func_status ret = FAIL; DBG_ENTER("mysqlnd_fetch_row_buffered"); DBG_INF_FMT("flags=%u row=%p", flags, row); @@ -1006,15 +1026,18 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags if (NULL == current_row[0]) { uint64_t row_num = (set->data_cursor - set->data) / result->meta->field_count; + enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num], + current_row, + result->meta->field_count, + result->meta->fields, + result->stored_data->persistent, + result->conn->options.numeric_and_datetime_as_unicode, + result->conn->options.int_and_float_native, + result->conn->stats TSRMLS_CC); + if (rc != PASS) { + DBG_RETURN(FAIL); + } set->initialized_rows++; - result->m.row_decoder(set->row_buffers[row_num], - current_row, - result->meta->field_count, - result->meta->fields, - result->stored_data->persistent, - result->conn->options.numeric_and_datetime_as_unicode, - result->conn->options.int_and_float_native, - result->conn->stats TSRMLS_CC); for (i = 0; i < result->field_count; i++) { /* NULL fields are 0 length, 0 is not more than 0 @@ -1070,13 +1093,15 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags set->data_cursor += result->meta->field_count; *fetched_anything = TRUE; MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); + ret = PASS; } else { set->data_cursor = NULL; *fetched_anything = FALSE; + ret = PASS; DBG_INF("EOF reached"); } DBG_INF_FMT("ret=PASS fetched=%d", *fetched_anything); - DBG_RETURN(PASS); + DBG_RETURN(ret); } /* }}} */ @@ -1210,11 +1235,11 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND * const conn, MYSQL /* libmysql's documentation says it should be so for SELECT statements */ conn->upsert_status.affected_rows = set->row_count; } + DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u", ret == PASS? "PASS":"FAIL", + set->row_count, conn->upsert_status.warning_count, conn->upsert_status.server_status); end: PACKET_FREE(row_packet); - DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u", ret == PASS? "PASS":"FAIL", - set->row_count, conn->upsert_status.warning_count, conn->upsert_status.server_status); DBG_RETURN(ret); } /* }}} */ @@ -1232,6 +1257,12 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, DBG_ENTER("mysqlnd_res::store_result"); DBG_INF_FMT("conn=%d ps_protocol=%d", conn->thread_id, ps_protocol); + /* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */ + result->conn = conn->m->get_reference(conn TSRMLS_CC); + result->type = MYSQLND_RES_NORMAL; + result->m.fetch_row = result->m.fetch_row_normal_buffered; + result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered; + result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC); result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long)); if (!result->result_set_memory_pool || !result->lengths) { @@ -1239,17 +1270,15 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, DBG_RETURN(NULL); } - /* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */ - result->conn = conn->m->get_reference(conn TSRMLS_CC); - result->type = MYSQLND_RES_NORMAL; - result->m.fetch_row = result->m.fetch_row_normal_buffered; - result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered; - CONN_SET_STATE(conn, CONN_FETCHING_DATA); ret = result->m.store_result_fetch_data(conn, result, result->meta, ps_protocol, to_cache TSRMLS_CC); if (FAIL == ret) { - conn->error_info = result->stored_data->error_info; + if (result->stored_data) { + conn->error_info = result->stored_data->error_info; + } else { + SET_OOM_ERROR(conn->error_info); + } DBG_RETURN(NULL); } /* libmysql's documentation says it should be so for SELECT statements */ @@ -1357,24 +1386,28 @@ static const MYSQLND_FIELD * MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC) { DBG_ENTER("mysqlnd_res::fetch_field"); - if (result->meta) { - /* - We optimize the result set, so we don't convert all the data from raw buffer format to - zval arrays during store. In the case someone doesn't read all the lines this will - save time. However, when a metadata call is done, we need to calculate max_length. - We don't have control whether max_length will be used, unfortunately. Otherwise we - could have been able to skip that step. - Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata, - then we can have max_length as dynamic property, which will be calculated during runtime and - not during mysqli_fetch_field() time. - */ - if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) { - DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request"); - /* we have to initialize the rest to get the updated max length */ - result->m.initialize_result_set_rest(result TSRMLS_CC); + do { + if (result->meta) { + /* + We optimize the result set, so we don't convert all the data from raw buffer format to + zval arrays during store. In the case someone doesn't read all the lines this will + save time. However, when a metadata call is done, we need to calculate max_length. + We don't have control whether max_length will be used, unfortunately. Otherwise we + could have been able to skip that step. + Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata, + then we can have max_length as dynamic property, which will be calculated during runtime and + not during mysqli_fetch_field() time. + */ + if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) { + DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request"); + /* we have to initialize the rest to get the updated max length */ + if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) { + break; + } + } + DBG_RETURN(result->meta->m->fetch_field(result->meta TSRMLS_CC)); } - DBG_RETURN(result->meta->m->fetch_field(result->meta TSRMLS_CC)); - } + } while (0); DBG_RETURN(NULL); } /* }}} */ @@ -1385,24 +1418,28 @@ static const MYSQLND_FIELD * MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC) { DBG_ENTER("mysqlnd_res::fetch_field_direct"); - if (result->meta) { - /* - We optimize the result set, so we don't convert all the data from raw buffer format to - zval arrays during store. In the case someone doesn't read all the lines this will - save time. However, when a metadata call is done, we need to calculate max_length. - We don't have control whether max_length will be used, unfortunately. Otherwise we - could have been able to skip that step. - Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata, - then we can have max_length as dynamic property, which will be calculated during runtime and - not during mysqli_fetch_field_direct() time. - */ - if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) { - DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request"); - /* we have to initialized the rest to get the updated max length */ - result->m.initialize_result_set_rest(result TSRMLS_CC); + do { + if (result->meta) { + /* + We optimize the result set, so we don't convert all the data from raw buffer format to + zval arrays during store. In the case someone doesn't read all the lines this will + save time. However, when a metadata call is done, we need to calculate max_length. + We don't have control whether max_length will be used, unfortunately. Otherwise we + could have been able to skip that step. + Well, if the mysqli API switches from returning stdClass to class like mysqli_field_metadata, + then we can have max_length as dynamic property, which will be calculated during runtime and + not during mysqli_fetch_field_direct() time. + */ + if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) { + DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request"); + /* we have to initialized the rest to get the updated max length */ + if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) { + break; + } + } + DBG_RETURN(result->meta->m->fetch_field_direct(result->meta, fieldnr TSRMLS_CC)); } - DBG_RETURN(result->meta->m->fetch_field_direct(result->meta, fieldnr TSRMLS_CC)); - } + } while (0); DBG_RETURN(NULL); } @@ -1414,13 +1451,17 @@ static const MYSQLND_FIELD * MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result TSRMLS_DC) { DBG_ENTER("mysqlnd_res::fetch_fields"); - if (result->meta) { - if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) { - /* we have to initialize the rest to get the updated max length */ - result->m.initialize_result_set_rest(result TSRMLS_CC); + do { + if (result->meta) { + if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) { + /* we have to initialize the rest to get the updated max length */ + if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) { + break; + } + } + DBG_RETURN(result->meta->m->fetch_fields(result->meta TSRMLS_CC)); } - DBG_RETURN(result->meta->m->fetch_fields(result->meta TSRMLS_CC)); - } + } while (0); DBG_RETURN(NULL); } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h index 21d90d6dc7c..fd0f1d1b904 100644 --- a/ext/mysqlnd/mysqlnd_structs.h +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -503,7 +503,7 @@ typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_fields)(MYSQLND_RES * con typedef enum_func_status (*func_mysqlnd_res__read_result_metadata)(MYSQLND_RES *result, MYSQLND * conn TSRMLS_DC); typedef unsigned long * (*func_mysqlnd_res__fetch_lengths)(MYSQLND_RES * const result TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_res__store_result_fetch_data)(MYSQLND * const conn, MYSQLND_RES *result, MYSQLND_RES_METADATA *meta, zend_bool binary_protocol, zend_bool to_cache TSRMLS_DC); -typedef void (*func_mysqlnd_res__initialize_result_set_rest)(MYSQLND_RES * const result TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_res__initialize_result_set_rest)(MYSQLND_RES * const result TSRMLS_DC); typedef void (*func_mysqlnd_res__free_result_buffers)(MYSQLND_RES * result TSRMLS_DC); /* private */ typedef enum_func_status (*func_mysqlnd_res__free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC); @@ -513,7 +513,7 @@ typedef void (*func_mysqlnd_res__free_buffered_data)(MYSQLND_RES *result TSRM typedef void (*func_mysqlnd_res__unbuffered_free_last_data)(MYSQLND_RES *result TSRMLS_DC); /* for decoding - binary or text protocol */ -typedef void (*func_mysqlnd_res__row_decoder)(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, +typedef enum_func_status (*func_mysqlnd_res__row_decoder)(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, unsigned int field_count, MYSQLND_FIELD *fields_metadata, zend_bool persistent, zend_bool as_unicode, zend_bool as_int_or_float, diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index c4889821b81..cdde0676311 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -1202,7 +1202,7 @@ php_mysqlnd_read_row_ex(MYSQLND * conn, MYSQLND_MEMORY_POOL * result_set_memory_ /* {{{ php_mysqlnd_rowp_read_binary_protocol */ -void +enum_func_status php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, unsigned int field_count, MYSQLND_FIELD *fields_metadata, zend_bool persistent, @@ -1217,7 +1217,9 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zv DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol"); end_field = (current_field = start_field = fields) + field_count; - + if (!current_field) { + DBG_RETURN(FAIL); + } /* skip the first byte, not EODATA_MARKER -> 0x0, status */ p++; @@ -1228,6 +1230,9 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zv for (i = 0; current_field < end_field; current_field++, i++) { DBG_INF("Directly creating zval"); MAKE_STD_ZVAL(*current_field); + if (!*current_field) { + DBG_RETURN(FAIL); + } DBG_INF_FMT("Into zval=%p decoding column %d [%s.%s.%s] type=%d field->flags&unsigned=%d flags=%u is_bit=%d as_unicode=%d", *current_field, i, @@ -1282,13 +1287,13 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zv } } - DBG_VOID_RETURN; + DBG_RETURN(PASS); } /* }}} */ /* {{{ php_mysqlnd_rowp_read_text_protocol */ -void +enum_func_status php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, unsigned int field_count, MYSQLND_FIELD *fields_metadata, zend_bool persistent, @@ -1305,6 +1310,10 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval DBG_ENTER("php_mysqlnd_rowp_read_text_protocol"); end_field = (current_field = start_field = fields) + field_count; + if (!current_field) { + DBG_RETURN(FAIL); + } + for (i = 0; current_field < end_field; current_field++, i++) { /* Don't reverse the order. It is significant!*/ zend_uchar *this_field_len_pos = p; @@ -1313,6 +1322,9 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval DBG_INF("Directly creating zval"); MAKE_STD_ZVAL(*current_field); + if (!*current_field) { + DBG_RETURN(FAIL); + } if (current_field > start_field && last_field_was_string) { /* @@ -1503,7 +1515,7 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval row_buffer->ptr[data_size] = '\0'; } - DBG_VOID_RETURN; + DBG_RETURN(PASS); } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h index 192129678cd..2666ecc5081 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.h +++ b/ext/mysqlnd/mysqlnd_wireprotocol.h @@ -257,14 +257,14 @@ zend_uchar * php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length); PHPAPI const extern char * const mysqlnd_empty_string; -void php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, +enum_func_status php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, unsigned int field_count, MYSQLND_FIELD *fields_metadata, zend_bool persistent, zend_bool as_unicode, zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC); -void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, +enum_func_status php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, unsigned int field_count, MYSQLND_FIELD *fields_metadata, zend_bool persistent, zend_bool as_unicode, zend_bool as_int_or_float,