Skip to content

Commit

Permalink
MySQLnd: Clean up and optimize mysqlnd result set handling
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nikic committed Dec 17, 2020
1 parent 890e4ca commit 33e9049
Show file tree
Hide file tree
Showing 17 changed files with 257 additions and 1,073 deletions.
5 changes: 5 additions & 0 deletions UPGRADING
Expand Up @@ -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.
Expand Down
6 changes: 1 addition & 5 deletions ext/mysqli/mysqli_api.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
11 changes: 3 additions & 8 deletions ext/mysqli/mysqli_nonapi.c
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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() */
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion ext/mysqli/mysqli_warning.c
Expand Up @@ -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;
Expand Down
6 changes: 2 additions & 4 deletions ext/mysqlnd/mysqlnd.h
Expand Up @@ -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))
Expand Down Expand Up @@ -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)
Expand Down
29 changes: 5 additions & 24 deletions ext/mysqlnd/mysqlnd_connection.c
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1810,7 +1810,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * cons

/* {{{ 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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -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);
}
Expand Down
12 changes: 0 additions & 12 deletions ext/mysqlnd/mysqlnd_enum_n_def.h
Expand Up @@ -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 */
21 changes: 3 additions & 18 deletions ext/mysqlnd/mysqlnd_ext_plugin.c
Expand Up @@ -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 *)));
}
/* }}} */

Expand Down Expand Up @@ -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,
Expand Down
6 changes: 2 additions & 4 deletions ext/mysqlnd/mysqlnd_ext_plugin.h
Expand Up @@ -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);
Expand All @@ -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))
Expand Down

0 comments on commit 33e9049

Please sign in to comment.