summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-12-14 15:05:23 +0100
committerNikita Popov <nikita.ppv@gmail.com>2020-12-17 10:15:02 +0100
commit33e904915ec9f19b697589d7b73c908953671020 (patch)
treeb521886510aa192e72f3aaf1cde0b70ed7994a50
parent890e4caf0be2bf625614e549f312ac18395651a0 (diff)
downloadphp-git-33e904915ec9f19b697589d7b73c908953671020.tar.gz
MySQLnd: Clean up and optimize mysqlnd result set handling
This is a larger overhaul of the mysqlnd result set infrastructure: * Drop support for two different types of buffered results sets ("c" and "zval"). Possibly these made sense at some earlier time, but now (with minor adjustments) one option is strictly worse than the other. Buffered result sets already buffer the full row packets, from which zvals can be decoded. The "zval" style additionally also buffered the decoded zvals. As result sets, even buffered ones, are generally only traversed once, this just ends up wasting memory. Now, a potentially useful variation here would be to buffer the decoded zvals instead of the row packets, but that's not what the code was doing. * To make it really strictly better, pre-allocate the zval row buffer and reuse it for all rows. Previously the "c" style always allocated a new buffer for each row. * The fetch_row API now provides a populated zval[]. The task of populating an array is deferred to fetch_row_into, which also avoids duplicating this code in multiple places. The fetch_row_c API is also implemented on top of fetch_row now, rather than duplicating large parts of the code. * The row fetching code for prepared statements and normal result sets has been mostly merged. These already used the same infrastructure, but prepared statements used separate row fetching functions that were nearly the same as the normal ones. This requires passing the stmt into the result set, rather than just a flag. The only part that remains separate is reading of unbuffered results in the presence of PS cursors.
-rw-r--r--UPGRADING5
-rw-r--r--ext/mysqli/mysqli_api.c6
-rw-r--r--ext/mysqli/mysqli_nonapi.c11
-rw-r--r--ext/mysqli/mysqli_warning.c2
-rw-r--r--ext/mysqlnd/mysqlnd.h6
-rw-r--r--ext/mysqlnd/mysqlnd_connection.c29
-rw-r--r--ext/mysqlnd/mysqlnd_enum_n_def.h12
-rw-r--r--ext/mysqlnd/mysqlnd_ext_plugin.c21
-rw-r--r--ext/mysqlnd/mysqlnd_ext_plugin.h6
-rw-r--r--ext/mysqlnd/mysqlnd_ps.c287
-rw-r--r--ext/mysqlnd/mysqlnd_ps.h4
-rw-r--r--ext/mysqlnd/mysqlnd_result.c821
-rw-r--r--ext/mysqlnd/mysqlnd_result.h5
-rw-r--r--ext/mysqlnd/mysqlnd_structs.h73
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.c34
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.h6
-rw-r--r--ext/mysqlnd/php_mysqlnd.c2
17 files changed, 257 insertions, 1073 deletions
diff --git a/UPGRADING b/UPGRADING
index 7f43588048..7f530497e7 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -25,6 +25,11 @@ PHP 8.1 UPGRADE NOTES
result set and taking the maximum length. This is what PHP was doing
internally previously.
. The MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH option no longer has an effect.
+ . The MYSQLI_STORE_RESULT_COPY_DATA option no longer has an effect.
+
+- MySQLnd:
+ . The mysqlnd.fetch_copy_data ini setting has been removed. However, this
+ should not result in user-visible behavior changes.
- Standard:
. version_compare() no longer accepts undocumented operator abbreviations.
diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c
index 405618896a..0fc7017960 100644
--- a/ext/mysqli/mysqli_api.c
+++ b/ext/mysqli/mysqli_api.c
@@ -1459,7 +1459,7 @@ void php_mysqli_init(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_method)
We create always persistent, as if the user want to connect
to p:somehost, we can't convert the handle then
*/
- if (!(mysql->mysql = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, TRUE)))
+ if (!(mysql->mysql = mysqlnd_init(MYSQLND_CLIENT_NO_FLAG, TRUE)))
#endif
{
efree(mysql);
@@ -2527,11 +2527,7 @@ PHP_FUNCTION(mysqli_store_result)
RETURN_THROWS();
}
MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
-#ifdef MYSQLI_USE_MYSQLND
- result = flags & MYSQLI_STORE_RESULT_COPY_DATA? mysqlnd_store_result_ofs(mysql->mysql) : mysqlnd_store_result(mysql->mysql);
-#else
result = mysql_store_result(mysql->mysql);
-#endif
if (!result) {
MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
RETURN_FALSE;
diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c
index 364638c44a..6ea09b1fc2 100644
--- a/ext/mysqli/mysqli_nonapi.c
+++ b/ext/mysqli/mysqli_nonapi.c
@@ -245,7 +245,7 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_conne
#ifndef MYSQLI_USE_MYSQLND
if (!(mysql->mysql = mysql_init(NULL))) {
#else
- if (!(mysql->mysql = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, persistent))) {
+ if (!(mysql->mysql = mysqlnd_init(MYSQLND_CLIENT_NO_FLAG, persistent))) {
#endif
goto err;
}
@@ -307,7 +307,7 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_conne
}
}
if (mysqlnd_connect(mysql->mysql, hostname, username, passwd, passwd_len, dbname, dbname_len,
- port, socket, flags, MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA) == NULL)
+ port, socket, flags, MYSQLND_CLIENT_NO_FLAG) == NULL)
#endif
{
/* Save error messages - for mysqli_connect_error() & mysqli_connect_errno() */
@@ -689,12 +689,7 @@ PHP_FUNCTION(mysqli_query)
switch (resultmode & ~MYSQLI_ASYNC) {
#endif
case MYSQLI_STORE_RESULT:
-#ifdef MYSQLI_USE_MYSQLND
- if (resultmode & MYSQLI_STORE_RESULT_COPY_DATA) {
- result = mysqlnd_store_result_ofs(mysql->mysql);
- } else
-#endif
- result = mysql_store_result(mysql->mysql);
+ result = mysql_store_result(mysql->mysql);
break;
case MYSQLI_USE_RESULT:
result = mysql_use_result(mysql->mysql);
diff --git a/ext/mysqli/mysqli_warning.c b/ext/mysqli/mysqli_warning.c
index 6dd7090911..30af6cb960 100644
--- a/ext/mysqli/mysqli_warning.c
+++ b/ext/mysqli/mysqli_warning.c
@@ -125,7 +125,7 @@ MYSQLI_WARNING * php_get_warnings(MYSQLND_CONN_DATA * mysql)
return NULL;
}
- result = mysql->m->use_result(mysql, 0);
+ result = mysql->m->use_result(mysql);
for (;;) {
zval *entry;
diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h
index b4d3d1f8c3..582d8f152b 100644
--- a/ext/mysqlnd/mysqlnd.h
+++ b/ext/mysqlnd/mysqlnd.h
@@ -112,9 +112,8 @@ PHPAPI void mysqlnd_debug(const char *mode);
PHPAPI enum_func_status mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num);
-#define mysqlnd_use_result(conn) ((conn)->data)->m->use_result((conn)->data, 0)
-#define mysqlnd_store_result(conn) ((conn)->data)->m->store_result((conn)->data, MYSQLND_STORE_NO_COPY)
-#define mysqlnd_store_result_ofs(conn) ((conn)->data)->m->store_result((conn)->data, MYSQLND_STORE_COPY)
+#define mysqlnd_use_result(conn) ((conn)->data)->m->use_result((conn)->data)
+#define mysqlnd_store_result(conn) ((conn)->data)->m->store_result((conn)->data)
#define mysqlnd_next_result(conn) ((conn)->data)->m->next_result((conn)->data)
#define mysqlnd_more_results(conn) ((conn)->data)->m->more_results((conn)->data)
#define mysqlnd_free_result(r,e_or_i) ((MYSQLND_RES*)r)->m.free_result(((MYSQLND_RES*)(r)), (e_or_i))
@@ -311,7 +310,6 @@ ZEND_BEGIN_MODULE_GLOBALS(mysqlnd)
zend_long debug_calloc_fail_threshold;
zend_long debug_realloc_fail_threshold;
char * sha256_server_public_key;
- zend_bool fetch_data_copy;
zend_bool collect_statistics;
zend_bool collect_memory_statistics;
ZEND_END_MODULE_GLOBALS(mysqlnd)
diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c
index 21eb7e703b..4d25488ee6 100644
--- a/ext/mysqlnd/mysqlnd_connection.c
+++ b/ext/mysqlnd/mysqlnd_connection.c
@@ -456,7 +456,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands)(MYSQLND_CONN_DATA * con
}
do {
if (conn->last_query_type == QUERY_SELECT) {
- MYSQLND_RES * result = conn->m->use_result(conn, 0);
+ MYSQLND_RES * result = conn->m->use_result(conn);
if (result) {
result->m.free_result(result, TRUE);
}
@@ -928,7 +928,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const c
}
if (PASS == conn->m->query(conn, show_query, show_query_len)) {
- result = conn->m->store_result(conn, MYSQLND_STORE_NO_COPY);
+ result = conn->m->store_result(conn);
}
if (show_query != query) {
mnd_sprintf_free(show_query);
@@ -1810,7 +1810,7 @@ end:
/* {{{ mysqlnd_conn_data::use_result */
static MYSQLND_RES *
-MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
+MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn)
{
const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), use_result);
MYSQLND_RES * result = NULL;
@@ -1852,7 +1852,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, co
/* {{{ mysqlnd_conn_data::store_result */
static MYSQLND_RES *
-MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
+MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn)
{
const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), store_result);
MYSQLND_RES * result = NULL;
@@ -1862,7 +1862,6 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn,
if (PASS == conn->m->local_tx_start(conn, this_func)) {
do {
- unsigned int f = flags;
if (!conn->current_result) {
break;
}
@@ -1875,25 +1874,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn,
}
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
-
- /* overwrite */
- if ((conn->m->get_client_api_capabilities(conn) & MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA)) {
- if (MYSQLND_G(fetch_data_copy)) {
- f &= ~MYSQLND_STORE_NO_COPY;
- f |= MYSQLND_STORE_COPY;
- }
- } else {
- /* if for some reason PDO borks something */
- if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
- f |= MYSQLND_STORE_COPY;
- }
- }
- if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
- SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Unknown fetch mode");
- DBG_ERR("Unknown fetch mode");
- break;
- }
- result = conn->current_result->m.store_result(conn->current_result, conn, f);
+ result = conn->current_result->m.store_result(conn->current_result, conn, NULL);
if (!result) {
conn->current_result->m.free_result(conn->current_result, TRUE);
}
diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h
index 191b8388b3..6d6cefbb46 100644
--- a/ext/mysqlnd/mysqlnd_enum_n_def.h
+++ b/ext/mysqlnd/mysqlnd_enum_n_def.h
@@ -686,18 +686,6 @@ enum php_mysqlnd_server_command
#define MYSQLND_REFRESH_BACKUP_LOG 0x200000L
-#define MYSQLND_STORE_PS 1
-#define MYSQLND_STORE_NO_COPY 2
-#define MYSQLND_STORE_COPY 4
-
-enum mysqlnd_buffered_type
-{
- MYSQLND_BUFFERED_TYPE_ZVAL = 1,
- MYSQLND_BUFFERED_TYPE_C
-};
-
-
#define MYSQLND_CLIENT_NO_FLAG 0
-#define MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA 1
#endif /* MYSQLND_ENUM_N_DEF_H */
diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.c b/ext/mysqlnd/mysqlnd_ext_plugin.c
index 58a4ee767e..cf28b81ea5 100644
--- a/ext/mysqlnd/mysqlnd_ext_plugin.c
+++ b/ext/mysqlnd/mysqlnd_ext_plugin.c
@@ -83,30 +83,16 @@ mysqlnd_plugin__get_plugin_result_unbuffered_data(const MYSQLND_RES_UNBUFFERED *
}
/* }}} */
-
-/* {{{ _mysqlnd_plugin__get_plugin_result_buffered_data */
-static void **
-mysqlnd_plugin__get_plugin_result_buffered_data_zval(const MYSQLND_RES_BUFFERED_ZVAL * result, const unsigned int plugin_id)
-{
- 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_ZVAL) + plugin_id * sizeof(void *)));
-}
-/* }}} */
-
/* {{{ mysqlnd_plugin__get_plugin_result_buffered_data */
static void **
-mysqlnd_plugin__get_plugin_result_buffered_data_c(const MYSQLND_RES_BUFFERED_C * result, const unsigned int plugin_id)
+mysqlnd_plugin__get_plugin_result_buffered_data(const MYSQLND_RES_BUFFERED * result, const unsigned int plugin_id)
{
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_C) + plugin_id * sizeof(void *)));
+ DBG_RETURN((void *)((char *)result + sizeof(MYSQLND_RES_BUFFERED) + plugin_id * sizeof(void *)));
}
/* }}} */
@@ -172,8 +158,7 @@ struct st_mysqlnd_plugin__plugin_area_getters mysqlnd_plugin_area_getters =
mysqlnd_plugin__get_plugin_connection_data_data,
mysqlnd_plugin__get_plugin_result_data,
mysqlnd_plugin__get_plugin_result_unbuffered_data,
- mysqlnd_plugin__get_plugin_result_buffered_data_zval,
- mysqlnd_plugin__get_plugin_result_buffered_data_c,
+ mysqlnd_plugin__get_plugin_result_buffered_data,
mysqlnd_plugin__get_plugin_stmt_data,
mysqlnd_plugin__get_plugin_protocol_data,
mysqlnd_plugin__get_plugin_pfc_data,
diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.h b/ext/mysqlnd/mysqlnd_ext_plugin.h
index 713eb1f44f..027fda976a 100644
--- a/ext/mysqlnd/mysqlnd_ext_plugin.h
+++ b/ext/mysqlnd/mysqlnd_ext_plugin.h
@@ -25,8 +25,7 @@ struct st_mysqlnd_plugin__plugin_area_getters
void ** (*get_connection_data_area)(const MYSQLND_CONN_DATA * conn, const unsigned int plugin_id);
void ** (*get_result_area)(const MYSQLND_RES * result, const unsigned int plugin_id);
void ** (*get_unbuffered_area)(const MYSQLND_RES_UNBUFFERED * result, const unsigned int plugin_id);
- void ** (*get_result_buffered_area)(const MYSQLND_RES_BUFFERED_ZVAL * result, const unsigned int plugin_id);
- void ** (*get_result_buffered_aread_c)(const MYSQLND_RES_BUFFERED_C * result, const unsigned int plugin_id);
+ void ** (*get_result_buffered_aread)(const MYSQLND_RES_BUFFERED * result, const unsigned int plugin_id);
void ** (*get_stmt_area)(const MYSQLND_STMT * stmt, const unsigned int plugin_id);
void ** (*get_protocol_decoder_area)(const MYSQLND_PROTOCOL_PAYLOAD_DECODER_FACTORY * factory, const unsigned int plugin_id);
void ** (*get_pfc_area)(const MYSQLND_PFC * pfc, const unsigned int plugin_id);
@@ -39,8 +38,7 @@ PHPAPI extern struct st_mysqlnd_plugin__plugin_area_getters mysqlnd_plugin_area_
#define mysqlnd_plugin_get_plugin_connection_data_data(c, p_id) mysqlnd_plugin_area_getters.get_connection_data_area((c), (p_id))
#define mysqlnd_plugin_get_plugin_result_data(res, p_id) mysqlnd_plugin_area_getters.get_result_area((res), (p_id))
#define mysqlnd_plugin_get_plugin_result_unbuffered_data(res, p_id) mysqlnd_plugin_area_getters.get_unbuffered_area((res), (p_id))
-#define mysqlnd_plugin_get_plugin_result_buffered_data_zval(res, p_id) mysqlnd_plugin_area_getters.get_result_buffered_area((res), (p_id))
-#define mysqlnd_plugin_get_plugin_result_buffered_data_c(res, p_id) mysqlnd_plugin_area_getters.get_result_buffered_aread_c((res), (p_id))
+#define mysqlnd_plugin_get_plugin_result_buffered_data_c(res, p_id) mysqlnd_plugin_area_getters.get_result_buffered_aread((res), (p_id))
#define mysqlnd_plugin_get_plugin_stmt_data(stmt, p_id) mysqlnd_plugin_area_getters.get_stmt_area((stmt), (p_id))
#define mysqlnd_plugin_get_plugin_protocol_data(proto, p_id) mysqlnd_plugin_area_getters.get_protocol_decoder_area((proto), (p_id))
#define mysqlnd_plugin_get_plugin_pfc_data(pfc, p_id) mysqlnd_plugin_area_getters.get_pfc_area((pfc), (p_id))
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c
index ccafa26675..7811f51832 100644
--- a/ext/mysqlnd/mysqlnd_ps.c
+++ b/ext/mysqlnd/mysqlnd_ps.c
@@ -79,7 +79,7 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
result->type = MYSQLND_RES_PS_BUF;
/* result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; */
- result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result, result->field_count, TRUE);
+ result->stored_data = mysqlnd_result_buffered_init(result, result->field_count, stmt);
if (!result->stored_data) {
SET_OOM_ERROR(conn->error_info);
DBG_RETURN(NULL);
@@ -87,30 +87,8 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s)
ret = result->m.store_result_fetch_data(conn, result, result->meta, &result->stored_data->row_buffers, TRUE);
- result->stored_data->m.fetch_row = mysqlnd_stmt_fetch_row_buffered;
-
if (PASS == ret) {
- if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
- MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
- if (result->stored_data->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 (result->stored_data->row_count * result->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)(result->stored_data->row_count * result->meta->field_count * sizeof(zval)));
- if (!set->data) {
- SET_OOM_ERROR(conn->error_info);
- DBG_RETURN(NULL);
- }
- memset(set->data, 0, (size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval)));
- }
- /* Position at the first row */
- set->data_cursor = set->data;
- } else if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_C) {
- /*TODO*/
- }
+ result->stored_data->current_row = 0;
/* libmysql API docs say it should be so for SELECT statements */
UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, stmt->result->stored_data->row_count);
@@ -183,7 +161,7 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s)
break;
}
- if (result->m.store_result(result, conn, MYSQLND_STORE_PS | MYSQLND_STORE_NO_COPY)) {
+ if (result->m.store_result(result, conn, stmt)) {
UPSERT_STATUS_SET_AFFECTED_ROWS(stmt->upsert_status, result->stored_data->row_count);
stmt->state = MYSQLND_STMT_PREPARED;
result->type = MYSQLND_RES_PS_BUF;
@@ -563,11 +541,6 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e
}
stmt->field_count = stmt->result->field_count = conn->field_count;
- 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;
/*
@@ -731,191 +704,6 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, const enum_my
/* }}} */
-/* {{{ mysqlnd_stmt_fetch_row_buffered */
-enum_func_status
-mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
-{
- MYSQLND_STMT * s = (MYSQLND_STMT *) param;
- MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
- 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;
- DBG_INF_FMT("stmt=%lu", stmt != NULL ? stmt->stmt_id : 0L);
-
- /* If we haven't read everything */
- if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
- MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
- if (set->data_cursor &&
- (set->data_cursor - set->data) < (result->stored_data->row_count * field_count))
- {
- /* The user could have skipped binding - don't crash*/
- if (stmt->result_bind) {
- unsigned int i;
- zval *current_row = set->data_cursor;
-
- if (Z_ISUNDEF(current_row[0])) {
- uint64_t row_num = (set->data_cursor - set->data) / field_count;
- enum_func_status rc = result->stored_data->m.row_decoder(&result->stored_data->row_buffers[row_num],
- current_row,
- meta->field_count,
- meta->fields,
- result->conn->options->int_and_float_native,
- result->conn->stats);
- if (PASS != rc) {
- DBG_RETURN(FAIL);
- }
- }
-
- for (i = 0; i < result->field_count; i++) {
- /* copy the type */
- zval *resultzv = &stmt->result_bind[i].zv;
- if (stmt->result_bind[i].bound == TRUE) {
- DBG_INF_FMT("i=%u type=%u", i, Z_TYPE(current_row[i]));
- ZEND_TRY_ASSIGN_COPY_EX(resultzv, &current_row[i], 0);
- }
- }
- }
- set->data_cursor += field_count;
- *fetched_anything = TRUE;
- /* buffered result sets don't have a connection */
- MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF);
- DBG_INF("row fetched");
- } else {
- set->data_cursor = NULL;
- DBG_INF("no more data");
- }
- } else if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_C) {
- /*TODO*/
- }
- DBG_INF("PASS");
- DBG_RETURN(PASS);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_stmt_fetch_row_unbuffered */
-enum_func_status
-mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
-{
- enum_func_status ret;
- MYSQLND_STMT * s = (MYSQLND_STMT *) param;
- MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
- MYSQLND_PACKET_ROW * row_packet;
- MYSQLND_CONN_DATA * conn = result->conn;
- void *checkpoint;
-
- DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered");
-
- *fetched_anything = FALSE;
-
- if (result->unbuf->eof_reached) {
- /* No more rows obviously */
- DBG_INF("EOF already reached");
- DBG_RETURN(PASS);
- }
- if (GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
- DBG_ERR("command out of sync");
- DBG_RETURN(FAIL);
- }
- if (!(row_packet = result->unbuf->row_packet)) {
- DBG_RETURN(FAIL);
- }
-
- checkpoint = result->memory_pool->checkpoint;
- mysqlnd_mempool_save_state(result->memory_pool);
-
- /*
- If we skip rows (stmt == NULL || stmt->result_bind == NULL) we have to
- result->unbuf->m.free_last_data() before it. The function returns always true.
- */
- if (PASS == (ret = PACKET_READ(conn, row_packet)) && !row_packet->eof) {
- unsigned int i, field_count = result->field_count;
-
- if (stmt && stmt->result_bind) {
- result->unbuf->m.free_last_data(result->unbuf, conn->stats);
-
- result->unbuf->last_row_buffer = row_packet->row_buffer;
- row_packet->row_buffer.ptr = NULL;
-
- 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,
- conn->options->int_and_float_native,
- conn->stats))
- {
- mysqlnd_mempool_restore_state(result->memory_pool);
- result->memory_pool->checkpoint = checkpoint;
- DBG_RETURN(FAIL);
- }
-
- for (i = 0; i < field_count; i++) {
- zval *resultzv = &stmt->result_bind[i].zv;
- if (stmt->result_bind[i].bound == TRUE) {
- zval *data = &result->unbuf->last_row_data[i];
- ZEND_TRY_ASSIGN_VALUE_EX(resultzv, data, 0);
- /* copied data, thus also the ownership. Thus null data */
- ZVAL_NULL(data);
- }
- }
- MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF);
- } else {
- DBG_INF("skipping extraction");
- /*
- 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.
- */
- row_packet->result_set_memory_pool->free_chunk(
- row_packet->result_set_memory_pool, row_packet->row_buffer.ptr);
- row_packet->row_buffer.ptr = NULL;
- }
-
- result->unbuf->row_count++;
- *fetched_anything = TRUE;
- } else if (ret == FAIL) {
- if (row_packet->error_info.error_no) {
- COPY_CLIENT_ERROR(conn->error_info, row_packet->error_info);
- if (stmt) {
- COPY_CLIENT_ERROR(stmt->error_info, row_packet->error_info);
- }
- }
- if (GET_CONNECTION_STATE(&conn->state) != CONN_QUIT_SENT) {
- SET_CONNECTION_STATE(&conn->state, CONN_READY);
- }
- result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
- } else if (row_packet->eof) {
- DBG_INF("EOF");
- /* Mark the connection as usable again */
- result->unbuf->eof_reached = TRUE;
- UPSERT_STATUS_RESET(conn->upsert_status);
- UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
- UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
-
- /*
- result->row_packet will be cleaned when
- destroying the result object
- */
- if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
- SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
- } else {
- SET_CONNECTION_STATE(&conn->state, CONN_READY);
- }
- }
-
- mysqlnd_mempool_restore_state(result->memory_pool);
- result->memory_pool->checkpoint = checkpoint;
-
- DBG_INF_FMT("ret=%s fetched_anything=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
/* {{{ mysqlnd_stmt::use_result */
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s)
@@ -945,9 +733,10 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s)
MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PS_UNBUFFERED_SETS);
result = stmt->result;
- result->m.use_result(stmt->result, TRUE);
- result->unbuf->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor:
- mysqlnd_stmt_fetch_row_unbuffered;
+ result->m.use_result(stmt->result, stmt);
+ if (stmt->cursor_exists) {
+ result->unbuf->m.fetch_row = mysqlnd_fetch_stmt_row_cursor;
+ }
stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
DBG_INF_FMT("%p", result);
@@ -958,12 +747,11 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s)
/* {{{ mysqlnd_fetch_row_cursor */
enum_func_status
-mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
+mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, zval **row_ptr, const unsigned int flags, zend_bool * fetched_anything)
{
enum_func_status ret;
- MYSQLND_STMT * s = (MYSQLND_STMT *) param;
- MYSQLND_STMT_DATA * stmt = s? s->data : NULL;
- MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL;
+ MYSQLND_STMT_DATA * stmt = result->unbuf->stmt;
+ MYSQLND_CONN_DATA * conn = stmt->conn;
zend_uchar buf[MYSQLND_STMT_ID_LENGTH /* statement id */ + 4 /* number of rows to fetch */];
MYSQLND_PACKET_ROW * row_packet;
@@ -1004,16 +792,13 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, const unsigned
UPSERT_STATUS_RESET(stmt->upsert_status);
if (PASS == (ret = PACKET_READ(conn, row_packet)) && !row_packet->eof) {
- unsigned int i, field_count = result->field_count;
-
- if (stmt->result_bind) {
- result->unbuf->m.free_last_data(result->unbuf, conn->stats);
-
+ if (row_ptr) {
result->unbuf->last_row_buffer = row_packet->row_buffer;
row_packet->row_buffer.ptr = NULL;
+ *row_ptr = result->row_data;
if (PASS != result->unbuf->m.row_decoder(&result->unbuf->last_row_buffer,
- result->unbuf->last_row_data,
+ result->row_data,
row_packet->field_count,
row_packet->fields_metadata,
conn->options->int_and_float_native,
@@ -1021,32 +806,8 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, const unsigned
{
DBG_RETURN(FAIL);
}
-
- /* If no result bind, do nothing. We consumed the data */
- for (i = 0; i < field_count; i++) {
- zval *resultzv = &stmt->result_bind[i].zv;
- if (stmt->result_bind[i].bound == TRUE) {
- zval *data = &result->unbuf->last_row_data[i];
-
- DBG_INF_FMT("i=%u bound_var=%p type=%u refc=%u", i, &stmt->result_bind[i].zv,
- Z_TYPE_P(data), Z_REFCOUNTED(stmt->result_bind[i].zv)?
- Z_REFCOUNT(stmt->result_bind[i].zv) : 0);
-
- ZEND_TRY_ASSIGN_VALUE_EX(resultzv, data, 0);
- /* copied data, thus also the ownership. Thus null data */
- ZVAL_NULL(data);
- }
- }
} else {
DBG_INF("skipping extraction");
- /*
- 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.
- */
- row_packet->result_set_memory_pool->free_chunk(
- row_packet->result_set_memory_pool, row_packet->row_buffer.ptr);
row_packet->row_buffer.ptr = NULL;
}
/* We asked for one row, the next one should be EOF, eat it */
@@ -1081,6 +842,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, const unsigned
row_packet->server_status, row_packet->warning_count,
result->unbuf->eof_reached);
DBG_RETURN(ret);
+ return FAIL;
}
/* }}} */
@@ -1112,7 +874,24 @@ MYSQLND_METHOD(mysqlnd_stmt, fetch)(MYSQLND_STMT * const s, zend_bool * const fe
SET_EMPTY_ERROR(stmt->error_info);
SET_EMPTY_ERROR(conn->error_info);
- ret = stmt->result->m.fetch_row(stmt->result, (void*)s, 0, fetched_anything);
+ if (stmt->result_bind) {
+ zval *row_data;
+ ret = stmt->result->m.fetch_row(stmt->result, &row_data, 0, fetched_anything);
+ if (ret == PASS && *fetched_anything) {
+ unsigned field_count = stmt->result->field_count;
+ for (unsigned i = 0; i < field_count; i++) {
+ zval *resultzv = &stmt->result_bind[i].zv;
+ if (stmt->result_bind[i].bound == TRUE) {
+ DBG_INF_FMT("i=%u type=%u", i, Z_TYPE(row_data[i]));
+ ZEND_TRY_ASSIGN_VALUE_EX(resultzv, &row_data[i], 0);
+ } else {
+ zval_ptr_dtor_nogc(&row_data[i]);
+ }
+ }
+ }
+ } else {
+ ret = stmt->result->m.fetch_row(stmt->result, NULL, 0, fetched_anything);
+ }
DBG_RETURN(ret);
}
/* }}} */
@@ -1728,7 +1507,7 @@ MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s)
break;
}
result_meta->type = MYSQLND_RES_NORMAL;
- result_meta->unbuf = mysqlnd_result_unbuffered_init(result_meta, stmt->field_count, TRUE);
+ result_meta->unbuf = mysqlnd_result_unbuffered_init(result_meta, stmt->field_count, stmt);
if (!result_meta->unbuf) {
break;
}
diff --git a/ext/mysqlnd/mysqlnd_ps.h b/ext/mysqlnd/mysqlnd_ps.h
index 0d4f7f8068..9e10fbab77 100644
--- a/ext/mysqlnd/mysqlnd_ps.h
+++ b/ext/mysqlnd/mysqlnd_ps.h
@@ -32,8 +32,8 @@ 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, const unsigned int flags, zend_bool * fetched_anything);
-enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything);
+enum_func_status mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, zval **row_data, const unsigned int flags, zend_bool * fetched_anything);
+enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, zval **row_data, const unsigned int flags, zend_bool * fetched_anything);
void _mysqlnd_init_ps_subsystem();/* This one is private, mysqlnd_library_init() will call it */
void _mysqlnd_init_ps_fetch_subsystem();
diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c
index 05a04296e6..620f205998 100644
--- a/ext/mysqlnd/mysqlnd_result.c
+++ b/ext/mysqlnd/mysqlnd_result.c
@@ -27,40 +27,11 @@
#include "mysqlnd_debug.h"
#include "mysqlnd_ext_plugin.h"
-/* {{{ mysqlnd_result_unbuffered::free_last_data */
-static void
-MYSQLND_METHOD(mysqlnd_result_unbuffered, free_last_data)(MYSQLND_RES_UNBUFFERED * unbuf, MYSQLND_STATS * const global_stats)
-{
- 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_buffer.ptr) {
- DBG_INF("Freeing last row buffer");
- for (unsigned i = 0; i < unbuf->field_count; i++) {
- zval_ptr_dtor_nogc(&unbuf->last_row_data[i]);
- }
-
- /* Nothing points to this buffer now, free it */
- unbuf->result_set_memory_pool->free_chunk(
- unbuf->result_set_memory_pool, unbuf->last_row_buffer.ptr);
- unbuf->last_row_buffer.ptr = NULL;
- }
-
- DBG_VOID_RETURN;
-}
-/* }}} */
-
-
/* {{{ mysqlnd_result_unbuffered::free_result */
static void
MYSQLND_METHOD(mysqlnd_result_unbuffered, free_result)(MYSQLND_RES_UNBUFFERED * const result, MYSQLND_STATS * const global_stats)
{
DBG_ENTER("mysqlnd_result_unbuffered, free_result");
- result->m.free_last_data(result, global_stats);
/* must be free before because references the memory pool */
if (result->row_packet) {
@@ -73,47 +44,15 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, free_result)(MYSQLND_RES_UNBUFFERED *
}
/* }}} */
-
-/* {{{ mysqlnd_result_buffered_zval::free_result */
-static void
-MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)(MYSQLND_RES_BUFFERED_ZVAL * const set)
+static void mysqlnd_result_free_prev_data(MYSQLND_RES *result)
{
- zval * data = set->data;
-
- DBG_ENTER("mysqlnd_result_buffered_zval::free_result");
-
- set->data = NULL; /* prevent double free if following loop is interrupted */
- if (data) {
- const unsigned int field_count = set->field_count;
- int64_t row;
-
- for (row = set->row_count - 1; row >= 0; row--) {
- zval *current_row = data + row * field_count;
- int64_t col;
-
- if (current_row != NULL) {
- for (col = field_count - 1; col >= 0; --col) {
- zval_ptr_dtor_nogc(&(current_row[col]));
- }
- }
+ if (result->free_row_data) {
+ for (unsigned i = 0; i < result->field_count; ++i) {
+ zval_ptr_dtor_nogc(&result->row_data[i]);
}
- mnd_efree(data);
+ result->free_row_data = 0;
}
- set->data_cursor = NULL;
- DBG_VOID_RETURN;
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_result_buffered_c::free_result */
-static void
-MYSQLND_METHOD(mysqlnd_result_buffered_c, free_result)(MYSQLND_RES_BUFFERED_C * const set)
-{
- DBG_ENTER("mysqlnd_result_buffered_c::free_result");
- DBG_VOID_RETURN;
}
-/* }}} */
-
/* {{{ mysqlnd_result_buffered::free_result */
static void
@@ -125,12 +64,6 @@ MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * cons
mysqlnd_error_info_free_contents(&set->error_info);
- if (set->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
- MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)((MYSQLND_RES_BUFFERED_ZVAL *) set);
- } if (set->type == MYSQLND_BUFFERED_TYPE_C) {
- MYSQLND_METHOD(mysqlnd_result_buffered_c, free_result)((MYSQLND_RES_BUFFERED_C *) set);
- }
-
if (set->row_buffers) {
mnd_efree(set->row_buffers);
set->row_buffers = NULL;
@@ -148,6 +81,8 @@ MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES * result)
DBG_ENTER("mysqlnd_res::free_result_buffers");
DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->stored_data? "buffered":"unknown"));
+ mysqlnd_result_free_prev_data(result);
+
if (result->meta) {
ZEND_ASSERT(zend_arena_contains(result->memory_pool->arena, result->meta));
result->meta->m->free_metadata(result->meta);
@@ -429,44 +364,11 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s)
completeness.
*/
static const size_t *
-MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_lengths)(const MYSQLND_RES_BUFFERED * const result)
-{
- const MYSQLND_RES_BUFFERED_ZVAL * const set = (const MYSQLND_RES_BUFFERED_ZVAL *) result;
- /*
- If:
- - unbuffered result
- - first row has not been read
- - last_row has been read
- */
- DBG_ENTER("mysqlnd_result_buffered_zval::fetch_lengths");
-
- if (set->data_cursor == NULL ||
- set->data_cursor == set->data ||
- ((set->data_cursor - set->data) > (result->row_count * result->field_count) ))
- {
- DBG_INF("EOF");
- DBG_RETURN(NULL);/* No rows or no more rows */
- }
- DBG_INF("non NULL");
- DBG_RETURN(result->lengths);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_result_buffered_c::fetch_lengths */
-/*
- Do lazy initialization for buffered results. As PHP strings have
- length inside, this function makes not much sense in the context
- of PHP, to be called as separate function. But let's have it for
- completeness.
-*/
-static const size_t *
-MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_lengths)(const MYSQLND_RES_BUFFERED * const result)
+MYSQLND_METHOD(mysqlnd_result_buffered, fetch_lengths)(const MYSQLND_RES_BUFFERED * const result)
{
- const MYSQLND_RES_BUFFERED_C * const set = (const MYSQLND_RES_BUFFERED_C *) result;
- DBG_ENTER("mysqlnd_result_buffered_c::fetch_lengths");
+ DBG_ENTER("mysqlnd_result_buffered::fetch_lengths");
- if (set->current_row > set->row_count || set->current_row == 0) {
+ if (result->current_row > result->row_count || result->current_row == 0) {
DBG_INF("EOF");
DBG_RETURN(NULL); /* No more rows, or no fetched row */
}
@@ -503,139 +405,21 @@ MYSQLND_METHOD(mysqlnd_res, fetch_lengths)(const MYSQLND_RES * const result)
/* }}} */
-/* {{{ 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)
-{
- enum_func_status ret;
- MYSQLND_ROW_C *row = (MYSQLND_ROW_C *) param;
- MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet;
- MYSQLND_RES_METADATA * const meta = result->meta;
- MYSQLND_CONN_DATA * const conn = result->conn;
- void *checkpoint;
-
- DBG_ENTER("mysqlnd_result_unbuffered::fetch_row_c");
-
- *fetched_anything = FALSE;
- if (result->unbuf->eof_reached) {
- /* No more rows obviously */
- DBG_RETURN(PASS);
- }
- if (!conn || GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(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);
- }
-
- checkpoint = result->memory_pool->checkpoint;
- mysqlnd_mempool_save_state(result->memory_pool);
-
- /*
- If we skip rows (row == NULL) we have to
- result->m.unbuffered_free_last_data() before it. The function returns always true.
- */
- if (PASS == (ret = PACKET_READ(conn, row_packet)) && !row_packet->eof) {
- result->unbuf->m.free_last_data(result->unbuf, conn->stats);
-
- result->unbuf->last_row_buffer = row_packet->row_buffer;
- row_packet->row_buffer.ptr = NULL;
-
- MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
-
- 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,
- conn->options->int_and_float_native,
- conn->stats);
- if (PASS != rc) {
- mysqlnd_mempool_restore_state(result->memory_pool);
- result->memory_pool->checkpoint = checkpoint;
- DBG_RETURN(FAIL);
- }
- {
- *row = mnd_emalloc(field_count * sizeof(char *));
- MYSQLND_FIELD * field = meta->fields;
- size_t * lengths = result->unbuf->lengths;
-
- for (i = 0; i < field_count; i++, field++) {
- zval * data = &result->unbuf->last_row_data[i];
- const size_t len = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
-
-/* 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 */
-
- if (lengths) {
- lengths[i] = len;
- }
- }
- }
- result->unbuf->row_count++;
- *fetched_anything = TRUE;
- } else if (ret == FAIL) {
- if (row_packet->error_info.error_no) {
- COPY_CLIENT_ERROR(conn->error_info, row_packet->error_info);
- DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
- }
- if (GET_CONNECTION_STATE(&conn->state) != CONN_QUIT_SENT) {
- SET_CONNECTION_STATE(&conn->state, CONN_READY);
- }
- result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
- } else if (row_packet->eof) {
- /* Mark the connection as usable again */
- DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
- result->unbuf->eof_reached = TRUE;
-
- UPSERT_STATUS_RESET(conn->upsert_status);
- UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
- UPSERT_STATUS_SET_SERVER_STATUS(conn->upsert_status, row_packet->server_status);
- /*
- result->row_packet will be cleaned when
- destroying the result object
- */
- if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS) {
- SET_CONNECTION_STATE(&conn->state, CONN_NEXT_RESULT_PENDING);
- } else {
- SET_CONNECTION_STATE(&conn->state, CONN_READY);
- }
- result->unbuf->m.free_last_data(result->unbuf, conn->stats);
- }
-
- mysqlnd_mempool_restore_state(result->memory_pool);
- result->memory_pool->checkpoint = checkpoint;
-
- DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
- DBG_RETURN(PASS);
-}
-/* }}} */
-
-
/* {{{ mysqlnd_result_unbuffered::fetch_row */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
+MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, zval **row_ptr, const unsigned int flags, zend_bool * fetched_anything)
{
- enum_func_status ret;
- zval *row = (zval *) param;
+ enum_func_status ret;
MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet;
const MYSQLND_RES_METADATA * const meta = result->meta;
+ MYSQLND_RES_UNBUFFERED *set = result->unbuf;
MYSQLND_CONN_DATA * const conn = result->conn;
void *checkpoint;
DBG_ENTER("mysqlnd_result_unbuffered::fetch_row");
*fetched_anything = FALSE;
- if (result->unbuf->eof_reached) {
+ if (set->eof_reached) {
/* No more rows obviously */
DBG_RETURN(PASS);
}
@@ -651,83 +435,53 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
checkpoint = result->memory_pool->checkpoint;
mysqlnd_mempool_save_state(result->memory_pool);
- /*
- If we skip rows (row == NULL) we have to
- result->m.unbuffered_free_last_data() before it. The function returns always true.
- */
if (PASS == (ret = PACKET_READ(conn, row_packet)) && !row_packet->eof) {
- result->unbuf->m.free_last_data(result->unbuf, conn->stats);
-
- result->unbuf->last_row_buffer = row_packet->row_buffer;
+ set->last_row_buffer = row_packet->row_buffer;
row_packet->row_buffer.ptr = NULL;
- MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
+ MYSQLND_INC_CONN_STATISTIC(conn->stats, set->stmt
+ ? STAT_ROWS_FETCHED_FROM_CLIENT_PS_UNBUF
+ : STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
- if (row) {
- unsigned int i, field_count = meta->field_count;
+ if (row_ptr) {
+ unsigned int 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,
- conn->options->int_and_float_native,
- conn->stats);
+ *row_ptr = result->row_data;
+ enum_func_status rc = set->m.row_decoder(
+ &set->last_row_buffer, result->row_data, field_count,
+ row_packet->fields_metadata, conn->options->int_and_float_native, conn->stats);
if (PASS != rc) {
mysqlnd_mempool_restore_state(result->memory_pool);
result->memory_pool->checkpoint = checkpoint;
DBG_RETURN(FAIL);
}
- {
- HashTable * row_ht = Z_ARRVAL_P(row);
- MYSQLND_FIELD * field = meta->fields;
- size_t * lengths = result->unbuf->lengths;
-
- for (i = 0; i < field_count; i++, field++) {
- zval * data = &result->unbuf->last_row_data[i];
- const size_t len = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
-
- if (flags & MYSQLND_FETCH_NUM) {
- if (zend_hash_index_add(row_ht, i, data) != NULL) {
- Z_TRY_ADDREF_P(data);
- }
- }
- 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_TRY_ADDREF_P(data);
- if (meta->fields[i].is_numeric == FALSE) {
- zend_hash_update(row_ht, meta->fields[i].sname, data);
- } else {
- zend_hash_index_update(row_ht, meta->fields[i].num_key, data);
- }
- }
- if (lengths) {
- lengths[i] = len;
- }
+ size_t *lengths = set->lengths;
+ if (lengths) {
+ for (unsigned i = 0; i < field_count; i++) {
+ zval *data = &result->row_data[i];
+ lengths[i] = Z_TYPE_P(data) == IS_STRING ? Z_STRLEN_P(data) : 0;
}
}
}
- result->unbuf->row_count++;
+ set->row_count++;
*fetched_anything = TRUE;
} else if (ret == FAIL) {
if (row_packet->error_info.error_no) {
COPY_CLIENT_ERROR(conn->error_info, row_packet->error_info);
+ if (set->stmt) {
+ COPY_CLIENT_ERROR(set->stmt->error_info, row_packet->error_info);
+ }
DBG_ERR_FMT("errorno=%u error=%s", row_packet->error_info.error_no, row_packet->error_info.error);
}
if (GET_CONNECTION_STATE(&conn->state) != CONN_QUIT_SENT) {
SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
- result->unbuf->eof_reached = TRUE; /* so next time we won't get an error */
+ set->eof_reached = TRUE; /* so next time we won't get an error */
} else if (row_packet->eof) {
/* Mark the connection as usable again */
DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status);
- result->unbuf->eof_reached = TRUE;
+ set->eof_reached = TRUE;
UPSERT_STATUS_RESET(conn->upsert_status);
UPSERT_STATUS_SET_WARNINGS(conn->upsert_status, row_packet->warning_count);
@@ -741,7 +495,6 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
} else {
SET_CONNECTION_STATE(&conn->state, CONN_READY);
}
- result->unbuf->m.free_last_data(result->unbuf, conn->stats);
}
mysqlnd_mempool_restore_state(result->memory_pool);
@@ -755,20 +508,20 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void
/* {{{ mysqlnd_res::use_result */
static MYSQLND_RES *
-MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, const zend_bool ps)
+MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, MYSQLND_STMT_DATA *stmt)
{
MYSQLND_CONN_DATA * const conn = result->conn;
DBG_ENTER("mysqlnd_res::use_result");
SET_EMPTY_ERROR(conn->error_info);
- if (ps == FALSE) {
- result->type = MYSQLND_RES_NORMAL;
+ if (!stmt) {
+ result->type = MYSQLND_RES_NORMAL;
} else {
- result->type = MYSQLND_RES_PS_UNBUF;
+ result->type = MYSQLND_RES_PS_UNBUF;
}
- result->unbuf = mysqlnd_result_unbuffered_init(result, result->field_count, ps);
+ result->unbuf = mysqlnd_result_unbuffered_init(result, result->field_count, stmt);
/*
Will be freed in the mysqlnd_internal_free_result_contents() called
@@ -782,7 +535,7 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, const zend_b
conn->payload_decoder_factory->m.init_row_packet(row_packet);
row_packet->result_set_memory_pool = result->unbuf->result_set_memory_pool;
row_packet->field_count = result->field_count;
- row_packet->binary_protocol = ps;
+ row_packet->binary_protocol = stmt != NULL;
row_packet->fields_metadata = result->meta->fields;
result->unbuf->row_packet = row_packet;
@@ -793,244 +546,68 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, const zend_b
/* }}} */
-/* {{{ mysqlnd_result_buffered::fetch_row_c */
+/* {{{ mysqlnd_result_buffered::fetch_row */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * const fetched_anything)
+MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)(MYSQLND_RES * result, zval **row_ptr, const unsigned int flags, zend_bool * fetched_anything)
{
- enum_func_status ret = FAIL;
- MYSQLND_ROW_C * row = (MYSQLND_ROW_C *) param;
- const MYSQLND_RES_METADATA * const meta = result->meta;
- const unsigned int field_count = meta->field_count;
- MYSQLND_CONN_DATA * const conn = result->conn;
- DBG_ENTER("mysqlnd_result_buffered::fetch_row_c");
-
- if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
- MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
-
- /* If we haven't read everything */
- if (set->data_cursor &&
- (set->data_cursor - set->data) < (result->stored_data->row_count * field_count))
- {
- zval *current_row = set->data_cursor;
- unsigned int i;
-
- if (Z_ISUNDEF(current_row[0])) {
- 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,
- field_count,
- meta->fields,
- conn->options->int_and_float_native,
- conn->stats);
- if (rc != PASS) {
- DBG_RETURN(FAIL);
- }
- }
-
-/* BEGIN difference between normal normal fetch and _c */
- *row = mnd_emalloc(field_count * sizeof(char *));
- for (i = 0; i < field_count; ++i) {
- zval * data = &current_row[i];
-
- set->lengths[i] = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
-
- if (Z_TYPE_P(data) != IS_NULL) {
- convert_to_string(data);
- (*row)[i] = Z_STRVAL_P(data);
- } else {
- (*row)[i] = NULL;
- }
- }
- set->data_cursor += field_count;
- MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
-/* END difference between normal normal fetch and _c */
-
- *fetched_anything = *row? TRUE:FALSE;
- ret = *row? PASS:FAIL;
- } else {
- set->data_cursor = NULL;
- DBG_INF("EOF reached");
- *fetched_anything = FALSE;
- ret = PASS;
- }
- } else if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_C) {
- /*
- We don't support _C with pdo because it uses the data in a different way - just references it.
- We will either leak or give nirvana pointers
- */
- *fetched_anything = FALSE;
- DBG_RETURN(FAIL);
- }
- DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_result_buffered_zval::fetch_row */
-static enum_func_status
-MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * const fetched_anything)
-{
- enum_func_status ret = FAIL;
- zval * row = (zval *) param;
- const MYSQLND_RES_METADATA * const meta = result->meta;
- const unsigned int field_count = meta->field_count;
- MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
- MYSQLND_CONN_DATA * const conn = result->conn;
+ MYSQLND_RES_BUFFERED *set = result->stored_data;
- DBG_ENTER("mysqlnd_result_buffered_zval::fetch_row");
+ 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 * field_count)) {
- unsigned int i;
- zval *current_row = set->data_cursor;
-
- if (Z_ISUNDEF(current_row[0])) {
- const size_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,
- field_count,
- meta->fields,
- conn->options->int_and_float_native,
- conn->stats);
+ if (set->current_row < set->row_count) {
+ if (row_ptr) {
+ const MYSQLND_RES_METADATA * const meta = result->meta;
+ const unsigned int field_count = meta->field_count;
+ MYSQLND_CONN_DATA * const conn = result->conn;
+ enum_func_status rc;
+ zval *current_row = result->row_data;
+ *row_ptr = result->row_data;
+ rc = result->stored_data->m.row_decoder(&set->row_buffers[set->current_row],
+ current_row,
+ field_count,
+ meta->fields,
+ conn->options->int_and_float_native,
+ conn->stats);
if (rc != PASS) {
DBG_RETURN(FAIL);
}
- }
-
- for (i = 0; i < field_count; ++i) {
- zval * data = &current_row[i];
- set->lengths[i] = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
-
- if (flags & MYSQLND_FETCH_NUM) {
- if (zend_hash_index_add(Z_ARRVAL_P(row), i, data) != NULL) {
- Z_TRY_ADDREF_P(data);
- }
- }
- 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_TRY_ADDREF_P(data);
- if (meta->fields[i].is_numeric == FALSE) {
- zend_hash_update(Z_ARRVAL_P(row), meta->fields[i].sname, data);
- } else {
- zend_hash_index_update(Z_ARRVAL_P(row), meta->fields[i].num_key, data);
+ if (set->lengths) {
+ for (unsigned i = 0; i < field_count; ++i) {
+ zval *data = &current_row[i];
+ set->lengths[i] = Z_TYPE_P(data) == IS_STRING ? Z_STRLEN_P(data) : 0;
}
}
}
- 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_result_buffered_c::fetch_row */
-static enum_func_status
-MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything)
-{
- enum_func_status ret = FAIL;
- zval * row = (zval *) param;
- const MYSQLND_RES_METADATA * const meta = result->meta;
- const unsigned int field_count = meta->field_count;
- MYSQLND_CONN_DATA * const conn = result->conn;
-
- MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result->stored_data;
- DBG_ENTER("mysqlnd_result_buffered_c::fetch_row");
-
- /* If we haven't read everything */
- if (set->current_row < set->row_count) {
- enum_func_status rc;
- zval * current_row;
- unsigned int i;
-
- current_row = mnd_emalloc(field_count * sizeof(zval));
- rc = result->stored_data->m.row_decoder(&result->stored_data->row_buffers[set->current_row],
- current_row,
- field_count,
- meta->fields,
- conn->options->int_and_float_native,
- conn->stats);
- if (rc != PASS) {
- DBG_RETURN(FAIL);
- }
-
- for (i = 0; i < field_count; ++i) {
- zval * data = &current_row[i];
-
- set->lengths[i] = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0;
-
- if (flags & MYSQLND_FETCH_NUM) {
- if (zend_hash_index_add(Z_ARRVAL_P(row), i, data)) {
- Z_TRY_ADDREF_P(data);
- }
- }
- 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_TRY_ADDREF_P(data);
- if (meta->fields[i].is_numeric == FALSE) {
- zend_hash_update(Z_ARRVAL_P(row), meta->fields[i].sname, data);
- } else {
- zend_hash_index_update(Z_ARRVAL_P(row), meta->fields[i].num_key, data);
- }
- }
- /*
- This will usually not destroy anything but decref.
- However, if neither NUM nor ASSOC is set we will free memory cleanly and won't leak.
- It also simplifies the handling of Z_ADDREF_P because we don't need to check if only
- either NUM or ASSOC is set but not both.
- */
- zval_ptr_dtor_nogc(data);
- }
- mnd_efree(current_row);
++set->current_row;
- MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
+ MYSQLND_INC_GLOBAL_STATISTIC(set->stmt
+ ? STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF : STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
*fetched_anything = TRUE;
- ret = PASS;
} else {
if (set->current_row == set->row_count) {
set->current_row = set->row_count + 1;
}
DBG_INF_FMT("EOF reached. current_row=%llu", (unsigned long long) set->current_row);
*fetched_anything = FALSE;
- ret = PASS;
}
DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything);
- DBG_RETURN(ret);
+ DBG_RETURN(PASS);
}
/* }}} */
/* {{{ mysqlnd_res::fetch_row */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_res, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool *fetched_anything)
+MYSQLND_METHOD(mysqlnd_res, fetch_row)(MYSQLND_RES *result, zval **row_ptr, const unsigned int flags, zend_bool *fetched_anything)
{
- const mysqlnd_fetch_row_func f = result->stored_data? result->stored_data->m.fetch_row:(result->unbuf? result->unbuf->m.fetch_row:NULL);
+ 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);
+ return f(result, row_ptr, flags, fetched_anything);
}
*fetched_anything = FALSE;
return PASS;
@@ -1104,13 +681,6 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
/* So row_packet's destructor function won't efree() it */
row_packet.row_buffer.ptr = NULL;
-
- /*
- No need to FREE_ALLOCA as we can reuse the
- 'lengths' and 'fields' arrays. For lengths its absolutely safe.
- 'fields' is reused because the ownership of the strings has been
- transferred above.
- */
}
/* Overflow ? */
MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats,
@@ -1173,7 +743,7 @@ end:
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
MYSQLND_CONN_DATA * const conn,
- const unsigned int flags)
+ MYSQLND_STMT_DATA *stmt)
{
enum_func_status ret;
MYSQLND_ROW_BUFFER **row_buffers = NULL;
@@ -1187,14 +757,10 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
SET_CONNECTION_STATE(&conn->state, CONN_FETCHING_DATA);
- if (flags & MYSQLND_STORE_NO_COPY) {
- result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result, result->field_count, flags & MYSQLND_STORE_PS);
- row_buffers = &result->stored_data->row_buffers;
- } else if (flags & MYSQLND_STORE_COPY) {
- result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_c_init(result, result->field_count, flags & MYSQLND_STORE_PS);
- row_buffers = &result->stored_data->row_buffers;
- }
- ret = result->m.store_result_fetch_data(conn, result, result->meta, row_buffers, flags & MYSQLND_STORE_PS);
+ result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_init(result, result->field_count, stmt);
+ row_buffers = &result->stored_data->row_buffers;
+
+ ret = result->m.store_result_fetch_data(conn, result, result->meta, row_buffers, stmt != NULL);
if (FAIL == ret) {
if (result->stored_data) {
@@ -1204,30 +770,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
}
DBG_RETURN(NULL);
} else {
- if (flags & MYSQLND_STORE_NO_COPY) {
- const MYSQLND_RES_METADATA * const meta = result->meta;
- MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) 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 * 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 * 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 * meta->field_count * sizeof(zval)));
- }
- /* Position at the first row */
- set->data_cursor = set->data;
- } else if (flags & MYSQLND_STORE_COPY) {
- MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result->stored_data;
- set->current_row = 0;
- }
+ result->stored_data->current_row = 0;
}
/* libmysql's documentation says it should be so for SELECT statements */
@@ -1298,36 +841,17 @@ MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES * const result, const uint64_
/* }}} */
-/* {{{ mysqlnd_result_buffered_zval::data_seek */
+/* {{{ mysqlnd_result_buffered::data_seek */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_result_buffered_zval, data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row)
+MYSQLND_METHOD(mysqlnd_result_buffered, data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row)
{
- MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result;
- DBG_ENTER("mysqlnd_result_buffered_zval::data_seek");
+ DBG_ENTER("mysqlnd_result_buffered::data_seek");
/* libmysql just moves to the end, it does traversing of a linked list */
- if (row >= set->row_count) {
- set->data_cursor = NULL;
+ if (row >= result->row_count) {
+ result->current_row = result->row_count;
} else {
- set->data_cursor = set->data + row * result->field_count;
- }
- DBG_RETURN(PASS);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_result_buffered_c::data_seek */
-static enum_func_status
-MYSQLND_METHOD(mysqlnd_result_buffered_c, data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row)
-{
- MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result;
- DBG_ENTER("mysqlnd_result_buffered_c::data_seek");
-
- /* libmysql just moves to the end, it does traversing of a linked list */
- if (row >= set->row_count) {
- set->current_row = set->row_count;
- } else {
- set->current_row = row;
+ result->current_row = row;
}
DBG_RETURN(PASS);
}
@@ -1443,31 +967,53 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, const unsigned int
zval *return_value ZEND_FILE_LINE_DC)
{
zend_bool fetched_anything;
- unsigned int array_size;
+ zval *row_data;
DBG_ENTER("mysqlnd_res::fetch_into");
+ if (FAIL == result->m.fetch_row(result, &row_data, flags, &fetched_anything)) {
+ php_error_docref(NULL, E_WARNING, "Error while reading a row");
+ RETVAL_FALSE;
+ DBG_VOID_RETURN;
+ } else if (fetched_anything == FALSE) {
+ 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.
- */
- array_size = result->field_count;
+ const MYSQLND_RES_METADATA * const meta = result->meta;
+ unsigned int array_size = meta->field_count;
if ((flags & (MYSQLND_FETCH_NUM|MYSQLND_FETCH_ASSOC)) == (MYSQLND_FETCH_NUM|MYSQLND_FETCH_ASSOC)) {
array_size *= 2;
}
array_init_size(return_value, array_size);
- if (FAIL == result->m.fetch_row(result, (void *)return_value, flags, &fetched_anything)) {
- php_error_docref(NULL, E_WARNING, "Error while reading a row");
- zend_array_destroy(Z_ARR_P(return_value));
- RETVAL_FALSE;
- } else if (fetched_anything == FALSE) {
- zend_array_destroy(Z_ARR_P(return_value));
- RETVAL_NULL();
+
+ HashTable *row_ht = Z_ARRVAL_P(return_value);
+ MYSQLND_FIELD *field = meta->fields;
+ for (unsigned i = 0; i < meta->field_count; i++, field++) {
+ zval *data = &row_data[i];
+
+ if (flags & MYSQLND_FETCH_NUM) {
+ if (zend_hash_index_add(row_ht, i, data) != NULL) {
+ Z_TRY_ADDREF_P(data);
+ }
+ }
+ 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_TRY_ADDREF_P(data);
+ if (meta->fields[i].is_numeric == FALSE) {
+ zend_hash_update(row_ht, meta->fields[i].sname, data);
+ } else {
+ zend_hash_index_update(row_ht, meta->fields[i].num_key, data);
+ }
+ }
+
+ zval_ptr_dtor_nogc(data);
}
- /*
- return_value is IS_NULL for no more data and an array for data. Thus it's ok
- to return here.
- */
DBG_VOID_RETURN;
}
/* }}} */
@@ -1478,16 +1024,26 @@ static MYSQLND_ROW_C
MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result)
{
zend_bool fetched_anything;
+ zval *row_data;
MYSQLND_ROW_C ret = NULL;
DBG_ENTER("mysqlnd_res::fetch_row_c");
- if (result->stored_data && result->stored_data->m.fetch_row == MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)) {
- MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(result, (void *) &ret, 0, &fetched_anything);
- } 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);
- } else {
- ret = NULL;
- php_error_docref(NULL, E_ERROR, "result->m.fetch_row has invalid value. Report to the developers");
+ mysqlnd_result_free_prev_data(result);
+ if (result->m.fetch_row(result, &row_data, 0, &fetched_anything) == PASS && fetched_anything) {
+ unsigned field_count = result->field_count;
+ MYSQLND_FIELD *field = result->meta->fields;
+
+ ret = mnd_emalloc(field_count * sizeof(char *));
+ for (unsigned i = 0; i < field_count; i++, field++) {
+ zval *data = &row_data[i];
+ if (Z_TYPE_P(data) != IS_NULL) {
+ convert_to_string(data);
+ ret[i] = Z_STRVAL_P(data);
+ } else {
+ ret[i] = NULL;
+ }
+ }
+ result->free_row_data = 1;
}
DBG_RETURN(ret);
}
@@ -1566,17 +1122,16 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_result_unbuffered)
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)
- NULL, /* fetch_row */
+ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row),
NULL, /* row_decoder */
MYSQLND_METHOD(mysqlnd_result_buffered, num_rows),
- NULL, /* fetch_lengths */
- NULL, /* data_seek */
+ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_lengths),
+ MYSQLND_METHOD(mysqlnd_result_buffered, data_seek),
MYSQLND_METHOD(mysqlnd_result_buffered, free_result)
MYSQLND_CLASS_METHODS_END;
@@ -1599,6 +1154,9 @@ mysqlnd_result_init(const unsigned int field_count)
ret = pool->get_chunk(pool, alloc_size);
memset(ret, 0, alloc_size);
+ ret->row_data = pool->get_chunk(pool, field_count * sizeof(zval));
+ ret->free_row_data = 0;
+
ret->memory_pool = pool;
ret->field_count = field_count;
ret->m = *mysqlnd_result_get_methods();
@@ -1612,7 +1170,7 @@ mysqlnd_result_init(const unsigned int field_count)
/* {{{ mysqlnd_result_unbuffered_init */
PHPAPI MYSQLND_RES_UNBUFFERED *
-mysqlnd_result_unbuffered_init(MYSQLND_RES *result, const unsigned int field_count, const zend_bool ps)
+mysqlnd_result_unbuffered_init(MYSQLND_RES *result, const unsigned int field_count, MYSQLND_STMT_DATA *stmt)
{
const size_t alloc_size = sizeof(MYSQLND_RES_UNBUFFERED) + mysqlnd_plugin_count() * sizeof(void *);
MYSQLND_MEMORY_POOL * pool = result->memory_pool;
@@ -1623,23 +1181,21 @@ mysqlnd_result_unbuffered_init(MYSQLND_RES *result, const unsigned int field_cou
ret = pool->get_chunk(pool, alloc_size);
memset(ret, 0, alloc_size);
- ret->lengths = pool->get_chunk(pool, field_count * sizeof(size_t));
- memset(ret->lengths, 0, field_count * sizeof(size_t));
-
- ret->last_row_data = pool->get_chunk(pool, field_count * sizeof(zval));
- memset(ret->last_row_data, 0, field_count * sizeof(zval));
-
ret->result_set_memory_pool = pool;
- ret->field_count= field_count;
- ret->ps = ps;
+ ret->field_count = field_count;
+ ret->stmt = stmt;
ret->m = *mysqlnd_result_unbuffered_get_methods();
- if (ps) {
+ if (stmt) {
+ ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
ret->m.fetch_lengths = NULL; /* makes no sense */
- ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
+ ret->lengths = NULL;
} else {
- ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol_zval;
+ ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol;
+
+ ret->lengths = pool->get_chunk(pool, field_count * sizeof(size_t));
+ memset(ret->lengths, 0, field_count * sizeof(size_t));
}
DBG_RETURN(ret);
@@ -1647,77 +1203,36 @@ mysqlnd_result_unbuffered_init(MYSQLND_RES *result, const unsigned int field_cou
/* }}} */
-/* {{{ mysqlnd_result_buffered_zval_init */
-PHPAPI MYSQLND_RES_BUFFERED_ZVAL *
-mysqlnd_result_buffered_zval_init(MYSQLND_RES * result, const unsigned int field_count, const zend_bool ps)
+/* {{{ mysqlnd_result_buffered_init */
+PHPAPI MYSQLND_RES_BUFFERED *
+mysqlnd_result_buffered_init(MYSQLND_RES * result, const unsigned int field_count, MYSQLND_STMT_DATA *stmt)
{
- const size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_ZVAL) + mysqlnd_plugin_count() * sizeof(void *);
+ const size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED) + mysqlnd_plugin_count() * sizeof(void *);
MYSQLND_MEMORY_POOL * pool = result->memory_pool;
- MYSQLND_RES_BUFFERED_ZVAL * ret;
+ MYSQLND_RES_BUFFERED * ret;
- DBG_ENTER("mysqlnd_result_buffered_zval_init");
+ DBG_ENTER("mysqlnd_result_buffered_init");
ret = pool->get_chunk(pool, alloc_size);
memset(ret, 0, alloc_size);
mysqlnd_error_info_init(&ret->error_info, /* persistent */ 0);
- ret->lengths = pool->get_chunk(pool, field_count * sizeof(size_t));
- memset(ret->lengths, 0, field_count * sizeof(size_t));
-
ret->result_set_memory_pool = pool;
ret->field_count= field_count;
- ret->ps = ps;
+ ret->stmt = stmt;
ret->m = *mysqlnd_result_buffered_get_methods();
- ret->type = MYSQLND_BUFFERED_TYPE_ZVAL;
- if (ps) {
+ if (stmt) {
+ ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
ret->m.fetch_lengths = NULL; /* makes no sense */
- ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
+ ret->lengths = NULL;
} else {
- ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol_zval;
- }
- ret->m.fetch_row = MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row);
- ret->m.fetch_lengths = MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_lengths);
- ret->m.data_seek = MYSQLND_METHOD(mysqlnd_result_buffered_zval, data_seek);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
-/* {{{ mysqlnd_result_buffered_c_init */
-PHPAPI MYSQLND_RES_BUFFERED_C *
-mysqlnd_result_buffered_c_init(MYSQLND_RES * result, const unsigned int field_count, const zend_bool ps)
-{
- const size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_C) + mysqlnd_plugin_count() * sizeof(void *);
- MYSQLND_MEMORY_POOL * pool = result->memory_pool;
- MYSQLND_RES_BUFFERED_C * ret;
-
- DBG_ENTER("mysqlnd_result_buffered_c_init");
-
- ret = pool->get_chunk(pool, alloc_size);
- memset(ret, 0, alloc_size);
+ ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol;
- mysqlnd_error_info_init(&ret->error_info, /* persistent */ 0);
-
- ret->lengths = pool->get_chunk(pool, field_count * sizeof(size_t));
- memset(ret->lengths, 0, field_count * sizeof(size_t));
-
- ret->result_set_memory_pool = pool;
- ret->field_count= field_count;
- ret->ps = ps;
- ret->m = *mysqlnd_result_buffered_get_methods();
- ret->type = MYSQLND_BUFFERED_TYPE_C;
-
- 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_c;
+ ret->lengths = pool->get_chunk(pool, field_count * sizeof(size_t));
+ memset(ret->lengths, 0, field_count * sizeof(size_t));
}
- ret->m.fetch_row = MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row);
- ret->m.fetch_lengths = MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_lengths);
- ret->m.data_seek = MYSQLND_METHOD(mysqlnd_result_buffered_c, data_seek);
DBG_RETURN(ret);
}
diff --git a/ext/mysqlnd/mysqlnd_result.h b/ext/mysqlnd/mysqlnd_result.h
index 14f894ccba..9dde8a5fad 100644
--- a/ext/mysqlnd/mysqlnd_result.h
+++ b/ext/mysqlnd/mysqlnd_result.h
@@ -19,9 +19,8 @@
#define MYSQLND_RESULT_H
PHPAPI MYSQLND_RES * mysqlnd_result_init(const unsigned int field_count);
-PHPAPI MYSQLND_RES_UNBUFFERED * mysqlnd_result_unbuffered_init(MYSQLND_RES * result, const unsigned int field_count, const zend_bool ps);
-PHPAPI MYSQLND_RES_BUFFERED_ZVAL * mysqlnd_result_buffered_zval_init(MYSQLND_RES * result, const unsigned int field_count, const zend_bool ps);
-PHPAPI MYSQLND_RES_BUFFERED_C * mysqlnd_result_buffered_c_init(MYSQLND_RES * result, const unsigned int field_count, const zend_bool ps);
+PHPAPI MYSQLND_RES_UNBUFFERED * mysqlnd_result_unbuffered_init(MYSQLND_RES * result, const unsigned int field_count, MYSQLND_STMT_DATA *stmt);
+PHPAPI MYSQLND_RES_BUFFERED * mysqlnd_result_buffered_init(MYSQLND_RES * result, const unsigned int field_count, MYSQLND_STMT_DATA *stmt);
enum_func_status mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * stmt);
diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h
index 5f4c03d7c8..47c8e491ec 100644
--- a/ext/mysqlnd/mysqlnd_structs.h
+++ b/ext/mysqlnd/mysqlnd_structs.h
@@ -279,9 +279,7 @@ typedef struct st_mysqlnd_param_bind MYSQLND_PARAM_BIND;
typedef struct st_mysqlnd_result_bind MYSQLND_RESULT_BIND;
typedef struct st_mysqlnd_result_metadata MYSQLND_RES_METADATA;
-typedef struct st_mysqlnd_buffered_result_parent MYSQLND_RES_BUFFERED;
-typedef struct st_mysqlnd_buffered_result_zval MYSQLND_RES_BUFFERED_ZVAL;
-typedef struct st_mysqlnd_buffered_result_c MYSQLND_RES_BUFFERED_C;
+typedef struct st_mysqlnd_buffered_result MYSQLND_RES_BUFFERED;
typedef struct st_mysqlnd_unbuffered_result MYSQLND_RES_UNBUFFERED;
typedef struct st_mysqlnd_debug MYSQLND_DEBUG;
@@ -289,7 +287,7 @@ typedef struct st_mysqlnd_debug MYSQLND_DEBUG;
typedef MYSQLND_RES* (*mysqlnd_stmt_use_or_store_func)(MYSQLND_STMT * const);
typedef enum_func_status (*mysqlnd_fetch_row_func)(MYSQLND_RES *result,
- void * param,
+ zval **row,
const unsigned int flags,
zend_bool * fetched_anything
);
@@ -441,8 +439,8 @@ typedef enum_func_status (*func_mysqlnd_conn_data__set_charset)(MYSQLND_CONN_DAT
typedef enum_func_status (*func_mysqlnd_conn_data__query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len);
typedef enum_func_status (*func_mysqlnd_conn_data__send_query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len, enum_mysqlnd_send_query_type type, zval *read_cb, zval *err_cb);
typedef enum_func_status (*func_mysqlnd_conn_data__reap_query)(MYSQLND_CONN_DATA * conn, enum_mysqlnd_reap_result_type type);
-typedef MYSQLND_RES * (*func_mysqlnd_conn_data__use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags);
-typedef MYSQLND_RES * (*func_mysqlnd_conn_data__store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags);
+typedef MYSQLND_RES * (*func_mysqlnd_conn_data__use_result)(MYSQLND_CONN_DATA * const conn);
+typedef MYSQLND_RES * (*func_mysqlnd_conn_data__store_result)(MYSQLND_CONN_DATA * const conn);
typedef enum_func_status (*func_mysqlnd_conn_data__next_result)(MYSQLND_CONN_DATA * const conn);
typedef zend_bool (*func_mysqlnd_conn_data__more_results)(const MYSQLND_CONN_DATA * const conn);
@@ -638,8 +636,8 @@ typedef enum_func_status (*func_mysqlnd_res__row_decoder)(MYSQLND_ROW_BUFFER * r
const zend_bool as_int_or_float, MYSQLND_STATS * const stats);
-typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, const zend_bool ps_protocol);
-typedef MYSQLND_RES * (*func_mysqlnd_res__store_result)(MYSQLND_RES * result, MYSQLND_CONN_DATA * const conn, const unsigned int flags);
+typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, MYSQLND_STMT_DATA *stmt);
+typedef MYSQLND_RES * (*func_mysqlnd_res__store_result)(MYSQLND_RES * result, MYSQLND_CONN_DATA * const conn, MYSQLND_STMT_DATA *stmt);
typedef void (*func_mysqlnd_res__fetch_into)(MYSQLND_RES *result, const unsigned int flags, zval *return_value ZEND_FILE_LINE_DC);
typedef MYSQLND_ROW_C (*func_mysqlnd_res__fetch_row_c)(MYSQLND_RES *result);
typedef void (*func_mysqlnd_res__fetch_all)(MYSQLND_RES *result, const unsigned int flags, zval *return_value ZEND_FILE_LINE_DC);
@@ -661,7 +659,6 @@ typedef void (*func_mysqlnd_res__free_result_buffers)(MYSQLND_RES * result);
typedef enum_func_status (*func_mysqlnd_res__free_result)(MYSQLND_RES * result, const zend_bool implicit);
typedef void (*func_mysqlnd_res__free_result_contents)(MYSQLND_RES *result);
typedef void (*func_mysqlnd_res__free_buffered_data)(MYSQLND_RES *result);
-typedef void (*func_mysqlnd_res__unbuffered_free_last_data)(MYSQLND_RES *result);
typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res__result_meta_init)(MYSQLND_RES *result, unsigned int field_count);
@@ -703,7 +700,6 @@ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_res)
typedef uint64_t (*func_mysqlnd_result_unbuffered__num_rows)(const MYSQLND_RES_UNBUFFERED * const result);
typedef const size_t * (*func_mysqlnd_result_unbuffered__fetch_lengths)(const MYSQLND_RES_UNBUFFERED * const result);
-typedef void (*func_mysqlnd_result_unbuffered__free_last_data)(MYSQLND_RES_UNBUFFERED * result, MYSQLND_STATS * const global_stats);
typedef void (*func_mysqlnd_result_unbuffered__free_result)(MYSQLND_RES_UNBUFFERED * const result, MYSQLND_STATS * const global_stats);
MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered)
@@ -712,7 +708,6 @@ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_unbuffered)
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;
};
@@ -1178,48 +1173,30 @@ struct st_mysqlnd_result_metadata
};
-#define def_mysqlnd_buffered_result_parent \
- MYSQLND_ROW_BUFFER *row_buffers; \
- uint64_t row_count; \
- \
- /* Column lengths of current row - both buffered and unbuffered. For buffered results it duplicates the data found in **data */ \
- size_t *lengths; \
- \
- MYSQLND_MEMORY_POOL *result_set_memory_pool; \
- \
- unsigned int references; \
- \
- MYSQLND_ERROR_INFO error_info; \
- \
- unsigned int field_count; \
- zend_bool ps; \
- MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered) m; \
- enum mysqlnd_buffered_type type; \
- void * unused1; \
- void * unused2; \
- void * unused3
-
-
-struct st_mysqlnd_buffered_result_parent
+struct st_mysqlnd_buffered_result
{
- def_mysqlnd_buffered_result_parent;
-};
+ MYSQLND_CLASS_METHODS_TYPE(mysqlnd_result_buffered) m;
+ MYSQLND_ROW_BUFFER *row_buffers;
+ uint64_t row_count;
-struct st_mysqlnd_buffered_result_zval
-{
- def_mysqlnd_buffered_result_parent;
+ /* Column lengths of current row - both buffered and unbuffered. For buffered results it duplicates the data found in **data */
+ size_t *lengths;
- zval *data;
- zval *data_cursor;
-};
+ MYSQLND_MEMORY_POOL *result_set_memory_pool;
+ unsigned int references;
-struct st_mysqlnd_buffered_result_c
-{
- def_mysqlnd_buffered_result_parent;
+ MYSQLND_ERROR_INFO error_info;
+
+ unsigned int field_count;
+ MYSQLND_STMT_DATA *stmt;
uint64_t current_row;
+
+ void * unused1;
+ void * unused2;
+ void * unused3;
};
@@ -1229,7 +1206,6 @@ struct st_mysqlnd_unbuffered_result
uint64_t row_count;
/* For unbuffered (both normal and PS) */
- zval *last_row_data;
MYSQLND_ROW_BUFFER last_row_buffer;
/*
@@ -1246,7 +1222,7 @@ struct st_mysqlnd_unbuffered_result
zend_bool eof_reached;
- zend_bool ps;
+ MYSQLND_STMT_DATA *stmt;
};
@@ -1256,6 +1232,9 @@ struct st_mysqlnd_res
enum_mysqlnd_res_type type;
unsigned int field_count;
+ zval *row_data;
+ bool free_row_data;
+
/* For metadata functions */
MYSQLND_RES_METADATA *meta;
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c
index a0ba748a6c..0d601e5b98 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -1369,7 +1369,7 @@ php_mysqlnd_read_row_ex(MYSQLND_PFC * pfc,
*/
/*
- We're allocating an extra byte, as php_mysqlnd_rowp_read_text_protocol_aux
+ We're allocating an extra byte, as php_mysqlnd_rowp_read_text_protocol
needs to be able to append a terminating \0 for atoi/atof.
*/
prealloc_more_bytes = 1;
@@ -1525,7 +1525,7 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fi
/* {{{ php_mysqlnd_rowp_read_text_protocol */
enum_func_status
-php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_ROW_BUFFER * row_buffer, zval * fields,
+php_mysqlnd_rowp_read_text_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fields,
unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
zend_bool as_int_or_float, MYSQLND_STATS * stats)
{
@@ -1535,7 +1535,7 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_ROW_BUFFER * row_buffer, zval *
const size_t data_size = row_buffer->size;
const zend_uchar * const packet_end = (zend_uchar*) p + data_size;
- DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_aux");
+ DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
if (!fields) {
DBG_RETURN(FAIL);
@@ -1667,34 +1667,6 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_ROW_BUFFER * row_buffer, zval *
/* }}} */
-/* {{{ php_mysqlnd_rowp_read_text_protocol_zval */
-enum_func_status
-php_mysqlnd_rowp_read_text_protocol_zval(MYSQLND_ROW_BUFFER * row_buffer, zval * fields,
- const unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
- const zend_bool as_int_or_float, MYSQLND_STATS * stats)
-{
- enum_func_status ret;
- DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_zval");
- ret = php_mysqlnd_rowp_read_text_protocol_aux(row_buffer, fields, field_count, fields_metadata, as_int_or_float, stats);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
-/* {{{ php_mysqlnd_rowp_read_text_protocol_c */
-enum_func_status
-php_mysqlnd_rowp_read_text_protocol_c(MYSQLND_ROW_BUFFER * row_buffer, zval * fields,
- const unsigned int field_count, const MYSQLND_FIELD * const fields_metadata,
- const zend_bool as_int_or_float, MYSQLND_STATS * const stats)
-{
- enum_func_status ret;
- DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_c");
- ret = php_mysqlnd_rowp_read_text_protocol_aux(row_buffer, fields, field_count, fields_metadata, as_int_or_float, stats);
- DBG_RETURN(ret);
-}
-/* }}} */
-
-
/* {{{ php_mysqlnd_rowp_read */
static enum_func_status
php_mysqlnd_rowp_read(MYSQLND_CONN_DATA * conn, void * _packet)
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h
index 28d521644e..7cbfe0afd6 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.h
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.h
@@ -308,11 +308,7 @@ enum_func_status php_mysqlnd_rowp_read_binary_protocol(MYSQLND_ROW_BUFFER * row_
zend_bool as_int_or_float, MYSQLND_STATS * stats);
-enum_func_status php_mysqlnd_rowp_read_text_protocol_zval(MYSQLND_ROW_BUFFER * row_buffer, zval * fields,
- unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
- zend_bool as_int_or_float, MYSQLND_STATS * stats);
-
-enum_func_status php_mysqlnd_rowp_read_text_protocol_c(MYSQLND_ROW_BUFFER * row_buffer, zval * fields,
+enum_func_status php_mysqlnd_rowp_read_text_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fields,
unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
zend_bool as_int_or_float, MYSQLND_STATS * stats);
diff --git a/ext/mysqlnd/php_mysqlnd.c b/ext/mysqlnd/php_mysqlnd.c
index 15be407414..0152abfdf9 100644
--- a/ext/mysqlnd/php_mysqlnd.c
+++ b/ext/mysqlnd/php_mysqlnd.c
@@ -153,7 +153,6 @@ static PHP_GINIT_FUNCTION(mysqlnd)
mysqlnd_globals->debug_calloc_fail_threshold = -1;
mysqlnd_globals->debug_realloc_fail_threshold = -1;
mysqlnd_globals->sha256_server_public_key = NULL;
- mysqlnd_globals->fetch_data_copy = FALSE;
}
/* }}} */
@@ -186,7 +185,6 @@ PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("mysqlnd.log_mask", "0", PHP_INI_ALL, OnUpdateLong, log_mask, zend_mysqlnd_globals, mysqlnd_globals)
STD_PHP_INI_ENTRY("mysqlnd.mempool_default_size","16000", PHP_INI_ALL, OnUpdateLong, mempool_default_size, zend_mysqlnd_globals, mysqlnd_globals)
STD_PHP_INI_ENTRY("mysqlnd.sha256_server_public_key",NULL, PHP_INI_PERDIR, OnUpdateString, sha256_server_public_key, zend_mysqlnd_globals, mysqlnd_globals)
- STD_PHP_INI_BOOLEAN("mysqlnd.fetch_data_copy", "0", PHP_INI_ALL, OnUpdateBool, fetch_data_copy, zend_mysqlnd_globals, mysqlnd_globals)
#if PHP_DEBUG
STD_PHP_INI_ENTRY("mysqlnd.debug_malloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_malloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals)