From 6b804b96b8c454954d30ed8f0cf1a05d91801c4f Mon Sep 17 00:00:00 2001 From: Andrey Hristov Date: Wed, 5 Mar 2014 16:22:23 +0200 Subject: Refactor the result set data structures. Move more to the buffered and unbuffered substructures. Add methods to these too. Preparing for pluggable interface for returning data to the engine (zvals, c-style, something else) --- ext/mysqlnd/mysqlnd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'ext/mysqlnd/mysqlnd.c') diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 39813145ba..ab38683786 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -1476,8 +1476,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, list_fields)(MYSQLND_CONN_DATA * conn, const c } result->type = MYSQLND_RES_NORMAL; - result->m.fetch_row = result->m.fetch_row_normal_unbuffered; - result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED)); + result->unbuf = mysqlnd_result_unbuffered_init(result->field_count, FALSE, result->persistent TSRMLS_CC); if (!result->unbuf) { /* OOM */ SET_OOM_ERROR(*conn->error_info); -- cgit v1.2.1 From 990f9c8f3f8ce926c9b82538be6dc538e10b6405 Mon Sep 17 00:00:00 2001 From: Andrey Hristov Date: Mon, 10 Mar 2014 17:25:53 +0200 Subject: Switch from a single flag to a flag byte --- ext/mysqlnd/mysqlnd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'ext/mysqlnd/mysqlnd.c') diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index ab38683786..3c1bc82f44 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -689,7 +689,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands)(MYSQLND_CONN_DATA * con break; } if (conn->last_query_type == QUERY_SELECT) { - MYSQLND_RES * result = conn->m->use_result(conn TSRMLS_CC); + MYSQLND_RES * result = conn->m->use_result(conn, 0 TSRMLS_CC); if (result) { result->m.free_result(result, TRUE TSRMLS_CC); } @@ -1522,7 +1522,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 TSRMLS_CC)) { - result = conn->m->store_result(conn TSRMLS_CC); + result = conn->m->store_result(conn, MYSQLND_STORE_NO_COPY TSRMLS_CC); } if (show_query != query) { mnd_sprintf_free(show_query); @@ -2518,7 +2518,7 @@ end: /* {{{ mysqlnd_conn_data::use_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC) { size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, use_result); MYSQLND_RES * result = NULL; @@ -2560,7 +2560,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn TSR /* {{{ mysqlnd_conn_data::store_result */ static MYSQLND_RES * -MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC) { size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, store_result); MYSQLND_RES * result = NULL; @@ -2583,7 +2583,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn T MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS); - result = conn->current_result->m.store_result(conn->current_result, conn, FALSE TSRMLS_CC); + result = conn->current_result->m.store_result(conn->current_result, conn, 0 TSRMLS_CC); if (!result) { conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC); } -- cgit v1.2.1 From d63ed101ee7bb632c54504b1278c79414a75a792 Mon Sep 17 00:00:00 2001 From: Andrey Hristov Date: Wed, 19 Mar 2014 18:32:19 +0200 Subject: Fix problem with mysqli_commt()/mysqli_rollback() --- ext/mysqlnd/mysqlnd.c | 59 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 12 deletions(-) (limited to 'ext/mysqlnd/mysqlnd.c') diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 39813145ba..bbfc456896 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -2652,24 +2652,24 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string)(const MYSQLND_CONN_D { if (mode & TRANS_COR_AND_CHAIN && !(mode & TRANS_COR_AND_NO_CHAIN)) { if (str->len) { - smart_str_appendl(str, ", ", sizeof(", ") - 1); + smart_str_appendl(str, " ", sizeof(" ") - 1); } smart_str_appendl(str, "AND CHAIN", sizeof("AND CHAIN") - 1); } else if (mode & TRANS_COR_AND_NO_CHAIN && !(mode & TRANS_COR_AND_CHAIN)) { if (str->len) { - smart_str_appendl(str, ", ", sizeof(", ") - 1); + smart_str_appendl(str, " ", sizeof(" ") - 1); } smart_str_appendl(str, "AND NO CHAIN", sizeof("AND NO CHAIN") - 1); } if (mode & TRANS_COR_RELEASE && !(mode & TRANS_COR_NO_RELEASE)) { if (str->len) { - smart_str_appendl(str, ", ", sizeof(", ") - 1); + smart_str_appendl(str, " ", sizeof(" ") - 1); } smart_str_appendl(str, "RELEASE", sizeof("RELEASE") - 1); } else if (mode & TRANS_COR_NO_RELEASE && !(mode & TRANS_COR_RELEASE)) { if (str->len) { - smart_str_appendl(str, ", ", sizeof(", ") - 1); + smart_str_appendl(str, " ", sizeof(" ") - 1); } smart_str_appendl(str, "NO RELEASE", sizeof("NO RELEASE") - 1); } @@ -2692,23 +2692,58 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * con conn->m->tx_cor_options_to_string(conn, &tmp_str, flags TSRMLS_CC); smart_str_0(&tmp_str); + { - char * commented_name = NULL; - unsigned int commented_name_len = name? mnd_sprintf(&commented_name, 0, " /*%s*/", name):0; char * query; - unsigned int query_len = mnd_sprintf(&query, 0, (commit? "COMMIT%s %s":"ROLLBACK%s %s"), - commented_name? commented_name:"", tmp_str.c? tmp_str.c:""); - smart_str_free(&tmp_str); + char * name_esc = NULL; + size_t query_len; + + if (name) { + const char * p_orig = name; + char * p_copy; + p_copy = name_esc = mnd_emalloc(strlen(name) + 1 + 2 + 2 + 1); /* space, open, close, NullS */ + *p_copy++ = ' '; + *p_copy++ = '/'; + *p_copy++ = '*'; + while (1) { + register char v = *p_orig; + if (v == 0) { + break; + } + if ((v >= '0' && v <= '9') || + (v >= 'a' && v <= 'z') || + (v >= 'A' && v <= 'Z') || + v == '-' || + v == '_' || + v == ' ' || + v == '=') + { + *p_copy = v; + } else { + *p_copy = '?'; + } + ++p_orig; + ++p_copy; + } + *p_copy++ = '*'; + *p_copy++ = '/'; + *p_copy++ = 0; + } + query_len = mnd_sprintf(&query, 0, (commit? "COMMIT%s %s":"ROLLBACK%s %s"), + name_esc? name_esc:"", tmp_str.c? tmp_str.c:""); + smart_str_free(&tmp_str); if (!query) { SET_OOM_ERROR(*conn->error_info); break; } + if (name_esc) { + mnd_efree(name_esc); + name_esc = NULL; + } + ret = conn->m->query(conn, query, query_len TSRMLS_CC); mnd_sprintf_free(query); - if (commented_name) { - mnd_sprintf_free(commented_name); - } } } while (0); conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC); -- cgit v1.2.1 From 059bc99d941a9984c88b80562e39421850fb2690 Mon Sep 17 00:00:00 2001 From: Andrey Hristov Date: Thu, 20 Mar 2014 12:11:16 +0200 Subject: don't replace with ?, just skip it --- ext/mysqlnd/mysqlnd.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'ext/mysqlnd/mysqlnd.c') diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index bbfc456896..c138c50ed9 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -2718,12 +2718,9 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * con v == ' ' || v == '=') { - *p_copy = v; - } else { - *p_copy = '?'; + *p_copy++ = v; } ++p_orig; - ++p_copy; } *p_copy++ = '*'; *p_copy++ = '/'; -- cgit v1.2.1 From 41b4b84ddadee26445b14556b9fdd01758ea3632 Mon Sep 17 00:00:00 2001 From: Andrey Hristov Date: Thu, 20 Mar 2014 16:23:40 +0200 Subject: Emit a warning in case of unallowed characters. Fix another place this code is used - reuse --- ext/mysqlnd/mysqlnd.c | 123 +++++++++++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 52 deletions(-) (limited to 'ext/mysqlnd/mysqlnd.c') diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index c138c50ed9..1222a7f302 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -2678,6 +2678,49 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string)(const MYSQLND_CONN_D /* }}} */ +/* {{{ mysqlnd_escape_string_for_tx_name_in_comment */ +static char * +mysqlnd_escape_string_for_tx_name_in_comment(const char * const name TSRMLS_DC) +{ + char * ret = NULL; + DBG_ENTER("mysqlnd_escape_string_for_tx_name_in_comment"); + if (name) { + zend_bool warned = FALSE; + const char * p_orig = name; + char * p_copy; + p_copy = ret = mnd_emalloc(strlen(name) + 1 + 2 + 2 + 1); /* space, open, close, NullS */ + *p_copy++ = ' '; + *p_copy++ = '/'; + *p_copy++ = '*'; + while (1) { + register char v = *p_orig; + if (v == 0) { + break; + } + if ((v >= '0' && v <= '9') || + (v >= 'a' && v <= 'z') || + (v >= 'A' && v <= 'Z') || + v == '-' || + v == '_' || + v == ' ' || + v == '=') + { + *p_copy++ = v; + } else if (warned == FALSE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Transaction name truncated. Must be only [0-9A-Za-z\\-_=]+"); + warned = TRUE; + } + ++p_orig; + } + *p_copy++ = '*'; + *p_copy++ = '/'; + *p_copy++ = 0; + } + DBG_RETURN(ret); +} +/* }}} */ + + /* {{{ mysqlnd_conn_data::tx_commit_ex */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * conn, const zend_bool commit, const unsigned int flags, const char * const name TSRMLS_DC) @@ -2695,49 +2738,20 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * con { char * query; - char * name_esc = NULL; size_t query_len; - - if (name) { - const char * p_orig = name; - char * p_copy; - p_copy = name_esc = mnd_emalloc(strlen(name) + 1 + 2 + 2 + 1); /* space, open, close, NullS */ - *p_copy++ = ' '; - *p_copy++ = '/'; - *p_copy++ = '*'; - while (1) { - register char v = *p_orig; - if (v == 0) { - break; - } - if ((v >= '0' && v <= '9') || - (v >= 'a' && v <= 'z') || - (v >= 'A' && v <= 'Z') || - v == '-' || - v == '_' || - v == ' ' || - v == '=') - { - *p_copy++ = v; - } - ++p_orig; - } - *p_copy++ = '*'; - *p_copy++ = '/'; - *p_copy++ = 0; - } - + char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name TSRMLS_CC); + query_len = mnd_sprintf(&query, 0, (commit? "COMMIT%s %s":"ROLLBACK%s %s"), name_esc? name_esc:"", tmp_str.c? tmp_str.c:""); smart_str_free(&tmp_str); - if (!query) { - SET_OOM_ERROR(*conn->error_info); - break; - } if (name_esc) { mnd_efree(name_esc); name_esc = NULL; } + if (!query) { + SET_OOM_ERROR(*conn->error_info); + break; + } ret = conn->m->query(conn, query, query_len TSRMLS_CC); mnd_sprintf_free(query); @@ -2768,36 +2782,41 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsi } smart_str_appendl(&tmp_str, "WITH CONSISTENT SNAPSHOT", sizeof("WITH CONSISTENT SNAPSHOT") - 1); } - if (mode & TRANS_START_READ_WRITE) { - if (tmp_str.len) { - smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1); - } - smart_str_appendl(&tmp_str, "READ WRITE", sizeof("READ WRITE") - 1); - } - if (mode & TRANS_START_READ_ONLY) { - if (tmp_str.len) { - smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1); + if (mode & (TRANS_START_READ_WRITE | TRANS_START_READ_ONLY)) { + unsigned long server_version = conn->m->get_server_version(conn TSRMLS_CC); + if (server_version < 50605L) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "This server version doesn't support 'READ WRITE' and 'READ ONLY'. Minimum 5.6.5 is required"); + smart_str_free(&tmp_str); + break; + } else if (mode & TRANS_START_READ_WRITE) { + if (tmp_str.len) { + smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1); + } + smart_str_appendl(&tmp_str, "READ WRITE", sizeof("READ WRITE") - 1); + } else if (mode & TRANS_START_READ_ONLY) { + if (tmp_str.len) { + smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1); + } + smart_str_appendl(&tmp_str, "READ ONLY", sizeof("READ ONLY") - 1); } - smart_str_appendl(&tmp_str, "READ ONLY", sizeof("READ ONLY") - 1); } smart_str_0(&tmp_str); { - char * commented_name = NULL; - unsigned int commented_name_len = name? mnd_sprintf(&commented_name, 0, " /*%s*/", name):0; + char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name TSRMLS_CC); char * query; - unsigned int query_len = mnd_sprintf(&query, 0, "START TRANSACTION%s %s", commented_name? commented_name:"", tmp_str.c? tmp_str.c:""); + unsigned int query_len = mnd_sprintf(&query, 0, "START TRANSACTION%s %s", name_esc? name_esc:"", tmp_str.c? tmp_str.c:""); smart_str_free(&tmp_str); - + if (name_esc) { + mnd_efree(name_esc); + name_esc = NULL; + } if (!query) { SET_OOM_ERROR(*conn->error_info); break; } ret = conn->m->query(conn, query, query_len TSRMLS_CC); mnd_sprintf_free(query); - if (commented_name) { - mnd_sprintf_free(commented_name); - } } } while (0); conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC); -- cgit v1.2.1 From 63791d055ad64762c3f63e08ca7ad8ba1f44e0ab Mon Sep 17 00:00:00 2001 From: Andrey Hristov Date: Thu, 10 Apr 2014 16:44:54 +0300 Subject: New result fetching mode for mysqlnd, which should use less memory but implies more memory copy. The old method is still available and can be used. It stays as default. Choosing the method is through a flag to mysqli_query()/mysqli_real_query() New mode can be forced with an INI setting, for all extensions that support this mode (ext/mysql and mysqli, because PDO due to it's architecture can't support it) The setting is mysqlnd.fetch_data_copy=[0|1] --- ext/mysqlnd/mysqlnd.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 5 deletions(-) (limited to 'ext/mysqlnd/mysqlnd.c') diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 3c1bc82f44..e64efe9122 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -1111,7 +1111,8 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn_handle, const char * db, unsigned int db_len, unsigned int port, const char * socket_or_pipe, - unsigned int mysql_flags + unsigned int mysql_flags, + unsigned int client_api_flags TSRMLS_DC) { enum_func_status ret = FAIL; @@ -1122,7 +1123,7 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn_handle, if (!conn_handle) { self_alloced = TRUE; - if (!(conn_handle = mysqlnd_init(FALSE))) { + if (!(conn_handle = mysqlnd_init(client_api_flags, FALSE))) { /* OOM */ DBG_RETURN(NULL); } @@ -2570,6 +2571,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) { do { + unsigned int f = flags; if (!conn->current_result) { break; } @@ -2583,7 +2585,24 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS); - result = conn->current_result->m.store_result(conn->current_result, conn, 0 TSRMLS_CC); + /* overwrite */ + if ((conn->m->get_client_api_capabilities(conn TSRMLS_CC) & 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 TSRMLS_CC); if (!result) { conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC); } @@ -2839,6 +2858,32 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn /* }}} */ +/* {{{ mysqlnd_conn_data::negotiate_client_api_capabilities */ +static unsigned int +MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC) +{ + unsigned int ret = 0; + DBG_ENTER("mysqlnd_conn_data::negotiate_client_api_capabilities"); + if (conn) { + ret = conn->client_api_capabilities; + conn->client_api_capabilities = flags; + } + + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_conn_data::get_client_api_capabilities */ +static unsigned int +MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC) +{ + DBG_ENTER("mysqlnd_conn_data::get_client_api_capabilities"); + DBG_RETURN(conn? conn->client_api_capabilities : 0); +} +/* }}} */ + + /* {{{ mysqlnd_conn_data::local_tx_start */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, size_t this_func TSRMLS_DC) @@ -2967,7 +3012,10 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data) MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request), MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name), - MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d) + MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d), + + MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities), + MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities) MYSQLND_CLASS_METHODS_END; @@ -3046,11 +3094,14 @@ MYSQLND_CLASS_METHODS_END; /* {{{ _mysqlnd_init */ PHPAPI MYSQLND * -_mysqlnd_init(zend_bool persistent TSRMLS_DC) +_mysqlnd_init(unsigned int flags, zend_bool persistent TSRMLS_DC) { MYSQLND * ret; DBG_ENTER("mysqlnd_init"); ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_connection(persistent TSRMLS_CC); + if (ret && ret->data) { + ret->data->m->negotiate_client_api_capabilities(ret->data, flags TSRMLS_CC); + } DBG_RETURN(ret); } /* }}} */ -- cgit v1.2.1