From 6b804b96b8c454954d30ed8f0cf1a05d91801c4f Mon Sep 17 00:00:00 2001 From: Andrey Hristov Date: Wed, 5 Mar 2014 16:22:23 +0200 Subject: [PATCH] Refactor the result set data structures. Move more to the buffered and unbuffered substructures. Add methods to these too. Preparing for pluggable interface for returning data to the engine (zvals, c-style, something else) --- .../tests/mysqli_debug_mysqlnd_only.phpt | 4 +- ext/mysqlnd/mysqlnd.c | 3 +- ext/mysqlnd/mysqlnd.h | 3 +- ext/mysqlnd/mysqlnd_ext_plugin.c | 62 ++ ext/mysqlnd/mysqlnd_ext_plugin.h | 13 + ext/mysqlnd/mysqlnd_priv.h | 6 + ext/mysqlnd/mysqlnd_ps.c | 76 +- ext/mysqlnd/mysqlnd_result.c | 765 +++++++++++------- ext/mysqlnd/mysqlnd_result.h | 2 + ext/mysqlnd/mysqlnd_result_meta.c | 14 +- ext/mysqlnd/mysqlnd_structs.h | 115 ++- 11 files changed, 676 insertions(+), 387 deletions(-) diff --git a/ext/mysqli/tests/mysqli_debug_mysqlnd_only.phpt b/ext/mysqli/tests/mysqli_debug_mysqlnd_only.phpt index 8ecf516cad..257926bae9 100644 --- a/ext/mysqli/tests/mysqli_debug_mysqlnd_only.phpt +++ b/ext/mysqli/tests/mysqli_debug_mysqlnd_only.phpt @@ -90,9 +90,9 @@ if (!$IS_MYSQLND) if (isset($functions_trace[$name])) $found++; - if ($found < (count($memory_funcs) - 2)) + if ($found < (count($memory_funcs) - 3)) printf("[016] Only %d memory functions have been found, expecting at least %d.\n", - $found, count($memory_funcs) - 2); + $found, count($memory_funcs) - 3); $trace = try_control_string($link, 't:O,' . $trace_file, $trace_file, 20); if (!strstr($trace, 'SELECT * FROM test') && !strstr($trace, 'mysql_real_query')) diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 39813145ba..ab38683786 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -1476,8 +1476,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, list_fields)(MYSQLND_CONN_DATA * conn, const c } result->type = MYSQLND_RES_NORMAL; - result->m.fetch_row = result->m.fetch_row_normal_unbuffered; - result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + result->unbuf = mysqlnd_result_unbuffered_init(result->field_count, FALSE, result->persistent TSRMLS_CC); if (!result->unbuf) { /* OOM */ SET_OOM_ERROR(*conn->error_info); diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h index bf9ad53b11..99f6fa48d1 100644 --- a/ext/mysqlnd/mysqlnd.h +++ b/ext/mysqlnd/mysqlnd.h @@ -150,8 +150,7 @@ PHPAPI enum_func_status _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQ #define mysqlnd_num_rows(result) (result)->m.num_rows((result) TSRMLS_CC) #define mysqlnd_num_fields(result) (result)->m.num_fields((result) TSRMLS_CC) -#define mysqlnd_fetch_lengths(result) _mysqlnd_fetch_lengths((result) TSRMLS_CC) -PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result TSRMLS_DC); +#define mysqlnd_fetch_lengths(result) (result)->m.fetch_lengths((result) TSRMLS_CC) #define mysqlnd_field_seek(result, ofs) (result)->m.seek_field((result), (ofs) TSRMLS_CC) #define mysqlnd_field_tell(result) (result)->m.field_tell((result) TSRMLS_CC) diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.c b/ext/mysqlnd/mysqlnd_ext_plugin.c index f40688b272..706111346f 100644 --- a/ext/mysqlnd/mysqlnd_ext_plugin.c +++ b/ext/mysqlnd/mysqlnd_ext_plugin.c @@ -70,6 +70,32 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result /* }}} */ +/* {{{ _mysqlnd_plugin_get_plugin_result_unbuffered_data */ +PHPAPI void ** _mysqlnd_plugin_get_plugin_result_unbuffered_data(const MYSQLND_RES_UNBUFFERED * result, unsigned int plugin_id TSRMLS_DC) +{ + DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data"); + DBG_INF_FMT("plugin_id=%u", plugin_id); + if (!result || plugin_id >= mysqlnd_plugin_count()) { + return NULL; + } + DBG_RETURN((void *)((char *)result + sizeof(MYSQLND_RES_UNBUFFERED) + plugin_id * sizeof(void *))); +} +/* }}} */ + + +/* {{{ _mysqlnd_plugin_get_plugin_result_buffered_data */ +PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data(const MYSQLND_RES_BUFFERED * result, unsigned int plugin_id TSRMLS_DC) +{ + DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data"); + DBG_INF_FMT("plugin_id=%u", plugin_id); + if (!result || plugin_id >= mysqlnd_plugin_count()) { + return NULL; + } + DBG_RETURN((void *)((char *)result + sizeof(MYSQLND_RES_BUFFERED) + plugin_id * sizeof(void *))); +} +/* }}} */ + + /* {{{ _mysqlnd_plugin_get_plugin_protocol_data */ PHPAPI void ** _mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id TSRMLS_DC) @@ -161,6 +187,42 @@ mysqlnd_result_set_methods(struct st_mysqlnd_res_methods * methods) /* }}} */ +/* {{{ mysqlnd_result_unbuffered_get_methods */ +PHPAPI struct st_mysqlnd_result_unbuffered_methods * +mysqlnd_result_unbuffered_get_methods() +{ + return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_unbuffered); +} +/* }}} */ + + +/* {{{ mysqlnd_result_unbuffered_set_methods */ +PHPAPI void +mysqlnd_result_unbuffered_set_methods(struct st_mysqlnd_result_unbuffered_methods * methods) +{ + MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_unbuffered) = *methods; +} +/* }}} */ + + +/* {{{ mysqlnd_result_buffered_get_methods */ +PHPAPI struct st_mysqlnd_result_buffered_methods * +mysqlnd_result_buffered_get_methods() +{ + return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_buffered); +} +/* }}} */ + + +/* {{{ mysqlnd_result_buffered_set_methods */ +PHPAPI void +mysqlnd_result_buffered_set_methods(struct st_mysqlnd_result_buffered_methods * methods) +{ + MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_buffered) = *methods; +} +/* }}} */ + + /* {{{ mysqlnd_stmt_get_methods */ PHPAPI struct st_mysqlnd_stmt_methods * mysqlnd_stmt_get_methods() diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.h b/ext/mysqlnd/mysqlnd_ext_plugin.h index 2a2416a042..6f15eee8ce 100644 --- a/ext/mysqlnd/mysqlnd_ext_plugin.h +++ b/ext/mysqlnd/mysqlnd_ext_plugin.h @@ -31,6 +31,13 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_connection_data_data(const MYSQLND_CON PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result, unsigned int plugin_id TSRMLS_DC); #define mysqlnd_plugin_get_plugin_result_data(r, p_id) _mysqlnd_plugin_get_plugin_result_data((r), (p_id) TSRMLS_CC) +PHPAPI void ** _mysqlnd_plugin_get_plugin_result_unbuffered_data(const MYSQLND_RES_UNBUFFERED * result, unsigned int plugin_id TSRMLS_DC); +#define mysqlnd_plugin_get_plugin_result_unbuffered_data(r, p_id) _mysqlnd_plugin_get_plugin_result_unbuffered_data((r), (p_id) TSRMLS_CC) + +PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data(const MYSQLND_RES_BUFFERED * result, unsigned int plugin_id TSRMLS_DC); +#define mysqlnd_plugin_get_plugin_result_buffered_data(r, p_id) _mysqlnd_plugin_get_plugin_result_buffered_data((r), (p_id) TSRMLS_CC) + + PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, unsigned int plugin_id TSRMLS_DC); #define mysqlnd_plugin_get_plugin_stmt_data(s, p_id) _mysqlnd_plugin_get_plugin_stmt_data((s), (p_id) TSRMLS_CC) @@ -50,6 +57,12 @@ PHPAPI void mysqlnd_conn_data_set_methods(struct st_mysqlnd_conn_data_methods * PHPAPI struct st_mysqlnd_res_methods * mysqlnd_result_get_methods(); PHPAPI void mysqlnd_result_set_methods(struct st_mysqlnd_res_methods * methods); +PHPAPI struct st_mysqlnd_result_unbuffered_methods * mysqlnd_result_unbuffered_get_methods(); +PHPAPI void mysqlnd_result_unbuffered_set_methods(struct st_mysqlnd_result_unbuffered_methods * methods); + +PHPAPI struct st_mysqlnd_result_buffered_methods * mysqlnd_result_buffered_get_methods(); +PHPAPI void mysqlnd_result_buffered_set_methods(struct st_mysqlnd_result_buffered_methods * methods); + PHPAPI struct st_mysqlnd_stmt_methods * mysqlnd_stmt_get_methods(); PHPAPI void mysqlnd_stmt_set_methods(struct st_mysqlnd_stmt_methods * methods); diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h index deb0bdc8c8..a44e29d9f9 100644 --- a/ext/mysqlnd/mysqlnd_priv.h +++ b/ext/mysqlnd/mysqlnd_priv.h @@ -171,6 +171,10 @@ struct st_mysqlnd_perm_bind { extern struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1]; +enum_func_status mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC); +enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC); + + PHPAPI extern const char * const mysqlnd_old_passwd; PHPAPI extern const char * const mysqlnd_out_of_sync; PHPAPI extern const char * const mysqlnd_server_gone; @@ -180,6 +184,8 @@ PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_object_factory); PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_conn); PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_conn_data); PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_res); +PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_result_unbuffered); +PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_result_buffered); PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_protocol); PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_net); diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index fa2abc71d7..dc97d2d36f 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -39,14 +39,6 @@ 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_fetch_row_buffered(MYSQLND_RES *result, void *param, - unsigned int flags, - zend_bool *fetched_anything TSRMLS_DC); - -enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, - unsigned int flags, - zend_bool *fetched_anything 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); @@ -95,14 +87,12 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC) result = stmt->result; result->type = MYSQLND_RES_PS_BUF; - result->m.fetch_row = mysqlnd_stmt_fetch_row_buffered; - result->m.fetch_lengths = NULL;/* makes no sense */ - result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; - - result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC); +/* result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; */ ret = result->m.store_result_fetch_data(conn, result, result->meta, TRUE TSRMLS_CC); + result->stored_data->m.fetch_row = mysqlnd_stmt_fetch_row_buffered; + if (PASS == ret) { /* Overflow ? */ MYSQLND_RES_BUFFERED * set = result->stored_data; @@ -541,7 +531,11 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s TSRMLS_DC) /* Update stmt->field_count as SHOW sets it to 0 at prepare */ stmt->field_count = stmt->result->field_count = conn->field_count; - stmt->result->lengths = NULL; + if (stmt->result->stored_data) { + stmt->result->stored_data->lengths = NULL; + } else if (stmt->result->unbuf) { + stmt->result->unbuf->lengths = NULL; + } if (stmt->field_count) { stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE; /* @@ -729,12 +723,13 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s TSRMLS_DC) /* {{{ mysqlnd_stmt_fetch_row_buffered */ enum_func_status -mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) +mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) { MYSQLND_STMT * s = (MYSQLND_STMT *) param; MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_RES_BUFFERED *set = result->stored_data; - unsigned int field_count = result->meta->field_count; + const MYSQLND_RES_METADATA * const meta = result->meta; + unsigned int field_count = meta->field_count; DBG_ENTER("mysqlnd_stmt_fetch_row_buffered"); *fetched_anything = FALSE; @@ -747,12 +742,11 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int f /* The user could have skipped binding - don't crash*/ if (stmt->result_bind) { unsigned int i; - MYSQLND_RES_METADATA * meta = result->meta; zval **current_row = set->data_cursor; 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], + enum_func_status rc = result->stored_data->m.row_decoder(set->row_buffers[row_num], current_row, meta->field_count, meta->fields, @@ -825,13 +819,14 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int f /* {{{ mysqlnd_stmt_fetch_row_unbuffered */ -static enum_func_status -mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) +enum_func_status +mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) { enum_func_status ret; MYSQLND_STMT * s = (MYSQLND_STMT *) param; MYSQLND_STMT_DATA * stmt = s? s->data:NULL; MYSQLND_PACKET_ROW * row_packet; + const MYSQLND_RES_METADATA * const meta = result->meta; DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered"); @@ -848,7 +843,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int DBG_ERR("command out of sync"); DBG_RETURN(FAIL); } - if (!(row_packet = result->row_packet)) { + if (!(row_packet = result->unbuf->row_packet)) { DBG_RETURN(FAIL); } @@ -857,20 +852,20 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int /* If we skip rows (stmt == NULL || stmt->result_bind == NULL) we have to - result->m.unbuffered_free_last_data() before it. The function returns always true. + result->unbuf->m.free_last_data() before it. The function returns always true. */ if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { unsigned int i, field_count = result->field_count; if (!row_packet->skip_extraction) { - result->m.unbuffered_free_last_data(result TSRMLS_CC); + result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC); result->unbuf->last_row_data = row_packet->fields; result->unbuf->last_row_buffer = row_packet->row_buffer; row_packet->fields = NULL; row_packet->row_buffer = NULL; - if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer, + if (PASS != result->unbuf->m.row_decoder(result->unbuf->last_row_buffer, result->unbuf->last_row_data, row_packet->field_count, row_packet->fields_metadata, @@ -885,14 +880,14 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int zval *data = result->unbuf->last_row_data[i]; /* stmt->result_bind[i].zv has been already destructed - in result->m.unbuffered_free_last_data() + 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); #endif if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) { - if ((Z_TYPE_P(data) == IS_STRING) && (result->meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data))) { - result->meta->fields[i].max_length = Z_STRLEN_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); } stmt->result_bind[i].zv->value = data->value; /* copied data, thus also the ownership. Thus null data */ @@ -904,7 +899,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int } else { DBG_INF("skipping extraction"); /* - Data has been allocated and usually result->m.unbuffered_free_last_data() + Data has been allocated and usually result->unbuf->m.free_last_data() frees it but we can't call this function as it will cause problems with the bound variables. Thus we need to do part of what it does or Zend will report leaks. @@ -979,7 +974,7 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s TSRMLS_DC) result = stmt->result; result->m.use_result(stmt->result, TRUE TSRMLS_CC); - result->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor: + result->unbuf->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor: mysqlnd_stmt_fetch_row_unbuffered; stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED; @@ -993,7 +988,7 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s TSRMLS_DC) /* {{{ mysqlnd_fetch_row_cursor */ enum_func_status -mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) +mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) { enum_func_status ret; MYSQLND_STMT * s = (MYSQLND_STMT *) param; @@ -1017,7 +1012,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla DBG_ERR("command out of sync"); DBG_RETURN(FAIL); } - if (!(row_packet = result->row_packet)) { + if (!(row_packet = result->unbuf->row_packet)) { DBG_RETURN(FAIL); } @@ -1038,17 +1033,18 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla memset(stmt->upsert_status, 0, sizeof(*stmt->upsert_status)); if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { + const MYSQLND_RES_METADATA * const meta = result->meta; unsigned int i, field_count = result->field_count; if (!row_packet->skip_extraction) { - result->m.unbuffered_free_last_data(result TSRMLS_CC); + result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC); result->unbuf->last_row_data = row_packet->fields; result->unbuf->last_row_buffer = row_packet->row_buffer; row_packet->fields = NULL; row_packet->row_buffer = NULL; - if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer, + if (PASS != result->unbuf->m.row_decoder(result->unbuf->last_row_buffer, result->unbuf->last_row_data, row_packet->field_count, row_packet->fields_metadata, @@ -1064,7 +1060,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla zval *data = result->unbuf->last_row_data[i]; /* stmt->result_bind[i].zv has been already destructed - in result->m.unbuffered_free_last_data() + 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); @@ -1072,8 +1068,8 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla 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) && (result->meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data))) { - result->meta->fields[i].max_length = Z_STRLEN_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); } stmt->result_bind[i].zv->value = data->value; /* copied data, thus also the ownership. Thus null data */ @@ -1084,7 +1080,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla } else { DBG_INF("skipping extraction"); /* - Data has been allocated and usually result->m.unbuffered_free_last_data() + Data has been allocated and usually result->unbuf->m.free_last_data() frees it but we can't call this function as it will cause problems with the bound variables. Thus we need to do part of what it does or Zend will report leaks. @@ -1792,7 +1788,8 @@ MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s TSRMLS_DC) if (stmt->update_max_length && stmt->result->stored_data) { /* stored result, we have to update the max_length before we clone the meta data :( */ - stmt->result->m.initialize_result_set_rest(stmt->result TSRMLS_CC); + stmt->result->stored_data->m.initialize_result_set_rest(stmt->result->stored_data, stmt->result->meta, stmt->conn->stats, + stmt->conn->options->int_and_float_native TSRMLS_CC); } /* TODO: This implementation is kind of a hack, @@ -1809,8 +1806,7 @@ MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s TSRMLS_DC) break; } result->type = MYSQLND_RES_NORMAL; - result->m.fetch_row = result->m.fetch_row_normal_unbuffered; - result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + result->unbuf = mysqlnd_result_unbuffered_init(stmt->field_count, TRUE, result->persistent TSRMLS_CC); if (!result->unbuf) { break; } diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index a4fb9f6cf4..75e745665a 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -33,36 +33,37 @@ #define MYSQLND_SILENT -/* {{{ mysqlnd_res::initialize_result_set_rest */ +/* {{{ mysqlnd_result_buffered::initialize_result_set_rest */ static enum_func_status -MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest)(MYSQLND_RES * const result TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_buffered, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta, + MYSQLND_STATS * stats, zend_bool int_and_float_native TSRMLS_DC) { unsigned int i; - 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; - uint64_t row_count = result->stored_data? result->stored_data->row_count:0; + zval **data_cursor = result->data; + zval **data_begin = result->data; + unsigned int field_count = meta->field_count; + uint64_t row_count = result->row_count; enum_func_status ret = PASS; - DBG_ENTER("mysqlnd_res::initialize_result_set_rest"); + DBG_ENTER("mysqlnd_result_buffered::initialize_result_set_rest"); - if (!data_cursor || row_count == result->stored_data->initialized_rows) { + if (!data_cursor || row_count == result->initialized_rows) { DBG_RETURN(ret); } while ((data_cursor - data_begin) < (int)(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], + result->row_buffers[(data_cursor - data_begin) / field_count], data_cursor, - result->meta->field_count, - result->meta->fields, - result->conn->options->int_and_float_native, - result->conn->stats TSRMLS_CC); + meta->field_count, + meta->fields, + int_and_float_native, + stats TSRMLS_CC); if (rc != PASS) { ret = FAIL; break; } - result->stored_data->initialized_rows++; - for (i = 0; i < result->field_count; i++) { + result->initialized_rows++; + for (i = 0; i < meta->field_count; i++) { /* NULL fields are 0 length, 0 is not more than 0 String of zero size, definitely can't be the next max_length. @@ -70,8 +71,8 @@ MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest)(MYSQLND_RES * const resu */ if (Z_TYPE_P(data_cursor[i]) >= IS_STRING) { unsigned long len = Z_STRLEN_P(data_cursor[i]); - if (result->meta->fields[i].max_length < len) { - result->meta->fields[i].max_length = len; + if (meta->fields[i].max_length < len) { + meta->fields[i].max_length = len; } } } @@ -88,6 +89,7 @@ static void mysqlnd_rset_zval_ptr_dtor(zval **zv, enum_mysqlnd_res_type type, zend_bool * copy_ctor_called TSRMLS_DC) { DBG_ENTER("mysqlnd_rset_zval_ptr_dtor"); + DBG_INF_FMT("type=%u", type); if (!zv || !*zv) { *copy_ctor_called = FALSE; DBG_ERR_FMT("zv was NULL"); @@ -121,42 +123,42 @@ mysqlnd_rset_zval_ptr_dtor(zval **zv, enum_mysqlnd_res_type type, zend_bool * co ZVAL_NULL(*zv); } } + DBG_INF_FMT("call the dtor on zval with refc %u", Z_REFCOUNT_PP(zv)); zval_ptr_dtor(zv); DBG_VOID_RETURN; } /* }}} */ -/* {{{ mysqlnd_res::unbuffered_free_last_data */ +/* {{{ mysqlnd_result_unbuffered::free_last_data */ static void -MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data)(MYSQLND_RES * result TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_unbuffered, free_last_data)(MYSQLND_RES_UNBUFFERED * unbuf, MYSQLND_STATS * const global_stats TSRMLS_DC) { - MYSQLND_RES_UNBUFFERED *unbuf = result->unbuf; - DBG_ENTER("mysqlnd_res::unbuffered_free_last_data"); if (!unbuf) { DBG_VOID_RETURN; } + DBG_INF_FMT("field_count=%u", unbuf->field_count); if (unbuf->last_row_data) { unsigned int i, ctor_called_count = 0; zend_bool copy_ctor_called; - MYSQLND_STATS *global_stats = result->conn? result->conn->stats:NULL; - for (i = 0; i < result->field_count; i++) { - mysqlnd_rset_zval_ptr_dtor(&(unbuf->last_row_data[i]), result->type, ©_ctor_called TSRMLS_CC); + for (i = 0; i < unbuf->field_count; i++) { + mysqlnd_rset_zval_ptr_dtor(&(unbuf->last_row_data[i]), unbuf->ps ? MYSQLND_RES_PS_UNBUF : MYSQLND_RES_NORMAL, ©_ctor_called TSRMLS_CC); if (copy_ctor_called) { ++ctor_called_count; } } + DBG_INF_FMT("copy_ctor_called_count=%u", ctor_called_count); /* By using value3 macros we hold a mutex only once, there is no value2 */ MYSQLND_INC_CONN_STATISTIC_W_VALUE2(global_stats, STAT_COPY_ON_WRITE_PERFORMED, ctor_called_count, STAT_COPY_ON_WRITE_SAVED, - result->field_count - ctor_called_count); + unbuf->field_count - ctor_called_count); /* Free last row's zvals */ mnd_efree(unbuf->last_row_data); unbuf->last_row_data = NULL; @@ -173,12 +175,41 @@ MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data)(MYSQLND_RES * result TSRM /* }}} */ -/* {{{ mysqlnd_res::free_buffered_data */ +/* {{{ mysqlnd_result_unbuffered::free_result */ static void -MYSQLND_METHOD(mysqlnd_res, free_buffered_data)(MYSQLND_RES * result TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_unbuffered, free_result)(MYSQLND_RES_UNBUFFERED * const result, MYSQLND_STATS * const global_stats TSRMLS_DC) { - MYSQLND_RES_BUFFERED *set = result->stored_data; - unsigned int field_count = result->field_count; + DBG_ENTER("mysqlnd_result_unbuffered, free_result"); + result->m.free_last_data(result, global_stats TSRMLS_CC); + + if (result->lengths) { + mnd_pefree(result->lengths, result->persistent); + result->lengths = NULL; + } + + /* must be free before because references the memory pool */ + if (result->row_packet) { + PACKET_FREE(result->row_packet); + result->row_packet = NULL; + } + + if (result->result_set_memory_pool) { + mysqlnd_mempool_destroy(result->result_set_memory_pool TSRMLS_CC); + result->result_set_memory_pool = NULL; + } + + + mnd_pefree(result, result->persistent); + DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_result_buffered::free_result */ +static void +MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * const set TSRMLS_DC) +{ + unsigned int field_count = set->field_count; int64_t row; DBG_ENTER("mysqlnd_res::free_buffered_data"); @@ -199,7 +230,7 @@ MYSQLND_METHOD(mysqlnd_res, free_buffered_data)(MYSQLND_RES * result TSRMLS_DC) for (col = field_count - 1; col >= 0; --col) { if (current_row[col]) { zend_bool copy_ctor_called; - mysqlnd_rset_zval_ptr_dtor(&(current_row[col]), result->type, ©_ctor_called TSRMLS_CC); + mysqlnd_rset_zval_ptr_dtor(&(current_row[col]), set->ps? MYSQLND_RES_PS_BUF : MYSQLND_RES_NORMAL, ©_ctor_called TSRMLS_CC); if (copy_ctor_called) { ++copy_on_write_performed; } else { @@ -216,14 +247,26 @@ MYSQLND_METHOD(mysqlnd_res, free_buffered_data)(MYSQLND_RES * result TSRMLS_DC) mnd_efree(data); } + if (set->lengths) { + mnd_pefree(set->lengths, set->persistent); + set->lengths = NULL; + } + if (set->row_buffers) { mnd_efree(set->row_buffers); set->row_buffers = NULL; } + + if (set->result_set_memory_pool) { + mysqlnd_mempool_destroy(set->result_set_memory_pool TSRMLS_CC); + set->result_set_memory_pool = NULL; + } + + set->data_cursor = NULL; set->row_count = 0; - mnd_efree(set); + mnd_pefree(set, set->persistent); DBG_VOID_RETURN; } @@ -238,39 +281,24 @@ MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES * result TSRMLS_DC) DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->stored_data? "buffered":"unknown")); if (result->unbuf) { - result->m.unbuffered_free_last_data(result TSRMLS_CC); - mnd_efree(result->unbuf); + result->unbuf->m.free_result(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC); result->unbuf = NULL; } else if (result->stored_data) { - result->m.free_buffered_data(result TSRMLS_CC); + result->stored_data->m.free_result(result->stored_data TSRMLS_CC); result->stored_data = NULL; } - if (result->lengths) { - mnd_efree(result->lengths); - result->lengths = NULL; - } - - if (result->row_packet) { - PACKET_FREE(result->row_packet); - result->row_packet = NULL; - } - - if (result->result_set_memory_pool) { - mysqlnd_mempool_destroy(result->result_set_memory_pool TSRMLS_CC); - result->result_set_memory_pool = NULL; - } DBG_VOID_RETURN; } /* }}} */ -/* {{{ mysqlnd_internal_free_result_contents */ +/* {{{ mysqlnd_res::free_result_contents_internal */ static -void mysqlnd_internal_free_result_contents(MYSQLND_RES * result TSRMLS_DC) +void MYSQLND_METHOD(mysqlnd_res, free_result_contents_internal)(MYSQLND_RES * result TSRMLS_DC) { - DBG_ENTER("mysqlnd_internal_free_result_contents"); + DBG_ENTER("mysqlnd_res::free_result_contents_internal"); result->m.free_result_buffers(result TSRMLS_CC); @@ -284,11 +312,13 @@ void mysqlnd_internal_free_result_contents(MYSQLND_RES * result TSRMLS_DC) /* }}} */ -/* {{{ mysqlnd_internal_free_result */ +/* {{{ mysqlnd_res::free_result_internal */ static -void mysqlnd_internal_free_result(MYSQLND_RES * result TSRMLS_DC) +void MYSQLND_METHOD(mysqlnd_res, free_result_internal)(MYSQLND_RES * result TSRMLS_DC) { - DBG_ENTER("mysqlnd_internal_free_result"); + DBG_ENTER("mysqlnd_res::free_result_internal"); + result->m.skip_result(result TSRMLS_CC); + result->m.free_result_contents(result TSRMLS_CC); if (result->conn) { @@ -552,7 +582,7 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s /* }}} */ -/* {{{ mysqlnd_fetch_lengths_buffered */ +/* {{{ mysqlnd_result_buffered::fetch_lengths */ /* Do lazy initialization for buffered results. As PHP strings have length inside, this function makes not much sense in the context @@ -560,11 +590,11 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s completeness. */ static unsigned long * -mysqlnd_fetch_lengths_buffered(MYSQLND_RES * const result TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_buffered, fetch_lengths)(MYSQLND_RES_BUFFERED * const result TSRMLS_DC) { unsigned int i; zval **previous_row; - MYSQLND_RES_BUFFERED *set = result->stored_data; + MYSQLND_RES_BUFFERED *set = result; /* If: @@ -574,13 +604,13 @@ mysqlnd_fetch_lengths_buffered(MYSQLND_RES * const result TSRMLS_DC) */ if (set->data_cursor == NULL || set->data_cursor == set->data || - ((set->data_cursor - set->data) > (set->row_count * result->meta->field_count) )) + ((set->data_cursor - set->data) > (set->row_count * result->field_count) )) { return NULL;/* No rows or no more rows */ } - previous_row = set->data_cursor - result->meta->field_count; - for (i = 0; i < result->meta->field_count; i++) { + previous_row = set->data_cursor - result->field_count; + for (i = 0; i < result->field_count; i++) { result->lengths[i] = (Z_TYPE_P(previous_row[i]) == IS_NULL)? 0:Z_STRLEN_P(previous_row[i]); } @@ -589,49 +619,56 @@ mysqlnd_fetch_lengths_buffered(MYSQLND_RES * const result TSRMLS_DC) /* }}} */ -/* {{{ mysqlnd_fetch_lengths_unbuffered */ +/* {{{ mysqlnd_result_unbuffered::fetch_lengths */ static unsigned long * -mysqlnd_fetch_lengths_unbuffered(MYSQLND_RES * const result TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_lengths)(MYSQLND_RES_UNBUFFERED * const result TSRMLS_DC) { /* simulate output of libmysql */ - return (!result->unbuf || result->unbuf->last_row_data || result->unbuf->eof_reached)? result->lengths:NULL; + return (result->last_row_data || result->eof_reached)? result->lengths : NULL; } /* }}} */ /* {{{ mysqlnd_res::fetch_lengths */ -PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result TSRMLS_DC) +static unsigned long * +MYSQLND_METHOD(mysqlnd_res, fetch_lengths)(MYSQLND_RES * const result TSRMLS_DC) { - return result->m.fetch_lengths? result->m.fetch_lengths(result TSRMLS_CC) : NULL; + unsigned long * ret; + DBG_ENTER("mysqlnd_res::fetch_lengths"); + ret = result->stored_data && result->stored_data->m.fetch_lengths ? + result->stored_data->m.fetch_lengths(result->stored_data TSRMLS_CC) : + (result->unbuf && result->unbuf->m.fetch_lengths ? + result->unbuf->m.fetch_lengths(result->unbuf TSRMLS_CC) : + NULL + ); + DBG_RETURN(ret); } /* }}} */ -/* {{{ mysqlnd_fetch_row_unbuffered_c */ -static MYSQLND_ROW_C -mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC) +/* {{{ mysqlnd_result_unbuffered::fetch_row_c */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) { enum_func_status ret; - MYSQLND_ROW_C retrow = NULL; - unsigned int i, - field_count = result->field_count; - MYSQLND_PACKET_ROW *row_packet = result->row_packet; - unsigned long *lengths = result->lengths; + MYSQLND_ROW_C *row = (MYSQLND_ROW_C *) param; + MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet; + const MYSQLND_RES_METADATA * const meta = result->meta; - DBG_ENTER("mysqlnd_fetch_row_unbuffered_c"); + DBG_ENTER("mysqlnd_result_unbuffered::fetch_row_c"); + *fetched_anything = FALSE; if (result->unbuf->eof_reached) { /* No more rows obviously */ - DBG_RETURN(retrow); + 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(retrow); + 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(retrow); + DBG_RETURN(FAIL); } /* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */ row_packet->skip_extraction = FALSE; @@ -641,9 +678,7 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC) 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++; - - result->m.unbuffered_free_last_data(result TSRMLS_CC); + result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC); result->unbuf->last_row_data = row_packet->fields; result->unbuf->last_row_buffer = row_packet->row_buffer; @@ -653,46 +688,51 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC) MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF); if (!row_packet->skip_extraction) { - MYSQLND_FIELD *field = result->meta->fields; - struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys; - - 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, - result->conn->options->int_and_float_native, - result->conn->stats TSRMLS_CC); + unsigned int i, field_count = meta->field_count; + + enum_func_status rc = result->unbuf->m.row_decoder(result->unbuf->last_row_buffer, + result->unbuf->last_row_data, + field_count, + row_packet->fields_metadata, + result->conn->options->int_and_float_native, + result->conn->stats TSRMLS_CC); if (PASS != rc) { - DBG_RETURN(retrow); + DBG_RETURN(FAIL); } + { + *row = mnd_malloc(field_count * sizeof(char *)); + if (*row) { + MYSQLND_FIELD * field = meta->fields; + unsigned long * lengths = result->unbuf->lengths; + + for (i = 0; i < field_count; i++, field++) { + zval * data = result->unbuf->last_row_data[i]; + unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); + +/* BEGIN difference between normal normal fetch and _c */ + if (Z_TYPE_P(data) != IS_NULL) { + convert_to_string(data); + (*row)[i] = Z_STRVAL_P(data); + } else { + (*row)[i] = NULL; + } +/* END difference between normal normal fetch and _c */ - retrow = mnd_malloc(result->field_count * sizeof(char *)); - if (retrow) { - for (i = 0; i < field_count; i++, field++, hash_key++) { - zval *data = result->unbuf->last_row_data[i]; - unsigned int len; - - if (Z_TYPE_P(data) != IS_NULL) { - convert_to_string(data); - retrow[i] = Z_STRVAL_P(data); - len = Z_STRLEN_P(data); - } else { - retrow[i] = NULL; - len = 0; - } - - if (lengths) { - lengths[i] = len; - } + if (lengths) { + lengths[i] = len; + } - if (field->max_length < len) { - field->max_length = len; + if (field->max_length < len) { + field->max_length = len; + } } + } else { + SET_OOM_ERROR(*result->conn->error_info); } - } else { - SET_OOM_ERROR(*result->conn->error_info); } } + result->unbuf->row_count++; + *fetched_anything = TRUE; } else if (ret == FAIL) { if (row_packet->error_info.error_no) { COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info); @@ -716,23 +756,25 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC) } else { CONN_SET_STATE(result->conn, CONN_READY); } - result->m.unbuffered_free_last_data(result TSRMLS_CC); + result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC); } - DBG_RETURN(retrow); + DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything); + DBG_RETURN(PASS); } /* }}} */ -/* {{{ mysqlnd_fetch_row_unbuffered */ +/* {{{ mysqlnd_result_unbuffered::fetch_row */ static enum_func_status -mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) { enum_func_status ret; zval *row = (zval *) param; - MYSQLND_PACKET_ROW *row_packet = result->row_packet; + MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet; + const MYSQLND_RES_METADATA * const meta = result->meta; - DBG_ENTER("mysqlnd_fetch_row_unbuffered"); + DBG_ENTER("mysqlnd_result_unbuffered::fetch_row"); *fetched_anything = FALSE; if (result->unbuf->eof_reached) { @@ -755,7 +797,7 @@ 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->m.unbuffered_free_last_data(result TSRMLS_CC); + result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC); result->unbuf->last_row_data = row_packet->fields; result->unbuf->last_row_buffer = row_packet->row_buffer; @@ -765,13 +807,9 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF); if (!row_packet->skip_extraction) { - HashTable *row_ht = Z_ARRVAL_P(row); - MYSQLND_FIELD *field = result->meta->fields; - struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys; - unsigned int i, field_count = result->field_count; - unsigned long *lengths = result->lengths; + unsigned int i, field_count = meta->field_count; - enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer, + enum_func_status rc = result->unbuf->m.row_decoder(result->unbuf->last_row_buffer, result->unbuf->last_row_data, field_count, row_packet->fields_metadata, @@ -780,46 +818,51 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla if (PASS != rc) { DBG_RETURN(FAIL); } - for (i = 0; i < field_count; i++, field++, hash_key++) { - zval *data = result->unbuf->last_row_data[i]; - unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); + { + HashTable * row_ht = Z_ARRVAL_P(row); + MYSQLND_FIELD * field = meta->fields; + unsigned long * lengths = result->unbuf->lengths; + + for (i = 0; i < field_count; i++, field++) { + zval * data = result->unbuf->last_row_data[i]; + unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); + + if (flags & MYSQLND_FETCH_NUM) { + Z_ADDREF_P(data); + zend_hash_next_index_insert(row_ht, &data, sizeof(zval *), NULL); + } + if (flags & MYSQLND_FETCH_ASSOC) { + /* zend_hash_quick_update needs length + trailing zero */ + /* QQ: Error handling ? */ + /* + zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether + the index is a numeric and convert it to it. This however means constant + hashing of the column name, which is not needed as it can be precomputed. + */ + Z_ADDREF_P(data); + if (meta->zend_hash_keys[i].is_numeric == FALSE) { + zend_hash_quick_update(Z_ARRVAL_P(row), + field->name, + field->name_length + 1, + meta->zend_hash_keys[i].key, + (void *) &data, sizeof(zval *), NULL); + } else { + zend_hash_index_update(Z_ARRVAL_P(row), meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); + } + } - if (lengths) { - lengths[i] = len; - } + if (lengths) { + lengths[i] = len; + } - if (flags & MYSQLND_FETCH_NUM) { - Z_ADDREF_P(data); - zend_hash_next_index_insert(row_ht, &data, sizeof(zval *), NULL); - } - if (flags & MYSQLND_FETCH_ASSOC) { - /* zend_hash_quick_update needs length + trailing zero */ - /* QQ: Error handling ? */ - /* - zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether - the index is a numeric and convert it to it. This however means constant - hashing of the column name, which is not needed as it can be precomputed. - */ - Z_ADDREF_P(data); - if (hash_key->is_numeric == FALSE) { - zend_hash_quick_update(Z_ARRVAL_P(row), - field->name, - field->name_length + 1, - hash_key->key, - (void *) &data, sizeof(zval *), NULL); - } else { - zend_hash_index_update(Z_ARRVAL_P(row), - hash_key->key, - (void *) &data, sizeof(zval *), NULL); + if (field->max_length < len) { + field->max_length = len; } } - if (field->max_length < len) { - field->max_length = len; - } } } - *fetched_anything = TRUE; result->unbuf->row_count++; + *fetched_anything = TRUE; } else if (ret == FAIL) { if (row_packet->error_info.error_no) { COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info); @@ -843,7 +886,7 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla } else { CONN_SET_STATE(result->conn, CONN_READY); } - result->m.unbuffered_free_last_data(result TSRMLS_CC); + result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC); } DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything); @@ -862,44 +905,31 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps if (ps == FALSE) { result->type = MYSQLND_RES_NORMAL; - result->m.fetch_row = result->m.fetch_row_normal_unbuffered; - result->m.fetch_lengths = mysqlnd_fetch_lengths_unbuffered; - result->m.row_decoder = php_mysqlnd_rowp_read_text_protocol; - result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long)); - if (!result->lengths) { - goto oom; - } } 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) { + result->unbuf = mysqlnd_result_unbuffered_init(result->field_count, ps, result->persistent TSRMLS_CC); + if (!result->unbuf) { goto oom; } /* Will be freed in the mysqlnd_internal_free_result_contents() called - by the resource destructor. mysqlnd_fetch_row_unbuffered() expects + by the resource destructor. mysqlnd_result_unbuffered::fetch_row() expects this to be not NULL. */ /* FALSE = non-persistent */ - result->row_packet = result->conn->protocol->m.get_row_packet(result->conn->protocol, FALSE TSRMLS_CC); - if (!result->row_packet) { + result->unbuf->row_packet = result->conn->protocol->m.get_row_packet(result->conn->protocol, FALSE TSRMLS_CC); + if (!result->unbuf->row_packet) { goto oom; } - result->row_packet->result_set_memory_pool = result->result_set_memory_pool; - result->row_packet->field_count = result->field_count; - result->row_packet->binary_protocol = ps; - result->row_packet->fields_metadata = result->meta->fields; - result->row_packet->bit_fields_count = result->meta->bit_fields_count; - result->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len; + result->unbuf->row_packet->result_set_memory_pool = result->unbuf->result_set_memory_pool; + result->unbuf->row_packet->field_count = result->field_count; + result->unbuf->row_packet->binary_protocol = ps; + result->unbuf->row_packet->fields_metadata = result->meta->fields; + result->unbuf->row_packet->bit_fields_count = result->meta->bit_fields_count; + result->unbuf->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len; DBG_RETURN(result); oom: @@ -909,37 +939,39 @@ oom: /* }}} */ -/* {{{ mysqlnd_fetch_row_buffered_c */ -static MYSQLND_ROW_C -mysqlnd_fetch_row_buffered_c(MYSQLND_RES * result TSRMLS_DC) +/* {{{ mysqlnd_result_buffered::fetch_row_c */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) { - MYSQLND_ROW_C ret = NULL; - MYSQLND_RES_BUFFERED *set = result->stored_data; + MYSQLND_ROW_C * row = (MYSQLND_ROW_C *) param; + MYSQLND_RES_BUFFERED * set = result->stored_data; + const MYSQLND_RES_METADATA * const meta = result->meta; + unsigned int field_count = meta->field_count; + enum_func_status ret = FAIL; - DBG_ENTER("mysqlnd_fetch_row_buffered_c"); + DBG_ENTER("mysqlnd_result_buffered::fetch_row_c"); /* If we haven't read everything */ if (set->data_cursor && - (set->data_cursor - set->data) < (set->row_count * result->meta->field_count)) + (set->data_cursor - set->data) < (set->row_count * field_count)) { zval **current_row = set->data_cursor; - MYSQLND_FIELD *field = result->meta->fields; - struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys; + MYSQLND_FIELD * field = meta->fields; unsigned int i; 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], + uint64_t row_num = (set->data_cursor - set->data) / field_count; + enum_func_status rc = set->m.row_decoder(set->row_buffers[row_num], current_row, - result->meta->field_count, - result->meta->fields, + field_count, + meta->fields, result->conn->options->int_and_float_native, result->conn->stats TSRMLS_CC); if (rc != PASS) { - DBG_RETURN(ret); + DBG_RETURN(FAIL); } set->initialized_rows++; - for (i = 0; i < result->field_count; i++) { + for (i = 0; i < field_count; i++) { /* NULL fields are 0 length, 0 is not more than 0 String of zero size, definitely can't be the next max_length. @@ -954,64 +986,72 @@ mysqlnd_fetch_row_buffered_c(MYSQLND_RES * result TSRMLS_DC) } } - set->data_cursor += result->meta->field_count; - MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); - - ret = mnd_malloc(result->field_count * sizeof(char *)); +/* BEGIN difference between normal normal fetch and _c */ + /* there is no conn handle in this function thus we can't set OOM in error_info */ + *row = mnd_malloc(field_count * sizeof(char *)); if (ret) { - for (i = 0; i < result->field_count; i++, field++, hash_key++) { - zval *data = current_row[i]; + for (i = 0; i < field_count; i++) { + zval * data = current_row[i]; if (Z_TYPE_P(data) != IS_NULL) { convert_to_string(data); - ret[i] = Z_STRVAL_P(data); + (*row)[i] = Z_STRVAL_P(data); } else { - ret[i] = NULL; + (*row)[i] = NULL; } } } - /* there is no conn handle in this function thus we can't set OOM in error_info */ +/* END difference between normal normal fetch and _c */ + + set->data_cursor += field_count; + MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); + *fetched_anything = TRUE; + ret = PASS; } else { set->data_cursor = NULL; DBG_INF("EOF reached"); + *fetched_anything = FALSE; + ret = PASS; } + DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything); DBG_RETURN(ret); } /* }}} */ -/* {{{ mysqlnd_fetch_row_buffered */ +/* {{{ mysqlnd_result_buffered::fetch_row */ static enum_func_status -mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) { - unsigned int i; - zval *row = (zval *) param; - MYSQLND_RES_BUFFERED *set = result->stored_data; + zval * row = (zval *) param; + MYSQLND_RES_BUFFERED * set = result->stored_data; + const MYSQLND_RES_METADATA * const meta = result->meta; + unsigned int field_count = meta->field_count; enum_func_status ret = FAIL; - DBG_ENTER("mysqlnd_fetch_row_buffered"); + DBG_ENTER("mysqlnd_result_buffered::fetch_row"); /* If we haven't read everything */ if (set->data_cursor && - (set->data_cursor - set->data) < (set->row_count * result->meta->field_count)) + (set->data_cursor - set->data) < (set->row_count * field_count)) { zval **current_row = set->data_cursor; - MYSQLND_FIELD *field = result->meta->fields; - struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys; + MYSQLND_FIELD * field = meta->fields; + unsigned int i; 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], + uint64_t row_num = (set->data_cursor - set->data) / field_count; + enum_func_status rc = set->m.row_decoder(set->row_buffers[row_num], current_row, - result->meta->field_count, - result->meta->fields, + field_count, + meta->fields, result->conn->options->int_and_float_native, result->conn->stats TSRMLS_CC); if (rc != PASS) { DBG_RETURN(FAIL); } set->initialized_rows++; - for (i = 0; i < result->field_count; i++) { + for (i = 0; i < field_count; i++) { /* NULL fields are 0 length, 0 is not more than 0 String of zero size, definitely can't be the next max_length. @@ -1026,8 +1066,8 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags } } - for (i = 0; i < result->field_count; i++, field++, hash_key++) { - zval *data = current_row[i]; + for (i = 0; i < field_count; i++, field++) { + zval * data = current_row[i]; if (flags & MYSQLND_FETCH_NUM) { Z_ADDREF_P(data); @@ -1042,28 +1082,28 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags hashing of the column name, which is not needed as it can be precomputed. */ Z_ADDREF_P(data); - if (hash_key->is_numeric == FALSE) { + if (meta->zend_hash_keys[i].is_numeric == FALSE) { zend_hash_quick_update(Z_ARRVAL_P(row), field->name, field->name_length + 1, - hash_key->key, + meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } else { zend_hash_index_update(Z_ARRVAL_P(row), - hash_key->key, + meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } } } - set->data_cursor += result->meta->field_count; - *fetched_anything = TRUE; + set->data_cursor += field_count; MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); + *fetched_anything = TRUE; ret = PASS; } else { set->data_cursor = NULL; + DBG_INF("EOF reached"); *fetched_anything = FALSE; ret = PASS; - DBG_INF("EOF reached"); } DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything); DBG_RETURN(ret); @@ -1071,22 +1111,37 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags /* }}} */ +/* {{{ mysqlnd_res::fetch_row */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_res, fetch_row)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) +{ + const mysqlnd_fetch_row_func f = result->stored_data? result->stored_data->m.fetch_row:(result->unbuf? result->unbuf->m.fetch_row:NULL); + if (f) { + return f(result, param, flags, fetched_anything TSRMLS_CC); + } + *fetched_anything = FALSE; + return PASS; +} +/* }}} */ + + #define STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY 2 /* {{{ mysqlnd_res::store_result_fetch_data */ enum_func_status MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result, - MYSQLND_RES_METADATA *meta, + MYSQLND_RES_METADATA * meta, zend_bool binary_protocol TSRMLS_DC) { enum_func_status ret; - MYSQLND_PACKET_ROW *row_packet = NULL; + MYSQLND_PACKET_ROW * row_packet = NULL; unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY, free_rows = 1; MYSQLND_RES_BUFFERED *set; DBG_ENTER("mysqlnd_res::store_result_fetch_data"); - result->stored_data = set = mnd_ecalloc(1, sizeof(MYSQLND_RES_BUFFERED)); + result->stored_data = set = mysqlnd_result_buffered_init(result->field_count, binary_protocol, result->persistent TSRMLS_CC); + if (!set) { SET_OOM_ERROR(*conn->error_info); ret = FAIL; @@ -1109,7 +1164,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c ret = FAIL; goto end; } - row_packet->result_set_memory_pool = result->result_set_memory_pool; + row_packet->result_set_memory_pool = result->stored_data->result_set_memory_pool; row_packet->field_count = meta->field_count; row_packet->binary_protocol = binary_protocol; row_packet->fields_metadata = meta->fields; @@ -1210,20 +1265,9 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, DBG_ENTER("mysqlnd_res::store_result"); /* 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->m.row_decoder = ps_protocol? php_mysqlnd_rowp_read_binary_protocol: - php_mysqlnd_rowp_read_text_protocol; - - 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) { - SET_OOM_ERROR(*conn->error_info); - DBG_RETURN(NULL); - } + /* In case of error the reference will be released in free_result_internal() called indirectly by our caller */ + result->conn = conn->m->get_reference(conn TSRMLS_CC); + result->type = MYSQLND_RES_NORMAL; CONN_SET_STATE(conn, CONN_FETCHING_DATA); @@ -1237,20 +1281,21 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, DBG_RETURN(NULL); } else { /* Overflow ? */ + MYSQLND_RES_METADATA * meta = result->meta; MYSQLND_RES_BUFFERED * set = result->stored_data; if (set->row_count) { /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */ - if (set->row_count * result->meta->field_count * sizeof(zval *) > SIZE_MAX) { + if (set->row_count * meta->field_count * sizeof(zval *) > SIZE_MAX) { SET_OOM_ERROR(*conn->error_info); 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)(set->row_count * result->meta->field_count * sizeof(zval *))); + set->data = mnd_emalloc((size_t)(set->row_count * meta->field_count * sizeof(zval *))); if (!set->data) { SET_OOM_ERROR(*conn->error_info); DBG_RETURN(NULL); } - memset(set->data, 0, (size_t)(set->row_count * result->meta->field_count * sizeof(zval *))); + memset(set->data, 0, (size_t)(set->row_count * meta->field_count * sizeof(zval *))); } /* Position at the first row */ set->data_cursor = set->data; @@ -1276,9 +1321,7 @@ MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC) A PS could be prepared - there is metadata and thus a stmt->result but the fetch_row function isn't actually set (NULL), thus we have to skip these. */ - if (!result->stored_data && result->unbuf && - !result->unbuf->eof_reached && result->m.fetch_row) - { + if (result->unbuf && !result->unbuf->eof_reached) { DBG_INF("skipping result"); /* We have to fetch all data to clean the line */ MYSQLND_INC_CONN_STATISTIC(result->conn->stats, @@ -1300,7 +1343,6 @@ MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, zend_bool implici { DBG_ENTER("mysqlnd_res::free_result"); - result->m.skip_result(result TSRMLS_CC); MYSQLND_INC_CONN_STATISTIC(result->conn? result->conn->stats : NULL, implicit == TRUE? STAT_FREE_RESULT_IMPLICIT: STAT_FREE_RESULT_EXPLICIT); @@ -1313,20 +1355,27 @@ MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, zend_bool implici /* {{{ mysqlnd_res::data_seek */ static enum_func_status -MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES * result, uint64_t row TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES * const result, const uint64_t row TSRMLS_DC) { DBG_ENTER("mysqlnd_res::data_seek"); DBG_INF_FMT("row=%lu", row); - if (!result->stored_data) { - return FAIL; - } + DBG_RETURN(result->stored_data? result->stored_data->m.data_seek(result->stored_data, row TSRMLS_CC) : FAIL); +} +/* }}} */ + + +/* {{{ mysqlnd_result_buffered::data_seek */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_result_buffered, data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row TSRMLS_DC) +{ + DBG_ENTER("mysqlnd_result_buffered::data_seek"); /* libmysql just moves to the end, it does traversing of a linked list */ - if (row >= result->stored_data->row_count) { - result->stored_data->data_cursor = NULL; + if (row >= result->row_count) { + result->data_cursor = NULL; } else { - result->stored_data->data_cursor = result->stored_data->data + row * result->meta->field_count; + result->data_cursor = result->data + row * result->field_count; } DBG_RETURN(PASS); @@ -1334,12 +1383,32 @@ MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES * result, uint64_t row TSRMLS /* }}} */ +/* {{{ mysqlnd_result_unbuffered::num_rows */ +static uint64_t +MYSQLND_METHOD(mysqlnd_result_unbuffered, num_rows)(const MYSQLND_RES_UNBUFFERED * const result TSRMLS_DC) +{ + /* Be compatible with libmysql. We count row_count, but will return 0 */ + return result->eof_reached? result->row_count:0; +} +/* }}} */ + + +/* {{{ mysqlnd_result_buffered::num_rows */ +static uint64_t +MYSQLND_METHOD(mysqlnd_result_buffered, num_rows)(const MYSQLND_RES_BUFFERED * const result TSRMLS_DC) +{ + return result->row_count; +} +/* }}} */ + + /* {{{ mysqlnd_res::num_rows */ static uint64_t MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const result TSRMLS_DC) { - /* Be compatible with libmysql. We count row_count, but will return 0 */ - return result->stored_data? result->stored_data->row_count:(result->unbuf && result->unbuf->eof_reached? result->unbuf->row_count:0); + return result->stored_data? + result->stored_data->m.num_rows(result->stored_data TSRMLS_CC) : + (result->unbuf? result->unbuf->m.num_rows(result->unbuf TSRMLS_CC) : 0); } /* }}} */ @@ -1373,7 +1442,9 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC) 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)) { + if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats, + result->conn->options->int_and_float_native TSRMLS_CC)) + { break; } } @@ -1387,7 +1458,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC) /* {{{ mysqlnd_res::fetch_field_direct */ static const MYSQLND_FIELD * -MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, const MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC) { DBG_ENTER("mysqlnd_res::fetch_field_direct"); do { @@ -1405,7 +1476,9 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, MYSQ 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)) { + if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats, + result->conn->options->int_and_float_native TSRMLS_CC)) + { break; } } @@ -1427,7 +1500,9 @@ MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result TSRMLS_DC) 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)) { + if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats, + result->conn->options->int_and_float_native TSRMLS_CC)) + { break; } } @@ -1439,17 +1514,11 @@ MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result TSRMLS_DC) /* }}} */ - /* {{{ mysqlnd_res::field_seek */ static MYSQLND_FIELD_OFFSET -MYSQLND_METHOD(mysqlnd_res, field_seek)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, field_seek)(MYSQLND_RES * const result, const MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC) { - MYSQLND_FIELD_OFFSET return_value = 0; - if (result->meta) { - return_value = result->meta->current_field; - result->meta->current_field = field_offset; - } - return return_value; + return result->meta? result->meta->m->field_seek(result->meta, field_offset TSRMLS_CC) : 0; } /* }}} */ @@ -1473,10 +1542,6 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, unsigned int flags DBG_ENTER("mysqlnd_res::fetch_into"); - if (!result->m.fetch_row) { - RETVAL_NULL(); - DBG_VOID_RETURN; - } /* Hint Zend how many elements we will have in the hash. Thus it won't extend and rehash the hash constantly. @@ -1511,17 +1576,17 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, unsigned int flags static MYSQLND_ROW_C MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result TSRMLS_DC) { + zend_bool fetched_anything; MYSQLND_ROW_C ret = NULL; DBG_ENTER("mysqlnd_res::fetch_row_c"); - if (result->m.fetch_row) { - if (result->m.fetch_row == result->m.fetch_row_normal_buffered) { - DBG_RETURN(mysqlnd_fetch_row_buffered_c(result TSRMLS_CC)); - } else if (result->m.fetch_row == result->m.fetch_row_normal_unbuffered) { - DBG_RETURN(mysqlnd_fetch_row_unbuffered_c(result TSRMLS_CC)); - } else { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "result->m.fetch_row has invalid value. Report to the developers"); - } + if (result->stored_data && result->stored_data->m.fetch_row == MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)) { + MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(result, (void *) &ret, 0, &fetched_anything TSRMLS_CC); + } else if (result->unbuf && result->unbuf->m.fetch_row == MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)) { + MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(result, (void *) &ret, 0, &fetched_anything TSRMLS_CC); + } else { + ret = NULL; + php_error_docref(NULL TSRMLS_CC, E_ERROR, "result->m.fetch_row has invalid value. Report to the developers"); } DBG_RETURN(ret); } @@ -1575,11 +1640,6 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES * result, unsigned int DBG_ENTER("mysqlnd_res::fetch_field_data"); DBG_INF_FMT("offset=%u", offset); - - if (!result->m.fetch_row) { - RETVAL_NULL(); - DBG_VOID_RETURN; - } /* Hint Zend how many elements we will have in the hash. Thus it won't extend and rehash the hash constantly. @@ -1610,9 +1670,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES * result, unsigned int MYSQLND_CLASS_METHODS_START(mysqlnd_res) - NULL, /* fetch_row */ - mysqlnd_fetch_row_buffered, - mysqlnd_fetch_row_unbuffered, + MYSQLND_METHOD(mysqlnd_res, fetch_row), MYSQLND_METHOD(mysqlnd_res, use_result), MYSQLND_METHOD(mysqlnd_res, store_result), MYSQLND_METHOD(mysqlnd_res, fetch_into), @@ -1629,19 +1687,34 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_res) MYSQLND_METHOD(mysqlnd_res, fetch_field_direct), MYSQLND_METHOD(mysqlnd_res, fetch_fields), MYSQLND_METHOD(mysqlnd_res, read_result_metadata), - NULL, /* fetch_lengths */ + MYSQLND_METHOD(mysqlnd_res, fetch_lengths), MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data), - MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest), MYSQLND_METHOD(mysqlnd_res, free_result_buffers), MYSQLND_METHOD(mysqlnd_res, free_result), + MYSQLND_METHOD(mysqlnd_res, free_result_internal), + MYSQLND_METHOD(mysqlnd_res, free_result_contents_internal), + mysqlnd_result_meta_init +MYSQLND_CLASS_METHODS_END; - mysqlnd_internal_free_result, /* free_result_internal */ - mysqlnd_internal_free_result_contents, /* free_result_contents */ - MYSQLND_METHOD(mysqlnd_res, free_buffered_data), - MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data), - NULL /* row_decoder */, - mysqlnd_result_meta_init +MYSQLND_CLASS_METHODS_START(mysqlnd_result_unbuffered) + MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row), + NULL, /* row_decoder */ + MYSQLND_METHOD(mysqlnd_result_unbuffered, num_rows), + MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_lengths), + MYSQLND_METHOD(mysqlnd_result_unbuffered, free_last_data), + MYSQLND_METHOD(mysqlnd_result_unbuffered, free_result) +MYSQLND_CLASS_METHODS_END; + + +MYSQLND_CLASS_METHODS_START(mysqlnd_result_buffered) + MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row), + NULL, /* row_decoder */ + MYSQLND_METHOD(mysqlnd_result_buffered, num_rows), + MYSQLND_METHOD(mysqlnd_result_buffered, fetch_lengths), + MYSQLND_METHOD(mysqlnd_result_buffered, data_seek), + MYSQLND_METHOD(mysqlnd_result_buffered, initialize_result_set_rest), + MYSQLND_METHOD(mysqlnd_result_buffered, free_result) MYSQLND_CLASS_METHODS_END; @@ -1650,7 +1723,7 @@ PHPAPI MYSQLND_RES * mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC) { size_t alloc_size = sizeof(MYSQLND_RES) + mysqlnd_plugin_count() * sizeof(void *); - MYSQLND_RES *ret = mnd_pecalloc(1, alloc_size, persistent); + MYSQLND_RES * ret = mnd_pecalloc(1, alloc_size, persistent); DBG_ENTER("mysqlnd_result_init"); @@ -1667,6 +1740,86 @@ mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC) /* }}} */ +/* {{{ mysqlnd_result_unbuffered_init */ +PHPAPI MYSQLND_RES_UNBUFFERED * +mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC) +{ + size_t alloc_size = sizeof(MYSQLND_RES_UNBUFFERED) + mysqlnd_plugin_count() * sizeof(void *); + MYSQLND_RES_UNBUFFERED * ret = mnd_pecalloc(1, alloc_size, persistent); + + DBG_ENTER("mysqlnd_result_unbuffered_init"); + + if (!ret) { + DBG_RETURN(NULL); + } + + if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(unsigned long), persistent))) { + mnd_pefree(ret, persistent); + DBG_RETURN(NULL); + } + if (!(ret->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC))) { + mnd_efree(ret->lengths); + mnd_pefree(ret, persistent); + DBG_RETURN(NULL); + } + + ret->persistent = persistent; + ret->field_count= field_count; + ret->ps = ps; + + ret->m = *mysqlnd_result_unbuffered_get_methods(); + + if (ps) { + ret->m.fetch_lengths = NULL; /* makes no sense */ + ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; + } else { + ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol; + } + + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_result_buffered_init */ +PHPAPI MYSQLND_RES_BUFFERED * +mysqlnd_result_buffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC) +{ + size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED) + mysqlnd_plugin_count() * sizeof(void *); + MYSQLND_RES_BUFFERED * ret = mnd_pecalloc(1, alloc_size, persistent); + + DBG_ENTER("mysqlnd_result_buffered_init"); + + if (!ret) { + DBG_RETURN(NULL); + } + if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(unsigned long), persistent))) { + mnd_pefree(ret, persistent); + DBG_RETURN(NULL); + } + if (!(ret->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC))) { + mnd_efree(ret->lengths); + mnd_pefree(ret, persistent); + DBG_RETURN(NULL); + } + + ret->persistent = persistent; + ret->field_count= field_count; + ret->ps = ps; + ret->m = *mysqlnd_result_buffered_get_methods(); + + if (ps) { + ret->m.fetch_lengths = NULL; /* makes no sense */ + ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; + } else { + ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol; + } + + DBG_RETURN(ret); +} +/* }}} */ + + /* * Local variables: * tab-width: 4 diff --git a/ext/mysqlnd/mysqlnd_result.h b/ext/mysqlnd/mysqlnd_result.h index 54f3b03d7d..0a54ea0454 100644 --- a/ext/mysqlnd/mysqlnd_result.h +++ b/ext/mysqlnd/mysqlnd_result.h @@ -24,6 +24,8 @@ #define MYSQLND_RESULT_H PHPAPI MYSQLND_RES * mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC); +PHPAPI MYSQLND_RES_UNBUFFERED * mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC); +PHPAPI MYSQLND_RES_BUFFERED * mysqlnd_result_buffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC); enum_func_status mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * stmt TSRMLS_DC); diff --git a/ext/mysqlnd/mysqlnd_result_meta.c b/ext/mysqlnd/mysqlnd_result_meta.c index 6523a431ca..85dc3d00c4 100644 --- a/ext/mysqlnd/mysqlnd_result_meta.c +++ b/ext/mysqlnd/mysqlnd_result_meta.c @@ -341,7 +341,7 @@ MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta /* {{{ mysqlnd_res_meta::fetch_field_direct */ static const MYSQLND_FIELD * -MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, const MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC) { DBG_ENTER("mysqlnd_res_meta::fetch_field_direct"); DBG_INF_FMT("fieldnr=%u", fieldnr); @@ -371,6 +371,17 @@ MYSQLND_METHOD(mysqlnd_res_meta, field_tell)(const MYSQLND_RES_METADATA * const } /* }}} */ +/* {{{ mysqlnd_res_meta::field_seek */ +static MYSQLND_FIELD_OFFSET +MYSQLND_METHOD(mysqlnd_res_meta, field_seek)(MYSQLND_RES_METADATA * const meta, const MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC) +{ + MYSQLND_FIELD_OFFSET return_value = 0; + DBG_ENTER("mysqlnd_res_meta::fetch_fields"); + return_value = meta->current_field; + meta->current_field = field_offset; + DBG_RETURN(return_value); +} +/* }}} */ static MYSQLND_CLASS_METHODS_START(mysqlnd_res_meta) @@ -378,6 +389,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_res_meta) MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct), MYSQLND_METHOD(mysqlnd_res_meta, fetch_fields), MYSQLND_METHOD(mysqlnd_res_meta, field_tell), + MYSQLND_METHOD(mysqlnd_res_meta, field_seek), MYSQLND_METHOD(mysqlnd_res_meta, read_metadata), MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata), MYSQLND_METHOD(mysqlnd_res_meta, free), diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h index a9048b2b13..5c035a5934 100644 --- a/ext/mysqlnd/mysqlnd_structs.h +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -243,11 +243,12 @@ typedef struct st_mysqlnd_debug MYSQLND_DEBUG; typedef MYSQLND_RES* (*mysqlnd_stmt_use_or_store_func)(MYSQLND_STMT * const TSRMLS_DC); typedef enum_func_status (*mysqlnd_fetch_row_func)(MYSQLND_RES *result, - void *param, + void * param, unsigned int flags, - zend_bool *fetched_anything + zend_bool * fetched_anything TSRMLS_DC); + typedef struct st_mysqlnd_stats MYSQLND_STATS; typedef void (*mysqlnd_stat_trigger)(MYSQLND_STATS * stats, enum_mysqlnd_collected_stats stat, int64_t change TSRMLS_DC); @@ -606,10 +607,11 @@ struct st_mysqlnd_conn_methods func_mysqlnd_conn__close close; }; + /* for decoding - binary or text protocol */ +typedef enum_func_status (*func_mysqlnd_res__row_decoder)(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, + unsigned int field_count, const MYSQLND_FIELD * fields_metadata, + zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC); -typedef mysqlnd_fetch_row_func fetch_row; -typedef mysqlnd_fetch_row_func fetch_row_normal_buffered; /* private */ -typedef mysqlnd_fetch_row_func fetch_row_normal_unbuffered; /* private */ typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC); typedef MYSQLND_RES * (*func_mysqlnd_res__store_result)(MYSQLND_RES * result, MYSQLND_CONN_DATA * const conn, zend_bool ps TSRMLS_DC); @@ -620,17 +622,16 @@ typedef void (*func_mysqlnd_res__fetch_field_data)(MYSQLND_RES *result, unsi typedef uint64_t (*func_mysqlnd_res__num_rows)(const MYSQLND_RES * const result TSRMLS_DC); typedef unsigned int (*func_mysqlnd_res__num_fields)(const MYSQLND_RES * const result TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_res__skip_result)(MYSQLND_RES * const result TSRMLS_DC); -typedef enum_func_status (*func_mysqlnd_res__seek_data)(MYSQLND_RES * result, uint64_t row TSRMLS_DC); -typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res__seek_field)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_res__seek_data)(MYSQLND_RES * const result, const uint64_t row TSRMLS_DC); +typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res__seek_field)(MYSQLND_RES * const result, const MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC); typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res__field_tell)(const MYSQLND_RES * const result TSRMLS_DC); typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_field)(MYSQLND_RES * const result TSRMLS_DC); -typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_field_direct)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC); +typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_field_direct)(MYSQLND_RES * const result, const MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC); typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_fields)(MYSQLND_RES * const result TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_res__read_result_metadata)(MYSQLND_RES * result, MYSQLND_CONN_DATA * 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_CONN_DATA * const conn, MYSQLND_RES * result, MYSQLND_RES_METADATA *meta, zend_bool binary_protocol 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); @@ -639,18 +640,12 @@ typedef void (*func_mysqlnd_res__free_result_contents)(MYSQLND_RES *result TS typedef void (*func_mysqlnd_res__free_buffered_data)(MYSQLND_RES *result TSRMLS_DC); typedef void (*func_mysqlnd_res__unbuffered_free_last_data)(MYSQLND_RES *result TSRMLS_DC); - /* for decoding - binary or text protocol */ -typedef enum_func_status (*func_mysqlnd_res__row_decoder)(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, - unsigned int field_count, const MYSQLND_FIELD * fields_metadata, - zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC); typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res__result_meta_init)(unsigned int field_count, zend_bool persistent TSRMLS_DC); struct st_mysqlnd_res_methods { mysqlnd_fetch_row_func fetch_row; - mysqlnd_fetch_row_func fetch_row_normal_buffered; /* private */ - mysqlnd_fetch_row_func fetch_row_normal_unbuffered; /* private */ func_mysqlnd_res__use_result use_result; func_mysqlnd_res__store_result store_result; @@ -670,16 +665,10 @@ struct st_mysqlnd_res_methods func_mysqlnd_res__read_result_metadata read_result_metadata; func_mysqlnd_res__fetch_lengths fetch_lengths; func_mysqlnd_res__store_result_fetch_data store_result_fetch_data; - func_mysqlnd_res__initialize_result_set_rest initialize_result_set_rest; func_mysqlnd_res__free_result_buffers free_result_buffers; func_mysqlnd_res__free_result free_result; func_mysqlnd_res__free_result_internal free_result_internal; func_mysqlnd_res__free_result_contents free_result_contents; - func_mysqlnd_res__free_buffered_data free_buffered_data; - func_mysqlnd_res__unbuffered_free_last_data unbuffered_free_last_data; - - /* for decoding - binary or text protocol */ - func_mysqlnd_res__row_decoder row_decoder; func_mysqlnd_res__result_meta_init result_meta_init; @@ -691,10 +680,45 @@ struct st_mysqlnd_res_methods }; +typedef uint64_t (*func_mysqlnd_result_unbuffered__num_rows)(const MYSQLND_RES_UNBUFFERED * const result TSRMLS_DC); +typedef unsigned long * (*func_mysqlnd_result_unbuffered__fetch_lengths)(MYSQLND_RES_UNBUFFERED * const result TSRMLS_DC); +typedef void (*func_mysqlnd_result_unbuffered__free_last_data)(MYSQLND_RES_UNBUFFERED * result, MYSQLND_STATS * const global_stats TSRMLS_DC); +typedef void (*func_mysqlnd_result_unbuffered__free_result)(MYSQLND_RES_UNBUFFERED * const result, MYSQLND_STATS * const global_stats TSRMLS_DC); + +struct st_mysqlnd_result_unbuffered_methods +{ + mysqlnd_fetch_row_func fetch_row; + func_mysqlnd_res__row_decoder row_decoder; + func_mysqlnd_result_unbuffered__num_rows num_rows; + func_mysqlnd_result_unbuffered__fetch_lengths fetch_lengths; + func_mysqlnd_result_unbuffered__free_last_data free_last_data; + func_mysqlnd_result_unbuffered__free_result free_result; +}; + +typedef uint64_t (*func_mysqlnd_result_buffered__num_rows)(const MYSQLND_RES_BUFFERED * const result TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_result_buffered__initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta, + MYSQLND_STATS * stats, zend_bool int_and_float_native TSRMLS_DC); +typedef unsigned long * (*func_mysqlnd_result_buffered__fetch_lengths)(MYSQLND_RES_BUFFERED * const result TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_result_buffered__data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row TSRMLS_DC); +typedef void (*func_mysqlnd_result_buffered__free_result)(MYSQLND_RES_BUFFERED * const result TSRMLS_DC); + +struct st_mysqlnd_result_buffered_methods +{ + mysqlnd_fetch_row_func fetch_row; + func_mysqlnd_res__row_decoder row_decoder; + func_mysqlnd_result_buffered__num_rows num_rows; + func_mysqlnd_result_buffered__fetch_lengths fetch_lengths; + func_mysqlnd_result_buffered__data_seek data_seek; + func_mysqlnd_result_buffered__initialize_result_set_rest initialize_result_set_rest; + func_mysqlnd_result_buffered__free_result free_result; +}; + + typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC); -typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC); +typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, const MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC); typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_fields)(MYSQLND_RES_METADATA * const meta TSRMLS_DC); typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res_meta__field_tell)(const MYSQLND_RES_METADATA * const meta TSRMLS_DC); +typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res_meta__field_seek)(MYSQLND_RES_METADATA * const meta, const MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_res_meta__read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND_CONN_DATA * conn TSRMLS_DC); typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res_meta__clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent TSRMLS_DC); typedef void (*func_mysqlnd_res_meta__free_metadata)(MYSQLND_RES_METADATA * meta TSRMLS_DC); @@ -705,6 +729,7 @@ struct st_mysqlnd_res_meta_methods func_mysqlnd_res_meta__fetch_field_direct fetch_field_direct; func_mysqlnd_res_meta__fetch_fields fetch_fields; func_mysqlnd_res_meta__field_tell field_tell; + func_mysqlnd_res_meta__field_seek field_seek; func_mysqlnd_res_meta__read_metadata read_metadata; func_mysqlnd_res_meta__clone_metadata clone_metadata; func_mysqlnd_res_meta__free_metadata free_metadata; @@ -962,7 +987,7 @@ struct st_mysqlnd_result_metadata size_t bit_fields_total_len; /* trailing \0 not counted */ zend_bool persistent; - struct st_mysqlnd_res_meta_methods *m; + struct st_mysqlnd_res_meta_methods * m; }; @@ -974,20 +999,51 @@ struct st_mysqlnd_buffered_result uint64_t row_count; uint64_t initialized_rows; + /* + Column lengths of current row - both buffered and unbuffered. + For buffered results it duplicates the data found in **data + */ + unsigned long *lengths; + + MYSQLND_MEMORY_POOL *result_set_memory_pool; + unsigned int references; MYSQLND_ERROR_INFO error_info; + + unsigned int field_count; + zend_bool ps; + zend_bool persistent; + + struct st_mysqlnd_result_buffered_methods m; }; struct st_mysqlnd_unbuffered_result { + /* For unbuffered (both normal and PS) */ zval **last_row_data; MYSQLND_MEMORY_POOL_CHUNK *last_row_buffer; + /* + Column lengths of current row - both buffered and unbuffered. + For buffered results it duplicates the data found in **data + */ + unsigned long *lengths; + + MYSQLND_MEMORY_POOL *result_set_memory_pool; + + struct st_mysqlnd_packet_row * row_packet; + uint64_t row_count; zend_bool eof_reached; + + unsigned int field_count; + zend_bool ps; + zend_bool persistent; + + struct st_mysqlnd_result_unbuffered_methods m; }; @@ -1001,18 +1057,9 @@ struct st_mysqlnd_res MYSQLND_RES_METADATA *meta; /* To be used with store_result() - both normal and PS */ - MYSQLND_RES_BUFFERED *stored_data; - MYSQLND_RES_UNBUFFERED *unbuf; - - /* - Column lengths of current row - both buffered and unbuffered. - For buffered results it duplicates the data found in **data - */ - unsigned long *lengths; - - struct st_mysqlnd_packet_row * row_packet; + MYSQLND_RES_BUFFERED *stored_data; + MYSQLND_RES_UNBUFFERED *unbuf; - MYSQLND_MEMORY_POOL * result_set_memory_pool; zend_bool persistent; struct st_mysqlnd_res_methods m; -- 2.40.0