summaryrefslogtreecommitdiff
path: root/ext/mysqlnd
diff options
context:
space:
mode:
Diffstat (limited to 'ext/mysqlnd')
-rw-r--r--ext/mysqlnd/config9.m412
-rw-r--r--ext/mysqlnd/mysqlnd.c103
-rw-r--r--ext/mysqlnd/mysqlnd.h16
-rw-r--r--ext/mysqlnd/mysqlnd_charset.c50
-rw-r--r--ext/mysqlnd/mysqlnd_enum_n_def.h84
-rw-r--r--ext/mysqlnd/mysqlnd_ext_plugin.c74
-rw-r--r--ext/mysqlnd/mysqlnd_ext_plugin.h15
-rw-r--r--ext/mysqlnd/mysqlnd_net.c5
-rw-r--r--ext/mysqlnd/mysqlnd_portability.h107
-rw-r--r--ext/mysqlnd/mysqlnd_priv.h6
-rw-r--r--ext/mysqlnd/mysqlnd_ps.c244
-rw-r--r--ext/mysqlnd/mysqlnd_ps_codec.c350
-rw-r--r--ext/mysqlnd/mysqlnd_result.c1228
-rw-r--r--ext/mysqlnd/mysqlnd_result.h3
-rw-r--r--ext/mysqlnd/mysqlnd_result_meta.c14
-rw-r--r--ext/mysqlnd/mysqlnd_reverse_api.c7
-rw-r--r--ext/mysqlnd/mysqlnd_reverse_api.h3
-rw-r--r--ext/mysqlnd/mysqlnd_structs.h188
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.c48
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.h7
-rw-r--r--ext/mysqlnd/php_mysqlnd.c21
21 files changed, 1692 insertions, 893 deletions
diff --git a/ext/mysqlnd/config9.m4 b/ext/mysqlnd/config9.m4
index 09aca5af8a..0e08b977af 100644
--- a/ext/mysqlnd/config9.m4
+++ b/ext/mysqlnd/config9.m4
@@ -48,16 +48,4 @@ fi
if test "$PHP_MYSQLND" != "no" || test "$PHP_MYSQLND_ENABLED" = "yes" || test "$PHP_MYSQLI" != "no"; then
PHP_ADD_BUILD_DIR([ext/mysqlnd], 1)
-
- dnl This creates a file so it has to be after above macros
- PHP_CHECK_TYPES([int8 uint8 int16 uint16 int32 uint32 uchar ulong int8_t uint8_t int16_t uint16_t int32_t uint32_t int64_t uint64_t], [
- ext/mysqlnd/php_mysqlnd_config.h
- ],[
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#endif
- ])
fi
diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c
index 7e982f56c5..e9cdfaf294 100644
--- a/ext/mysqlnd/mysqlnd.c
+++ b/ext/mysqlnd/mysqlnd.c
@@ -321,6 +321,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request)(MYSQLND_CONN_DATA
DBG_ENTER("mysqlnd_conn_data::simple_command_send_request");
DBG_INF_FMT("command=%s silent=%u", mysqlnd_command_to_text[command], silent);
DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
+ DBG_INF_FMT("sending %u bytes", arg_len + 1); /* + 1 is for the command */
switch (CONN_GET_STATE(conn)) {
case CONN_READY:
@@ -448,6 +449,31 @@ mysqlnd_switch_to_ssl_if_needed(
const MYSQLND_CHARSET * charset;
MYSQLND_PACKET_AUTH * auth_packet;
DBG_ENTER("mysqlnd_switch_to_ssl_if_needed");
+ DBG_INF_FMT("client_capability_flags=%lu", mysql_flags);
+ DBG_INF_FMT("CLIENT_LONG_PASSWORD= %d", mysql_flags & CLIENT_LONG_PASSWORD? 1:0);
+ DBG_INF_FMT("CLIENT_FOUND_ROWS= %d", mysql_flags & CLIENT_FOUND_ROWS? 1:0);
+ DBG_INF_FMT("CLIENT_LONG_FLAG= %d", mysql_flags & CLIENT_LONG_FLAG? 1:0);
+ DBG_INF_FMT("CLIENT_NO_SCHEMA= %d", mysql_flags & CLIENT_NO_SCHEMA? 1:0);
+ DBG_INF_FMT("CLIENT_COMPRESS= %d", mysql_flags & CLIENT_COMPRESS? 1:0);
+ DBG_INF_FMT("CLIENT_ODBC= %d", mysql_flags & CLIENT_ODBC? 1:0);
+ DBG_INF_FMT("CLIENT_LOCAL_FILES= %d", mysql_flags & CLIENT_LOCAL_FILES? 1:0);
+ DBG_INF_FMT("CLIENT_IGNORE_SPACE= %d", mysql_flags & CLIENT_IGNORE_SPACE? 1:0);
+ DBG_INF_FMT("CLIENT_PROTOCOL_41= %d", mysql_flags & CLIENT_PROTOCOL_41? 1:0);
+ DBG_INF_FMT("CLIENT_INTERACTIVE= %d", mysql_flags & CLIENT_INTERACTIVE? 1:0);
+ DBG_INF_FMT("CLIENT_SSL= %d", mysql_flags & CLIENT_SSL? 1:0);
+ DBG_INF_FMT("CLIENT_IGNORE_SIGPIPE= %d", mysql_flags & CLIENT_IGNORE_SIGPIPE? 1:0);
+ DBG_INF_FMT("CLIENT_TRANSACTIONS= %d", mysql_flags & CLIENT_TRANSACTIONS? 1:0);
+ DBG_INF_FMT("CLIENT_RESERVED= %d", mysql_flags & CLIENT_RESERVED? 1:0);
+ DBG_INF_FMT("CLIENT_SECURE_CONNECTION=%d", mysql_flags & CLIENT_SECURE_CONNECTION? 1:0);
+ DBG_INF_FMT("CLIENT_MULTI_STATEMENTS=%d", mysql_flags & CLIENT_MULTI_STATEMENTS? 1:0);
+ DBG_INF_FMT("CLIENT_MULTI_RESULTS= %d", mysql_flags & CLIENT_MULTI_RESULTS? 1:0);
+ DBG_INF_FMT("CLIENT_PS_MULTI_RESULTS=%d", mysql_flags & CLIENT_PS_MULTI_RESULTS? 1:0);
+ DBG_INF_FMT("CLIENT_CONNECT_ATTRS= %d", mysql_flags & CLIENT_PLUGIN_AUTH? 1:0);
+ DBG_INF_FMT("CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA= %d", mysql_flags & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA? 1:0);
+ DBG_INF_FMT("CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS= %d", mysql_flags & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS? 1:0);
+ DBG_INF_FMT("CLIENT_SESSION_TRACK= %d", mysql_flags & CLIENT_SESSION_TRACK? 1:0);
+ DBG_INF_FMT("CLIENT_SSL_VERIFY_SERVER_CERT= %d", mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? 1:0);
+ DBG_INF_FMT("CLIENT_REMEMBER_OPTIONS= %d", mysql_flags & CLIENT_REMEMBER_OPTIONS? 1:0);
auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
if (!auth_packet) {
@@ -689,7 +715,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);
}
@@ -1111,7 +1137,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 +1149,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);
}
@@ -1476,8 +1503,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);
@@ -1523,7 +1549,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);
@@ -1863,6 +1889,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn TSR
enum_func_status ret = PASS;
MYSQLND_NET * net = conn->net;
php_stream * net_stream = net->data->m.get_stream(net TSRMLS_CC);
+ enum mysqlnd_connection_state state;
DBG_ENTER("mysqlnd_send_close");
DBG_INF_FMT("conn=%llu net->data->stream->abstract=%p", conn->thread_id, net_stream? net_stream->abstract:NULL);
@@ -1873,7 +1900,9 @@ MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn TSR
MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
}
}
- switch (CONN_GET_STATE(conn)) {
+ state = CONN_GET_STATE(conn);
+ DBG_INF_FMT("state=%u", state);
+ switch (state) {
case CONN_READY:
DBG_INF("Connection clean, sending COM_QUIT");
if (net_stream) {
@@ -2519,7 +2548,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;
@@ -2561,7 +2590,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;
@@ -2571,6 +2600,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn T
if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
do {
+ unsigned int f = flags;
if (!conn->current_result) {
break;
}
@@ -2584,7 +2614,24 @@ 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);
+ /* 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);
}
@@ -2891,6 +2938,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)
@@ -3019,7 +3092,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;
@@ -3098,11 +3174,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);
}
/* }}} */
diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h
index 15d5c781cf..3c688b6bbc 100644
--- a/ext/mysqlnd/mysqlnd.h
+++ b/ext/mysqlnd/mysqlnd.h
@@ -85,15 +85,16 @@ PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const char
/* Connect */
-#define mysqlnd_init(persistent) _mysqlnd_init((persistent) TSRMLS_CC)
-PHPAPI MYSQLND * _mysqlnd_init(zend_bool persistent TSRMLS_DC);
+#define mysqlnd_init(client_flags, persistent) _mysqlnd_init((client_flags), (persistent) TSRMLS_CC)
+PHPAPI MYSQLND * _mysqlnd_init(unsigned int client_flags, zend_bool persistent TSRMLS_DC);
PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn,
const char * host, const char * user,
const char * passwd, unsigned int passwd_len,
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);
#define mysqlnd_change_user(conn, user, passwd, db, silent) ((conn)->data)->m->change_user((conn)->data, (user), (passwd), (db), (silent), strlen((passwd)) TSRMLS_CC)
@@ -119,8 +120,9 @@ PHPAPI void _mysqlnd_debug(const char *mode TSRMLS_DC);
PHPAPI enum_func_status _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num TSRMLS_DC);
-#define mysqlnd_use_result(conn) ((conn)->data)->m->use_result((conn)->data TSRMLS_CC)
-#define mysqlnd_store_result(conn) ((conn)->data)->m->store_result((conn)->data TSRMLS_CC)
+#define mysqlnd_use_result(conn) ((conn)->data)->m->use_result((conn)->data, 0 TSRMLS_CC)
+#define mysqlnd_store_result(conn) ((conn)->data)->m->store_result((conn)->data, MYSQLND_STORE_NO_COPY TSRMLS_CC)
+#define mysqlnd_store_result_ofs(conn) ((conn)->data)->m->store_result((conn)->data, MYSQLND_STORE_COPY TSRMLS_CC)
#define mysqlnd_next_result(conn) ((conn)->data)->m->next_result((conn)->data TSRMLS_CC)
#define mysqlnd_more_results(conn) ((conn)->data)->m->more_results((conn)->data TSRMLS_CC)
#define mysqlnd_free_result(r,e_or_i) ((MYSQLND_RES*)r)->m.free_result(((MYSQLND_RES*)(r)), (e_or_i) TSRMLS_CC)
@@ -150,8 +152,7 @@ PHPAPI enum_func_status _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQ
#define mysqlnd_num_rows(result) (result)->m.num_rows((result) TSRMLS_CC)
#define mysqlnd_num_fields(result) (result)->m.num_fields((result) TSRMLS_CC)
-#define mysqlnd_fetch_lengths(result) _mysqlnd_fetch_lengths((result) TSRMLS_CC)
-PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result TSRMLS_DC);
+#define mysqlnd_fetch_lengths(result) (result)->m.fetch_lengths((result) TSRMLS_CC)
#define mysqlnd_field_seek(result, ofs) (result)->m.seek_field((result), (ofs) TSRMLS_CC)
#define mysqlnd_field_tell(result) (result)->m.field_tell((result) TSRMLS_CC)
@@ -282,6 +283,7 @@ ZEND_BEGIN_MODULE_GLOBALS(mysqlnd)
long debug_calloc_fail_threshold;
long debug_realloc_fail_threshold;
char * sha256_server_public_key;
+ zend_bool fetch_data_copy;
ZEND_END_MODULE_GLOBALS(mysqlnd)
PHPAPI ZEND_EXTERN_MODULE_GLOBALS(mysqlnd)
diff --git a/ext/mysqlnd/mysqlnd_charset.c b/ext/mysqlnd/mysqlnd_charset.c
index a9631f0c74..d23c3251e6 100644
--- a/ext/mysqlnd/mysqlnd_charset.c
+++ b/ext/mysqlnd/mysqlnd_charset.c
@@ -418,20 +418,60 @@ static uint mysqlnd_mbcharlen_utf16(unsigned int utf16)
/* {{{ utf32 functions */
-static uint
-check_mb_utf32(const char *start __attribute((unused)), const char *end __attribute((unused)))
+static unsigned int check_mb_utf32(const char *start __attribute((unused)), const char *end __attribute((unused)))
{
return 4;
}
-static uint
-mysqlnd_mbcharlen_utf32(unsigned int utf32 __attribute((unused)))
+static unsigned int mysqlnd_mbcharlen_utf32(unsigned int utf32 __attribute((unused)))
{
return 4;
}
/* }}} */
+
+/* {{{ gb18030 functions */
+#define is_gb18030_odd(c) (0x81 <= (zend_uchar) (c) && (zend_uchar) (c) <= 0xFE)
+#define is_gb18030_even_2(c) ((0x40 <= (zend_uchar) (c) && (zend_uchar) (c) <= 0x7E) || (0x80 <= (zend_uchar) (c) && (zend_uchar) (c) <= 0xFE))
+#define is_gb18030_even_4(c) (0x30 <= (zend_uchar) (c) && (zend_uchar) (c) <= 0x39)
+
+
+static unsigned int mysqlnd_mbcharlen_gb18030(unsigned int c)
+{
+ if (c <= 0xFF) {
+ return !is_gb18030_odd(c);
+ }
+ if (c > 0xFFFF || !is_gb18030_odd((c >> 8) & 0xFF)) {
+ return 0;
+ }
+ if (is_gb18030_even_2((c & 0xFF))) {
+ return 2;
+ }
+ if (is_gb18030_even_4((c & 0xFF))) {
+ return 4;
+ }
+
+ return 0;
+}
+
+
+static unsigned int my_ismbchar_gb18030(const char * start, const char * end)
+{
+ if (end - start <= 1 || !is_gb18030_odd(start[0])) {
+ return 0;
+ }
+
+ if (is_gb18030_even_2(start[1])) {
+ return 2;
+ } else if (end - start > 3 && is_gb18030_even_4(start[1]) && is_gb18030_odd(start[2]) && is_gb18030_even_4(start[3])) {
+ return 4;
+ }
+
+ return 0;
+}
+/* }}} */
+
/*
The server compiles sometimes the full utf-8 (the mb4) as utf8m4, and the old as utf8,
for BC reasons. Sometimes, utf8mb4 is just utf8 but the old charsets are utf8mb3.
@@ -643,6 +683,8 @@ const MYSQLND_CHARSET mysqlnd_charsets[] =
{ 245, UTF8_MB4, UTF8_MB4"_croatian_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
{ 246, UTF8_MB4, UTF8_MB4"_unicode_520_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
{ 247, UTF8_MB4, UTF8_MB4"_vietnamese_ci", 1, 4, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
+ { 248, "gb18030", "gb18030_chinese_ci", 1, 4, "", mysqlnd_mbcharlen_gb18030, my_ismbchar_gb18030},
+ { 249, "gb18030", "gb18030_bin", 1, 4, "", mysqlnd_mbcharlen_gb18030, my_ismbchar_gb18030},
{ 254, UTF8_MB3, UTF8_MB3"_general_cs", 1, 3, "", mysqlnd_mbcharlen_utf8, check_mb_utf8_valid},
{ 0, NULL, NULL, 0, 0, NULL, NULL, NULL}
diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h
index 59fbaad6b0..300d7a8ced 100644
--- a/ext/mysqlnd/mysqlnd_enum_n_def.h
+++ b/ext/mysqlnd/mysqlnd_enum_n_def.h
@@ -100,11 +100,13 @@
#define CLIENT_CONNECT_ATTRS (1UL << 20) /* Client supports connection attributes */
#define CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA (1UL << 21) /* Enable authentication response packet to be larger than 255 bytes. */
#define CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS (1UL << 22) /* Don't close the connection for a connection with expired password. */
-#define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
+#define CLIENT_SESSION_TRACK (1UL << 23) /* Extended OK */
+#define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
+#define CLIENT_REMEMBER_OPTIONS (1UL << 31)
#define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \
CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \
- CLIENT_MULTI_RESULTS | CLIENT_PS_MULTI_RESULTS | CLIENT_LOCAL_FILES | CLIENT_PLUGIN_AUTH)
+ CLIENT_MULTI_RESULTS | CLIENT_LOCAL_FILES | CLIENT_PLUGIN_AUTH)
#define MYSQLND_NET_FLAG_USE_COMPRESSION 1
@@ -214,23 +216,23 @@ typedef enum mysqlnd_protocol_type
typedef enum mysqlnd_field_types
{
- MYSQL_TYPE_DECIMAL,
- MYSQL_TYPE_TINY,
- MYSQL_TYPE_SHORT,
- MYSQL_TYPE_LONG,
- MYSQL_TYPE_FLOAT,
- MYSQL_TYPE_DOUBLE,
- MYSQL_TYPE_NULL,
- MYSQL_TYPE_TIMESTAMP,
- MYSQL_TYPE_LONGLONG,
- MYSQL_TYPE_INT24,
- MYSQL_TYPE_DATE,
- MYSQL_TYPE_TIME,
- MYSQL_TYPE_DATETIME,
- MYSQL_TYPE_YEAR,
- MYSQL_TYPE_NEWDATE,
- MYSQL_TYPE_VARCHAR,
- MYSQL_TYPE_BIT,
+ MYSQL_TYPE_DECIMAL = 0,
+ MYSQL_TYPE_TINY = 1,
+ MYSQL_TYPE_SHORT = 2,
+ MYSQL_TYPE_LONG = 3,
+ MYSQL_TYPE_FLOAT = 4,
+ MYSQL_TYPE_DOUBLE = 5,
+ MYSQL_TYPE_NULL = 6,
+ MYSQL_TYPE_TIMESTAMP= 7,
+ MYSQL_TYPE_LONGLONG = 8,
+ MYSQL_TYPE_INT24 = 9,
+ MYSQL_TYPE_DATE = 10,
+ MYSQL_TYPE_TIME = 11,
+ MYSQL_TYPE_DATETIME = 12,
+ MYSQL_TYPE_YEAR = 13,
+ MYSQL_TYPE_NEWDATE = 14,
+ MYSQL_TYPE_VARCHAR = 15,
+ MYSQL_TYPE_BIT = 16,
MYSQL_TYPE_NEWDECIMAL=246,
MYSQL_TYPE_ENUM=247,
MYSQL_TYPE_SET=248,
@@ -322,23 +324,23 @@ typedef enum mysqlnd_server_option
typedef enum mysqlnd_connection_state
{
CONN_ALLOCED = 0,
- CONN_READY,
- CONN_QUERY_SENT,
- CONN_SENDING_LOAD_DATA,
- CONN_FETCHING_DATA,
- CONN_NEXT_RESULT_PENDING,
- CONN_QUIT_SENT /* object is "destroyed" at this stage */
+ CONN_READY = 1,
+ CONN_QUERY_SENT = 2,
+ CONN_SENDING_LOAD_DATA = 3,
+ CONN_FETCHING_DATA = 4,
+ CONN_NEXT_RESULT_PENDING = 5,
+ CONN_QUIT_SENT = 6 /* object is "destroyed" at this stage */
} enum_mysqlnd_connection_state;
typedef enum mysqlnd_stmt_state
{
MYSQLND_STMT_INITTED = 0,
- MYSQLND_STMT_PREPARED,
- MYSQLND_STMT_EXECUTED,
- MYSQLND_STMT_WAITING_USE_OR_STORE,
- MYSQLND_STMT_USE_OR_STORE_CALLED,
- MYSQLND_STMT_USER_FETCHING /* fetch_row_buff or fetch_row_unbuf */
+ MYSQLND_STMT_PREPARED = 1,
+ MYSQLND_STMT_EXECUTED = 2,
+ MYSQLND_STMT_WAITING_USE_OR_STORE = 3,
+ MYSQLND_STMT_USE_OR_STORE_CALLED = 4,
+ MYSQLND_STMT_USER_FETCHING = 5/* fetch_row_buff or fetch_row_unbuf */
} enum_mysqlnd_stmt_state;
@@ -596,9 +598,10 @@ enum php_mysqlnd_server_command
COM_STMT_RESET = 26,
COM_SET_OPTION = 27,
COM_STMT_FETCH = 28,
- COM_DAEMON,
- COM_BINLOG_DUMP_GTID,
- COM_RESET_CONNECTION,
+ COM_DAEMON = 29,
+ COM_BINLOG_DUMP_GTID = 30,
+ COM_RESET_CONNECTION = 31,
+ COM_STMT_EXECUTE_BATCH = 32,
COM_END
};
@@ -615,6 +618,21 @@ enum php_mysqlnd_server_command
#define MYSQLND_REFRESH_MASTER 128 /* Remove all bin logs in the index */
#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 45c64926cf..c85e85d60e 100644
--- a/ext/mysqlnd/mysqlnd_ext_plugin.c
+++ b/ext/mysqlnd/mysqlnd_ext_plugin.c
@@ -70,6 +70,44 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result
/* }}} */
+/* {{{ _mysqlnd_plugin_get_plugin_result_unbuffered_data */
+PHPAPI void ** _mysqlnd_plugin_get_plugin_result_unbuffered_data(const MYSQLND_RES_UNBUFFERED * result, unsigned int plugin_id TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
+ DBG_INF_FMT("plugin_id=%u", plugin_id);
+ if (!result || plugin_id >= mysqlnd_plugin_count()) {
+ return NULL;
+ }
+ DBG_RETURN((void *)((char *)result + sizeof(MYSQLND_RES_UNBUFFERED) + plugin_id * sizeof(void *)));
+}
+/* }}} */
+
+
+/* {{{ _mysqlnd_plugin_get_plugin_result_buffered_data */
+PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_zval(const MYSQLND_RES_BUFFERED_ZVAL * result, unsigned int plugin_id TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
+ DBG_INF_FMT("plugin_id=%u", plugin_id);
+ if (!result || plugin_id >= mysqlnd_plugin_count()) {
+ return NULL;
+ }
+ DBG_RETURN((void *)((char *)result + sizeof(MYSQLND_RES_BUFFERED_ZVAL) + plugin_id * sizeof(void *)));
+}
+/* }}} */
+
+/* {{{ _mysqlnd_plugin_get_plugin_result_buffered_data */
+PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_c(const MYSQLND_RES_BUFFERED_C * result, unsigned int plugin_id TSRMLS_DC)
+{
+ DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data");
+ DBG_INF_FMT("plugin_id=%u", plugin_id);
+ if (!result || plugin_id >= mysqlnd_plugin_count()) {
+ return NULL;
+ }
+ DBG_RETURN((void *)((char *)result + sizeof(MYSQLND_RES_BUFFERED_C) + plugin_id * sizeof(void *)));
+}
+/* }}} */
+
+
/* {{{ _mysqlnd_plugin_get_plugin_protocol_data */
PHPAPI void **
_mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id TSRMLS_DC)
@@ -161,6 +199,42 @@ mysqlnd_result_set_methods(struct st_mysqlnd_res_methods * methods)
/* }}} */
+/* {{{ mysqlnd_result_unbuffered_get_methods */
+PHPAPI struct st_mysqlnd_result_unbuffered_methods *
+mysqlnd_result_unbuffered_get_methods()
+{
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_unbuffered);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_unbuffered_set_methods */
+PHPAPI void
+mysqlnd_result_unbuffered_set_methods(struct st_mysqlnd_result_unbuffered_methods * methods)
+{
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_unbuffered) = *methods;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_buffered_get_methods */
+PHPAPI struct st_mysqlnd_result_buffered_methods *
+mysqlnd_result_buffered_get_methods()
+{
+ return &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_buffered);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_buffered_set_methods */
+PHPAPI void
+mysqlnd_result_buffered_set_methods(struct st_mysqlnd_result_buffered_methods * methods)
+{
+ MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_result_buffered) = *methods;
+}
+/* }}} */
+
+
/* {{{ mysqlnd_stmt_get_methods */
PHPAPI struct st_mysqlnd_stmt_methods *
mysqlnd_stmt_get_methods()
diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.h b/ext/mysqlnd/mysqlnd_ext_plugin.h
index 86a36936b2..8208ae5b88 100644
--- a/ext/mysqlnd/mysqlnd_ext_plugin.h
+++ b/ext/mysqlnd/mysqlnd_ext_plugin.h
@@ -31,6 +31,15 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_connection_data_data(const MYSQLND_CON
PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result, unsigned int plugin_id TSRMLS_DC);
#define mysqlnd_plugin_get_plugin_result_data(r, p_id) _mysqlnd_plugin_get_plugin_result_data((r), (p_id) TSRMLS_CC)
+PHPAPI void ** _mysqlnd_plugin_get_plugin_result_unbuffered_data(const MYSQLND_RES_UNBUFFERED * result, unsigned int plugin_id TSRMLS_DC);
+#define mysqlnd_plugin_get_plugin_result_unbuffered_data(r, p_id) _mysqlnd_plugin_get_plugin_result_unbuffered_data((r), (p_id) TSRMLS_CC)
+
+PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_zval(const MYSQLND_RES_BUFFERED_ZVAL * result, unsigned int plugin_id TSRMLS_DC);
+#define mysqlnd_plugin_get_plugin_result_buffered_data_zval(r, p_id) _mysqlnd_plugin_get_plugin_result_buffered_data_zval((r), (p_id) TSRMLS_CC)
+
+PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_c(const MYSQLND_RES_BUFFERED_C * result, unsigned int plugin_id TSRMLS_DC);
+#define mysqlnd_plugin_get_plugin_result_buffered_data_c(r, p_id) _mysqlnd_plugin_get_plugin_result_buffered_data_c((r), (p_id) TSRMLS_CC)
+
PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, unsigned int plugin_id TSRMLS_DC);
#define mysqlnd_plugin_get_plugin_stmt_data(s, p_id) _mysqlnd_plugin_get_plugin_stmt_data((s), (p_id) TSRMLS_CC)
@@ -50,6 +59,12 @@ PHPAPI void mysqlnd_conn_data_set_methods(struct st_mysqlnd_conn_data_methods *
PHPAPI struct st_mysqlnd_res_methods * mysqlnd_result_get_methods();
PHPAPI void mysqlnd_result_set_methods(struct st_mysqlnd_res_methods * methods);
+PHPAPI struct st_mysqlnd_result_unbuffered_methods * mysqlnd_result_unbuffered_get_methods();
+PHPAPI void mysqlnd_result_unbuffered_set_methods(struct st_mysqlnd_result_unbuffered_methods * methods);
+
+PHPAPI struct st_mysqlnd_result_buffered_methods * mysqlnd_result_buffered_get_methods();
+PHPAPI void mysqlnd_result_buffered_set_methods(struct st_mysqlnd_result_buffered_methods * methods);
+
PHPAPI struct st_mysqlnd_stmt_methods * mysqlnd_stmt_get_methods();
PHPAPI void mysqlnd_stmt_set_methods(struct st_mysqlnd_stmt_methods * methods);
diff --git a/ext/mysqlnd/mysqlnd_net.c b/ext/mysqlnd/mysqlnd_net.c
index 83e87cd1ea..8683248e4c 100644
--- a/ext/mysqlnd/mysqlnd_net.c
+++ b/ext/mysqlnd/mysqlnd_net.c
@@ -99,6 +99,7 @@ MYSQLND_METHOD(mysqlnd_net, network_write_ex)(MYSQLND_NET * const net, const zen
{
size_t ret;
DBG_ENTER("mysqlnd_net::network_write_ex");
+ DBG_INF_FMT("sending %u bytes", count);
ret = php_stream_write(net->data->m.get_stream(net TSRMLS_CC), (char *)buffer, count);
DBG_RETURN(ret);
}
@@ -357,6 +358,10 @@ MYSQLND_METHOD(mysqlnd_net, send_ex)(MYSQLND_NET * const net, zend_uchar * const
do {
to_be_sent = MIN(left, MYSQLND_MAX_PACKET_SIZE);
+ DBG_INF_FMT("to_be_sent=%u", to_be_sent);
+ DBG_INF_FMT("packets_sent=%u", packets_sent);
+ DBG_INF_FMT("compressed_envelope_packet_no=%u", net->compressed_envelope_packet_no);
+ DBG_INF_FMT("packet_no=%u", net->packet_no);
#ifdef MYSQLND_COMPRESSION_ENABLED
if (net->data->compressed == TRUE) {
/* here we need to compress the data and then write it, first comes the compressed header */
diff --git a/ext/mysqlnd/mysqlnd_portability.h b/ext/mysqlnd/mysqlnd_portability.h
index b9479150ae..72a156a795 100644
--- a/ext/mysqlnd/mysqlnd_portability.h
+++ b/ext/mysqlnd/mysqlnd_portability.h
@@ -36,8 +36,6 @@ This file is public domain and comes with NO WARRANTY of any kind */
#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32)
# include "ext/mysqlnd/config-win.h"
-#else
-# include <ext/mysqlnd/php_mysqlnd_config.h>
#endif /* _WIN32... */
#if __STDC_VERSION__ < 199901L && !defined(atoll)
@@ -45,14 +43,7 @@ This file is public domain and comes with NO WARRANTY of any kind */
#define atoll atol
#endif
-
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#endif
+#include "php_stdint.h"
#if SIZEOF_LONG_LONG > 4 && !defined(_LONG_LONG)
#define _LONG_LONG 1 /* For AIX string library */
@@ -70,102 +61,6 @@ This file is public domain and comes with NO WARRANTY of any kind */
#define HAVE_LONG_LONG 1
#endif
-
-/* Typdefs for easyier portability */
-#ifndef HAVE_INT8_T
-#ifndef HAVE_INT8
-typedef signed char int8_t; /* Signed integer >= 8 bits */
-#else
-typedef int8 int8_t; /* Signed integer >= 8 bits */
-#endif
-#endif
-
-#ifndef HAVE_UINT8_T
-#ifndef HAVE_UINT8
-typedef unsigned char uint8_t; /* Unsigned integer >= 8 bits */
-#else
-typedef uint8 uint8_t; /* Signed integer >= 8 bits */
-#endif
-#endif
-
-#ifndef HAVE_INT16_T
-#ifndef HAVE_INT16
-typedef signed short int16_t; /* Signed integer >= 16 bits */
-#else
-typedef int16 int16_t; /* Signed integer >= 16 bits */
-#endif
-#endif
-
-#ifndef HAVE_UINT16_T
-#ifndef HAVE_UINT16
-typedef unsigned short uint16_t; /* Signed integer >= 16 bits */
-#else
-typedef uint16 uint16_t; /* Signed integer >= 16 bits */
-#endif
-#endif
-
-
-#ifndef HAVE_INT32_T
-#ifdef HAVE_INT32
-typedef int32 int32_t;
-#elif SIZEOF_INT == 4
-typedef signed int int32_t;
-#elif SIZEOF_LONG == 4
-typedef signed long int32_t;
-#else
-error "Neither int nor long is of 4 bytes width"
-#endif
-#endif /* HAVE_INT32_T */
-
-#ifndef HAVE_UINT32_T
-#ifdef HAVE_UINT32
-typedef uint32 uint32_t;
-#elif SIZEOF_INT == 4
-typedef unsigned int uint32_t;
-#elif SIZEOF_LONG == 4
-typedef unsigned long uint32_t;
-#else
-#error "Neither int nor long is of 4 bytes width"
-#endif
-#endif /* HAVE_UINT32_T */
-
-#ifndef HAVE_INT64_T
-#ifdef HAVE_INT64
-typedef int64 int64_t;
-#elif SIZEOF_INT == 8
-typedef signed int int64_t;
-#elif SIZEOF_LONG == 8
-typedef signed long int64_t;
-#elif SIZEOF_LONG_LONG == 8
-#ifdef PHP_WIN32
-typedef __int64 int64_t;
-#else
-typedef signed long long int64_t;
-#endif
-#else
-#error "Neither int nor long nor long long is of 8 bytes width"
-#endif
-#endif /* HAVE_INT64_T */
-
-#ifndef HAVE_UINT64_T
-#ifdef HAVE_UINT64
-typedef uint64 uint64_t;
-#elif SIZEOF_INT == 8
-typedef unsigned int uint64_t;
-#elif SIZEOF_LONG == 8
-typedef unsigned long uint64_t;
-#elif SIZEOF_LONG_LONG == 8
-#ifdef PHP_WIN32
-typedef unsigned __int64 uint64_t;
-#else
-typedef unsigned long long uint64_t;
-#endif
-#else
-#error "Neither int nor long nor long long is of 8 bytes width"
-#endif
-#endif /* HAVE_INT64_T */
-
-
#ifdef PHP_WIN32
#define MYSQLND_LLU_SPEC "%I64u"
#define MYSQLND_LL_SPEC "%I64d"
diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h
index 887057f17f..5c4342a09b 100644
--- a/ext/mysqlnd/mysqlnd_priv.h
+++ b/ext/mysqlnd/mysqlnd_priv.h
@@ -171,6 +171,10 @@ struct st_mysqlnd_perm_bind {
extern struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
+enum_func_status mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC);
+enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC);
+
+
PHPAPI extern const char * const mysqlnd_old_passwd;
PHPAPI extern const char * const mysqlnd_out_of_sync;
PHPAPI extern const char * const mysqlnd_server_gone;
@@ -180,6 +184,8 @@ PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_object_factory);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_conn);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_conn_data);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_res);
+PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_result_unbuffered);
+PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_result_buffered);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_protocol);
PHPAPI extern MYSQLND_CLASS_METHOD_TABLE_NAME_FORWARD(mysqlnd_net);
diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c
index 4ae8cddfa9..a4faa2dcd7 100644
--- a/ext/mysqlnd/mysqlnd_ps.c
+++ b/ext/mysqlnd/mysqlnd_ps.c
@@ -39,19 +39,9 @@ const char * const mysqlnd_stmt_not_prepared = "Statement not prepared";
/* Exported by mysqlnd_ps_codec.c */
enum_func_status mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC);
-enum_func_status mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param,
- unsigned int flags,
- zend_bool *fetched_anything TSRMLS_DC);
-
-enum_func_status mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param,
- unsigned int flags,
- zend_bool *fetched_anything TSRMLS_DC);
-
static void mysqlnd_stmt_separate_result_bind(MYSQLND_STMT * const stmt TSRMLS_DC);
static void mysqlnd_stmt_separate_one_result_bind(MYSQLND_STMT * const stmt, unsigned int param_no TSRMLS_DC);
-static void MYSQLND_METHOD(mysqlnd_stmt, free_stmt_result)(MYSQLND_STMT * const s TSRMLS_DC);
-
/* {{{ mysqlnd_stmt::store_result */
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC)
@@ -96,15 +86,42 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC)
result = stmt->result;
result->type = MYSQLND_RES_PS_BUF;
- result->m.fetch_row = mysqlnd_stmt_fetch_row_buffered;
- result->m.fetch_lengths = NULL;/* makes no sense */
- result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
+/* result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; */
- result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC);
+ result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result->field_count, TRUE, result->persistent TSRMLS_CC);
+ if (!result->stored_data) {
+ SET_OOM_ERROR(*conn->error_info);
+ DBG_RETURN(NULL);
+ }
+
+ ret = result->m.store_result_fetch_data(conn, result, result->meta, &result->stored_data->row_buffers, TRUE TSRMLS_CC);
- ret = result->m.store_result_fetch_data(conn, result, result->meta, TRUE TSRMLS_CC);
+ result->stored_data->m.fetch_row = mysqlnd_stmt_fetch_row_buffered;
if (PASS == ret) {
+ /* Overflow ? */
+ 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_ZVAL) {
+ /*TODO*/
+ }
+
/* libmysql API docs say it should be so for SELECT statements */
stmt->upsert_status->affected_rows = stmt->result->stored_data->row_count;
@@ -172,7 +189,7 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s TSRMLS_DC)
break;
}
- if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) {
+ if ((result = result->m.store_result(result, conn, MYSQLND_STORE_PS | MYSQLND_STORE_NO_COPY TSRMLS_CC))) {
stmt->upsert_status->affected_rows = result->stored_data->row_count;
stmt->state = MYSQLND_STMT_PREPARED;
result->type = MYSQLND_RES_PS_BUF;
@@ -227,7 +244,7 @@ MYSQLND_METHOD(mysqlnd_stmt, next_result)(MYSQLND_STMT * s TSRMLS_DC)
DBG_INF_FMT("server_status=%u cursor=%u", stmt->upsert_status->server_status, stmt->upsert_status->server_status & SERVER_STATUS_CURSOR_EXISTS);
/* Free space for next result */
- MYSQLND_METHOD(mysqlnd_stmt, free_stmt_result)(s TSRMLS_CC);
+ s->m->free_stmt_result(s TSRMLS_CC);
{
enum_func_status ret = s->m->parse_execute_response(s TSRMLS_CC);
DBG_RETURN(ret);
@@ -522,7 +539,11 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s TSRMLS_DC)
/* Update stmt->field_count as SHOW sets it to 0 at prepare */
stmt->field_count = stmt->result->field_count = conn->field_count;
- stmt->result->lengths = NULL;
+ if (stmt->result->stored_data) {
+ stmt->result->stored_data->lengths = NULL;
+ } else if (stmt->result->unbuf) {
+ stmt->result->unbuf->lengths = NULL;
+ }
if (stmt->field_count) {
stmt->state = MYSQLND_STMT_WAITING_USE_OR_STORE;
/*
@@ -710,94 +731,98 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const s TSRMLS_DC)
/* {{{ mysqlnd_stmt_fetch_row_buffered */
enum_func_status
-mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
+mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
{
MYSQLND_STMT * s = (MYSQLND_STMT *) param;
MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
- MYSQLND_RES_BUFFERED *set = result->stored_data;
- unsigned int field_count = result->meta->field_count;
+ const MYSQLND_RES_METADATA * const meta = result->meta;
+ unsigned int field_count = meta->field_count;
DBG_ENTER("mysqlnd_stmt_fetch_row_buffered");
*fetched_anything = FALSE;
DBG_INF_FMT("stmt=%lu", stmt != NULL ? stmt->stmt_id : 0L);
/* If we haven't read everything */
- if (set->data_cursor &&
- (set->data_cursor - set->data) < (set->row_count * field_count))
- {
- /* The user could have skipped binding - don't crash*/
- if (stmt->result_bind) {
- unsigned int i;
- MYSQLND_RES_METADATA * meta = result->meta;
- zval **current_row = set->data_cursor;
-
- if (NULL == current_row[0]) {
- uint64_t row_num = (set->data_cursor - set->data) / field_count;
- enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
- current_row,
- meta->field_count,
- meta->fields,
- result->conn->options->int_and_float_native,
- result->conn->stats TSRMLS_CC);
- if (PASS != rc) {
- DBG_RETURN(FAIL);
- }
- set->initialized_rows++;
- if (stmt->update_max_length) {
- for (i = 0; i < result->field_count; i++) {
- /*
- NULL fields are 0 length, 0 is not more than 0
- String of zero size, definitely can't be the next max_length.
- Thus for NULL and zero-length we are quite efficient.
- */
- if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
- unsigned long len = Z_STRLEN_P(current_row[i]);
- if (meta->fields[i].max_length < len) {
- meta->fields[i].max_length = len;
+ 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 (NULL == 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 TSRMLS_CC);
+ if (PASS != rc) {
+ DBG_RETURN(FAIL);
+ }
+ result->stored_data->initialized_rows++;
+ if (stmt->update_max_length) {
+ for (i = 0; i < result->field_count; i++) {
+ /*
+ NULL fields are 0 length, 0 is not more than 0
+ String of zero size, definitely can't be the next max_length.
+ Thus for NULL and zero-length we are quite efficient.
+ */
+ if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
+ unsigned long len = Z_STRLEN_P(current_row[i]);
+ if (meta->fields[i].max_length < len) {
+ meta->fields[i].max_length = len;
+ }
}
}
}
}
- }
- for (i = 0; i < result->field_count; i++) {
- /* Clean what we copied last time */
+ for (i = 0; i < result->field_count; i++) {
+ /* Clean what we copied last time */
#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
- if (stmt->result_bind[i].zv) {
- zval_dtor(stmt->result_bind[i].zv);
- }
+ if (stmt->result_bind[i].zv) {
+ zval_dtor(stmt->result_bind[i].zv);
+ }
#endif
- /* copy the type */
- if (stmt->result_bind[i].bound == TRUE) {
- DBG_INF_FMT("i=%u type=%u", i, Z_TYPE_P(current_row[i]));
- if (Z_TYPE_P(current_row[i]) != IS_NULL) {
- /*
- Copy the value.
- Pre-condition is that the zvals in the result_bind buffer
- have been ZVAL_NULL()-ed or to another simple type
- (int, double, bool but not string). Because of the reference
- counting the user can't delete the strings the variables point to.
- */
-
- Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(current_row[i]);
- stmt->result_bind[i].zv->value = current_row[i]->value;
+ /* copy the type */
+ if (stmt->result_bind[i].bound == TRUE) {
+ DBG_INF_FMT("i=%u type=%u", i, Z_TYPE_P(current_row[i]));
+ if (Z_TYPE_P(current_row[i]) != IS_NULL) {
+ /*
+ Copy the value.
+ Pre-condition is that the zvals in the result_bind buffer
+ have been ZVAL_NULL()-ed or to another simple type
+ (int, double, bool but not string). Because of the reference
+ counting the user can't delete the strings the variables point to.
+ */
+
+ Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(current_row[i]);
+ stmt->result_bind[i].zv->value = current_row[i]->value;
#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
- zval_copy_ctor(stmt->result_bind[i].zv);
+ zval_copy_ctor(stmt->result_bind[i].zv);
#endif
- } else {
- ZVAL_NULL(stmt->result_bind[i].zv);
+ } else {
+ ZVAL_NULL(stmt->result_bind[i].zv);
+ }
}
}
}
+ 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");
}
- 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);
@@ -806,13 +831,14 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES *result, void *param, unsigned int f
/* {{{ mysqlnd_stmt_fetch_row_unbuffered */
-static enum_func_status
-mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
+enum_func_status
+mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
{
enum_func_status ret;
MYSQLND_STMT * s = (MYSQLND_STMT *) param;
MYSQLND_STMT_DATA * stmt = s? s->data:NULL;
MYSQLND_PACKET_ROW * row_packet;
+ const MYSQLND_RES_METADATA * const meta = result->meta;
DBG_ENTER("mysqlnd_stmt_fetch_row_unbuffered");
@@ -829,7 +855,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int
DBG_ERR("command out of sync");
DBG_RETURN(FAIL);
}
- if (!(row_packet = result->row_packet)) {
+ if (!(row_packet = result->unbuf->row_packet)) {
DBG_RETURN(FAIL);
}
@@ -838,20 +864,20 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int
/*
If we skip rows (stmt == NULL || stmt->result_bind == NULL) we have to
- result->m.unbuffered_free_last_data() before it. The function returns always true.
+ result->unbuf->m.free_last_data() before it. The function returns always true.
*/
if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
unsigned int i, field_count = result->field_count;
if (!row_packet->skip_extraction) {
- result->m.unbuffered_free_last_data(result TSRMLS_CC);
+ result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
row_packet->fields = NULL;
row_packet->row_buffer = NULL;
- if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer,
+ if (PASS != result->unbuf->m.row_decoder(result->unbuf->last_row_buffer,
result->unbuf->last_row_data,
row_packet->field_count,
row_packet->fields_metadata,
@@ -866,14 +892,14 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int
zval *data = result->unbuf->last_row_data[i];
/*
stmt->result_bind[i].zv has been already destructed
- in result->m.unbuffered_free_last_data()
+ in result->unbuf->m.free_last_data()
*/
#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
zval_dtor(stmt->result_bind[i].zv);
#endif
if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data)) ) {
- if ((Z_TYPE_P(data) == IS_STRING) && (result->meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data))) {
- result->meta->fields[i].max_length = Z_STRLEN_P(data);
+ if ((Z_TYPE_P(data) == IS_STRING) && (meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data))) {
+ meta->fields[i].max_length = Z_STRLEN_P(data);
}
stmt->result_bind[i].zv->value = data->value;
/* copied data, thus also the ownership. Thus null data */
@@ -885,7 +911,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int
} else {
DBG_INF("skipping extraction");
/*
- Data has been allocated and usually result->m.unbuffered_free_last_data()
+ Data has been allocated and usually result->unbuf->m.free_last_data()
frees it but we can't call this function as it will cause problems with
the bound variables. Thus we need to do part of what it does or Zend will
report leaks.
@@ -960,7 +986,7 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s TSRMLS_DC)
result = stmt->result;
result->m.use_result(stmt->result, TRUE TSRMLS_CC);
- result->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor:
+ result->unbuf->m.fetch_row = stmt->cursor_exists? mysqlnd_fetch_stmt_row_cursor:
mysqlnd_stmt_fetch_row_unbuffered;
stmt->state = MYSQLND_STMT_USE_OR_STORE_CALLED;
@@ -974,7 +1000,7 @@ MYSQLND_METHOD(mysqlnd_stmt, use_result)(MYSQLND_STMT * s TSRMLS_DC)
/* {{{ mysqlnd_fetch_row_cursor */
enum_func_status
-mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
+mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
{
enum_func_status ret;
MYSQLND_STMT * s = (MYSQLND_STMT *) param;
@@ -998,7 +1024,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla
DBG_ERR("command out of sync");
DBG_RETURN(FAIL);
}
- if (!(row_packet = result->row_packet)) {
+ if (!(row_packet = result->unbuf->row_packet)) {
DBG_RETURN(FAIL);
}
@@ -1019,17 +1045,18 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla
memset(stmt->upsert_status, 0, sizeof(*stmt->upsert_status));
if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
+ const MYSQLND_RES_METADATA * const meta = result->meta;
unsigned int i, field_count = result->field_count;
if (!row_packet->skip_extraction) {
- result->m.unbuffered_free_last_data(result TSRMLS_CC);
+ result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
row_packet->fields = NULL;
row_packet->row_buffer = NULL;
- if (PASS != result->m.row_decoder(result->unbuf->last_row_buffer,
+ if (PASS != result->unbuf->m.row_decoder(result->unbuf->last_row_buffer,
result->unbuf->last_row_data,
row_packet->field_count,
row_packet->fields_metadata,
@@ -1045,7 +1072,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla
zval *data = result->unbuf->last_row_data[i];
/*
stmt->result_bind[i].zv has been already destructed
- in result->m.unbuffered_free_last_data()
+ in result->unbuf->m.free_last_data()
*/
#ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF
zval_dtor(stmt->result_bind[i].zv);
@@ -1053,8 +1080,8 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla
DBG_INF_FMT("i=%u bound_var=%p type=%u refc=%u", i, stmt->result_bind[i].zv,
Z_TYPE_P(data), Z_REFCOUNT_P(stmt->result_bind[i].zv));
if (IS_NULL != (Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(data))) {
- if ((Z_TYPE_P(data) == IS_STRING) && (result->meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data))) {
- result->meta->fields[i].max_length = Z_STRLEN_P(data);
+ if ((Z_TYPE_P(data) == IS_STRING) && (meta->fields[i].max_length < (unsigned long) Z_STRLEN_P(data))) {
+ meta->fields[i].max_length = Z_STRLEN_P(data);
}
stmt->result_bind[i].zv->value = data->value;
/* copied data, thus also the ownership. Thus null data */
@@ -1065,7 +1092,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla
} else {
DBG_INF("skipping extraction");
/*
- Data has been allocated and usually result->m.unbuffered_free_last_data()
+ Data has been allocated and usually result->unbuf->m.free_last_data()
frees it but we can't call this function as it will cause problems with
the bound variables. Thus we need to do part of what it does or Zend will
report leaks.
@@ -1773,7 +1800,8 @@ MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s TSRMLS_DC)
if (stmt->update_max_length && stmt->result->stored_data) {
/* stored result, we have to update the max_length before we clone the meta data :( */
- stmt->result->m.initialize_result_set_rest(stmt->result TSRMLS_CC);
+ stmt->result->stored_data->m.initialize_result_set_rest(stmt->result->stored_data, stmt->result->meta, stmt->conn->stats,
+ stmt->conn->options->int_and_float_native TSRMLS_CC);
}
/*
TODO: This implementation is kind of a hack,
@@ -1790,8 +1818,7 @@ MYSQLND_METHOD(mysqlnd_stmt, result_metadata)(MYSQLND_STMT * const s TSRMLS_DC)
break;
}
result->type = MYSQLND_RES_NORMAL;
- result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
- result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
+ result->unbuf = mysqlnd_result_unbuffered_init(stmt->field_count, TRUE, result->persistent TSRMLS_CC);
if (!result->unbuf) {
break;
}
@@ -2125,7 +2152,7 @@ MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content)(MYSQLND_STMT * const s TSRMLS_DC
stmt->param_bind = NULL;
}
- MYSQLND_METHOD(mysqlnd_stmt, free_stmt_result)(s TSRMLS_CC);
+ s->m->free_stmt_result(s TSRMLS_CC);
DBG_VOID_RETURN;
}
/* }}} */
@@ -2344,7 +2371,8 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_stmt)
mysqlnd_stmt_execute_generate_request,
mysqlnd_stmt_execute_parse_response,
MYSQLND_METHOD(mysqlnd_stmt, free_stmt_content),
- MYSQLND_METHOD(mysqlnd_stmt, flush)
+ MYSQLND_METHOD(mysqlnd_stmt, flush),
+ MYSQLND_METHOD(mysqlnd_stmt, free_stmt_result)
MYSQLND_CLASS_METHODS_END;
diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c
index a5b3b04e90..9a70af0f36 100644
--- a/ext/mysqlnd/mysqlnd_ps_codec.c
+++ b/ext/mysqlnd/mysqlnd_ps_codec.c
@@ -525,68 +525,70 @@ mysqlnd_stmt_copy_it(zval *** copies, zval * original, unsigned int param_count,
/* }}} */
-/* {{{ mysqlnd_stmt_execute_store_params */
-static enum_func_status
-mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t *buf_len TSRMLS_DC)
+/* {{{ mysqlnd_stmt_free_copies */
+static void
+mysqlnd_stmt_free_copies(MYSQLND_STMT_DATA * stmt, zval ** copies TSRMLS_DC)
{
- MYSQLND_STMT_DATA * stmt = s->data;
- unsigned int i = 0;
- zend_uchar * provided_buffer = *buf;
- size_t left = (*buf_len - (*p - *buf));
- size_t data_size = 0;
- zval **copies = NULL;/* if there are different types */
- enum_func_status ret = FAIL;
- int resend_types_next_time = 0;
- size_t null_byte_offset;
+ if (copies) {
+ unsigned int i;
+ for (i = 0; i < stmt->param_count; i++) {
+ if (copies[i]) {
+ zval_ptr_dtor(&copies[i]);
+ }
+ }
+ mnd_efree(copies);
+ }
+}
+/* }}} */
- DBG_ENTER("mysqlnd_stmt_execute_store_params");
- {
- unsigned int null_count = (stmt->param_count + 7) / 8;
- /* give it some reserved space - 20 bytes */
- if (left < (null_count + 20)) {
- unsigned int offset = *p - *buf;
- zend_uchar *tmp_buf;
- *buf_len = offset + null_count + 20;
- tmp_buf = mnd_emalloc(*buf_len);
- if (!tmp_buf) {
- SET_OOM_ERROR(*stmt->error_info);
- goto end;
- }
- memcpy(tmp_buf, *buf, offset);
- if (*buf != provided_buffer) {
- mnd_efree(*buf);
- }
- *buf = tmp_buf;
+/* {{{ mysqlnd_stmt_execute_check_n_enlarge_buffer */
+static enum_func_status
+mysqlnd_stmt_execute_check_n_enlarge_buffer(zend_uchar **buf, zend_uchar **p, size_t * buf_len, zend_uchar * const provided_buffer, size_t needed_bytes TSRMLS_DC)
+{
+ const size_t overalloc = 5;
+ size_t left = (*buf_len - (*p - *buf));
- /* Update our pos pointer */
- *p = *buf + offset;
+ if (left < (needed_bytes + overalloc)) {
+ size_t offset = *p - *buf;
+ zend_uchar *tmp_buf;
+ *buf_len = offset + needed_bytes + overalloc;
+ tmp_buf = mnd_emalloc(*buf_len);
+ if (!tmp_buf) {
+ return FAIL;
}
- /* put `null` bytes */
- null_byte_offset = *p - *buf;
- memset(*p, 0, null_count);
- *p += null_count;
+ memcpy(tmp_buf, *buf, offset);
+ if (*buf != provided_buffer) {
+ mnd_efree(*buf);
+ }
+ *buf = tmp_buf;
+ /* Update our pos pointer */
+ *p = *buf + offset;
}
+ return PASS;
+}
+/* }}} */
- left = (*buf_len - (*p - *buf));
-/* 1. Store type information */
- /*
- check if need to send the types even if stmt->send_types_to_server is 0. This is because
- if we send "i" (42) then the type will be int and the server will expect int. However, if next
- time we try to send > LONG_MAX, the conversion to string will send a string and the server
- won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
- occur, and force resend for the next execution.
- */
+
+/* {{{ mysqlnd_stmt_execute_prepare_param_types */
+static enum_func_status
+mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval *** copies_param, int * resend_types_next_time TSRMLS_DC)
+{
+ unsigned int i;
+ DBG_ENTER("mysqlnd_stmt_execute_prepare_param_types");
for (i = 0; i < stmt->param_count; i++) {
short current_type = stmt->param_bind[i].type;
+
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
+ zval ** copies;
/* always copy the var, because we do many conversions */
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG &&
- PASS != mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC))
+ PASS != mysqlnd_stmt_copy_it(copies_param, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC))
{
SET_OOM_ERROR(*stmt->error_info);
goto end;
}
+ copies = *copies_param;
/*
if it doesn't fit in a long send it as a string.
Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
@@ -611,7 +613,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
We do transformation here, which will be used later when sending types. The code later relies on this.
*/
if (Z_DVAL_P(tmp_data_copy) > LONG_MAX || Z_DVAL_P(tmp_data_copy) < LONG_MIN) {
- stmt->send_types_to_server = resend_types_next_time = 1;
+ stmt->send_types_to_server = *resend_types_next_time = 1;
convert_to_string_ex(&tmp_data);
} else {
convert_to_long_ex(&tmp_data);
@@ -621,69 +623,63 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
}
}
}
+ DBG_RETURN(PASS);
+end:
+ DBG_RETURN(FAIL);
+}
+/* }}} */
- int1store(*p, stmt->send_types_to_server);
- (*p)++;
-
- if (stmt->send_types_to_server) {
- /* 2 bytes per type, and leave 20 bytes for future use */
- if (left < ((stmt->param_count * 2) + 20)) {
- unsigned int offset = *p - *buf;
- zend_uchar *tmp_buf;
- *buf_len = offset + stmt->param_count * 2 + 20;
- tmp_buf = mnd_emalloc(*buf_len);
- if (!tmp_buf) {
- SET_OOM_ERROR(*stmt->error_info);
- goto end;
- }
- memcpy(tmp_buf, *buf, offset);
- if (*buf != provided_buffer) {
- mnd_efree(*buf);
- }
- *buf = tmp_buf;
- /* Update our pos pointer */
- *p = *buf + offset;
- }
- for (i = 0; i < stmt->param_count; i++) {
- short current_type = stmt->param_bind[i].type;
- /* our types are not unsigned */
+/* {{{ mysqlnd_stmt_execute_store_types */
+static void
+mysqlnd_stmt_execute_store_types(MYSQLND_STMT_DATA * stmt, zval ** copies, zend_uchar ** p)
+{
+ unsigned int i;
+ for (i = 0; i < stmt->param_count; i++) {
+ short current_type = stmt->param_bind[i].type;
+ /* our types are not unsigned */
#if SIZEOF_LONG==8
- if (current_type == MYSQL_TYPE_LONG) {
- current_type = MYSQL_TYPE_LONGLONG;
- }
+ if (current_type == MYSQL_TYPE_LONG) {
+ current_type = MYSQL_TYPE_LONGLONG;
+ }
#endif
- if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
+ if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
+ /*
+ if it doesn't fit in a long send it as a string.
+ Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
+ */
+ if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
+ const zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
/*
- if it doesn't fit in a long send it as a string.
- Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
+ In case of IS_LONG we do nothing, it is ok, in case of string, we just need to set current_type.
+ The actual transformation has been performed several dozens line above.
*/
- if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
- zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ if (Z_TYPE_P(tmp_data) == IS_STRING) {
+ current_type = MYSQL_TYPE_VAR_STRING;
/*
- In case of IS_LONG we do nothing, it is ok, in case of string, we just need to set current_type.
- The actual transformation has been performed several dozens line above.
+ don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
+ we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
+ if the type is however not long, then we will do a goto in the next switch.
+ We want to preserve the original bind type given by the user. Thus, we do these hacks.
*/
- if (Z_TYPE_P(tmp_data) == IS_STRING) {
- current_type = MYSQL_TYPE_VAR_STRING;
- /*
- don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
- we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
- if the type is however not long, then we will do a goto in the next switch.
- We want to preserve the original bind type given by the user. Thus, we do these hacks.
- */
- }
}
}
- int2store(*p, current_type);
- *p+= 2;
}
+ int2store(*p, current_type);
+ *p+= 2;
}
- stmt->send_types_to_server = resend_types_next_time;
+}
+/* }}} */
-/* 2. Store data */
- /* 2.1 Calculate how much space we need */
+
+/* {{{ mysqlnd_stmt_execute_calculate_param_values_size */
+static enum_func_status
+mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt, zval *** copies_param, size_t * data_size TSRMLS_DC)
+{
+ unsigned int i;
+ DBG_ENTER("mysqlnd_stmt_execute_calculate_param_values_size");
for (i = 0; i < stmt->param_count; i++) {
+ unsigned short is_longlong = 0;
unsigned int j;
zval *the_var = stmt->param_bind[i].zv;
@@ -693,22 +689,22 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
for (j = i + 1; j < stmt->param_count; j++) {
if (stmt->param_bind[j].zv == the_var) {
/* Double binding of the same zval, make a copy */
- if (!copies || !copies[i]) {
- if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
+ if (!*copies_param || !(*copies_param)[i]) {
+ if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info);
goto end;
}
}
- break;
+ break;
}
}
switch (stmt->param_bind[i].type) {
case MYSQL_TYPE_DOUBLE:
- data_size += 8;
+ *data_size += 8;
if (Z_TYPE_P(the_var) != IS_DOUBLE) {
- if (!copies || !copies[i]) {
- if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
+ if (!*copies_param || !(*copies_param)[i]) {
+ if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info);
goto end;
}
@@ -716,24 +712,17 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
}
break;
case MYSQL_TYPE_LONGLONG:
- {
- zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
- if (Z_TYPE_P(tmp_data) == IS_STRING) {
- goto use_string;
- }
- convert_to_long_ex(&tmp_data);
- }
- data_size += 8;
- break;
+ is_longlong = 4;
+ /* fall-through */
case MYSQL_TYPE_LONG:
{
- zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ zval *tmp_data = (*copies_param && (*copies_param)[i])? (*copies_param)[i]: stmt->param_bind[i].zv;
if (Z_TYPE_P(tmp_data) == IS_STRING) {
goto use_string;
}
convert_to_long_ex(&tmp_data);
}
- data_size += 4;
+ *data_size += 4 + is_longlong;
break;
case MYSQL_TYPE_LONG_BLOB:
if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
@@ -742,58 +731,43 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
Empty string has length of 0, encoded in 1 byte. No real
data will follows after it.
*/
- data_size++;
+ (*data_size)++;
}
break;
case MYSQL_TYPE_VAR_STRING:
use_string:
- data_size += 8; /* max 8 bytes for size */
+ *data_size += 8; /* max 8 bytes for size */
if (Z_TYPE_P(the_var) != IS_STRING) {
- if (!copies || !copies[i]) {
- if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
+ if (!*copies_param || !(*copies_param)[i]) {
+ if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i TSRMLS_CC)) {
SET_OOM_ERROR(*stmt->error_info);
goto end;
}
}
- the_var = copies[i];
+ the_var = (*copies_param)[i];
}
convert_to_string_ex(&the_var);
- data_size += Z_STRLEN_P(the_var);
+ *data_size += Z_STRLEN_P(the_var);
break;
}
}
+ DBG_RETURN(PASS);
+end:
+ DBG_RETURN(FAIL);
+}
+/* }}} */
- /* 2.2 Enlarge the buffer, if needed */
- left = (*buf_len - (*p - *buf));
- if (left < data_size) {
- unsigned int offset = *p - *buf;
- zend_uchar *tmp_buf;
- *buf_len = offset + data_size + 10; /* Allocate + 10 for safety */
- tmp_buf = mnd_emalloc(*buf_len);
- if (!tmp_buf) {
- SET_OOM_ERROR(*stmt->error_info);
- goto end;
- }
- memcpy(tmp_buf, *buf, offset);
- /*
- When too many columns the buffer provided to the function might not be sufficient.
- In this case new buffer has been allocated above. When we allocate a buffer and then
- allocate a bigger one here, we should free the first one.
- */
- if (*buf != provided_buffer) {
- mnd_efree(*buf);
- }
- *buf = tmp_buf;
- /* Update our pos pointer */
- *p = *buf + offset;
- }
- /* 2.3 Store the actual data */
+/* {{{ mysqlnd_stmt_execute_store_param_values */
+static void
+mysqlnd_stmt_execute_store_param_values(MYSQLND_STMT_DATA * stmt, zval ** copies, zend_uchar * buf, zend_uchar ** p, size_t null_byte_offset)
+{
+ unsigned int i;
for (i = 0; i < stmt->param_count; i++) {
- zval *data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
+ zval * data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
/* Handle long data */
if (stmt->param_bind[i].zv && Z_TYPE_P(data) == IS_NULL) {
- (*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
+ (buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
} else {
switch (stmt->param_bind[i].type) {
case MYSQL_TYPE_DOUBLE:
@@ -828,7 +802,7 @@ use_string:
case MYSQL_TYPE_VAR_STRING:
send_string:
{
- unsigned int len = Z_STRLEN_P(data);
+ size_t len = Z_STRLEN_P(data);
/* to is after p. The latter hasn't been moved */
*p = php_mysqlnd_net_store_length(*p, len);
memcpy(*p, Z_STRVAL_P(data), len);
@@ -837,22 +811,86 @@ send_string:
break;
default:
/* Won't happen, but set to NULL */
- (*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
+ (buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
break;
}
}
}
- ret = PASS;
-end:
- if (copies) {
- for (i = 0; i < stmt->param_count; i++) {
- if (copies[i]) {
- zval_ptr_dtor(&copies[i]);
- }
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_stmt_execute_store_params */
+static enum_func_status
+mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t *buf_len TSRMLS_DC)
+{
+ MYSQLND_STMT_DATA * stmt = s->data;
+ unsigned int i = 0;
+ zend_uchar * provided_buffer = *buf;
+ size_t data_size = 0;
+ zval **copies = NULL;/* if there are different types */
+ enum_func_status ret = FAIL;
+ int resend_types_next_time = 0;
+ size_t null_byte_offset;
+
+ DBG_ENTER("mysqlnd_stmt_execute_store_params");
+
+ {
+ unsigned int null_count = (stmt->param_count + 7) / 8;
+ if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, null_count TSRMLS_CC)) {
+ SET_OOM_ERROR(*stmt->error_info);
+ goto end;
}
- mnd_efree(copies);
+ /* put `null` bytes */
+ null_byte_offset = *p - *buf;
+ memset(*p, 0, null_count);
+ *p += null_count;
+ }
+
+/* 1. Store type information */
+ /*
+ check if need to send the types even if stmt->send_types_to_server is 0. This is because
+ if we send "i" (42) then the type will be int and the server will expect int. However, if next
+ time we try to send > LONG_MAX, the conversion to string will send a string and the server
+ won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
+ occur, and force resend for the next execution.
+ */
+ if (FAIL == mysqlnd_stmt_execute_prepare_param_types(stmt, &copies, &resend_types_next_time TSRMLS_CC)) {
+ goto end;
+ }
+
+ int1store(*p, stmt->send_types_to_server);
+ (*p)++;
+
+ if (stmt->send_types_to_server) {
+ if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, stmt->param_count * 2 TSRMLS_CC)) {
+ SET_OOM_ERROR(*stmt->error_info);
+ goto end;
+ }
+ mysqlnd_stmt_execute_store_types(stmt, copies, p);
+ }
+
+ stmt->send_types_to_server = resend_types_next_time;
+
+/* 2. Store data */
+ /* 2.1 Calculate how much space we need */
+ if (FAIL == mysqlnd_stmt_execute_calculate_param_values_size(stmt, &copies, &data_size TSRMLS_CC)) {
+ goto end;
+ }
+
+ /* 2.2 Enlarge the buffer, if needed */
+ if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, data_size TSRMLS_CC)) {
+ SET_OOM_ERROR(*stmt->error_info);
+ goto end;
}
+ /* 2.3 Store the actual data */
+ mysqlnd_stmt_execute_store_param_values(stmt, copies, *buf, p, null_byte_offset);
+
+ ret = PASS;
+end:
+ mysqlnd_stmt_free_copies(stmt, copies TSRMLS_CC);
+
DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
DBG_RETURN(ret);
}
diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c
index 25588f7158..847e032a2a 100644
--- a/ext/mysqlnd/mysqlnd_result.c
+++ b/ext/mysqlnd/mysqlnd_result.c
@@ -32,37 +32,39 @@
#define MYSQLND_SILENT
-
-/* {{{ mysqlnd_res::initialize_result_set_rest */
+/* {{{ mysqlnd_result_buffered_zval::initialize_result_set_rest */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest)(MYSQLND_RES * const result TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta,
+ MYSQLND_STATS * stats, zend_bool int_and_float_native TSRMLS_DC)
{
unsigned int i;
- zval **data_cursor = result->stored_data? result->stored_data->data:NULL;
- zval **data_begin = result->stored_data? result->stored_data->data:NULL;
- unsigned int field_count = result->meta? result->meta->field_count : 0;
- uint64_t row_count = result->stored_data? result->stored_data->row_count:0;
enum_func_status ret = PASS;
- DBG_ENTER("mysqlnd_res::initialize_result_set_rest");
+ const unsigned int field_count = meta->field_count;
+ const uint64_t row_count = result->row_count;
+ enum_func_status rc;
+
+ zval **data_begin = ((MYSQLND_RES_BUFFERED_ZVAL *) result)->data;
+ zval **data_cursor = data_begin;
- if (!data_cursor || row_count == result->stored_data->initialized_rows) {
+ DBG_ENTER("mysqlnd_result_buffered_zval::initialize_result_set_rest");
+
+ if (!data_cursor || row_count == result->initialized_rows) {
DBG_RETURN(ret);
}
while ((data_cursor - data_begin) < (int)(row_count * field_count)) {
if (NULL == data_cursor[0]) {
- enum_func_status rc = result->m.row_decoder(
- result->stored_data->row_buffers[(data_cursor - data_begin) / field_count],
+ rc = result->m.row_decoder(result->row_buffers[(data_cursor - data_begin) / field_count],
data_cursor,
- result->meta->field_count,
- result->meta->fields,
- result->conn->options->int_and_float_native,
- result->conn->stats TSRMLS_CC);
+ field_count,
+ meta->fields,
+ int_and_float_native,
+ stats TSRMLS_CC);
if (rc != PASS) {
ret = FAIL;
break;
}
- result->stored_data->initialized_rows++;
- for (i = 0; i < result->field_count; i++) {
+ result->initialized_rows++;
+ for (i = 0; i < field_count; i++) {
/*
NULL fields are 0 length, 0 is not more than 0
String of zero size, definitely can't be the next max_length.
@@ -70,8 +72,8 @@ MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest)(MYSQLND_RES * const resu
*/
if (Z_TYPE_P(data_cursor[i]) >= IS_STRING) {
unsigned long len = Z_STRLEN_P(data_cursor[i]);
- if (result->meta->fields[i].max_length < len) {
- result->meta->fields[i].max_length = len;
+ if (meta->fields[i].max_length < len) {
+ meta->fields[i].max_length = len;
}
}
}
@@ -83,11 +85,68 @@ MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest)(MYSQLND_RES * const resu
/* }}} */
+/* {{{ mysqlnd_result_buffered_c::initialize_result_set_rest */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta,
+ MYSQLND_STATS * stats, zend_bool int_and_float_native TSRMLS_DC)
+{
+ unsigned int i;
+ enum_func_status ret = PASS;
+ const unsigned int field_count = meta->field_count;
+ const uint64_t row_count = result->row_count;
+ enum_func_status rc;
+ DBG_ENTER("mysqlnd_result_buffered_c::initialize_result_set_rest");
+
+ if (result->initialized_rows < row_count) {
+ zend_uchar * initialized = ((MYSQLND_RES_BUFFERED_C *) result)->initialized;
+ zval ** current_row = mnd_emalloc(field_count * sizeof(zval *));
+
+ if (!current_row) {
+ DBG_RETURN(FAIL);
+ }
+
+ for (i = 0; i < result->row_count; i++) {
+ /* (i / 8) & the_bit_for_i*/
+ if (initialized[i >> 3] & (1 << (i & 7))) {
+ continue;
+ }
+
+ rc = result->m.row_decoder(result->row_buffers[i], current_row, field_count, meta->fields, int_and_float_native, stats TSRMLS_CC);
+
+ if (rc != PASS) {
+ ret = FAIL;
+ break;
+ }
+ result->initialized_rows++;
+ initialized[i >> 3] |= (1 << (i & 7));
+ for (i = 0; i < field_count; i++) {
+ /*
+ NULL fields are 0 length, 0 is not more than 0
+ String of zero size, definitely can't be the next max_length.
+ Thus for NULL and zero-length we are quite efficient.
+ */
+ if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
+ unsigned long len = Z_STRLEN_P(current_row[i]);
+ if (meta->fields[i].max_length < len) {
+ meta->fields[i].max_length = len;
+ }
+ }
+ zval_ptr_dtor(&current_row[i]);
+ }
+ }
+ mnd_efree(current_row);
+ }
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
/* {{{ mysqlnd_rset_zval_ptr_dtor */
static void
mysqlnd_rset_zval_ptr_dtor(zval **zv, enum_mysqlnd_res_type type, zend_bool * copy_ctor_called TSRMLS_DC)
{
DBG_ENTER("mysqlnd_rset_zval_ptr_dtor");
+ DBG_INF_FMT("type=%u", type);
if (!zv || !*zv) {
*copy_ctor_called = FALSE;
DBG_ERR_FMT("zv was NULL");
@@ -121,42 +180,42 @@ mysqlnd_rset_zval_ptr_dtor(zval **zv, enum_mysqlnd_res_type type, zend_bool * co
ZVAL_NULL(*zv);
}
}
+ DBG_INF_FMT("call the dtor on zval with refc %u", Z_REFCOUNT_PP(zv));
zval_ptr_dtor(zv);
DBG_VOID_RETURN;
}
/* }}} */
-/* {{{ mysqlnd_res::unbuffered_free_last_data */
+/* {{{ mysqlnd_result_unbuffered::free_last_data */
static void
-MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data)(MYSQLND_RES * result TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_result_unbuffered, free_last_data)(MYSQLND_RES_UNBUFFERED * unbuf, MYSQLND_STATS * const global_stats TSRMLS_DC)
{
- MYSQLND_RES_UNBUFFERED *unbuf = result->unbuf;
-
DBG_ENTER("mysqlnd_res::unbuffered_free_last_data");
if (!unbuf) {
DBG_VOID_RETURN;
}
+ DBG_INF_FMT("field_count=%u", unbuf->field_count);
if (unbuf->last_row_data) {
unsigned int i, ctor_called_count = 0;
zend_bool copy_ctor_called;
- MYSQLND_STATS *global_stats = result->conn? result->conn->stats:NULL;
- for (i = 0; i < result->field_count; i++) {
- mysqlnd_rset_zval_ptr_dtor(&(unbuf->last_row_data[i]), result->type, &copy_ctor_called TSRMLS_CC);
+ for (i = 0; i < unbuf->field_count; i++) {
+ mysqlnd_rset_zval_ptr_dtor(&(unbuf->last_row_data[i]), unbuf->ps ? MYSQLND_RES_PS_UNBUF : MYSQLND_RES_NORMAL, &copy_ctor_called TSRMLS_CC);
if (copy_ctor_called) {
++ctor_called_count;
}
}
+
DBG_INF_FMT("copy_ctor_called_count=%u", ctor_called_count);
/* By using value3 macros we hold a mutex only once, there is no value2 */
MYSQLND_INC_CONN_STATISTIC_W_VALUE2(global_stats,
STAT_COPY_ON_WRITE_PERFORMED,
ctor_called_count,
STAT_COPY_ON_WRITE_SAVED,
- result->field_count - ctor_called_count);
+ unbuf->field_count - ctor_called_count);
/* Free last row's zvals */
mnd_efree(unbuf->last_row_data);
unbuf->last_row_data = NULL;
@@ -173,33 +232,60 @@ MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data)(MYSQLND_RES * result TSRM
/* }}} */
-/* {{{ mysqlnd_res::free_buffered_data */
+/* {{{ mysqlnd_result_unbuffered::free_result */
static void
-MYSQLND_METHOD(mysqlnd_res, free_buffered_data)(MYSQLND_RES * result TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_result_unbuffered, free_result)(MYSQLND_RES_UNBUFFERED * const result, MYSQLND_STATS * const global_stats TSRMLS_DC)
{
- MYSQLND_RES_BUFFERED *set = result->stored_data;
- unsigned int field_count = result->field_count;
- int64_t row;
+ DBG_ENTER("mysqlnd_result_unbuffered, free_result");
+ result->m.free_last_data(result, global_stats TSRMLS_CC);
- DBG_ENTER("mysqlnd_res::free_buffered_data");
- DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count);
+ if (result->lengths) {
+ mnd_pefree(result->lengths, result->persistent);
+ result->lengths = NULL;
+ }
+
+ /* must be free before because references the memory pool */
+ if (result->row_packet) {
+ PACKET_FREE(result->row_packet);
+ result->row_packet = NULL;
+ }
+
+ if (result->result_set_memory_pool) {
+ mysqlnd_mempool_destroy(result->result_set_memory_pool TSRMLS_CC);
+ result->result_set_memory_pool = NULL;
+ }
+
+
+ mnd_pefree(result, result->persistent);
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_buffered_zval::free_result */
+static void
+MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)(MYSQLND_RES_BUFFERED_ZVAL * const set TSRMLS_DC)
+{
+ zval ** data = set->data;
- if (set->data) {
+ DBG_ENTER("mysqlnd_result_buffered_zval::free_result");
+
+ set->data = NULL; /* prevent double free if following loop is interrupted */
+ if (data) {
unsigned int copy_on_write_performed = 0;
unsigned int copy_on_write_saved = 0;
- zval **data = set->data;
- set->data = NULL; /* prevent double free if following loop is interrupted */
-
+ 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;
- MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row];
int64_t col;
if (current_row != NULL) {
for (col = field_count - 1; col >= 0; --col) {
if (current_row[col]) {
zend_bool copy_ctor_called;
- mysqlnd_rset_zval_ptr_dtor(&(current_row[col]), result->type, &copy_ctor_called TSRMLS_CC);
+ mysqlnd_rset_zval_ptr_dtor(&(current_row[col]), set->ps? MYSQLND_RES_PS_BUF : MYSQLND_RES_NORMAL, &copy_ctor_called TSRMLS_CC);
if (copy_ctor_called) {
++copy_on_write_performed;
} else {
@@ -208,22 +294,68 @@ MYSQLND_METHOD(mysqlnd_res, free_buffered_data)(MYSQLND_RES * result TSRMLS_DC)
}
}
}
- current_buffer->free_chunk(current_buffer TSRMLS_CC);
}
-
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_COPY_ON_WRITE_PERFORMED, copy_on_write_performed,
STAT_COPY_ON_WRITE_SAVED, copy_on_write_saved);
mnd_efree(data);
}
+ 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 TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_result_buffered_c::free_result");
+ mnd_pefree(set->initialized, set->persistent);
+ set->initialized = NULL;
+ DBG_VOID_RETURN;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_buffered::free_result */
+static void
+MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * const set TSRMLS_DC)
+{
+ int64_t row;
+
+ DBG_ENTER("mysqlnd_result_buffered::free_result");
+ DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count);
+
+ if (set->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
+ MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)((MYSQLND_RES_BUFFERED_ZVAL *) set TSRMLS_CC);
+ } if (set->type == MYSQLND_BUFFERED_TYPE_C) {
+ MYSQLND_METHOD(mysqlnd_result_buffered_c, free_result)((MYSQLND_RES_BUFFERED_C *) set TSRMLS_CC);
+ }
+
+ for (row = set->row_count - 1; row >= 0; row--) {
+ MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row];
+ current_buffer->free_chunk(current_buffer TSRMLS_CC);
+ }
+
+ if (set->lengths) {
+ mnd_pefree(set->lengths, set->persistent);
+ set->lengths = NULL;
+ }
if (set->row_buffers) {
- mnd_efree(set->row_buffers);
- set->row_buffers = NULL;
+ mnd_pefree(set->row_buffers, 0);
+ set->row_buffers = NULL;
}
- set->data_cursor = NULL;
+
+ if (set->result_set_memory_pool) {
+ mysqlnd_mempool_destroy(set->result_set_memory_pool TSRMLS_CC);
+ set->result_set_memory_pool = NULL;
+ }
+
+
set->row_count = 0;
- mnd_efree(set);
+ mnd_pefree(set, set->persistent);
DBG_VOID_RETURN;
}
@@ -238,39 +370,24 @@ MYSQLND_METHOD(mysqlnd_res, free_result_buffers)(MYSQLND_RES * result TSRMLS_DC)
DBG_INF_FMT("%s", result->unbuf? "unbuffered":(result->stored_data? "buffered":"unknown"));
if (result->unbuf) {
- result->m.unbuffered_free_last_data(result TSRMLS_CC);
- mnd_efree(result->unbuf);
+ result->unbuf->m.free_result(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
result->unbuf = NULL;
} else if (result->stored_data) {
- result->m.free_buffered_data(result TSRMLS_CC);
+ result->stored_data->m.free_result(result->stored_data TSRMLS_CC);
result->stored_data = NULL;
}
- if (result->lengths) {
- mnd_efree(result->lengths);
- result->lengths = NULL;
- }
-
- if (result->row_packet) {
- PACKET_FREE(result->row_packet);
- result->row_packet = NULL;
- }
-
- if (result->result_set_memory_pool) {
- mysqlnd_mempool_destroy(result->result_set_memory_pool TSRMLS_CC);
- result->result_set_memory_pool = NULL;
- }
DBG_VOID_RETURN;
}
/* }}} */
-/* {{{ mysqlnd_internal_free_result_contents */
+/* {{{ mysqlnd_res::free_result_contents_internal */
static
-void mysqlnd_internal_free_result_contents(MYSQLND_RES * result TSRMLS_DC)
+void MYSQLND_METHOD(mysqlnd_res, free_result_contents_internal)(MYSQLND_RES * result TSRMLS_DC)
{
- DBG_ENTER("mysqlnd_internal_free_result_contents");
+ DBG_ENTER("mysqlnd_res::free_result_contents_internal");
result->m.free_result_buffers(result TSRMLS_CC);
@@ -284,11 +401,13 @@ void mysqlnd_internal_free_result_contents(MYSQLND_RES * result TSRMLS_DC)
/* }}} */
-/* {{{ mysqlnd_internal_free_result */
+/* {{{ mysqlnd_res::free_result_internal */
static
-void mysqlnd_internal_free_result(MYSQLND_RES * result TSRMLS_DC)
+void MYSQLND_METHOD(mysqlnd_res, free_result_internal)(MYSQLND_RES * result TSRMLS_DC)
{
- DBG_ENTER("mysqlnd_internal_free_result");
+ DBG_ENTER("mysqlnd_res::free_result_internal");
+ result->m.skip_result(result TSRMLS_CC);
+
result->m.free_result_contents(result TSRMLS_CC);
if (result->conn) {
@@ -552,7 +671,7 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s
/* }}} */
-/* {{{ mysqlnd_fetch_lengths_buffered */
+/* {{{ mysqlnd_result_buffered::fetch_lengths */
/*
Do lazy initialization for buffered results. As PHP strings have
length inside, this function makes not much sense in the context
@@ -560,78 +679,103 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s
completeness.
*/
static unsigned long *
-mysqlnd_fetch_lengths_buffered(MYSQLND_RES * const result TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_lengths)(MYSQLND_RES_BUFFERED * const result TSRMLS_DC)
{
- unsigned int i;
- zval **previous_row;
- MYSQLND_RES_BUFFERED *set = result->stored_data;
-
+ const MYSQLND_RES_BUFFERED_ZVAL * set = (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) > (set->row_count * result->meta->field_count) ))
+ ((set->data_cursor - set->data) > (result->row_count * result->field_count) ))
{
- return NULL;/* No rows or no more rows */
+ DBG_INF("EOF");
+ DBG_RETURN(NULL);/* No rows or no more rows */
}
+ DBG_INF("non NULL");
+ DBG_RETURN(result->lengths);
+}
+/* }}} */
- previous_row = set->data_cursor - result->meta->field_count;
- for (i = 0; i < result->meta->field_count; i++) {
- result->lengths[i] = (Z_TYPE_P(previous_row[i]) == IS_NULL)? 0:Z_STRLEN_P(previous_row[i]);
- }
- 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 unsigned long *
+MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_lengths)(MYSQLND_RES_BUFFERED * const result TSRMLS_DC)
+{
+ const MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result;
+ DBG_ENTER("mysqlnd_result_buffered_c::fetch_lengths");
+
+ if (set->current_row > set->row_count || set->current_row == 0) {
+ DBG_INF("EOF");
+ DBG_RETURN(NULL); /* No more rows, or no fetched row */
+ }
+ DBG_INF("non NULL");
+ DBG_RETURN(result->lengths);
}
/* }}} */
-/* {{{ mysqlnd_fetch_lengths_unbuffered */
+/* {{{ mysqlnd_result_unbuffered::fetch_lengths */
static unsigned long *
-mysqlnd_fetch_lengths_unbuffered(MYSQLND_RES * const result TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_lengths)(MYSQLND_RES_UNBUFFERED * const result TSRMLS_DC)
{
/* simulate output of libmysql */
- return (!result->unbuf || result->unbuf->last_row_data || result->unbuf->eof_reached)? result->lengths:NULL;
+ return (result->last_row_data || result->eof_reached)? result->lengths : NULL;
}
/* }}} */
/* {{{ mysqlnd_res::fetch_lengths */
-PHPAPI unsigned long * _mysqlnd_fetch_lengths(MYSQLND_RES * const result TSRMLS_DC)
+static unsigned long *
+MYSQLND_METHOD(mysqlnd_res, fetch_lengths)(MYSQLND_RES * const result TSRMLS_DC)
{
- return result->m.fetch_lengths? result->m.fetch_lengths(result TSRMLS_CC) : NULL;
+ unsigned long * ret;
+ DBG_ENTER("mysqlnd_res::fetch_lengths");
+ ret = result->stored_data && result->stored_data->m.fetch_lengths ?
+ result->stored_data->m.fetch_lengths(result->stored_data TSRMLS_CC) :
+ (result->unbuf && result->unbuf->m.fetch_lengths ?
+ result->unbuf->m.fetch_lengths(result->unbuf TSRMLS_CC) :
+ NULL
+ );
+ DBG_RETURN(ret);
}
/* }}} */
-/* {{{ mysqlnd_fetch_row_unbuffered_c */
-static MYSQLND_ROW_C
-mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC)
+/* {{{ mysqlnd_result_unbuffered::fetch_row_c */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
{
enum_func_status ret;
- MYSQLND_ROW_C retrow = NULL;
- unsigned int i,
- field_count = result->field_count;
- MYSQLND_PACKET_ROW *row_packet = result->row_packet;
- unsigned long *lengths = result->lengths;
+ MYSQLND_ROW_C *row = (MYSQLND_ROW_C *) param;
+ MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet;
+ const MYSQLND_RES_METADATA * const meta = result->meta;
- DBG_ENTER("mysqlnd_fetch_row_unbuffered_c");
+ DBG_ENTER("mysqlnd_result_unbuffered::fetch_row_c");
+ *fetched_anything = FALSE;
if (result->unbuf->eof_reached) {
/* No more rows obviously */
- DBG_RETURN(retrow);
+ DBG_RETURN(PASS);
}
if (CONN_GET_STATE(result->conn) != CONN_FETCHING_DATA) {
- SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC,
- UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
- DBG_RETURN(retrow);
+ SET_CLIENT_ERROR(*result->conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
+ DBG_RETURN(FAIL);
}
if (!row_packet) {
/* Not fully initialized object that is being cleaned up */
- DBG_RETURN(retrow);
+ DBG_RETURN(FAIL);
}
/* Let the row packet fill our buffer and skip additional mnd_malloc + memcpy */
row_packet->skip_extraction = FALSE;
@@ -641,9 +785,7 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC)
result->m.unbuffered_free_last_data() before it. The function returns always true.
*/
if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
- result->unbuf->row_count++;
-
- result->m.unbuffered_free_last_data(result TSRMLS_CC);
+ result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
@@ -653,46 +795,51 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC)
MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
if (!row_packet->skip_extraction) {
- MYSQLND_FIELD *field = result->meta->fields;
- struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
-
- enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer,
- result->unbuf->last_row_data,
- row_packet->field_count,
- row_packet->fields_metadata,
- result->conn->options->int_and_float_native,
- result->conn->stats TSRMLS_CC);
+ unsigned int i, field_count = meta->field_count;
+
+ enum_func_status rc = result->unbuf->m.row_decoder(result->unbuf->last_row_buffer,
+ result->unbuf->last_row_data,
+ field_count,
+ row_packet->fields_metadata,
+ result->conn->options->int_and_float_native,
+ result->conn->stats TSRMLS_CC);
if (PASS != rc) {
- DBG_RETURN(retrow);
+ DBG_RETURN(FAIL);
}
+ {
+ *row = mnd_malloc(field_count * sizeof(char *));
+ if (*row) {
+ MYSQLND_FIELD * field = meta->fields;
+ unsigned long * lengths = result->unbuf->lengths;
+
+ for (i = 0; i < field_count; i++, field++) {
+ zval * data = result->unbuf->last_row_data[i];
+ unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
+
+/* BEGIN difference between normal normal fetch and _c */
+ if (Z_TYPE_P(data) != IS_NULL) {
+ convert_to_string(data);
+ (*row)[i] = Z_STRVAL_P(data);
+ } else {
+ (*row)[i] = NULL;
+ }
+/* END difference between normal normal fetch and _c */
- retrow = mnd_malloc(result->field_count * sizeof(char *));
- if (retrow) {
- for (i = 0; i < field_count; i++, field++, hash_key++) {
- zval *data = result->unbuf->last_row_data[i];
- unsigned int len;
-
- if (Z_TYPE_P(data) != IS_NULL) {
- convert_to_string(data);
- retrow[i] = Z_STRVAL_P(data);
- len = Z_STRLEN_P(data);
- } else {
- retrow[i] = NULL;
- len = 0;
- }
-
- if (lengths) {
- lengths[i] = len;
- }
+ if (lengths) {
+ lengths[i] = len;
+ }
- if (field->max_length < len) {
- field->max_length = len;
+ if (field->max_length < len) {
+ field->max_length = len;
+ }
}
+ } else {
+ SET_OOM_ERROR(*result->conn->error_info);
}
- } else {
- SET_OOM_ERROR(*result->conn->error_info);
}
}
+ result->unbuf->row_count++;
+ *fetched_anything = TRUE;
} else if (ret == FAIL) {
if (row_packet->error_info.error_no) {
COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info);
@@ -716,23 +863,25 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC)
} else {
CONN_SET_STATE(result->conn, CONN_READY);
}
- result->m.unbuffered_free_last_data(result TSRMLS_CC);
+ result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
}
- DBG_RETURN(retrow);
+ DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
+ DBG_RETURN(PASS);
}
/* }}} */
-/* {{{ mysqlnd_fetch_row_unbuffered */
+/* {{{ mysqlnd_result_unbuffered::fetch_row */
static enum_func_status
-mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
{
enum_func_status ret;
zval *row = (zval *) param;
- MYSQLND_PACKET_ROW *row_packet = result->row_packet;
+ MYSQLND_PACKET_ROW *row_packet = result->unbuf->row_packet;
+ const MYSQLND_RES_METADATA * const meta = result->meta;
- DBG_ENTER("mysqlnd_fetch_row_unbuffered");
+ DBG_ENTER("mysqlnd_result_unbuffered::fetch_row");
*fetched_anything = FALSE;
if (result->unbuf->eof_reached) {
@@ -755,7 +904,7 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla
result->m.unbuffered_free_last_data() before it. The function returns always true.
*/
if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) {
- result->m.unbuffered_free_last_data(result TSRMLS_CC);
+ result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
result->unbuf->last_row_data = row_packet->fields;
result->unbuf->last_row_buffer = row_packet->row_buffer;
@@ -765,13 +914,9 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla
MYSQLND_INC_CONN_STATISTIC(result->conn->stats, STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_UNBUF);
if (!row_packet->skip_extraction) {
- HashTable *row_ht = Z_ARRVAL_P(row);
- MYSQLND_FIELD *field = result->meta->fields;
- struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
- unsigned int i, field_count = result->field_count;
- unsigned long *lengths = result->lengths;
+ unsigned int i, field_count = meta->field_count;
- enum_func_status rc = result->m.row_decoder(result->unbuf->last_row_buffer,
+ enum_func_status rc = result->unbuf->m.row_decoder(result->unbuf->last_row_buffer,
result->unbuf->last_row_data,
field_count,
row_packet->fields_metadata,
@@ -780,46 +925,51 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla
if (PASS != rc) {
DBG_RETURN(FAIL);
}
- for (i = 0; i < field_count; i++, field++, hash_key++) {
- zval *data = result->unbuf->last_row_data[i];
- unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
+ {
+ HashTable * row_ht = Z_ARRVAL_P(row);
+ MYSQLND_FIELD * field = meta->fields;
+ unsigned long * lengths = result->unbuf->lengths;
+
+ for (i = 0; i < field_count; i++, field++) {
+ zval * data = result->unbuf->last_row_data[i];
+ unsigned int len = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
+
+ if (flags & MYSQLND_FETCH_NUM) {
+ Z_ADDREF_P(data);
+ zend_hash_next_index_insert(row_ht, &data, sizeof(zval *), NULL);
+ }
+ if (flags & MYSQLND_FETCH_ASSOC) {
+ /* zend_hash_quick_update needs length + trailing zero */
+ /* QQ: Error handling ? */
+ /*
+ zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether
+ the index is a numeric and convert it to it. This however means constant
+ hashing of the column name, which is not needed as it can be precomputed.
+ */
+ Z_ADDREF_P(data);
+ if (meta->zend_hash_keys[i].is_numeric == FALSE) {
+ zend_hash_quick_update(Z_ARRVAL_P(row),
+ field->name,
+ field->name_length + 1,
+ meta->zend_hash_keys[i].key,
+ (void *) &data, sizeof(zval *), NULL);
+ } else {
+ zend_hash_index_update(Z_ARRVAL_P(row), meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL);
+ }
+ }
- if (lengths) {
- lengths[i] = len;
- }
+ if (lengths) {
+ lengths[i] = len;
+ }
- if (flags & MYSQLND_FETCH_NUM) {
- Z_ADDREF_P(data);
- zend_hash_next_index_insert(row_ht, &data, sizeof(zval *), NULL);
- }
- if (flags & MYSQLND_FETCH_ASSOC) {
- /* zend_hash_quick_update needs length + trailing zero */
- /* QQ: Error handling ? */
- /*
- zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether
- the index is a numeric and convert it to it. This however means constant
- hashing of the column name, which is not needed as it can be precomputed.
- */
- Z_ADDREF_P(data);
- if (hash_key->is_numeric == FALSE) {
- zend_hash_quick_update(Z_ARRVAL_P(row),
- field->name,
- field->name_length + 1,
- hash_key->key,
- (void *) &data, sizeof(zval *), NULL);
- } else {
- zend_hash_index_update(Z_ARRVAL_P(row),
- hash_key->key,
- (void *) &data, sizeof(zval *), NULL);
+ if (field->max_length < len) {
+ field->max_length = len;
}
}
- if (field->max_length < len) {
- field->max_length = len;
- }
}
}
- *fetched_anything = TRUE;
result->unbuf->row_count++;
+ *fetched_anything = TRUE;
} else if (ret == FAIL) {
if (row_packet->error_info.error_no) {
COPY_CLIENT_ERROR(*result->conn->error_info, row_packet->error_info);
@@ -843,7 +993,7 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla
} else {
CONN_SET_STATE(result->conn, CONN_READY);
}
- result->m.unbuffered_free_last_data(result TSRMLS_CC);
+ result->unbuf->m.free_last_data(result->unbuf, result->conn? result->conn->stats : NULL TSRMLS_CC);
}
DBG_INF_FMT("ret=%s fetched=%u", ret == PASS? "PASS":"FAIL", *fetched_anything);
@@ -862,44 +1012,31 @@ MYSQLND_METHOD(mysqlnd_res, use_result)(MYSQLND_RES * const result, zend_bool ps
if (ps == FALSE) {
result->type = MYSQLND_RES_NORMAL;
- result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
- result->m.fetch_lengths = mysqlnd_fetch_lengths_unbuffered;
- result->m.row_decoder = php_mysqlnd_rowp_read_text_protocol;
- result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long));
- if (!result->lengths) {
- goto oom;
- }
} else {
result->type = MYSQLND_RES_PS_UNBUF;
- result->m.fetch_row = NULL;
- /* result->m.fetch_row() will be set in mysqlnd_ps.c */
- result->m.fetch_lengths = NULL; /* makes no sense */
- result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
- result->lengths = NULL;
}
- result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC);
- result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
- if (!result->result_set_memory_pool || !result->unbuf) {
+ result->unbuf = mysqlnd_result_unbuffered_init(result->field_count, ps, result->persistent TSRMLS_CC);
+ if (!result->unbuf) {
goto oom;
}
/*
Will be freed in the mysqlnd_internal_free_result_contents() called
- by the resource destructor. mysqlnd_fetch_row_unbuffered() expects
+ by the resource destructor. mysqlnd_result_unbuffered::fetch_row() expects
this to be not NULL.
*/
/* FALSE = non-persistent */
- result->row_packet = result->conn->protocol->m.get_row_packet(result->conn->protocol, FALSE TSRMLS_CC);
- if (!result->row_packet) {
+ result->unbuf->row_packet = result->conn->protocol->m.get_row_packet(result->conn->protocol, FALSE TSRMLS_CC);
+ if (!result->unbuf->row_packet) {
goto oom;
}
- result->row_packet->result_set_memory_pool = result->result_set_memory_pool;
- result->row_packet->field_count = result->field_count;
- result->row_packet->binary_protocol = ps;
- result->row_packet->fields_metadata = result->meta->fields;
- result->row_packet->bit_fields_count = result->meta->bit_fields_count;
- result->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len;
+ result->unbuf->row_packet->result_set_memory_pool = result->unbuf->result_set_memory_pool;
+ result->unbuf->row_packet->field_count = result->field_count;
+ result->unbuf->row_packet->binary_protocol = ps;
+ result->unbuf->row_packet->fields_metadata = result->meta->fields;
+ result->unbuf->row_packet->bit_fields_count = result->meta->bit_fields_count;
+ result->unbuf->row_packet->bit_fields_total_len = result->meta->bit_fields_total_len;
DBG_RETURN(result);
oom:
@@ -909,37 +1046,130 @@ oom:
/* }}} */
-/* {{{ mysqlnd_fetch_row_buffered_c */
-static MYSQLND_ROW_C
-mysqlnd_fetch_row_buffered_c(MYSQLND_RES * result TSRMLS_DC)
+/* {{{ mysqlnd_result_buffered::fetch_row_c */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
{
- MYSQLND_ROW_C ret = NULL;
- MYSQLND_RES_BUFFERED *set = result->stored_data;
+ MYSQLND_ROW_C * row = (MYSQLND_ROW_C *) param;
+ const MYSQLND_RES_METADATA * const meta = result->meta;
+ unsigned int field_count = meta->field_count;
+ enum_func_status ret = FAIL;
+ 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 (NULL == 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,
+ result->conn->options->int_and_float_native,
+ result->conn->stats TSRMLS_CC);
+ if (rc != PASS) {
+ DBG_RETURN(FAIL);
+ }
+ set->initialized_rows++;
+ for (i = 0; i < field_count; i++) {
+ /*
+ NULL fields are 0 length, 0 is not more than 0
+ String of zero size, definitely can't be the next max_length.
+ Thus for NULL and zero-length we are quite efficient.
+ */
+ if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
+ unsigned long len = Z_STRLEN_P(current_row[i]);
+ if (meta->fields[i].max_length < len) {
+ meta->fields[i].max_length = len;
+ }
+ }
+ }
+ }
+
+/* BEGIN difference between normal normal fetch and _c */
+ /* there is no conn handle in this function thus we can't set OOM in error_info */
+ *row = mnd_malloc(field_count * sizeof(char *));
+ if (*row) {
+ for (i = 0; i < field_count; i++) {
+ zval * data = current_row[i];
+
+ set->lengths[i] = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
+
+ 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);
+ } else {
+ SET_OOM_ERROR(*result->conn->error_info);
+ }
+/* 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);
+}
+/* }}} */
- DBG_ENTER("mysqlnd_fetch_row_buffered_c");
+
+/* {{{ 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 * fetched_anything TSRMLS_DC)
+{
+ zval * row = (zval *) param;
+ const MYSQLND_RES_METADATA * const meta = result->meta;
+ unsigned int field_count = meta->field_count;
+ enum_func_status ret = FAIL;
+ MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data;
+
+ DBG_ENTER("mysqlnd_result_buffered_zval::fetch_row");
/* If we haven't read everything */
if (set->data_cursor &&
- (set->data_cursor - set->data) < (set->row_count * result->meta->field_count))
+ (set->data_cursor - set->data) < (set->row_count * field_count))
{
- zval **current_row = set->data_cursor;
- MYSQLND_FIELD *field = result->meta->fields;
- struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
unsigned int i;
+ zval **current_row = set->data_cursor;
if (NULL == current_row[0]) {
- uint64_t row_num = (set->data_cursor - set->data) / result->meta->field_count;
- enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
+ uint64_t row_num = (set->data_cursor - set->data) / field_count;
+ enum_func_status rc = set->m.row_decoder(set->row_buffers[row_num],
current_row,
- result->meta->field_count,
- result->meta->fields,
+ field_count,
+ meta->fields,
result->conn->options->int_and_float_native,
result->conn->stats TSRMLS_CC);
if (rc != PASS) {
- DBG_RETURN(ret);
+ DBG_RETURN(FAIL);
}
set->initialized_rows++;
- for (i = 0; i < result->field_count; i++) {
+ for (i = 0; i < field_count; i++) {
/*
NULL fields are 0 length, 0 is not more than 0
String of zero size, definitely can't be the next max_length.
@@ -947,71 +1177,100 @@ mysqlnd_fetch_row_buffered_c(MYSQLND_RES * result TSRMLS_DC)
*/
if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
unsigned long len = Z_STRLEN_P(current_row[i]);
- if (field->max_length < len) {
- field->max_length = len;
+ if (meta->fields[i].max_length < len) {
+ meta->fields[i].max_length = len;
}
}
}
}
- set->data_cursor += result->meta->field_count;
- MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
+ for (i = 0; i < field_count; i++) {
+ zval * data = current_row[i];
- ret = mnd_malloc(result->field_count * sizeof(char *));
- if (ret) {
- for (i = 0; i < result->field_count; i++, field++, hash_key++) {
- zval *data = current_row[i];
+ set->lengths[i] = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
- if (Z_TYPE_P(data) != IS_NULL) {
- convert_to_string(data);
- ret[i] = Z_STRVAL_P(data);
+ if (flags & MYSQLND_FETCH_NUM) {
+ Z_ADDREF_P(data);
+ zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL);
+ }
+ if (flags & MYSQLND_FETCH_ASSOC) {
+ /* zend_hash_quick_update needs length + trailing zero */
+ /* QQ: Error handling ? */
+ /*
+ zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether
+ the index is a numeric and convert it to it. This however means constant
+ hashing of the column name, which is not needed as it can be precomputed.
+ */
+ Z_ADDREF_P(data);
+ if (meta->zend_hash_keys[i].is_numeric == FALSE) {
+ zend_hash_quick_update(Z_ARRVAL_P(row),
+ meta->fields[i].name,
+ meta->fields[i].name_length + 1,
+ meta->zend_hash_keys[i].key,
+ (void *) &data, sizeof(zval *), NULL);
} else {
- ret[i] = NULL;
+ zend_hash_index_update(Z_ARRVAL_P(row),
+ meta->zend_hash_keys[i].key,
+ (void *) &data, sizeof(zval *), NULL);
}
}
}
- /* there is no conn handle in this function thus we can't set OOM in error_info */
+ set->data_cursor += field_count;
+ MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
+ *fetched_anything = TRUE;
+ ret = PASS;
} else {
set->data_cursor = NULL;
DBG_INF("EOF reached");
+ *fetched_anything = FALSE;
+ ret = PASS;
}
+ DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything);
DBG_RETURN(ret);
}
/* }}} */
-/* {{{ mysqlnd_fetch_row_buffered */
+/* {{{ mysqlnd_result_buffered_c::fetch_row */
static enum_func_status
-mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything TSRMLS_DC)
{
- unsigned int i;
- zval *row = (zval *) param;
- MYSQLND_RES_BUFFERED *set = result->stored_data;
+ zval * row = (zval *) param;
+ const MYSQLND_RES_METADATA * const meta = result->meta;
+ unsigned int field_count = meta->field_count;
enum_func_status ret = FAIL;
- DBG_ENTER("mysqlnd_fetch_row_buffered");
+ 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->data_cursor &&
- (set->data_cursor - set->data) < (set->row_count * result->meta->field_count))
- {
- zval **current_row = set->data_cursor;
- MYSQLND_FIELD *field = result->meta->fields;
- struct mysqlnd_field_hash_key * hash_key = result->meta->zend_hash_keys;
+ if (set->current_row < set->row_count) {
+ zval **current_row;
+ enum_func_status rc;
+ unsigned int i;
+
+ current_row = mnd_emalloc(field_count * sizeof(zval *));
+ if (!current_row) {
+ SET_OOM_ERROR(*result->conn->error_info);
+ DBG_RETURN(FAIL);
+ }
+
+ rc = result->stored_data->m.row_decoder(result->stored_data->row_buffers[set->current_row],
+ current_row,
+ field_count,
+ meta->fields,
+ result->conn->options->int_and_float_native,
+ result->conn->stats TSRMLS_CC);
+ if (rc != PASS) {
+ DBG_RETURN(FAIL);
+ }
+ if (!(set->initialized[set->current_row >> 3] & (1 << (set->current_row & 7)))) {
+ set->initialized[set->current_row >> 3] |= (1 << (set->current_row & 7)); /* mark initialized */
- if (NULL == current_row[0]) {
- uint64_t row_num = (set->data_cursor - set->data) / result->meta->field_count;
- enum_func_status rc = result->m.row_decoder(set->row_buffers[row_num],
- current_row,
- result->meta->field_count,
- result->meta->fields,
- result->conn->options->int_and_float_native,
- result->conn->stats TSRMLS_CC);
- if (rc != PASS) {
- DBG_RETURN(FAIL);
- }
set->initialized_rows++;
- for (i = 0; i < result->field_count; i++) {
+
+ for (i = 0; i < field_count; i++) {
/*
NULL fields are 0 length, 0 is not more than 0
String of zero size, definitely can't be the next max_length.
@@ -1019,16 +1278,18 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags
*/
if (Z_TYPE_P(current_row[i]) >= IS_STRING) {
unsigned long len = Z_STRLEN_P(current_row[i]);
- if (field->max_length < len) {
- field->max_length = len;
+ if (meta->fields[i].max_length < len) {
+ meta->fields[i].max_length = len;
}
}
}
}
- for (i = 0; i < result->field_count; i++, field++, hash_key++) {
- zval *data = current_row[i];
+ for (i = 0; i < field_count; i++) {
+ zval * data = current_row[i];
+ set->lengths[i] = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data);
+
if (flags & MYSQLND_FETCH_NUM) {
Z_ADDREF_P(data);
zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL);
@@ -1042,59 +1303,85 @@ mysqlnd_fetch_row_buffered(MYSQLND_RES * result, void *param, unsigned int flags
hashing of the column name, which is not needed as it can be precomputed.
*/
Z_ADDREF_P(data);
- if (hash_key->is_numeric == FALSE) {
+ if (meta->zend_hash_keys[i].is_numeric == FALSE) {
zend_hash_quick_update(Z_ARRVAL_P(row),
- field->name,
- field->name_length + 1,
- hash_key->key,
+ meta->fields[i].name,
+ meta->fields[i].name_length + 1,
+ meta->zend_hash_keys[i].key,
(void *) &data, sizeof(zval *), NULL);
} else {
zend_hash_index_update(Z_ARRVAL_P(row),
- hash_key->key,
+ meta->zend_hash_keys[i].key,
(void *) &data, sizeof(zval *), NULL);
}
}
+ /*
+ 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(&data);
}
- set->data_cursor += result->meta->field_count;
- *fetched_anything = TRUE;
+ mnd_efree(current_row);
+ set->current_row++;
MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF);
+ *fetched_anything = TRUE;
ret = PASS;
} else {
- set->data_cursor = NULL;
+ 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("EOF reached");
}
+
DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything);
DBG_RETURN(ret);
}
/* }}} */
+/* {{{ 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 TSRMLS_DC)
+{
+ const mysqlnd_fetch_row_func f = result->stored_data? result->stored_data->m.fetch_row:(result->unbuf? result->unbuf->m.fetch_row:NULL);
+ if (f) {
+ return f(result, param, flags, fetched_anything TSRMLS_CC);
+ }
+ *fetched_anything = FALSE;
+ return PASS;
+}
+/* }}} */
+
+
#define STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY 2
/* {{{ mysqlnd_res::store_result_fetch_data */
enum_func_status
MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result,
- MYSQLND_RES_METADATA *meta,
+ MYSQLND_RES_METADATA * meta,
+ MYSQLND_MEMORY_POOL_CHUNK ***row_buffers,
zend_bool binary_protocol TSRMLS_DC)
{
enum_func_status ret;
- MYSQLND_PACKET_ROW *row_packet = NULL;
+ MYSQLND_PACKET_ROW * row_packet = NULL;
unsigned int next_extend = STORE_RESULT_PREALLOCATED_SET_IF_NOT_EMPTY, free_rows = 1;
MYSQLND_RES_BUFFERED *set;
DBG_ENTER("mysqlnd_res::store_result_fetch_data");
- result->stored_data = set = mnd_ecalloc(1, sizeof(MYSQLND_RES_BUFFERED));
- if (!set) {
- SET_OOM_ERROR(*conn->error_info);
+ set = result->stored_data;
+
+ if (!set || !row_buffers) {
ret = FAIL;
goto end;
}
if (free_rows) {
- set->row_buffers = mnd_emalloc((size_t)(free_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)));
- if (!set->row_buffers) {
+ *row_buffers = mnd_pemalloc((size_t)(free_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0);
+ if (!*row_buffers) {
SET_OOM_ERROR(*conn->error_info);
ret = FAIL;
goto end;
@@ -1109,7 +1396,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
ret = FAIL;
goto end;
}
- row_packet->result_set_memory_pool = result->result_set_memory_pool;
+ row_packet->result_set_memory_pool = result->stored_data->result_set_memory_pool;
row_packet->field_count = meta->field_count;
row_packet->binary_protocol = binary_protocol;
row_packet->fields_metadata = meta->fields;
@@ -1130,16 +1417,16 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
ret = FAIL;
goto end;
}
- new_row_buffers = mnd_erealloc(set->row_buffers, (size_t)(total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)));
+ new_row_buffers = mnd_perealloc(*row_buffers, (size_t)(total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0);
if (!new_row_buffers) {
SET_OOM_ERROR(*conn->error_info);
ret = FAIL;
goto end;
}
- set->row_buffers = new_row_buffers;
+ *row_buffers = new_row_buffers;
}
free_rows--;
- set->row_buffers[set->row_count] = row_packet->row_buffer;
+ (*row_buffers)[set->row_count] = row_packet->row_buffer;
set->row_count++;
@@ -1155,23 +1442,6 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
*/
}
/* Overflow ? */
- 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);
- ret = FAIL;
- goto end;
- }
- /* 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);
- ret = FAIL;
- goto end;
- }
- memset(set->data, 0, (size_t)(set->row_count * meta->field_count * sizeof(zval *)));
- }
-
MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats,
binary_protocol? STAT_ROWS_BUFFERED_FROM_CLIENT_PS:
STAT_ROWS_BUFFERED_FROM_CLIENT_NORMAL,
@@ -1191,7 +1461,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
ret = FAIL;
goto end;
}
- set->row_buffers = mnd_erealloc(set->row_buffers, (size_t) (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)));
+ *row_buffers = mnd_perealloc(*row_buffers, (size_t) (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0);
}
if (conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) {
@@ -1203,9 +1473,6 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
if (ret == FAIL) {
COPY_CLIENT_ERROR(set->error_info, row_packet->error_info);
} else {
- /* Position at the first row */
- set->data_cursor = set->data;
-
/* libmysql's documentation says it should be so for SELECT statements */
conn->upsert_status->affected_rows = set->row_count;
}
@@ -1213,7 +1480,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c
ret == PASS? "PASS":"FAIL", (uint) set->row_count, conn->upsert_status->warning_count, conn->upsert_status->server_status);
end:
PACKET_FREE(row_packet);
-
+ DBG_INF_FMT("rows=%llu", (unsigned long long)result->stored_data->row_count);
DBG_RETURN(ret);
}
/* }}} */
@@ -1223,31 +1490,37 @@ end:
static MYSQLND_RES *
MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
MYSQLND_CONN_DATA * const conn,
- zend_bool ps_protocol TSRMLS_DC)
+ const unsigned int flags TSRMLS_DC)
{
enum_func_status ret;
+ MYSQLND_MEMORY_POOL_CHUNK ***row_buffers = NULL;
DBG_ENTER("mysqlnd_res::store_result");
/* We need the conn because we are doing lazy zval initialization in buffered_fetch_row */
- result->conn = conn->m->get_reference(conn TSRMLS_CC);
- result->type = MYSQLND_RES_NORMAL;
- result->m.fetch_row = result->m.fetch_row_normal_buffered;
- result->m.fetch_lengths = mysqlnd_fetch_lengths_buffered;
- result->m.row_decoder = ps_protocol? php_mysqlnd_rowp_read_binary_protocol:
- php_mysqlnd_rowp_read_text_protocol;
+ /* In case of error the reference will be released in free_result_internal() called indirectly by our caller */
+ result->conn = conn->m->get_reference(conn TSRMLS_CC);
+ result->type = MYSQLND_RES_NORMAL;
- result->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC);
- result->lengths = mnd_ecalloc(result->field_count, sizeof(unsigned long));
+ CONN_SET_STATE(conn, CONN_FETCHING_DATA);
- if (!result->result_set_memory_pool || !result->lengths) {
- SET_OOM_ERROR(*conn->error_info);
- DBG_RETURN(NULL);
+ if (flags & MYSQLND_STORE_NO_COPY) {
+ result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result->field_count, flags & MYSQLND_STORE_PS, result->persistent TSRMLS_CC);
+ if (!result->stored_data) {
+ SET_OOM_ERROR(*conn->error_info);
+ DBG_RETURN(NULL);
+ }
+ 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->field_count, flags & MYSQLND_STORE_PS, result->persistent TSRMLS_CC);
+ if (!result->stored_data) {
+ SET_OOM_ERROR(*conn->error_info);
+ DBG_RETURN(NULL);
+ }
+ row_buffers = &result->stored_data->row_buffers;
}
+ ret = result->m.store_result_fetch_data(conn, result, result->meta, row_buffers, flags & MYSQLND_STORE_PS TSRMLS_CC);
- CONN_SET_STATE(conn, CONN_FETCHING_DATA);
-
- ret = result->m.store_result_fetch_data(conn, result, result->meta, ps_protocol TSRMLS_CC);
if (FAIL == ret) {
if (result->stored_data) {
COPY_CLIENT_ERROR(*conn->error_info, result->stored_data->error_info);
@@ -1255,7 +1528,34 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result,
SET_OOM_ERROR(*conn->error_info);
}
DBG_RETURN(NULL);
+ } else {
+ /* Overflow ? */
+ if (flags & MYSQLND_STORE_NO_COPY) {
+ MYSQLND_RES_METADATA * 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;
+ set->initialized = mnd_pecalloc((set->row_count / 8) + 1, sizeof(zend_uchar), set->persistent); /* +1 for safety */
+ }
}
+
/* libmysql's documentation says it should be so for SELECT statements */
conn->upsert_status->affected_rows = result->stored_data->row_count;
@@ -1276,9 +1576,7 @@ MYSQLND_METHOD(mysqlnd_res, skip_result)(MYSQLND_RES * const result TSRMLS_DC)
A PS could be prepared - there is metadata and thus a stmt->result but the
fetch_row function isn't actually set (NULL), thus we have to skip these.
*/
- if (!result->stored_data && result->unbuf &&
- !result->unbuf->eof_reached && result->m.fetch_row)
- {
+ if (result->unbuf && !result->unbuf->eof_reached) {
DBG_INF("skipping result");
/* We have to fetch all data to clean the line */
MYSQLND_INC_CONN_STATISTIC(result->conn->stats,
@@ -1300,7 +1598,6 @@ MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, zend_bool implici
{
DBG_ENTER("mysqlnd_res::free_result");
- result->m.skip_result(result TSRMLS_CC);
MYSQLND_INC_CONN_STATISTIC(result->conn? result->conn->stats : NULL,
implicit == TRUE? STAT_FREE_RESULT_IMPLICIT:
STAT_FREE_RESULT_EXPLICIT);
@@ -1313,33 +1610,78 @@ MYSQLND_METHOD(mysqlnd_res, free_result)(MYSQLND_RES * result, zend_bool implici
/* {{{ mysqlnd_res::data_seek */
static enum_func_status
-MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES * result, uint64_t row TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES * const result, const uint64_t row TSRMLS_DC)
{
DBG_ENTER("mysqlnd_res::data_seek");
DBG_INF_FMT("row=%lu", row);
- if (!result->stored_data) {
- return FAIL;
- }
+ DBG_RETURN(result->stored_data? result->stored_data->m.data_seek(result->stored_data, row TSRMLS_CC) : FAIL);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_buffered_zval::data_seek */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_result_buffered_zval, data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row TSRMLS_DC)
+{
+ MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result;
+ DBG_ENTER("mysqlnd_result_buffered_zval::data_seek");
/* libmysql just moves to the end, it does traversing of a linked list */
- if (row >= result->stored_data->row_count) {
- result->stored_data->data_cursor = NULL;
+ if (row >= set->row_count) {
+ set->data_cursor = NULL;
} else {
- result->stored_data->data_cursor = result->stored_data->data + row * result->meta->field_count;
+ 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 TSRMLS_DC)
+{
+ 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;
+ }
DBG_RETURN(PASS);
}
/* }}} */
+/* {{{ mysqlnd_result_unbuffered::num_rows */
+static uint64_t
+MYSQLND_METHOD(mysqlnd_result_unbuffered, num_rows)(const MYSQLND_RES_UNBUFFERED * const result TSRMLS_DC)
+{
+ /* Be compatible with libmysql. We count row_count, but will return 0 */
+ return result->eof_reached? result->row_count:0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_buffered::num_rows */
+static uint64_t
+MYSQLND_METHOD(mysqlnd_result_buffered, num_rows)(const MYSQLND_RES_BUFFERED * const result TSRMLS_DC)
+{
+ return result->row_count;
+}
+/* }}} */
+
+
/* {{{ mysqlnd_res::num_rows */
static uint64_t
MYSQLND_METHOD(mysqlnd_res, num_rows)(const MYSQLND_RES * const result TSRMLS_DC)
{
- /* Be compatible with libmysql. We count row_count, but will return 0 */
- return result->stored_data? result->stored_data->row_count:(result->unbuf && result->unbuf->eof_reached? result->unbuf->row_count:0);
+ return result->stored_data?
+ result->stored_data->m.num_rows(result->stored_data TSRMLS_CC) :
+ (result->unbuf? result->unbuf->m.num_rows(result->unbuf TSRMLS_CC) : 0);
}
/* }}} */
@@ -1373,7 +1715,9 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC)
if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
/* we have to initialize the rest to get the updated max length */
- if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) {
+ if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats,
+ result->conn->options->int_and_float_native TSRMLS_CC))
+ {
break;
}
}
@@ -1387,7 +1731,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field)(MYSQLND_RES * const result TSRMLS_DC)
/* {{{ mysqlnd_res::fetch_field_direct */
static const MYSQLND_FIELD *
-MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, const MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
{
DBG_ENTER("mysqlnd_res::fetch_field_direct");
do {
@@ -1405,7 +1749,9 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_direct)(MYSQLND_RES * const result, MYSQ
if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
DBG_INF_FMT("We have decode the whole result set to be able to satisfy this meta request");
/* we have to initialized the rest to get the updated max length */
- if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) {
+ if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats,
+ result->conn->options->int_and_float_native TSRMLS_CC))
+ {
break;
}
}
@@ -1427,7 +1773,9 @@ MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result TSRMLS_DC)
if (result->meta) {
if (result->stored_data && (result->stored_data->initialized_rows < result->stored_data->row_count)) {
/* we have to initialize the rest to get the updated max length */
- if (PASS != result->m.initialize_result_set_rest(result TSRMLS_CC)) {
+ if (PASS != result->stored_data->m.initialize_result_set_rest(result->stored_data, result->meta, result->conn->stats,
+ result->conn->options->int_and_float_native TSRMLS_CC))
+ {
break;
}
}
@@ -1439,17 +1787,11 @@ MYSQLND_METHOD(mysqlnd_res, fetch_fields)(MYSQLND_RES * const result TSRMLS_DC)
/* }}} */
-
/* {{{ mysqlnd_res::field_seek */
static MYSQLND_FIELD_OFFSET
-MYSQLND_METHOD(mysqlnd_res, field_seek)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_res, field_seek)(MYSQLND_RES * const result, const MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC)
{
- MYSQLND_FIELD_OFFSET return_value = 0;
- if (result->meta) {
- return_value = result->meta->current_field;
- result->meta->current_field = field_offset;
- }
- return return_value;
+ return result->meta? result->meta->m->field_seek(result->meta, field_offset TSRMLS_CC) : 0;
}
/* }}} */
@@ -1465,7 +1807,7 @@ MYSQLND_METHOD(mysqlnd_res, field_tell)(const MYSQLND_RES * const result TSRMLS_
/* {{{ mysqlnd_res::fetch_into */
static void
-MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, unsigned int flags,
+MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, const unsigned int flags,
zval *return_value,
enum_mysqlnd_extension extension TSRMLS_DC ZEND_FILE_LINE_DC)
{
@@ -1473,10 +1815,6 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, unsigned int flags
DBG_ENTER("mysqlnd_res::fetch_into");
- if (!result->m.fetch_row) {
- RETVAL_NULL();
- DBG_VOID_RETURN;
- }
/*
Hint Zend how many elements we will have in the hash. Thus it won't
extend and rehash the hash constantly.
@@ -1511,17 +1849,17 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, unsigned int flags
static MYSQLND_ROW_C
MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result TSRMLS_DC)
{
+ zend_bool fetched_anything;
MYSQLND_ROW_C ret = NULL;
DBG_ENTER("mysqlnd_res::fetch_row_c");
- if (result->m.fetch_row) {
- if (result->m.fetch_row == result->m.fetch_row_normal_buffered) {
- DBG_RETURN(mysqlnd_fetch_row_buffered_c(result TSRMLS_CC));
- } else if (result->m.fetch_row == result->m.fetch_row_normal_unbuffered) {
- DBG_RETURN(mysqlnd_fetch_row_unbuffered_c(result TSRMLS_CC));
- } else {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "result->m.fetch_row has invalid value. Report to the developers");
- }
+ if (result->stored_data && result->stored_data->m.fetch_row == MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)) {
+ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(result, (void *) &ret, 0, &fetched_anything TSRMLS_CC);
+ } else if (result->unbuf && result->unbuf->m.fetch_row == MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)) {
+ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(result, (void *) &ret, 0, &fetched_anything TSRMLS_CC);
+ } else {
+ ret = NULL;
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "result->m.fetch_row has invalid value. Report to the developers");
}
DBG_RETURN(ret);
}
@@ -1530,7 +1868,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result TSRMLS_DC)
/* {{{ mysqlnd_res::fetch_all */
static void
-MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES * result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC)
+MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES * result, const unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC)
{
zval *row;
ulong i = 0;
@@ -1575,11 +1913,6 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES * result, unsigned int
DBG_ENTER("mysqlnd_res::fetch_field_data");
DBG_INF_FMT("offset=%u", offset);
-
- if (!result->m.fetch_row) {
- RETVAL_NULL();
- DBG_VOID_RETURN;
- }
/*
Hint Zend how many elements we will have in the hash. Thus it won't
extend and rehash the hash constantly.
@@ -1610,9 +1943,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_field_data)(MYSQLND_RES * result, unsigned int
MYSQLND_CLASS_METHODS_START(mysqlnd_res)
- NULL, /* fetch_row */
- mysqlnd_fetch_row_buffered,
- mysqlnd_fetch_row_unbuffered,
+ MYSQLND_METHOD(mysqlnd_res, fetch_row),
MYSQLND_METHOD(mysqlnd_res, use_result),
MYSQLND_METHOD(mysqlnd_res, store_result),
MYSQLND_METHOD(mysqlnd_res, fetch_into),
@@ -1629,19 +1960,34 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_res)
MYSQLND_METHOD(mysqlnd_res, fetch_field_direct),
MYSQLND_METHOD(mysqlnd_res, fetch_fields),
MYSQLND_METHOD(mysqlnd_res, read_result_metadata),
- NULL, /* fetch_lengths */
+ MYSQLND_METHOD(mysqlnd_res, fetch_lengths),
MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data),
- MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest),
MYSQLND_METHOD(mysqlnd_res, free_result_buffers),
MYSQLND_METHOD(mysqlnd_res, free_result),
+ MYSQLND_METHOD(mysqlnd_res, free_result_internal),
+ MYSQLND_METHOD(mysqlnd_res, free_result_contents_internal),
+ mysqlnd_result_meta_init
+MYSQLND_CLASS_METHODS_END;
- mysqlnd_internal_free_result, /* free_result_internal */
- mysqlnd_internal_free_result_contents, /* free_result_contents */
- MYSQLND_METHOD(mysqlnd_res, free_buffered_data),
- MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data),
- NULL /* row_decoder */,
- mysqlnd_result_meta_init
+MYSQLND_CLASS_METHODS_START(mysqlnd_result_unbuffered)
+ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row),
+ NULL, /* row_decoder */
+ MYSQLND_METHOD(mysqlnd_result_unbuffered, num_rows),
+ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_lengths),
+ MYSQLND_METHOD(mysqlnd_result_unbuffered, free_last_data),
+ MYSQLND_METHOD(mysqlnd_result_unbuffered, free_result)
+MYSQLND_CLASS_METHODS_END;
+
+
+MYSQLND_CLASS_METHODS_START(mysqlnd_result_buffered)
+ NULL, /* fetch_row */
+ NULL, /* row_decoder */
+ MYSQLND_METHOD(mysqlnd_result_buffered, num_rows),
+ NULL, /* fetch_lengths */
+ NULL, /* data_seek */
+ NULL, /* initialize_result_set_rest */
+ MYSQLND_METHOD(mysqlnd_result_buffered, free_result)
MYSQLND_CLASS_METHODS_END;
@@ -1650,7 +1996,7 @@ PHPAPI MYSQLND_RES *
mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC)
{
size_t alloc_size = sizeof(MYSQLND_RES) + mysqlnd_plugin_count() * sizeof(void *);
- MYSQLND_RES *ret = mnd_pecalloc(1, alloc_size, persistent);
+ MYSQLND_RES * ret = mnd_pecalloc(1, alloc_size, persistent);
DBG_ENTER("mysqlnd_result_init");
@@ -1667,6 +2013,134 @@ mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC)
/* }}} */
+/* {{{ mysqlnd_result_unbuffered_init */
+PHPAPI MYSQLND_RES_UNBUFFERED *
+mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC)
+{
+ size_t alloc_size = sizeof(MYSQLND_RES_UNBUFFERED) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_RES_UNBUFFERED * ret = mnd_pecalloc(1, alloc_size, persistent);
+
+ DBG_ENTER("mysqlnd_result_unbuffered_init");
+
+ if (!ret) {
+ DBG_RETURN(NULL);
+ }
+
+ if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(unsigned long), persistent))) {
+ mnd_pefree(ret, persistent);
+ DBG_RETURN(NULL);
+ }
+ if (!(ret->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC))) {
+ mnd_efree(ret->lengths);
+ mnd_pefree(ret, persistent);
+ DBG_RETURN(NULL);
+ }
+
+ ret->persistent = persistent;
+ ret->field_count= field_count;
+ ret->ps = ps;
+
+ ret->m = *mysqlnd_result_unbuffered_get_methods();
+
+ if (ps) {
+ ret->m.fetch_lengths = NULL; /* makes no sense */
+ ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol;
+ } else {
+ ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol_zval;
+ }
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_buffered_zval_init */
+PHPAPI MYSQLND_RES_BUFFERED_ZVAL *
+mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC)
+{
+ size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_ZVAL) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_RES_BUFFERED_ZVAL * ret = mnd_pecalloc(1, alloc_size, persistent);
+
+ DBG_ENTER("mysqlnd_result_buffered_zval_init");
+
+ if (!ret) {
+ DBG_RETURN(NULL);
+ }
+ if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(unsigned long), persistent))) {
+ mnd_pefree(ret, persistent);
+ DBG_RETURN(NULL);
+ }
+ if (!(ret->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC))) {
+ mnd_efree(ret->lengths);
+ mnd_pefree(ret, persistent);
+ DBG_RETURN(NULL);
+ }
+
+ ret->persistent = persistent;
+ ret->field_count= field_count;
+ ret->ps = ps;
+ ret->m = *mysqlnd_result_buffered_get_methods();
+ ret->type = MYSQLND_BUFFERED_TYPE_ZVAL;
+
+ 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_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);
+ ret->m.initialize_result_set_rest = MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_result_buffered_c_init */
+PHPAPI MYSQLND_RES_BUFFERED_C *
+mysqlnd_result_buffered_c_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC)
+{
+ size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_C) + mysqlnd_plugin_count() * sizeof(void *);
+ MYSQLND_RES_BUFFERED_C * ret = mnd_pecalloc(1, alloc_size, persistent);
+
+ DBG_ENTER("mysqlnd_result_buffered_c_init");
+
+ if (!ret) {
+ DBG_RETURN(NULL);
+ }
+ if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(unsigned long), persistent))) {
+ mnd_pefree(ret, persistent);
+ DBG_RETURN(NULL);
+ }
+ if (!(ret->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC))) {
+ mnd_efree(ret->lengths);
+ mnd_pefree(ret, persistent);
+ DBG_RETURN(NULL);
+ }
+
+ ret->persistent = persistent;
+ ret->field_count= field_count;
+ ret->ps = ps;
+ ret->m = *mysqlnd_result_buffered_get_methods();
+ 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->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);
+ ret->m.initialize_result_set_rest = MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest);
+
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/mysqlnd/mysqlnd_result.h b/ext/mysqlnd/mysqlnd_result.h
index cd64c17a80..2ddbb7d381 100644
--- a/ext/mysqlnd/mysqlnd_result.h
+++ b/ext/mysqlnd/mysqlnd_result.h
@@ -24,6 +24,9 @@
#define MYSQLND_RESULT_H
PHPAPI MYSQLND_RES * mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC);
+PHPAPI MYSQLND_RES_UNBUFFERED * mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC);
+PHPAPI MYSQLND_RES_BUFFERED_ZVAL * mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC);
+PHPAPI MYSQLND_RES_BUFFERED_C * mysqlnd_result_buffered_c_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC);
enum_func_status mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * stmt TSRMLS_DC);
diff --git a/ext/mysqlnd/mysqlnd_result_meta.c b/ext/mysqlnd/mysqlnd_result_meta.c
index 33dc65342e..53f8d6d809 100644
--- a/ext/mysqlnd/mysqlnd_result_meta.c
+++ b/ext/mysqlnd/mysqlnd_result_meta.c
@@ -341,7 +341,7 @@ MYSQLND_METHOD(mysqlnd_res_meta, fetch_field)(MYSQLND_RES_METADATA * const meta
/* {{{ mysqlnd_res_meta::fetch_field_direct */
static const MYSQLND_FIELD *
-MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
+MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, const MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC)
{
DBG_ENTER("mysqlnd_res_meta::fetch_field_direct");
DBG_INF_FMT("fieldnr=%u", fieldnr);
@@ -371,6 +371,17 @@ MYSQLND_METHOD(mysqlnd_res_meta, field_tell)(const MYSQLND_RES_METADATA * const
}
/* }}} */
+/* {{{ mysqlnd_res_meta::field_seek */
+static MYSQLND_FIELD_OFFSET
+MYSQLND_METHOD(mysqlnd_res_meta, field_seek)(MYSQLND_RES_METADATA * const meta, const MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC)
+{
+ MYSQLND_FIELD_OFFSET return_value = 0;
+ DBG_ENTER("mysqlnd_res_meta::fetch_fields");
+ return_value = meta->current_field;
+ meta->current_field = field_offset;
+ DBG_RETURN(return_value);
+}
+/* }}} */
static
MYSQLND_CLASS_METHODS_START(mysqlnd_res_meta)
@@ -378,6 +389,7 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_res_meta)
MYSQLND_METHOD(mysqlnd_res_meta, fetch_field_direct),
MYSQLND_METHOD(mysqlnd_res_meta, fetch_fields),
MYSQLND_METHOD(mysqlnd_res_meta, field_tell),
+ MYSQLND_METHOD(mysqlnd_res_meta, field_seek),
MYSQLND_METHOD(mysqlnd_res_meta, read_metadata),
MYSQLND_METHOD(mysqlnd_res_meta, clone_metadata),
MYSQLND_METHOD(mysqlnd_res_meta, free),
diff --git a/ext/mysqlnd/mysqlnd_reverse_api.c b/ext/mysqlnd/mysqlnd_reverse_api.c
index 47605a6b8b..e3de2e9d1e 100644
--- a/ext/mysqlnd/mysqlnd_reverse_api.c
+++ b/ext/mysqlnd/mysqlnd_reverse_api.c
@@ -61,14 +61,14 @@ PHPAPI void
mysqlnd_reverse_api_register_api(MYSQLND_REVERSE_API * apiext TSRMLS_DC)
{
zend_hash_add(&mysqlnd_api_ext_ht, apiext->module->name, strlen(apiext->module->name) + 1, &apiext,
- sizeof(MYSQLND_REVERSE_API), NULL);
+ sizeof(MYSQLND_REVERSE_API *), NULL);
}
/* }}} */
/* {{{ zval_to_mysqlnd */
PHPAPI MYSQLND *
-zval_to_mysqlnd(zval * zv TSRMLS_DC)
+zval_to_mysqlnd(zval * zv, const unsigned int client_api_capabilities, unsigned int * save_client_api_capabilities TSRMLS_DC)
{
MYSQLND * retval;
MYSQLND_REVERSE_API ** elem;
@@ -80,6 +80,9 @@ zval_to_mysqlnd(zval * zv TSRMLS_DC)
if ((*elem)->conversion_cb) {
retval = (*elem)->conversion_cb(zv TSRMLS_CC);
if (retval) {
+ if (retval->data) {
+ *save_client_api_capabilities = retval->data->m->negotiate_client_api_capabilities(retval->data, client_api_capabilities TSRMLS_CC);
+ }
return retval;
}
}
diff --git a/ext/mysqlnd/mysqlnd_reverse_api.h b/ext/mysqlnd/mysqlnd_reverse_api.h
index 875537a124..e8eb4066d5 100644
--- a/ext/mysqlnd/mysqlnd_reverse_api.h
+++ b/ext/mysqlnd/mysqlnd_reverse_api.h
@@ -34,8 +34,7 @@ PHPAPI void mysqlnd_reverse_api_end(TSRMLS_D);
PHPAPI HashTable * mysqlnd_reverse_api_get_api_list(TSRMLS_D);
PHPAPI void mysqlnd_reverse_api_register_api(MYSQLND_REVERSE_API * apiext TSRMLS_DC);
-
-PHPAPI MYSQLND * zval_to_mysqlnd(zval * zv TSRMLS_DC);
+PHPAPI MYSQLND * zval_to_mysqlnd(zval * zv, const unsigned int client_api_capabilities, unsigned int * save_client_api_capabilities TSRMLS_DC);
#endif /* MYSQLND_REVERSE_API_H */
diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h
index 5cb6b24913..170c977c2b 100644
--- a/ext/mysqlnd/mysqlnd_structs.h
+++ b/ext/mysqlnd/mysqlnd_structs.h
@@ -235,7 +235,9 @@ 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 MYSQLND_RES_BUFFERED;
+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_unbuffered_result MYSQLND_RES_UNBUFFERED;
typedef struct st_mysqlnd_debug MYSQLND_DEBUG;
@@ -243,11 +245,12 @@ typedef struct st_mysqlnd_debug MYSQLND_DEBUG;
typedef MYSQLND_RES* (*mysqlnd_stmt_use_or_store_func)(MYSQLND_STMT * const TSRMLS_DC);
typedef enum_func_status (*mysqlnd_fetch_row_func)(MYSQLND_RES *result,
- void *param,
- unsigned int flags,
- zend_bool *fetched_anything
+ void * param,
+ const unsigned int flags,
+ zend_bool * fetched_anything
TSRMLS_DC);
+
typedef struct st_mysqlnd_stats MYSQLND_STATS;
typedef void (*mysqlnd_stat_trigger)(MYSQLND_STATS * stats, enum_mysqlnd_collected_stats stat, int64_t change TSRMLS_DC);
@@ -417,8 +420,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 * query, unsigned int query_len TSRMLS_DC);
typedef enum_func_status (*func_mysqlnd_conn_data__send_query)(MYSQLND_CONN_DATA * conn, const char *query, unsigned int query_len TSRMLS_DC);
typedef enum_func_status (*func_mysqlnd_conn_data__reap_query)(MYSQLND_CONN_DATA * conn TSRMLS_DC);
-typedef MYSQLND_RES * (*func_mysqlnd_conn_data__use_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
-typedef MYSQLND_RES * (*func_mysqlnd_conn_data__store_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_conn_data__use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_conn_data__store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC);
typedef enum_func_status (*func_mysqlnd_conn_data__next_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
typedef zend_bool (*func_mysqlnd_conn_data__more_results)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
@@ -499,6 +502,11 @@ typedef struct st_mysqlnd_authentication_plugin * (*func_mysqlnd_conn_data__fetc
typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option_2d)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_option option, const char * const key, const char * const value TSRMLS_DC);
+
+typedef unsigned int (*func_mysqlnd_conn_data__negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC);
+typedef unsigned int (*func_mysqlnd_conn_data__get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC);
+
+
struct st_mysqlnd_conn_data_methods
{
func_mysqlnd_conn_data__init init;
@@ -590,6 +598,9 @@ struct st_mysqlnd_conn_data_methods
func_mysqlnd_conn_data__fetch_auth_plugin_by_name fetch_auth_plugin_by_name;
func_mysqlnd_conn_data__set_client_option_2d set_client_option_2d;
+
+ func_mysqlnd_conn_data__negotiate_client_api_capabilities negotiate_client_api_capabilities;
+ func_mysqlnd_conn_data__get_client_api_capabilities get_client_api_capabilities;
};
@@ -606,31 +617,31 @@ struct st_mysqlnd_conn_methods
func_mysqlnd_conn__close close;
};
+ /* for decoding - binary or text protocol */
+typedef enum_func_status (*func_mysqlnd_res__row_decoder)(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
+ unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
+ zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC);
-typedef mysqlnd_fetch_row_func fetch_row;
-typedef mysqlnd_fetch_row_func fetch_row_normal_buffered; /* private */
-typedef mysqlnd_fetch_row_func fetch_row_normal_unbuffered; /* private */
typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC);
-typedef MYSQLND_RES * (*func_mysqlnd_res__store_result)(MYSQLND_RES * result, MYSQLND_CONN_DATA * const conn, zend_bool ps TSRMLS_DC);
-typedef void (*func_mysqlnd_res__fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC);
+typedef MYSQLND_RES * (*func_mysqlnd_res__store_result)(MYSQLND_RES * result, MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC);
+typedef void (*func_mysqlnd_res__fetch_into)(MYSQLND_RES *result, const unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC);
typedef MYSQLND_ROW_C (*func_mysqlnd_res__fetch_row_c)(MYSQLND_RES *result TSRMLS_DC);
-typedef void (*func_mysqlnd_res__fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC);
+typedef void (*func_mysqlnd_res__fetch_all)(MYSQLND_RES *result, const unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC);
typedef void (*func_mysqlnd_res__fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC);
typedef uint64_t (*func_mysqlnd_res__num_rows)(const MYSQLND_RES * const result TSRMLS_DC);
typedef unsigned int (*func_mysqlnd_res__num_fields)(const MYSQLND_RES * const result TSRMLS_DC);
typedef enum_func_status (*func_mysqlnd_res__skip_result)(MYSQLND_RES * const result TSRMLS_DC);
-typedef enum_func_status (*func_mysqlnd_res__seek_data)(MYSQLND_RES * result, uint64_t row TSRMLS_DC);
-typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res__seek_field)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_res__seek_data)(MYSQLND_RES * const result, const uint64_t row TSRMLS_DC);
+typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res__seek_field)(MYSQLND_RES * const result, const MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC);
typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res__field_tell)(const MYSQLND_RES * const result TSRMLS_DC);
typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_field)(MYSQLND_RES * const result TSRMLS_DC);
-typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_field_direct)(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC);
+typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_field_direct)(MYSQLND_RES * const result, const MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC);
typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_fields)(MYSQLND_RES * const result TSRMLS_DC);
typedef enum_func_status (*func_mysqlnd_res__read_result_metadata)(MYSQLND_RES * result, MYSQLND_CONN_DATA * conn TSRMLS_DC);
typedef unsigned long * (*func_mysqlnd_res__fetch_lengths)(MYSQLND_RES * const result TSRMLS_DC);
-typedef enum_func_status (*func_mysqlnd_res__store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result, MYSQLND_RES_METADATA *meta, zend_bool binary_protocol TSRMLS_DC);
-typedef enum_func_status (*func_mysqlnd_res__initialize_result_set_rest)(MYSQLND_RES * const result TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_res__store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result, MYSQLND_RES_METADATA * meta, MYSQLND_MEMORY_POOL_CHUNK *** row_buffers, zend_bool binary_protocol TSRMLS_DC);
typedef void (*func_mysqlnd_res__free_result_buffers)(MYSQLND_RES * result TSRMLS_DC); /* private */
typedef enum_func_status (*func_mysqlnd_res__free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC);
@@ -639,18 +650,12 @@ typedef void (*func_mysqlnd_res__free_result_contents)(MYSQLND_RES *result TS
typedef void (*func_mysqlnd_res__free_buffered_data)(MYSQLND_RES *result TSRMLS_DC);
typedef void (*func_mysqlnd_res__unbuffered_free_last_data)(MYSQLND_RES *result TSRMLS_DC);
- /* for decoding - binary or text protocol */
-typedef enum_func_status (*func_mysqlnd_res__row_decoder)(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
- unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
- zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC);
typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res__result_meta_init)(unsigned int field_count, zend_bool persistent TSRMLS_DC);
struct st_mysqlnd_res_methods
{
mysqlnd_fetch_row_func fetch_row;
- mysqlnd_fetch_row_func fetch_row_normal_buffered; /* private */
- mysqlnd_fetch_row_func fetch_row_normal_unbuffered; /* private */
func_mysqlnd_res__use_result use_result;
func_mysqlnd_res__store_result store_result;
@@ -670,16 +675,10 @@ struct st_mysqlnd_res_methods
func_mysqlnd_res__read_result_metadata read_result_metadata;
func_mysqlnd_res__fetch_lengths fetch_lengths;
func_mysqlnd_res__store_result_fetch_data store_result_fetch_data;
- func_mysqlnd_res__initialize_result_set_rest initialize_result_set_rest;
func_mysqlnd_res__free_result_buffers free_result_buffers;
func_mysqlnd_res__free_result free_result;
func_mysqlnd_res__free_result_internal free_result_internal;
func_mysqlnd_res__free_result_contents free_result_contents;
- func_mysqlnd_res__free_buffered_data free_buffered_data;
- func_mysqlnd_res__unbuffered_free_last_data unbuffered_free_last_data;
-
- /* for decoding - binary or text protocol */
- func_mysqlnd_res__row_decoder row_decoder;
func_mysqlnd_res__result_meta_init result_meta_init;
@@ -691,10 +690,45 @@ struct st_mysqlnd_res_methods
};
+typedef uint64_t (*func_mysqlnd_result_unbuffered__num_rows)(const MYSQLND_RES_UNBUFFERED * const result TSRMLS_DC);
+typedef unsigned long * (*func_mysqlnd_result_unbuffered__fetch_lengths)(MYSQLND_RES_UNBUFFERED * const result TSRMLS_DC);
+typedef void (*func_mysqlnd_result_unbuffered__free_last_data)(MYSQLND_RES_UNBUFFERED * result, MYSQLND_STATS * const global_stats TSRMLS_DC);
+typedef void (*func_mysqlnd_result_unbuffered__free_result)(MYSQLND_RES_UNBUFFERED * const result, MYSQLND_STATS * const global_stats TSRMLS_DC);
+
+struct st_mysqlnd_result_unbuffered_methods
+{
+ mysqlnd_fetch_row_func fetch_row;
+ func_mysqlnd_res__row_decoder row_decoder;
+ func_mysqlnd_result_unbuffered__num_rows num_rows;
+ func_mysqlnd_result_unbuffered__fetch_lengths fetch_lengths;
+ func_mysqlnd_result_unbuffered__free_last_data free_last_data;
+ func_mysqlnd_result_unbuffered__free_result free_result;
+};
+
+typedef uint64_t (*func_mysqlnd_result_buffered__num_rows)(const MYSQLND_RES_BUFFERED * const result TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_result_buffered__initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta,
+ MYSQLND_STATS * stats, zend_bool int_and_float_native TSRMLS_DC);
+typedef unsigned long * (*func_mysqlnd_result_buffered__fetch_lengths)(MYSQLND_RES_BUFFERED * const result TSRMLS_DC);
+typedef enum_func_status (*func_mysqlnd_result_buffered__data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row TSRMLS_DC);
+typedef void (*func_mysqlnd_result_buffered__free_result)(MYSQLND_RES_BUFFERED * const result TSRMLS_DC);
+
+struct st_mysqlnd_result_buffered_methods
+{
+ mysqlnd_fetch_row_func fetch_row;
+ func_mysqlnd_res__row_decoder row_decoder;
+ func_mysqlnd_result_buffered__num_rows num_rows;
+ func_mysqlnd_result_buffered__fetch_lengths fetch_lengths;
+ func_mysqlnd_result_buffered__data_seek data_seek;
+ func_mysqlnd_result_buffered__initialize_result_set_rest initialize_result_set_rest;
+ func_mysqlnd_result_buffered__free_result free_result;
+};
+
+
typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_field)(MYSQLND_RES_METADATA * const meta TSRMLS_DC);
-typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC);
+typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_field_direct)(const MYSQLND_RES_METADATA * const meta, const MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC);
typedef const MYSQLND_FIELD * (*func_mysqlnd_res_meta__fetch_fields)(MYSQLND_RES_METADATA * const meta TSRMLS_DC);
typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res_meta__field_tell)(const MYSQLND_RES_METADATA * const meta TSRMLS_DC);
+typedef MYSQLND_FIELD_OFFSET (*func_mysqlnd_res_meta__field_seek)(MYSQLND_RES_METADATA * const meta, const MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC);
typedef enum_func_status (*func_mysqlnd_res_meta__read_metadata)(MYSQLND_RES_METADATA * const meta, MYSQLND_CONN_DATA * conn TSRMLS_DC);
typedef MYSQLND_RES_METADATA * (*func_mysqlnd_res_meta__clone_metadata)(const MYSQLND_RES_METADATA * const meta, zend_bool persistent TSRMLS_DC);
typedef void (*func_mysqlnd_res_meta__free_metadata)(MYSQLND_RES_METADATA * meta TSRMLS_DC);
@@ -705,6 +739,7 @@ struct st_mysqlnd_res_meta_methods
func_mysqlnd_res_meta__fetch_field_direct fetch_field_direct;
func_mysqlnd_res_meta__fetch_fields fetch_fields;
func_mysqlnd_res_meta__field_tell field_tell;
+ func_mysqlnd_res_meta__field_seek field_seek;
func_mysqlnd_res_meta__read_metadata read_metadata;
func_mysqlnd_res_meta__clone_metadata clone_metadata;
func_mysqlnd_res_meta__free_metadata free_metadata;
@@ -752,6 +787,7 @@ typedef enum_func_status (*func_mysqlnd_stmt__generate_execute_request)(MYSQLND
typedef enum_func_status (*func_mysqlnd_stmt__parse_execute_response)(MYSQLND_STMT * const s TSRMLS_DC);
typedef void (*func_mysqlnd_stmt__free_stmt_content)(MYSQLND_STMT * const s TSRMLS_DC);
typedef enum_func_status (*func_mysqlnd_stmt__flush)(MYSQLND_STMT * const stmt TSRMLS_DC);
+typedef void (*func_mysqlnd_stmt__free_stmt_result)(MYSQLND_STMT * const s TSRMLS_DC);
struct st_mysqlnd_stmt_methods
{
@@ -807,6 +843,8 @@ struct st_mysqlnd_stmt_methods
func_mysqlnd_stmt__free_stmt_content free_stmt_content;
func_mysqlnd_stmt__flush flush;
+
+ func_mysqlnd_stmt__free_stmt_result free_stmt_result;
};
@@ -922,9 +960,6 @@ struct st_mysqlnd_connection_data
/* Temporal storage for mysql_query */
unsigned int field_count;
- /* persistent connection */
- zend_bool persistent;
-
/* options */
MYSQLND_OPTIONS * options;
MYSQLND_OPTIONS options_impl;
@@ -932,7 +967,12 @@ struct st_mysqlnd_connection_data
/* stats */
MYSQLND_STATS * stats;
+ unsigned int client_api_capabilities;
+
struct st_mysqlnd_conn_data_methods * m;
+
+ /* persistent connection */
+ zend_bool persistent;
};
@@ -962,32 +1002,83 @@ struct st_mysqlnd_result_metadata
size_t bit_fields_total_len; /* trailing \0 not counted */
zend_bool persistent;
- struct st_mysqlnd_res_meta_methods *m;
+ struct st_mysqlnd_res_meta_methods * m;
};
-struct st_mysqlnd_buffered_result
+#define def_mysqlnd_buffered_result_parent \
+ MYSQLND_MEMORY_POOL_CHUNK **row_buffers; \
+ uint64_t row_count; \
+ uint64_t initialized_rows; \
+ \
+ /* Column lengths of current row - both buffered and unbuffered. For buffered results it duplicates the data found in **data */ \
+ unsigned long *lengths; \
+ \
+ MYSQLND_MEMORY_POOL *result_set_memory_pool; \
+ \
+ unsigned int references; \
+ \
+ MYSQLND_ERROR_INFO error_info; \
+ \
+ unsigned int field_count; \
+ zend_bool ps; \
+ zend_bool persistent; \
+ struct st_mysqlnd_result_buffered_methods m; \
+ enum mysqlnd_buffered_type type; \
+ void * unused1; \
+ void * unused2; \
+ void * unused3
+
+
+struct st_mysqlnd_buffered_result_parent
{
- zval **data;
- zval **data_cursor;
- MYSQLND_MEMORY_POOL_CHUNK **row_buffers;
- uint64_t row_count;
- uint64_t initialized_rows;
+ def_mysqlnd_buffered_result_parent;
+};
+
+
+struct st_mysqlnd_buffered_result_zval
+{
+ def_mysqlnd_buffered_result_parent;
- unsigned int references;
+ zval **data;
+ zval **data_cursor;
+};
+
+
+struct st_mysqlnd_buffered_result_c
+{
+ def_mysqlnd_buffered_result_parent;
- MYSQLND_ERROR_INFO error_info;
+ zend_uchar *initialized; /* every row is a single bit */
+ uint64_t current_row;
};
struct st_mysqlnd_unbuffered_result
{
+
/* For unbuffered (both normal and PS) */
zval **last_row_data;
MYSQLND_MEMORY_POOL_CHUNK *last_row_buffer;
+ /*
+ Column lengths of current row - both buffered and unbuffered.
+ For buffered results it duplicates the data found in **data
+ */
+ unsigned long *lengths;
+
+ MYSQLND_MEMORY_POOL *result_set_memory_pool;
+
+ struct st_mysqlnd_packet_row * row_packet;
+
uint64_t row_count;
zend_bool eof_reached;
+
+ unsigned int field_count;
+ zend_bool ps;
+ zend_bool persistent;
+
+ struct st_mysqlnd_result_unbuffered_methods m;
};
@@ -1001,18 +1092,9 @@ struct st_mysqlnd_res
MYSQLND_RES_METADATA *meta;
/* To be used with store_result() - both normal and PS */
- MYSQLND_RES_BUFFERED *stored_data;
- MYSQLND_RES_UNBUFFERED *unbuf;
-
- /*
- Column lengths of current row - both buffered and unbuffered.
- For buffered results it duplicates the data found in **data
- */
- unsigned long *lengths;
-
- struct st_mysqlnd_packet_row * row_packet;
+ MYSQLND_RES_BUFFERED *stored_data;
+ MYSQLND_RES_UNBUFFERED *unbuf;
- MYSQLND_MEMORY_POOL * result_set_memory_pool;
zend_bool persistent;
struct st_mysqlnd_res_methods m;
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c
index 8de0cfc1d2..c7c0bf9190 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -1572,13 +1572,13 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zv
}
/* }}} */
-
/* {{{ php_mysqlnd_rowp_read_text_protocol */
enum_func_status
-php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
+php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
- zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC)
+ zend_bool as_int_or_float, zend_bool copy_data, MYSQLND_STATS * stats TSRMLS_DC)
{
+
unsigned int i;
zend_bool last_field_was_string = FALSE;
zval **current_field, **end_field, **start_field;
@@ -1586,7 +1586,7 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval
size_t data_size = row_buffer->app;
zend_uchar * bit_area = (zend_uchar*) row_buffer->ptr + data_size + 1; /* we allocate from here */
- DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
+ DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_aux");
if (!fields) {
DBG_RETURN(FAIL);
@@ -1608,7 +1608,7 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval
/* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
unsigned long len = php_mysqlnd_net_field_length(&p);
- if (current_field > start_field && last_field_was_string) {
+ if (copy_data == FALSE && current_field > start_field && last_field_was_string) {
/*
Normal queries:
We have to put \0 now to the end of the previous field, if it was
@@ -1733,22 +1733,22 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval
p -= len;
if (Z_TYPE_PP(current_field) == IS_LONG) {
bit_area += 1 + sprintf((char *)start, "%ld", Z_LVAL_PP(current_field));
- ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
+ ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, copy_data);
} else if (Z_TYPE_PP(current_field) == IS_STRING){
memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field));
bit_area += Z_STRLEN_PP(current_field);
*bit_area++ = '\0';
zval_dtor(*current_field);
- ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
+ ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, copy_data);
}
} else {
- ZVAL_STRINGL(*current_field, (char *)p, len, 0);
+ ZVAL_STRINGL(*current_field, (char *)p, len, copy_data);
}
p += len;
last_field_was_string = TRUE;
}
}
- if (last_field_was_string) {
+ if (copy_data == FALSE && last_field_was_string) {
/* Normal queries: The buffer has one more byte at the end, because we need it */
row_buffer->ptr[data_size] = '\0';
}
@@ -1758,6 +1758,36 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval
/* }}} */
+/* {{{ php_mysqlnd_rowp_read_text_protocol_zval */
+enum_func_status
+php_mysqlnd_rowp_read_text_protocol_zval(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
+ unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
+ zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC)
+{
+ 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, FALSE, stats TSRMLS_CC);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+/* {{{ php_mysqlnd_rowp_read_text_protocol_c */
+enum_func_status
+php_mysqlnd_rowp_read_text_protocol_c(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
+ unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
+ zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC)
+{
+ 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, TRUE, stats TSRMLS_CC);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+
+
+
/* {{{ php_mysqlnd_rowp_read */
/*
if normal statements => packet->fields is created by this function,
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h
index 8dd02f1708..3983ebab77 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.h
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.h
@@ -302,6 +302,7 @@ PHPAPI void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * c
unsigned long php_mysqlnd_net_field_length(zend_uchar **packet);
zend_uchar * php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length);
+size_t php_mysqlnd_net_store_length_size(uint64_t length);
PHPAPI const extern char * const mysqlnd_empty_string;
@@ -311,7 +312,11 @@ enum_func_status php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK
zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC);
-enum_func_status php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
+enum_func_status php_mysqlnd_rowp_read_text_protocol_zval(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
+ unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
+ zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC);
+
+enum_func_status php_mysqlnd_rowp_read_text_protocol_c(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC);
diff --git a/ext/mysqlnd/php_mysqlnd.c b/ext/mysqlnd/php_mysqlnd.c
index 8889c717fc..3d0225a332 100644
--- a/ext/mysqlnd/php_mysqlnd.c
+++ b/ext/mysqlnd/php_mysqlnd.c
@@ -107,17 +107,17 @@ static void
mysqlnd_minfo_dump_api_plugins(smart_str * buffer TSRMLS_DC)
{
HashTable *ht = mysqlnd_reverse_api_get_api_list(TSRMLS_C);
- Bucket *p;
+ HashPosition pos;
+ MYSQLND_REVERSE_API **ext;
- p = ht->pListHead;
- while(p != NULL) {
- MYSQLND_REVERSE_API * ext = *(MYSQLND_REVERSE_API **) p->pData;
+ for (zend_hash_internal_pointer_reset_ex(ht, &pos);
+ zend_hash_get_current_data_ex(ht, (void **) &ext, &pos) == SUCCESS;
+ zend_hash_move_forward_ex(ht, &pos)
+ ) {
if (buffer->len) {
smart_str_appendc(buffer, ',');
}
- smart_str_appends(buffer, ext->module->name);
-
- p = p->pListNext;
+ smart_str_appends(buffer, (*ext)->module->name);
}
}
/* }}} */
@@ -209,6 +209,7 @@ 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;
}
/* }}} */
@@ -227,8 +228,8 @@ static PHP_INI_MH(OnUpdateNetCmdBufferSize)
/* {{{ PHP_INI_BEGIN
*/
PHP_INI_BEGIN()
- STD_PHP_INI_BOOLEAN("mysqlnd.collect_statistics", "1", PHP_INI_ALL, OnUpdateBool, collect_statistics, zend_mysqlnd_globals, mysqlnd_globals)
- STD_PHP_INI_BOOLEAN("mysqlnd.collect_memory_statistics", "0", PHP_INI_SYSTEM, OnUpdateBool, collect_memory_statistics, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_BOOLEAN("mysqlnd.collect_statistics", "1", PHP_INI_ALL, OnUpdateBool, collect_statistics, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_BOOLEAN("mysqlnd.collect_memory_statistics","0",PHP_INI_SYSTEM, OnUpdateBool, collect_memory_statistics, zend_mysqlnd_globals, mysqlnd_globals)
STD_PHP_INI_ENTRY("mysqlnd.debug", NULL, PHP_INI_SYSTEM, OnUpdateString, debug, zend_mysqlnd_globals, mysqlnd_globals)
STD_PHP_INI_ENTRY("mysqlnd.trace_alloc", NULL, PHP_INI_SYSTEM, OnUpdateString, trace_alloc_settings, zend_mysqlnd_globals, mysqlnd_globals)
STD_PHP_INI_ENTRY("mysqlnd.net_cmd_buffer_size", MYSQLND_NET_CMD_BUFFER_MIN_SIZE_STR, PHP_INI_ALL, OnUpdateNetCmdBufferSize, net_cmd_buffer_size, zend_mysqlnd_globals, mysqlnd_globals)
@@ -237,7 +238,7 @@ 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_emalloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_emalloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals)
STD_PHP_INI_ENTRY("mysqlnd.debug_ecalloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_ecalloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals)