From: Andrey Hristov Date: Mon, 9 Nov 2015 14:53:42 +0000 (+0100) Subject: MNDR: X-Git-Tag: php-7.1.0alpha1~766 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=80d59a3a362052cfa5767f99dcd3954182947c55;p=php MNDR: - intermediate step to move MYSQLND_NET from mysqlnd_vio.c to mysqlnd_wireprotocol.c In following commits MYSQLND_NET's methods and data will be moved to MYSQLND_PROTOCOL --- diff --git a/ext/mysqlnd/mysqlnd_vio.c b/ext/mysqlnd/mysqlnd_vio.c index e2c125fdfb..0aa06e21df 100644 --- a/ext/mysqlnd/mysqlnd_vio.c +++ b/ext/mysqlnd/mysqlnd_vio.c @@ -29,9 +29,6 @@ #include "mysqlnd_ext_plugin.h" #include "php_network.h" #include "zend_ini.h" -#ifdef MYSQLND_COMPRESSION_ENABLED -#include -#endif #ifndef PHP_WIN32 #include @@ -44,7 +41,6 @@ static int mysqlnd_set_sock_no_delay(php_stream * stream) { - int socketd = ((php_netstream_data_t*)stream->abstract)->socket; int ret = SUCCESS; int flag = 1; @@ -65,7 +61,6 @@ mysqlnd_set_sock_no_delay(php_stream * stream) static int mysqlnd_set_sock_keepalive(php_stream * stream) { - int socketd = ((php_netstream_data_t*)stream->abstract)->socket; int ret = SUCCESS; int flag = 1; @@ -315,18 +310,6 @@ MYSQLND_METHOD(mysqlnd_vio, get_open_stream)(MYSQLND_VIO * const vio, const MYSQ /* }}} */ -/* {{{ mysqlnd_net::connect */ -static enum_func_status -MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * const net, const MYSQLND_CSTRING scheme, const zend_bool persistent, - MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info) -{ - DBG_ENTER("mysqlnd_net::connect"); - net->packet_no = net->compressed_envelope_packet_no = 0; - DBG_RETURN(PASS); -} -/* }}} */ - - /* {{{ mysqlnd_vio::connect */ static enum_func_status MYSQLND_METHOD(mysqlnd_vio, connect)(MYSQLND_VIO * const vio, const MYSQLND_CSTRING scheme, const zend_bool persistent, @@ -353,407 +336,6 @@ MYSQLND_METHOD(mysqlnd_vio, connect)(MYSQLND_VIO * const vio, const MYSQLND_CSTR /* }}} */ -/* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */ -#define COPY_HEADER(T,A) do { \ - *(((char *)(T))) = *(((char *)(A)));\ - *(((char *)(T))+1) = *(((char *)(A))+1);\ - *(((char *)(T))+2) = *(((char *)(A))+2);\ - *(((char *)(T))+3) = *(((char *)(A))+3); } while (0) -#define STORE_HEADER_SIZE(safe_storage, buffer) COPY_HEADER((safe_storage), (buffer)) -#define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer)) - - -/* {{{ mysqlnd_net::send */ -/* - IMPORTANT : It's expected that buffer has place in the beginning for MYSQLND_HEADER_SIZE !!!! - This is done for performance reasons in the caller of this function. - Otherwise we will have to do send two TCP packets, or do new alloc and memcpy. - Neither are quick, thus the clients of this function are obligated to do - what they are asked for. - - `count` is actually the length of the payload data. Thus : - count + MYSQLND_HEADER_SIZE = sizeof(buffer) (not the pointer but the actual buffer) -*/ -static size_t -MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND_NET * const net, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count, - MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info) -{ - zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))]; - zend_uchar * safe_storage = safe_buf; - size_t bytes_sent, packets_sent = 1; - size_t left = count; - zend_uchar * p = (zend_uchar *) buffer; - zend_uchar * compress_buf = NULL; - size_t to_be_sent; - - DBG_ENTER("mysqlnd_net::send"); - DBG_INF_FMT("count=" MYSQLND_SZ_T_SPEC " compression=%u", count, net->data->compressed); - - if (net->data->compressed == TRUE) { - size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE); - DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC, comp_buf_size); - compress_buf = mnd_emalloc(comp_buf_size); - } - - 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 */ - size_t tmp_complen = to_be_sent; - size_t payload_size; - zend_uchar * uncompressed_payload = p; /* should include the header */ - - STORE_HEADER_SIZE(safe_storage, uncompressed_payload); - int3store(uncompressed_payload, to_be_sent); - int1store(uncompressed_payload + 3, net->packet_no); - if (PASS == net->data->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, - uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE)) - { - int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent + MYSQLND_HEADER_SIZE); - payload_size = tmp_complen; - } else { - int3store(compress_buf + MYSQLND_HEADER_SIZE, 0); - memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE); - payload_size = to_be_sent + MYSQLND_HEADER_SIZE; - } - RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage); - - int3store(compress_buf, payload_size); - int1store(compress_buf + 3, net->packet_no); - DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC" bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE); - bytes_sent = vio->data->m.network_write(vio, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, conn_stats, error_info); - net->compressed_envelope_packet_no++; - #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY - if (res == Z_OK) { - size_t decompressed_size = left + MYSQLND_HEADER_SIZE; - zend_uchar * decompressed_data = mnd_malloc(decompressed_size); - int error = net->data->m.decode(decompressed_data, decompressed_size, - compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size); - if (error == Z_OK) { - int i; - DBG_INF("success decompressing"); - for (i = 0 ; i < decompressed_size; i++) { - if (i && (i % 30 == 0)) { - printf("\n\t\t"); - } - printf("%.2X ", (int)*((char*)&(decompressed_data[i]))); - DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i]))); - } - } else { - DBG_INF("error decompressing"); - } - mnd_free(decompressed_data); - } - #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */ - } else -#endif /* MYSQLND_COMPRESSION_ENABLED */ - { - DBG_INF("no compression"); - STORE_HEADER_SIZE(safe_storage, p); - int3store(p, to_be_sent); - int1store(p + 3, net->packet_no); - bytes_sent = vio->data->m.network_write(vio, p, to_be_sent + MYSQLND_HEADER_SIZE, conn_stats, error_info); - RESTORE_HEADER_SIZE(p, safe_storage); - net->compressed_envelope_packet_no++; - } - net->packet_no++; - - p += to_be_sent; - left -= to_be_sent; - packets_sent++; - /* - if left is 0 then there is nothing more to send, but if the last packet was exactly - with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has - empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is - indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty - packet will be sent and this loop will end. - */ - } while (bytes_sent && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE)); - - DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC" packet_no=%u", left, net->packet_no); - - MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, - STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE, - STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE, - STAT_PACKETS_SENT, packets_sent); - - if (compress_buf) { - mnd_efree(compress_buf); - } - - /* Even for zero size payload we have to send a packet */ - if (!bytes_sent) { - DBG_ERR_FMT("Can't %u send bytes", count); - SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); - } - DBG_RETURN(bytes_sent); -} -/* }}} */ - - -#ifdef MYSQLND_COMPRESSION_ENABLED -/* {{{ php_mysqlnd_read_buffer_is_empty */ -static zend_bool -php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER * buffer) -{ - return buffer->len? FALSE:TRUE; -} -/* }}} */ - - -/* {{{ php_mysqlnd_read_buffer_read */ -static void -php_mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer, size_t count, zend_uchar * dest) -{ - if (buffer->len >= count) { - memcpy(dest, buffer->data + buffer->offset, count); - buffer->offset += count; - buffer->len -= count; - } -} -/* }}} */ - - -/* {{{ php_mysqlnd_read_buffer_bytes_left */ -static size_t -php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER * buffer) -{ - return buffer->len; -} -/* }}} */ - - -/* {{{ php_mysqlnd_read_buffer_free */ -static void -php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer) -{ - DBG_ENTER("php_mysqlnd_read_buffer_free"); - if (*buffer) { - mnd_efree((*buffer)->data); - mnd_efree(*buffer); - *buffer = NULL; - } - DBG_VOID_RETURN; -} -/* }}} */ - - -/* {{{ php_mysqlnd_create_read_buffer */ -static MYSQLND_READ_BUFFER * -mysqlnd_create_read_buffer(size_t count) -{ - MYSQLND_READ_BUFFER * ret = mnd_emalloc(sizeof(MYSQLND_READ_BUFFER)); - DBG_ENTER("mysqlnd_create_read_buffer"); - ret->is_empty = php_mysqlnd_read_buffer_is_empty; - ret->read = php_mysqlnd_read_buffer_read; - ret->bytes_left = php_mysqlnd_read_buffer_bytes_left; - ret->free_buffer = php_mysqlnd_read_buffer_free; - ret->data = mnd_emalloc(count); - ret->size = ret->len = count; - ret->offset = 0; - DBG_RETURN(ret); -} -/* }}} */ - - -/* {{{ mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer */ -static enum_func_status -MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer) - (MYSQLND_NET * net, MYSQLND_VIO * vio, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info) -{ - size_t decompressed_size; - enum_func_status retval = PASS; - zend_uchar * compressed_data = NULL; - zend_uchar comp_header[COMPRESSED_HEADER_SIZE]; - DBG_ENTER("mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer"); - - /* Read the compressed header */ - if (FAIL == vio->data->m.network_read(vio, comp_header, COMPRESSED_HEADER_SIZE, conn_stats, error_info)) { - DBG_RETURN(FAIL); - } - decompressed_size = uint3korr(comp_header); - - /* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */ - /* we need to decompress the data */ - - if (decompressed_size) { - compressed_data = mnd_emalloc(net_payload_size); - if (FAIL == vio->data->m.network_read(vio, compressed_data, net_payload_size, conn_stats, error_info)) { - retval = FAIL; - goto end; - } - net->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size); - retval = net->data->m.decode(net->uncompressed_data->data, decompressed_size, compressed_data, net_payload_size); - if (FAIL == retval) { - goto end; - } - } else { - DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size); - net->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size); - if (FAIL == vio->data->m.network_read(vio, net->uncompressed_data->data, net_payload_size, conn_stats, error_info)) { - retval = FAIL; - goto end; - } - } -end: - if (compressed_data) { - mnd_efree(compressed_data); - } - DBG_RETURN(retval); -} -/* }}} */ -#endif /* MYSQLND_COMPRESSION_ENABLED */ - - -/* {{{ mysqlnd_net::decode */ -static enum_func_status -MYSQLND_METHOD(mysqlnd_net, decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len, - const zend_uchar * const compressed_data, const size_t compressed_data_len) -{ -#ifdef MYSQLND_COMPRESSION_ENABLED - int error; - uLongf tmp_complen = uncompressed_data_len; - DBG_ENTER("mysqlnd_net::decode"); - error = uncompress(uncompressed_data, &tmp_complen, compressed_data, compressed_data_len); - - DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size="MYSQLND_SZ_T_SPEC, tmp_complen, compressed_data_len); - if (error != Z_OK) { - DBG_INF_FMT("decompression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR); - } - DBG_RETURN(error == Z_OK? PASS:FAIL); -#else - DBG_ENTER("mysqlnd_net::decode"); - DBG_RETURN(FAIL); -#endif -} -/* }}} */ - - -/* {{{ mysqlnd_net::encode */ -static enum_func_status -MYSQLND_METHOD(mysqlnd_net, encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len, - const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len) -{ -#ifdef MYSQLND_COMPRESSION_ENABLED - int error; - uLongf tmp_complen = *compress_buffer_len; - DBG_ENTER("mysqlnd_net::encode"); - error = compress(compress_buffer, &tmp_complen, uncompressed_data, uncompressed_data_len); - - if (error != Z_OK) { - DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR); - } else { - *compress_buffer_len = tmp_complen; - DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen); - } - - DBG_RETURN(error == Z_OK? PASS:FAIL); -#else - DBG_ENTER("mysqlnd_net::encode"); - DBG_RETURN(FAIL); -#endif -} -/* }}} */ - - -/* {{{ mysqlnd_net::receive */ -static enum_func_status -MYSQLND_METHOD(mysqlnd_net, receive)(MYSQLND_NET * const net, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count, - MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info) -{ - size_t to_read = count; - zend_uchar * p = buffer; - - DBG_ENTER("mysqlnd_net::receive"); -#ifdef MYSQLND_COMPRESSION_ENABLED - if (net->data->compressed) { - if (net->uncompressed_data) { - size_t to_read_from_buffer = MIN(net->uncompressed_data->bytes_left(net->uncompressed_data), to_read); - DBG_INF_FMT("reading "MYSQLND_SZ_T_SPEC" from uncompressed_data buffer", to_read_from_buffer); - if (to_read_from_buffer) { - net->uncompressed_data->read(net->uncompressed_data, to_read_from_buffer, (zend_uchar *) p); - p += to_read_from_buffer; - to_read -= to_read_from_buffer; - } - DBG_INF_FMT("left "MYSQLND_SZ_T_SPEC" to read", to_read); - if (TRUE == net->uncompressed_data->is_empty(net->uncompressed_data)) { - /* Everything was consumed. This should never happen here, but for security */ - net->uncompressed_data->free_buffer(&net->uncompressed_data); - } - } - if (to_read) { - zend_uchar net_header[MYSQLND_HEADER_SIZE]; - size_t net_payload_size; - zend_uchar packet_no; - - if (FAIL == vio->data->m.network_read(vio, net_header, MYSQLND_HEADER_SIZE, conn_stats, error_info)) { - DBG_RETURN(FAIL); - } - net_payload_size = uint3korr(net_header); - packet_no = uint1korr(net_header + 3); - if (net->compressed_envelope_packet_no != packet_no) { - DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC, - net->compressed_envelope_packet_no, packet_no, net_payload_size); - - php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC, - net->compressed_envelope_packet_no, packet_no, net_payload_size); - DBG_RETURN(FAIL); - } - net->compressed_envelope_packet_no++; -#ifdef MYSQLND_DUMP_HEADER_N_BODY - DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (zend_ulong) net_payload_size); -#endif - /* Now let's read from the wire, decompress it and fill the read buffer */ - net->data->m.read_compressed_packet_from_stream_and_fill_read_buffer(net, vio, net_payload_size, conn_stats, error_info); - - /* - Now a bit of recursion - read from the read buffer, - if the data which we have just read from the wire - is not enough, then the recursive call will try to - satisfy it until it is satisfied. - */ - DBG_RETURN(net->data->m.receive(net, vio, p, to_read, conn_stats, error_info)); - } - DBG_RETURN(PASS); - } -#endif /* MYSQLND_COMPRESSION_ENABLED */ - DBG_RETURN(vio->data->m.network_read(vio, p, to_read, conn_stats, error_info)); -} -/* }}} */ - - -/* {{{ mysqlnd_net::set_client_option */ -static enum_func_status -MYSQLND_METHOD(mysqlnd_net, set_client_option)(MYSQLND_NET * const net, enum_mysqlnd_client_option option, const char * const value) -{ - DBG_ENTER("mysqlnd_net::set_client_option"); - DBG_INF_FMT("option=%u", option); - switch (option) { - case MYSQL_OPT_COMPRESS: - net->data->options.flags |= MYSQLND_NET_FLAG_USE_COMPRESSION; - break; - case MYSQL_SERVER_PUBLIC_KEY: - { - zend_bool pers = net->persistent; - if (net->data->options.sha256_server_public_key) { - mnd_pefree(net->data->options.sha256_server_public_key, pers); - } - net->data->options.sha256_server_public_key = value? mnd_pestrdup(value, pers) : NULL; - break; - } - default: - DBG_RETURN(FAIL); - } - DBG_RETURN(PASS); -} -/* }}} */ - - /* {{{ mysqlnd_vio::set_client_option */ static enum_func_status MYSQLND_METHOD(mysqlnd_vio, set_client_option)(MYSQLND_VIO * const net, enum_mysqlnd_client_option option, const char * const value) @@ -1054,28 +636,6 @@ MYSQLND_METHOD(mysqlnd_vio, disable_ssl)(MYSQLND_VIO * const vio) /* }}} */ -/* {{{ mysqlnd_net::free_contents */ -static void -MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET * net) -{ - zend_bool pers = net->persistent; - DBG_ENTER("mysqlnd_net::free_contents"); - -#ifdef MYSQLND_COMPRESSION_ENABLED - if (net->uncompressed_data) { - net->uncompressed_data->free_buffer(&net->uncompressed_data); - } -#endif - if (net->data->options.sha256_server_public_key) { - mnd_pefree(net->data->options.sha256_server_public_key, pers); - net->data->options.sha256_server_public_key = NULL; - } - - DBG_VOID_RETURN; -} -/* }}} */ - - /* {{{ mysqlnd_vio::free_contents */ static void MYSQLND_METHOD(mysqlnd_vio, free_contents)(MYSQLND_VIO * net) @@ -1139,15 +699,6 @@ MYSQLND_METHOD(mysqlnd_vio, close_stream)(MYSQLND_VIO * const net, MYSQLND_STATS /* }}} */ -/* {{{ mysqlnd_net::init */ -static enum_func_status -MYSQLND_METHOD(mysqlnd_net, init)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info) -{ - return PASS; -} -/* }}} */ - - /* {{{ mysqlnd_vio::init */ static enum_func_status MYSQLND_METHOD(mysqlnd_vio, init)(MYSQLND_VIO * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info) @@ -1169,22 +720,6 @@ MYSQLND_METHOD(mysqlnd_vio, init)(MYSQLND_VIO * const net, MYSQLND_STATS * const /* }}} */ -/* {{{ mysqlnd_net::dtor */ -static void -MYSQLND_METHOD(mysqlnd_net, dtor)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info) -{ - DBG_ENTER("mysqlnd_net::dtor"); - if (net) { - net->data->m.free_contents(net); - - mnd_pefree(net->data, net->data->persistent); - mnd_pefree(net, net->persistent); - } - DBG_VOID_RETURN; -} -/* }}} */ - - /* {{{ mysqlnd_vio::dtor */ static void MYSQLND_METHOD(mysqlnd_vio, dtor)(MYSQLND_VIO * const vio, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info) @@ -1234,29 +769,6 @@ MYSQLND_METHOD(mysqlnd_vio, set_stream)(MYSQLND_VIO * const net, php_stream * ne /* }}} */ -MYSQLND_CLASS_METHODS_START(mysqlnd_net) - MYSQLND_METHOD(mysqlnd_net, init), - MYSQLND_METHOD(mysqlnd_net, dtor), - MYSQLND_METHOD(mysqlnd_net, connect), - - MYSQLND_METHOD(mysqlnd_net, set_client_option), - - MYSQLND_METHOD(mysqlnd_net, decode), - MYSQLND_METHOD(mysqlnd_net, encode), - - MYSQLND_METHOD(mysqlnd_net, send), - MYSQLND_METHOD(mysqlnd_net, receive), - -#ifdef MYSQLND_COMPRESSION_ENABLED - MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer), -#else - NULL, -#endif - - MYSQLND_METHOD(mysqlnd_net, free_contents), -MYSQLND_CLASS_METHODS_END; - - MYSQLND_CLASS_METHODS_START(mysqlnd_vio) MYSQLND_METHOD(mysqlnd_vio, init), MYSQLND_METHOD(mysqlnd_vio, dtor), @@ -1286,31 +798,6 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_vio) MYSQLND_CLASS_METHODS_END; -/* {{{ mysqlnd_net_init */ -PHPAPI MYSQLND_NET * -mysqlnd_net_init(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info) -{ - MYSQLND_NET * net; - DBG_ENTER("mysqlnd_net_init"); - net = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_net(persistent, stats, error_info); - DBG_RETURN(net); -} -/* }}} */ - - -/* {{{ mysqlnd_net_free */ -PHPAPI void -mysqlnd_net_free(MYSQLND_NET * const net, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info) -{ - DBG_ENTER("mysqlnd_net_free"); - if (net) { - net->data->m.dtor(net, stats, error_info); - } - DBG_VOID_RETURN; -} -/* }}} */ - - /* {{{ mysqlnd_vio_init */ PHPAPI MYSQLND_VIO * mysqlnd_vio_init(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info) diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index e2fd68e5d3..7199178b2c 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -27,6 +27,9 @@ #include "mysqlnd_statistics.h" #include "mysqlnd_debug.h" #include "zend_ini.h" +#ifdef MYSQLND_COMPRESSION_ENABLED +#include +#endif #define MYSQLND_SILENT 1 #define MYSQLND_DUMP_HEADER_N_BODY @@ -2933,6 +2936,515 @@ MYSQLND_METHOD(mysqlnd_protocol, send_command_handle_response)( +/* {{{ mysqlnd_net::connect */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, connect)(MYSQLND_NET * const net, const MYSQLND_CSTRING scheme, const zend_bool persistent, + MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info) +{ + DBG_ENTER("mysqlnd_net::connect"); + net->packet_no = net->compressed_envelope_packet_no = 0; + DBG_RETURN(PASS); +} +/* }}} */ + + +/* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */ +#define COPY_HEADER(T,A) do { \ + *(((char *)(T))) = *(((char *)(A)));\ + *(((char *)(T))+1) = *(((char *)(A))+1);\ + *(((char *)(T))+2) = *(((char *)(A))+2);\ + *(((char *)(T))+3) = *(((char *)(A))+3); } while (0) +#define STORE_HEADER_SIZE(safe_storage, buffer) COPY_HEADER((safe_storage), (buffer)) +#define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer)) + + +/* {{{ mysqlnd_net::send */ +/* + IMPORTANT : It's expected that buffer has place in the beginning for MYSQLND_HEADER_SIZE !!!! + This is done for performance reasons in the caller of this function. + Otherwise we will have to do send two TCP packets, or do new alloc and memcpy. + Neither are quick, thus the clients of this function are obligated to do + what they are asked for. + + `count` is actually the length of the payload data. Thus : + count + MYSQLND_HEADER_SIZE = sizeof(buffer) (not the pointer but the actual buffer) +*/ +static size_t +MYSQLND_METHOD(mysqlnd_net, send)(MYSQLND_NET * const net, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count, + MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info) +{ + zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))]; + zend_uchar * safe_storage = safe_buf; + size_t bytes_sent, packets_sent = 1; + size_t left = count; + zend_uchar * p = (zend_uchar *) buffer; + zend_uchar * compress_buf = NULL; + size_t to_be_sent; + + DBG_ENTER("mysqlnd_net::send"); + DBG_INF_FMT("count=" MYSQLND_SZ_T_SPEC " compression=%u", count, net->data->compressed); + + if (net->data->compressed == TRUE) { + size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE); + DBG_INF_FMT("compress_buf_size="MYSQLND_SZ_T_SPEC, comp_buf_size); + compress_buf = mnd_emalloc(comp_buf_size); + } + + 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 */ + size_t tmp_complen = to_be_sent; + size_t payload_size; + zend_uchar * uncompressed_payload = p; /* should include the header */ + + STORE_HEADER_SIZE(safe_storage, uncompressed_payload); + int3store(uncompressed_payload, to_be_sent); + int1store(uncompressed_payload + 3, net->packet_no); + if (PASS == net->data->m.encode((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, + uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE)) + { + int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent + MYSQLND_HEADER_SIZE); + payload_size = tmp_complen; + } else { + int3store(compress_buf + MYSQLND_HEADER_SIZE, 0); + memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE); + payload_size = to_be_sent + MYSQLND_HEADER_SIZE; + } + RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage); + + int3store(compress_buf, payload_size); + int1store(compress_buf + 3, net->packet_no); + DBG_INF_FMT("writing "MYSQLND_SZ_T_SPEC" bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE); + bytes_sent = vio->data->m.network_write(vio, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, conn_stats, error_info); + net->compressed_envelope_packet_no++; + #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY + if (res == Z_OK) { + size_t decompressed_size = left + MYSQLND_HEADER_SIZE; + zend_uchar * decompressed_data = mnd_malloc(decompressed_size); + int error = net->data->m.decode(decompressed_data, decompressed_size, + compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size); + if (error == Z_OK) { + int i; + DBG_INF("success decompressing"); + for (i = 0 ; i < decompressed_size; i++) { + if (i && (i % 30 == 0)) { + printf("\n\t\t"); + } + printf("%.2X ", (int)*((char*)&(decompressed_data[i]))); + DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i]))); + } + } else { + DBG_INF("error decompressing"); + } + mnd_free(decompressed_data); + } + #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */ + } else +#endif /* MYSQLND_COMPRESSION_ENABLED */ + { + DBG_INF("no compression"); + STORE_HEADER_SIZE(safe_storage, p); + int3store(p, to_be_sent); + int1store(p + 3, net->packet_no); + bytes_sent = vio->data->m.network_write(vio, p, to_be_sent + MYSQLND_HEADER_SIZE, conn_stats, error_info); + RESTORE_HEADER_SIZE(p, safe_storage); + net->compressed_envelope_packet_no++; + } + net->packet_no++; + + p += to_be_sent; + left -= to_be_sent; + packets_sent++; + /* + if left is 0 then there is nothing more to send, but if the last packet was exactly + with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has + empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is + indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty + packet will be sent and this loop will end. + */ + } while (bytes_sent && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE)); + + DBG_INF_FMT("packet_size="MYSQLND_SZ_T_SPEC" packet_no=%u", left, net->packet_no); + + MYSQLND_INC_CONN_STATISTIC_W_VALUE3(conn_stats, + STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE, + STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE, + STAT_PACKETS_SENT, packets_sent); + + if (compress_buf) { + mnd_efree(compress_buf); + } + + /* Even for zero size payload we have to send a packet */ + if (!bytes_sent) { + DBG_ERR_FMT("Can't %u send bytes", count); + SET_CLIENT_ERROR(error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone); + } + DBG_RETURN(bytes_sent); +} +/* }}} */ + + +#ifdef MYSQLND_COMPRESSION_ENABLED +/* {{{ php_mysqlnd_read_buffer_is_empty */ +static zend_bool +php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER * buffer) +{ + return buffer->len? FALSE:TRUE; +} +/* }}} */ + + +/* {{{ php_mysqlnd_read_buffer_read */ +static void +php_mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer, size_t count, zend_uchar * dest) +{ + if (buffer->len >= count) { + memcpy(dest, buffer->data + buffer->offset, count); + buffer->offset += count; + buffer->len -= count; + } +} +/* }}} */ + + +/* {{{ php_mysqlnd_read_buffer_bytes_left */ +static size_t +php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER * buffer) +{ + return buffer->len; +} +/* }}} */ + + +/* {{{ php_mysqlnd_read_buffer_free */ +static void +php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer) +{ + DBG_ENTER("php_mysqlnd_read_buffer_free"); + if (*buffer) { + mnd_efree((*buffer)->data); + mnd_efree(*buffer); + *buffer = NULL; + } + DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ php_mysqlnd_create_read_buffer */ +static MYSQLND_READ_BUFFER * +mysqlnd_create_read_buffer(size_t count) +{ + MYSQLND_READ_BUFFER * ret = mnd_emalloc(sizeof(MYSQLND_READ_BUFFER)); + DBG_ENTER("mysqlnd_create_read_buffer"); + ret->is_empty = php_mysqlnd_read_buffer_is_empty; + ret->read = php_mysqlnd_read_buffer_read; + ret->bytes_left = php_mysqlnd_read_buffer_bytes_left; + ret->free_buffer = php_mysqlnd_read_buffer_free; + ret->data = mnd_emalloc(count); + ret->size = ret->len = count; + ret->offset = 0; + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer) + (MYSQLND_NET * net, MYSQLND_VIO * vio, size_t net_payload_size, MYSQLND_STATS * conn_stats, MYSQLND_ERROR_INFO * error_info) +{ + size_t decompressed_size; + enum_func_status retval = PASS; + zend_uchar * compressed_data = NULL; + zend_uchar comp_header[COMPRESSED_HEADER_SIZE]; + DBG_ENTER("mysqlnd_net::read_compressed_packet_from_stream_and_fill_read_buffer"); + + /* Read the compressed header */ + if (FAIL == vio->data->m.network_read(vio, comp_header, COMPRESSED_HEADER_SIZE, conn_stats, error_info)) { + DBG_RETURN(FAIL); + } + decompressed_size = uint3korr(comp_header); + + /* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */ + /* we need to decompress the data */ + + if (decompressed_size) { + compressed_data = mnd_emalloc(net_payload_size); + if (FAIL == vio->data->m.network_read(vio, compressed_data, net_payload_size, conn_stats, error_info)) { + retval = FAIL; + goto end; + } + net->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size); + retval = net->data->m.decode(net->uncompressed_data->data, decompressed_size, compressed_data, net_payload_size); + if (FAIL == retval) { + goto end; + } + } else { + DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size); + net->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size); + if (FAIL == vio->data->m.network_read(vio, net->uncompressed_data->data, net_payload_size, conn_stats, error_info)) { + retval = FAIL; + goto end; + } + } +end: + if (compressed_data) { + mnd_efree(compressed_data); + } + DBG_RETURN(retval); +} +/* }}} */ +#endif /* MYSQLND_COMPRESSION_ENABLED */ + + +/* {{{ mysqlnd_net::decode */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, decode)(zend_uchar * uncompressed_data, const size_t uncompressed_data_len, + const zend_uchar * const compressed_data, const size_t compressed_data_len) +{ +#ifdef MYSQLND_COMPRESSION_ENABLED + int error; + uLongf tmp_complen = uncompressed_data_len; + DBG_ENTER("mysqlnd_net::decode"); + error = uncompress(uncompressed_data, &tmp_complen, compressed_data, compressed_data_len); + + DBG_INF_FMT("compressed data: decomp_len=%lu compressed_size="MYSQLND_SZ_T_SPEC, tmp_complen, compressed_data_len); + if (error != Z_OK) { + DBG_INF_FMT("decompression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR); + } + DBG_RETURN(error == Z_OK? PASS:FAIL); +#else + DBG_ENTER("mysqlnd_net::decode"); + DBG_RETURN(FAIL); +#endif +} +/* }}} */ + + +/* {{{ mysqlnd_net::encode */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, encode)(zend_uchar * compress_buffer, size_t * compress_buffer_len, + const zend_uchar * const uncompressed_data, const size_t uncompressed_data_len) +{ +#ifdef MYSQLND_COMPRESSION_ENABLED + int error; + uLongf tmp_complen = *compress_buffer_len; + DBG_ENTER("mysqlnd_net::encode"); + error = compress(compress_buffer, &tmp_complen, uncompressed_data, uncompressed_data_len); + + if (error != Z_OK) { + DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", error, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR); + } else { + *compress_buffer_len = tmp_complen; + DBG_INF_FMT("compression successful. compressed size=%lu", tmp_complen); + } + + DBG_RETURN(error == Z_OK? PASS:FAIL); +#else + DBG_ENTER("mysqlnd_net::encode"); + DBG_RETURN(FAIL); +#endif +} +/* }}} */ + + +/* {{{ mysqlnd_net::receive */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, receive)(MYSQLND_NET * const net, MYSQLND_VIO * const vio, zend_uchar * const buffer, const size_t count, + MYSQLND_STATS * const conn_stats, MYSQLND_ERROR_INFO * const error_info) +{ + size_t to_read = count; + zend_uchar * p = buffer; + + DBG_ENTER("mysqlnd_net::receive"); +#ifdef MYSQLND_COMPRESSION_ENABLED + if (net->data->compressed) { + if (net->uncompressed_data) { + size_t to_read_from_buffer = MIN(net->uncompressed_data->bytes_left(net->uncompressed_data), to_read); + DBG_INF_FMT("reading "MYSQLND_SZ_T_SPEC" from uncompressed_data buffer", to_read_from_buffer); + if (to_read_from_buffer) { + net->uncompressed_data->read(net->uncompressed_data, to_read_from_buffer, (zend_uchar *) p); + p += to_read_from_buffer; + to_read -= to_read_from_buffer; + } + DBG_INF_FMT("left "MYSQLND_SZ_T_SPEC" to read", to_read); + if (TRUE == net->uncompressed_data->is_empty(net->uncompressed_data)) { + /* Everything was consumed. This should never happen here, but for security */ + net->uncompressed_data->free_buffer(&net->uncompressed_data); + } + } + if (to_read) { + zend_uchar net_header[MYSQLND_HEADER_SIZE]; + size_t net_payload_size; + zend_uchar packet_no; + + if (FAIL == vio->data->m.network_read(vio, net_header, MYSQLND_HEADER_SIZE, conn_stats, error_info)) { + DBG_RETURN(FAIL); + } + net_payload_size = uint3korr(net_header); + packet_no = uint1korr(net_header + 3); + if (net->compressed_envelope_packet_no != packet_no) { + DBG_ERR_FMT("Transport level: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC, + net->compressed_envelope_packet_no, packet_no, net_payload_size); + + php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC, + net->compressed_envelope_packet_no, packet_no, net_payload_size); + DBG_RETURN(FAIL); + } + net->compressed_envelope_packet_no++; +#ifdef MYSQLND_DUMP_HEADER_N_BODY + DBG_INF_FMT("HEADER: hwd_packet_no=%u size=%3u", packet_no, (zend_ulong) net_payload_size); +#endif + /* Now let's read from the wire, decompress it and fill the read buffer */ + net->data->m.read_compressed_packet_from_stream_and_fill_read_buffer(net, vio, net_payload_size, conn_stats, error_info); + + /* + Now a bit of recursion - read from the read buffer, + if the data which we have just read from the wire + is not enough, then the recursive call will try to + satisfy it until it is satisfied. + */ + DBG_RETURN(net->data->m.receive(net, vio, p, to_read, conn_stats, error_info)); + } + DBG_RETURN(PASS); + } +#endif /* MYSQLND_COMPRESSION_ENABLED */ + DBG_RETURN(vio->data->m.network_read(vio, p, to_read, conn_stats, error_info)); +} +/* }}} */ + + +/* {{{ mysqlnd_net::set_client_option */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, set_client_option)(MYSQLND_NET * const net, enum_mysqlnd_client_option option, const char * const value) +{ + DBG_ENTER("mysqlnd_net::set_client_option"); + DBG_INF_FMT("option=%u", option); + switch (option) { + case MYSQL_OPT_COMPRESS: + net->data->options.flags |= MYSQLND_NET_FLAG_USE_COMPRESSION; + break; + case MYSQL_SERVER_PUBLIC_KEY: + { + zend_bool pers = net->persistent; + if (net->data->options.sha256_server_public_key) { + mnd_pefree(net->data->options.sha256_server_public_key, pers); + } + net->data->options.sha256_server_public_key = value? mnd_pestrdup(value, pers) : NULL; + break; + } + default: + DBG_RETURN(FAIL); + } + DBG_RETURN(PASS); +} +/* }}} */ + + +/* {{{ mysqlnd_net::free_contents */ +static void +MYSQLND_METHOD(mysqlnd_net, free_contents)(MYSQLND_NET * net) +{ + zend_bool pers = net->persistent; + DBG_ENTER("mysqlnd_net::free_contents"); + +#ifdef MYSQLND_COMPRESSION_ENABLED + if (net->uncompressed_data) { + net->uncompressed_data->free_buffer(&net->uncompressed_data); + } +#endif + if (net->data->options.sha256_server_public_key) { + mnd_pefree(net->data->options.sha256_server_public_key, pers); + net->data->options.sha256_server_public_key = NULL; + } + + DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_net::init */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_net, init)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info) +{ + return PASS; +} +/* }}} */ + + +/* {{{ mysqlnd_net::dtor */ +static void +MYSQLND_METHOD(mysqlnd_net, dtor)(MYSQLND_NET * const net, MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info) +{ + DBG_ENTER("mysqlnd_net::dtor"); + if (net) { + net->data->m.free_contents(net); + + mnd_pefree(net->data, net->data->persistent); + mnd_pefree(net, net->persistent); + } + DBG_VOID_RETURN; +} +/* }}} */ + + +MYSQLND_CLASS_METHODS_START(mysqlnd_net) + MYSQLND_METHOD(mysqlnd_net, init), + MYSQLND_METHOD(mysqlnd_net, dtor), + MYSQLND_METHOD(mysqlnd_net, connect), + + MYSQLND_METHOD(mysqlnd_net, set_client_option), + + MYSQLND_METHOD(mysqlnd_net, decode), + MYSQLND_METHOD(mysqlnd_net, encode), + + MYSQLND_METHOD(mysqlnd_net, send), + MYSQLND_METHOD(mysqlnd_net, receive), + +#ifdef MYSQLND_COMPRESSION_ENABLED + MYSQLND_METHOD(mysqlnd_net, read_compressed_packet_from_stream_and_fill_read_buffer), +#else + NULL, +#endif + + MYSQLND_METHOD(mysqlnd_net, free_contents), +MYSQLND_CLASS_METHODS_END; + + +/* {{{ mysqlnd_net_init */ +PHPAPI MYSQLND_NET * +mysqlnd_net_init(zend_bool persistent, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info) +{ + MYSQLND_NET * net; + DBG_ENTER("mysqlnd_net_init"); + net = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_net(persistent, stats, error_info); + DBG_RETURN(net); +} +/* }}} */ + + +/* {{{ mysqlnd_net_free */ +PHPAPI void +mysqlnd_net_free(MYSQLND_NET * const net, MYSQLND_STATS * stats, MYSQLND_ERROR_INFO * error_info) +{ + DBG_ENTER("mysqlnd_net_free"); + if (net) { + net->data->m.dtor(net, stats, error_info); + } + DBG_VOID_RETURN; +} +/* }}} */ + + + MYSQLND_CLASS_METHODS_START(mysqlnd_protocol_payload_decoder_factory) MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet), MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet),