PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LTP GCOV extension - code coverage report
Current view: directory - mysqlnd - mysqlnd_wireprotocol.c
Test: PHP Code Coverage
Date: 2009-11-23 Instrumented lines: 938
Code covered: 71.7 % Executed lines: 673
Legend: not executed executed

       1                 : 
       2                 : /*
       3                 :   +----------------------------------------------------------------------+
       4                 :   | PHP Version 6                                                        |
       5                 :   +----------------------------------------------------------------------+
       6                 :   | Copyright (c) 2006-2009 The PHP Group                                |
       7                 :   +----------------------------------------------------------------------+
       8                 :   | This source file is subject to version 3.01 of the PHP license,      |
       9                 :   | that is bundled with this package in the file LICENSE, and is        |
      10                 :   | available through the world-wide-web at the following url:           |
      11                 :   | http://www.php.net/license/3_01.txt                                  |
      12                 :   | If you did not receive a copy of the PHP license and are unable to   |
      13                 :   | obtain it through the world-wide-web, please send a note to          |
      14                 :   | license@php.net so we can mail you a copy immediately.               |
      15                 :   +----------------------------------------------------------------------+
      16                 :   | Authors: Georg Richter <georg@mysql.com>                             |
      17                 :   |          Andrey Hristov <andrey@mysql.com>                           |
      18                 :   |          Ulf Wendel <uwendel@mysql.com>                              |
      19                 :   +----------------------------------------------------------------------+
      20                 : */
      21                 : #include "php.h"
      22                 : #include "php_globals.h"
      23                 : #include "mysqlnd.h"
      24                 : #include "mysqlnd_priv.h"
      25                 : #include "mysqlnd_wireprotocol.h"
      26                 : #include "mysqlnd_statistics.h"
      27                 : #include "mysqlnd_palloc.h"
      28                 : #include "mysqlnd_debug.h"
      29                 : #include "mysqlnd_block_alloc.h"
      30                 : #include "ext/standard/sha1.h"
      31                 : #include "php_network.h"
      32                 : #include "zend_ini.h"
      33                 : #ifdef MYSQLND_COMPRESSION_ENABLED
      34                 : #include <zlib.h>
      35                 : #endif
      36                 : 
      37                 : #ifndef PHP_WIN32
      38                 : #include <netinet/tcp.h>
      39                 : #else
      40                 : #include <winsock.h>
      41                 : #endif
      42                 : 
      43                 : #define MYSQLND_SILENT 1
      44                 : 
      45                 : #define MYSQLND_DUMP_HEADER_N_BODY
      46                 : 
      47                 : 
      48                 : #define PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type_as_text, packet_type) \
      49                 :         { \
      50                 :                 DBG_INF_FMT("buf=%p size=%u", (buf), (buf_size)); \
      51                 :                 if (FAIL == mysqlnd_read_header((conn), &((packet)->header) TSRMLS_CC)) {\
      52                 :                         CONN_SET_STATE(conn, CONN_QUIT_SENT); \
      53                 :                         SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
      54                 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
      55                 :                         DBG_ERR_FMT("Can't read %s's header", (packet_type_as_text)); \
      56                 :                         DBG_RETURN(FAIL);\
      57                 :                 }\
      58                 :                 if ((buf_size) < (packet)->header.size) { \
      59                 :                         DBG_ERR_FMT("Packet buffer %u wasn't big enough %u, %u bytes will be unread", \
      60                 :                                                 (buf_size), (packet)->header.size, (packet)->header.size - (buf_size)); \
      61                 :                                                 DBG_RETURN(FAIL); \
      62                 :                 }\
      63                 :                 if (FAIL == mysqlnd_read_body((conn), &((packet)->header), (buf) TSRMLS_CC)) { \
      64                 :                         CONN_SET_STATE(conn, CONN_QUIT_SENT); \
      65                 :                         SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
      66                 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
      67                 :                         DBG_ERR_FMT("Empty '%s' packet body", (packet_type_as_text)); \
      68                 :                         DBG_RETURN(FAIL);\
      69                 :                 } \
      70                 :                 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(&conn->stats, packet_type_to_statistic_byte_count[packet_type], \
      71                 :                                                                                         MYSQLND_HEADER_SIZE + (packet)->header.size, \
      72                 :                                                                                         packet_type_to_statistic_packet_count[packet_type], \
      73                 :                                                                                         1); \
      74                 :         }
      75                 : 
      76                 : 
      77                 : extern mysqlnd_packet_methods packet_methods[];
      78                 : 
      79                 : static const char *unknown_sqlstate= "HY000";
      80                 : 
      81                 : char * const mysqlnd_empty_string = "";
      82                 : 
      83                 : /* Used in mysqlnd_debug.c */
      84                 : char * mysqlnd_read_header_name = "mysqlnd_read_header";
      85                 : char * mysqlnd_read_body_name   = "mysqlnd_read_body";
      86                 : 
      87                 : 
      88                 : /* {{{ mysqlnd_command_to_text
      89                 :  */
      90                 : const char * const mysqlnd_command_to_text[COM_END] =
      91                 : {
      92                 :   "SLEEP", "QUIT", "INIT_DB", "QUERY", "FIELD_LIST",
      93                 :   "CREATE_DB", "DROP_DB", "REFRESH", "SHUTDOWN", "STATISTICS",
      94                 :   "PROCESS_INFO", "CONNECT", "PROCESS_KILL", "DEBUG", "PING",
      95                 :   "TIME", "DELAYED_INSERT", "CHANGE_USER", "BINLOG_DUMP",
      96                 :   "TABLE_DUMP", "CONNECT_OUT", "REGISTER_SLAVE",
      97                 :   "STMT_PREPARE", "STMT_EXECUTE", "STMT_SEND_LONG_DATA", "STMT_CLOSE",
      98                 :   "STMT_RESET", "SET_OPTION", "STMT_FETCH", "DAEMON"
      99                 : };
     100                 : /* }}} */
     101                 : 
     102                 : 
     103                 : 
     104                 : static enum_mysqlnd_collected_stats packet_type_to_statistic_byte_count[PROT_LAST] =
     105                 : {
     106                 :         STAT_LAST,
     107                 :         STAT_LAST,
     108                 :         STAT_BYTES_RECEIVED_OK,
     109                 :         STAT_BYTES_RECEIVED_EOF,
     110                 :         STAT_LAST,
     111                 :         STAT_BYTES_RECEIVED_RSET_HEADER,
     112                 :         STAT_BYTES_RECEIVED_RSET_FIELD_META,
     113                 :         STAT_BYTES_RECEIVED_RSET_ROW,
     114                 :         STAT_BYTES_RECEIVED_PREPARE_RESPONSE,
     115                 :         STAT_BYTES_RECEIVED_CHANGE_USER,
     116                 : };
     117                 : 
     118                 : static enum_mysqlnd_collected_stats packet_type_to_statistic_packet_count[PROT_LAST] =
     119                 : {
     120                 :         STAT_LAST,
     121                 :         STAT_LAST,
     122                 :         STAT_PACKETS_RECEIVED_OK,
     123                 :         STAT_PACKETS_RECEIVED_EOF,
     124                 :         STAT_LAST,
     125                 :         STAT_PACKETS_RECEIVED_RSET_HEADER,
     126                 :         STAT_PACKETS_RECEIVED_RSET_FIELD_META,
     127                 :         STAT_PACKETS_RECEIVED_RSET_ROW,
     128                 :         STAT_PACKETS_RECEIVED_PREPARE_RESPONSE,
     129                 :         STAT_PACKETS_RECEIVED_CHANGE_USER,
     130                 : };
     131                 : 
     132                 : 
     133                 : /* {{{ php_mysqlnd_net_field_length
     134                 :    Get next field's length */
     135                 : unsigned long php_mysqlnd_net_field_length(zend_uchar **packet)
     136          230005 : {
     137          230005 :         register zend_uchar *p= (zend_uchar *)*packet;
     138                 : 
     139          230005 :         if (*p < 251) {
     140          225076 :                 (*packet)++;
     141          225076 :                 return (unsigned long) *p;
     142                 :         }
     143                 : 
     144            4929 :         switch (*p) {
     145                 :                 case 251:
     146             747 :                         (*packet)++;
     147             747 :                         return MYSQLND_NULL_LENGTH;
     148                 :                 case 252:
     149            4169 :                         (*packet) += 3;
     150            4169 :                         return (unsigned long) uint2korr(p+1);
     151                 :                 case 253:
     152              12 :                         (*packet) += 4;
     153              12 :                         return (unsigned long) uint3korr(p+1);
     154                 :                 default:
     155               1 :                         (*packet) += 9;
     156               1 :                         return (unsigned long) uint4korr(p+1);
     157                 :         }
     158                 : }
     159                 : /* }}} */
     160                 : 
     161                 : 
     162                 : /* {{{ php_mysqlnd_net_field_length_ll
     163                 :    Get next field's length */
     164                 : uint64_t php_mysqlnd_net_field_length_ll(zend_uchar **packet)
     165           28546 : {
     166           28546 :         register zend_uchar *p= (zend_uchar *)*packet;
     167                 : 
     168           28546 :         if (*p < 251) {
     169           28539 :                 (*packet)++;
     170           28539 :                 return (uint64_t) *p;
     171                 :         }
     172                 : 
     173               7 :         switch (*p) {
     174                 :                 case 251:
     175               0 :                         (*packet)++;
     176               0 :                         return (uint64_t) MYSQLND_NULL_LENGTH;
     177                 :                 case 252:
     178               4 :                         (*packet) += 3;
     179               4 :                         return (uint64_t) uint2korr(p + 1);
     180                 :                 case 253:
     181               0 :                         (*packet) += 4;
     182               0 :                         return (uint64_t) uint3korr(p + 1);
     183                 :                 default:
     184               3 :                         (*packet) += 9;
     185               3 :                         return (uint64_t) uint8korr(p + 1);
     186                 :         }
     187                 : }
     188                 : /* }}} */
     189                 : 
     190                 : 
     191                 : /* {{{ php_mysqlnd_net_store_length */
     192                 : zend_uchar *php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length)
     193             608 : {
     194             608 :         if (length < (uint64_t) L64(251)) {
     195             541 :                 *packet = (zend_uchar) length;
     196             541 :                 return packet + 1;
     197                 :         }
     198                 : 
     199              67 :         if (length < (uint64_t) L64(65536)) {
     200              63 :                 *packet++ = 252;
     201              63 :                 int2store(packet,(unsigned int) length);
     202              63 :                 return packet + 2;
     203                 :         }
     204                 : 
     205               4 :         if (length < (uint64_t) L64(16777216)) {
     206               4 :                 *packet++ = 253;
     207               4 :                 int3store(packet,(ulong) length);
     208               4 :                 return packet + 3;
     209                 :         }
     210               0 :         *packet++ = 254;
     211               0 :         int8store(packet, length);
     212               0 :         return packet + 8;
     213                 : }
     214                 : /* }}} */
     215                 : 
     216                 : 
     217                 : #ifdef MYSQLND_COMPRESSION_ENABLED
     218                 : /* {{{ php_mysqlnd_read_buffer_is_empty */
     219                 : static zend_bool
     220                 : php_mysqlnd_read_buffer_is_empty(MYSQLND_READ_BUFFER * buffer)
     221               0 : {
     222               0 :         return buffer->len? FALSE:TRUE;
     223                 : }
     224                 : /* }}} */
     225                 : 
     226                 : 
     227                 : /* {{{ php_mysqlnd_read_buffer_read */
     228                 : static void
     229                 : php_mysqlnd_read_buffer_read(MYSQLND_READ_BUFFER * buffer, size_t count, zend_uchar * dest)
     230               0 : {
     231               0 :         if (buffer->len >= count) {
     232               0 :                 memcpy(dest, buffer->data + buffer->offset, count);
     233               0 :                 buffer->offset += count;
     234               0 :                 buffer->len -= count;
     235                 :         }
     236               0 : }
     237                 : /* }}} */
     238                 : 
     239                 : 
     240                 : /* {{{ php_mysqlnd_read_buffer_bytes_left */
     241                 : static size_t
     242                 : php_mysqlnd_read_buffer_bytes_left(MYSQLND_READ_BUFFER * buffer)
     243               0 : {
     244               0 :         return buffer->len;
     245                 : }
     246                 : /* }}} */
     247                 : 
     248                 : 
     249                 : /* {{{ php_mysqlnd_read_buffer_free */
     250                 : static void
     251                 : php_mysqlnd_read_buffer_free(MYSQLND_READ_BUFFER ** buffer TSRMLS_DC)
     252               0 : {
     253               0 :         DBG_ENTER("php_mysqlnd_read_buffer_free");
     254               0 :         if (*buffer) {
     255               0 :                 mnd_efree((*buffer)->data);
     256               0 :                 mnd_efree(*buffer);
     257               0 :                 *buffer = NULL;
     258                 :         }
     259                 :         DBG_VOID_RETURN;
     260                 : }
     261                 : /* }}} */
     262                 : 
     263                 : 
     264                 : /* {{{ php_mysqlnd_create_read_buffer */
     265                 : static MYSQLND_READ_BUFFER *
     266                 : mysqlnd_create_read_buffer(size_t count TSRMLS_DC)
     267               0 : {
     268               0 :         MYSQLND_READ_BUFFER * ret = mnd_emalloc(sizeof(MYSQLND_READ_BUFFER));
     269               0 :         DBG_ENTER("mysqlnd_create_read_buffer");
     270               0 :         ret->is_empty = php_mysqlnd_read_buffer_is_empty;
     271               0 :         ret->read = php_mysqlnd_read_buffer_read;
     272               0 :         ret->bytes_left = php_mysqlnd_read_buffer_bytes_left;
     273               0 :         ret->free = php_mysqlnd_read_buffer_free;
     274               0 :         ret->data = mnd_emalloc(count);
     275               0 :         ret->size = ret->len = count;
     276               0 :         ret->offset = 0;
     277               0 :         DBG_RETURN(ret);
     278                 : }
     279                 : /* }}} */
     280                 : #endif
     281                 : 
     282                 : 
     283                 : /* {{{ php_mysqlnd_consume_uneaten_data */
     284                 : #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
     285                 : size_t php_mysqlnd_consume_uneaten_data(MYSQLND * const conn, enum php_mysqlnd_server_command cmd TSRMLS_DC)
     286                 : {
     287                 : 
     288                 :         /*
     289                 :           Switch to non-blocking mode and try to consume something from
     290                 :           the line, if possible, then continue. This saves us from looking for
     291                 :           the actuall place where out-of-order packets have been sent.
     292                 :           If someone is completely sure that everything is fine, he can switch it
     293                 :           off.
     294                 :         */
     295                 :         char tmp_buf[256];
     296                 :         MYSQLND_NET *net = &conn->net;
     297                 :         size_t skipped_bytes = 0;
     298                 :         int opt = PHP_STREAM_OPTION_BLOCKING;
     299                 :         int was_blocked = net->stream->ops->set_option(net->stream, opt, 0, NULL TSRMLS_CC);
     300                 : 
     301                 :         DBG_ENTER("php_mysqlnd_consume_uneaten_data");
     302                 : 
     303                 :         if (PHP_STREAM_OPTION_RETURN_ERR != was_blocked) {
     304                 :                 /* Do a read of 1 byte */
     305                 :                 int bytes_consumed;
     306                 : 
     307                 :                 do {
     308                 :                         skipped_bytes += (bytes_consumed = php_stream_read(net->stream, tmp_buf, sizeof(tmp_buf)));
     309                 :                 } while (bytes_consumed == sizeof(tmp_buf));
     310                 : 
     311                 :                 if (was_blocked) {
     312                 :                         net->stream->ops->set_option(net->stream, opt, 1, NULL TSRMLS_CC);
     313                 :                 }
     314                 : 
     315                 :                 if (bytes_consumed) {
     316                 :                         DBG_ERR_FMT("Skipped %u bytes. Last command %s hasn't consumed all the output from the server",
     317                 :                                                 bytes_consumed, mysqlnd_command_to_text[net->last_command]);
     318                 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Skipped %u bytes. Last command %s hasn't "
     319                 :                                                          "consumed all the output from the server",
     320                 :                                                          bytes_consumed, mysqlnd_command_to_text[net->last_command]);
     321                 :                 }
     322                 :         }
     323                 :         net->last_command = cmd;
     324                 : 
     325                 :         DBG_RETURN(skipped_bytes);
     326                 : }
     327                 : #endif
     328                 : /* }}} */
     329                 : 
     330                 : 
     331                 : /* {{{ php_mysqlnd_read_error_from_line */
     332                 : static enum_func_status
     333                 : php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len,
     334                 :                                                                 char *error, int error_buf_len,
     335                 :                                                                 unsigned int *error_no, char *sqlstate TSRMLS_DC)
     336             736 : {
     337             736 :         zend_uchar *p = buf;
     338             736 :         int error_msg_len= 0;
     339                 : 
     340             736 :         DBG_ENTER("php_mysqlnd_read_error_from_line");
     341                 : 
     342             736 :         if (buf_len > 2) {
     343             736 :                 *error_no = uint2korr(p);
     344             736 :                 p+= 2;
     345                 :                 /* sqlstate is following */
     346             736 :                 if (*p == '#') {
     347             736 :                         memcpy(sqlstate, ++p, MYSQLND_SQLSTATE_LENGTH);
     348             736 :                         p+= MYSQLND_SQLSTATE_LENGTH;
     349                 :                 }
     350             736 :                 error_msg_len = buf_len - (p - buf);
     351             736 :                 error_msg_len = MIN(error_msg_len, error_buf_len - 1);
     352             736 :                 memcpy(error, p, error_msg_len);
     353                 :         } else {
     354               0 :                 *error_no = CR_UNKNOWN_ERROR;
     355               0 :                 memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH);
     356                 :         }
     357             736 :         sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0';
     358             736 :         error[error_msg_len]= '\0';
     359                 : 
     360             736 :         DBG_RETURN(FAIL);
     361                 : }
     362                 : /* }}} */
     363                 : 
     364                 : 
     365                 : /* {{{ mysqlnd_set_sock_no_delay */
     366                 : int mysqlnd_set_sock_no_delay(php_stream *stream)
     367               0 : {
     368                 : 
     369               0 :         int socketd = ((php_netstream_data_t*)stream->abstract)->socket;
     370               0 :         int ret = SUCCESS;
     371               0 :         int flag = 1;
     372               0 :         int result = setsockopt(socketd, IPPROTO_TCP,  TCP_NODELAY, (char *) &flag, sizeof(int));
     373                 :         TSRMLS_FETCH();
     374                 : 
     375               0 :         DBG_ENTER("mysqlnd_set_sock_no_delay");
     376                 : 
     377               0 :         if (result == -1) {
     378               0 :                 ret = FAILURE;
     379                 :         }
     380                 : 
     381               0 :         DBG_RETURN(ret);
     382                 : }
     383                 : /* }}} */
     384                 : 
     385                 : 
     386                 : /* {{{ mysqlnd_stream_write */
     387                 : size_t mysqlnd_stream_write(MYSQLND * const conn, const zend_uchar * const buf, size_t count TSRMLS_DC)
     388           31257 : {
     389                 :         size_t ret;
     390           31257 :         DBG_ENTER("mysqlnd_stream_write");
     391           31257 :         ret = php_stream_write(conn->net.stream, (char *)buf, count);
     392           31257 :         DBG_RETURN(ret);
     393                 : }
     394                 : /* }}} */
     395                 : 
     396                 : 
     397                 : /* We assume that MYSQLND_HEADER_SIZE is 4 bytes !! */
     398                 : #define STORE_HEADER_SIZE(safe_storage, buffer)  int4store((safe_storage), (*(uint32_t *)(buffer)))
     399                 : #define RESTORE_HEADER_SIZE(buffer, safe_storage) STORE_HEADER_SIZE((safe_storage), (buffer))
     400                 : 
     401                 : /* {{{ mysqlnd_stream_write_w_header */
     402                 : /*
     403                 :   IMPORTANT : It's expected that buf has place in the beginning for MYSQLND_HEADER_SIZE !!!!
     404                 :                           This is done for performance reasons in the caller of this function.
     405                 :                           Otherwise we will have to do send two TCP packets, or do new alloc and memcpy.
     406                 :                           Neither are quick, thus the clients of this function are obligated to do
     407                 :                           what they are asked for.
     408                 : 
     409                 :   `count` is actually the length of the payload data. Thus :
     410                 :   count + MYSQLND_HEADER_SIZE = sizeof(buf) (not the pointer but the actual buffer)
     411                 : */
     412                 : size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, size_t count TSRMLS_DC)
     413           31257 : {
     414                 :         zend_uchar safe_buf[((MYSQLND_HEADER_SIZE) + (sizeof(zend_uchar)) - 1) / (sizeof(zend_uchar))];
     415           31257 :         zend_uchar *safe_storage = safe_buf;
     416           31257 :         MYSQLND_NET *net = &conn->net;
     417           31257 :         size_t old_chunk_size = net->stream->chunk_size;
     418           31257 :         size_t ret, left = count, packets_sent = 1;
     419           31257 :         zend_uchar *p = (zend_uchar *) buf;
     420           31257 :         zend_uchar * compress_buf = NULL;
     421           31257 :         size_t comp_buf_size = 0;
     422                 : 
     423           31257 :         DBG_ENTER("mysqlnd_stream_write_w_header");
     424           31257 :         DBG_INF_FMT("conn=%llu count=%lu compression=%d", conn->thread_id, count, net->compressed);
     425                 : 
     426           31257 :         net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE;
     427                 : 
     428           31257 :         if (net->compressed == TRUE) {
     429               0 :                 comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
     430               0 :                 DBG_INF_FMT("compress_buf_size=%d", comp_buf_size);
     431               0 :                 compress_buf = emalloc(comp_buf_size);
     432                 :         }
     433                 : 
     434           62514 :         while (left > MYSQLND_MAX_PACKET_SIZE) {
     435                 : #ifdef MYSQLND_COMPRESSION_ENABLED
     436                 : 
     437               0 :                 if (net->compressed == TRUE) {
     438                 : 
     439                 :                         /* here we need to compress the data and then write it, first comes the compressed header */
     440               0 :                         uLong tmp_complen = MYSQLND_MAX_PACKET_SIZE;
     441                 :                         size_t payload_size;
     442               0 :                         zend_uchar * uncompressed_payload = p; /* should include the header */
     443                 :                         int res;
     444                 : 
     445               0 :                         STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
     446               0 :                         int3store(uncompressed_payload, MYSQLND_MAX_PACKET_SIZE);
     447               0 :                         int1store(uncompressed_payload + 3, net->packet_no);
     448                 : 
     449               0 :                         DBG_INF_FMT("compress(%p, %p, %p, %d)", (compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, p, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
     450               0 :                         res = compress((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, uncompressed_payload, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
     451               0 :                         if (res == Z_OK) {
     452               0 :                                 DBG_INF_FMT("compression successful. compressed size=%d", tmp_complen);
     453               0 :                                 int3store(compress_buf + MYSQLND_HEADER_SIZE, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
     454               0 :                                 payload_size = tmp_complen;
     455                 :                         } else {
     456               0 :                                 DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", res, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
     457               0 :                                 int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
     458               0 :                                 memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
     459               0 :                                 payload_size = MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE;
     460                 :                         }
     461               0 :                         RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
     462                 : 
     463               0 :                         int3store(compress_buf, payload_size);
     464               0 :                         int1store(compress_buf + 3, net->packet_no);
     465               0 :                         DBG_INF_FMT("writing %d bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
     466               0 :                         ret = conn->net.stream_write(conn, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE TSRMLS_CC);
     467               0 :                         net->compressed_envelope_packet_no++;
     468                 :                 } else
     469                 : #endif /* MYSQLND_COMPRESSION_ENABLED */
     470                 :                 {
     471               0 :                         DBG_INF("no compression");
     472               0 :                         STORE_HEADER_SIZE(safe_storage, p);
     473               0 :                         int3store(p, MYSQLND_MAX_PACKET_SIZE);
     474               0 :                         int1store(p + 3, net->packet_no);
     475               0 :                         ret = conn->net.stream_write(conn, p, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE TSRMLS_CC);
     476               0 :                         RESTORE_HEADER_SIZE(p, safe_storage);
     477               0 :                         net->compressed_envelope_packet_no++;
     478                 :                 }
     479               0 :                 net->packet_no++;
     480                 : 
     481               0 :                 p += MYSQLND_MAX_PACKET_SIZE;
     482               0 :                 left -= MYSQLND_MAX_PACKET_SIZE;
     483                 : 
     484               0 :                 packets_sent++;
     485                 :         }
     486                 : 
     487           31257 :         DBG_INF_FMT("packet_size=%d packet_no=%d", left, net->packet_no);
     488                 :         /* Even for zero size payload we have to send a packet */
     489                 : 
     490                 : #ifdef MYSQLND_COMPRESSION_ENABLED
     491           31257 :         if (net->compressed == TRUE) {
     492                 :                 /* here we need to compress the data and then write it, first comes the compressed header */
     493               0 :                 uLong tmp_complen = left;
     494                 :                 size_t payload_size;
     495               0 :                 zend_uchar * uncompressed_payload = p; /* should include the header */
     496               0 :                 int res = Z_BUF_ERROR;
     497                 : 
     498               0 :                 STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
     499               0 :                 int3store(uncompressed_payload, left);
     500               0 :                 int1store(uncompressed_payload + 3, net->packet_no);
     501               0 :                 if ((left + MYSQLND_HEADER_SIZE) > MYSQLND_MIN_COMPRESS_LEN) {
     502               0 :                         DBG_INF_FMT("compress(%p, %p, %p, %d)", (compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, p, left + MYSQLND_HEADER_SIZE);
     503               0 :                         res = compress((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, uncompressed_payload, left + MYSQLND_HEADER_SIZE);
     504               0 :                         if (res == Z_OK) {
     505               0 :                                 DBG_INF_FMT("compression successful. compressed size=%d", tmp_complen);
     506               0 :                                 int3store(compress_buf + MYSQLND_HEADER_SIZE, left + MYSQLND_HEADER_SIZE);
     507               0 :                                 payload_size = tmp_complen;
     508                 :                         }
     509                 :                 } else {
     510               0 :                         DBG_INF("Too short for compression");
     511                 :                 }
     512               0 :                 if (res != Z_OK) {
     513               0 :                         DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", res, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
     514               0 :                         int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
     515               0 :                         memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, left + MYSQLND_HEADER_SIZE);
     516               0 :                         payload_size = left + MYSQLND_HEADER_SIZE;
     517                 :                 }
     518               0 :                 RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
     519                 : 
     520               0 :                 int3store(compress_buf, payload_size);
     521               0 :                 int1store(compress_buf + 3, net->packet_no);
     522               0 :                 DBG_INF_FMT("writing %d bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
     523               0 :                 ret = conn->net.stream_write(conn, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE TSRMLS_CC);
     524                 : 
     525               0 :                 net->compressed_envelope_packet_no++;
     526                 :   #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
     527                 :                 if (res == Z_OK) {
     528                 :                         size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
     529                 :                         uLongf tmp_complen2 = decompressed_size;
     530                 :                         zend_uchar * decompressed_data = malloc(decompressed_size);
     531                 :                         int error = uncompress(decompressed_data, &tmp_complen2, compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
     532                 :                         if (error == Z_OK) {
     533                 :                                 int i;
     534                 :                                 DBG_INF("success decompressing");
     535                 :                                 for (i = 0 ; i < decompressed_size; i++) {
     536                 :                                         if (i && (i % 30 == 0)) {
     537                 :                                                 printf("\n\t\t");
     538                 :                                         }
     539                 :                                         printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
     540                 :                                         DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
     541                 :                                 }
     542                 :                         } else {
     543                 :                                 DBG_INF("error decompressing");
     544                 :                         }
     545                 :                         free(decompressed_data);
     546                 :                 }
     547                 :   #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
     548                 : 
     549                 :         } else
     550                 : #endif /* MYSQLND_COMPRESSION_ENABLED */
     551                 :         {
     552           31257 :                 DBG_INF("no compression");
     553           31257 :                 STORE_HEADER_SIZE(safe_storage, p);
     554           31257 :                 int3store(p, left);
     555           31257 :                 int1store(p + 3, net->packet_no);
     556           31257 :                 ret = conn->net.stream_write(conn, p, left + MYSQLND_HEADER_SIZE TSRMLS_CC);
     557           31257 :                 RESTORE_HEADER_SIZE(p, safe_storage);
     558                 :         }
     559           31257 :         net->packet_no++;
     560                 : 
     561           31257 :         if (!ret) {
     562               8 :                 DBG_ERR_FMT("Can't %u send bytes", count);
     563               8 :                 conn->state = CONN_QUIT_SENT;
     564               8 :                 SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
     565                 :         }
     566                 : 
     567           31257 :         MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats,
     568                 :                         STAT_BYTES_SENT, count + packets_sent * MYSQLND_HEADER_SIZE,
     569                 :                         STAT_PROTOCOL_OVERHEAD_OUT, packets_sent * MYSQLND_HEADER_SIZE,
     570                 :                         STAT_PACKETS_SENT, packets_sent);
     571                 : 
     572           31257 :         net->stream->chunk_size = old_chunk_size;
     573           31257 :         if (compress_buf) {
     574               0 :                 efree(compress_buf);
     575                 :         }
     576           31257 :         DBG_RETURN(ret);
     577                 : }
     578                 : /* }}} */
     579                 : 
     580                 : 
     581                 : /* {{{ mysqlnd_read_from_stream */
     582                 : enum_func_status
     583                 : mysqlnd_read_from_stream(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC)
     584          191232 : {
     585          191232 :         size_t to_read = count, ret;
     586          191232 :         size_t old_chunk_size = conn->net.stream->chunk_size;
     587          191232 :         DBG_ENTER("mysqlnd_read_from_stream");
     588          191232 :         DBG_INF_FMT("count=%u", count);
     589          191232 :         conn->net.stream->chunk_size = MIN(to_read, conn->options.net_read_buffer_size);
     590          573746 :         while (to_read) {
     591          191286 :                 if (!(ret = php_stream_read(conn->net.stream, (char *) buffer, to_read))) {
     592               4 :                         DBG_ERR_FMT("Error while reading header from socket");
     593               4 :                         DBG_RETURN(FAIL);
     594                 :                 }
     595          191282 :                 buffer += ret;
     596          191282 :                 to_read -= ret;
     597                 :         }
     598          191228 :         MYSQLND_INC_CONN_STATISTIC_W_VALUE(&conn->stats, STAT_BYTES_RECEIVED, count);
     599          191228 :         conn->net.stream->chunk_size = old_chunk_size;
     600          191228 :         DBG_RETURN(PASS);
     601                 : }
     602                 : /* }}} */
     603                 : 
     604                 : 
     605                 : #ifdef MYSQLND_COMPRESSION_ENABLED
     606                 : /* {{{ mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer */
     607                 : static enum_func_status
     608                 : mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(MYSQLND * conn, size_t net_payload_size TSRMLS_DC)
     609               0 : {
     610               0 :         MYSQLND_NET * net = &conn->net;
     611                 :         size_t decompressed_size;
     612               0 :         enum_func_status ret = PASS;
     613               0 :         zend_uchar * compressed_data = NULL;
     614                 :         zend_uchar comp_header[COMPRESSED_HEADER_SIZE];
     615               0 :         DBG_ENTER("mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer");
     616                 : 
     617                 :         /* Read the compressed header */
     618               0 :         if (FAIL == conn->net.stream_read(conn, comp_header, COMPRESSED_HEADER_SIZE TSRMLS_CC)) {
     619               0 :                 DBG_RETURN(FAIL);
     620                 :         }
     621               0 :         decompressed_size = uint3korr(comp_header);
     622                 : 
     623                 :         /* When decompressed_size is 0, then the data is not compressed, and we have wasted 3 bytes */
     624                 :         /* we need to decompress the data */
     625                 : 
     626               0 :         if (decompressed_size) {
     627                 :                 int error;
     628               0 :                 uLongf tmp_complen = decompressed_size;
     629               0 :                 compressed_data = emalloc(net_payload_size);
     630               0 :                 if (FAIL == conn->net.stream_read(conn, compressed_data, net_payload_size TSRMLS_CC)) {
     631               0 :                         ret = FAIL;
     632               0 :                         goto end;
     633                 :                 }
     634                 : 
     635               0 :                 net->uncompressed_data = mysqlnd_create_read_buffer(decompressed_size TSRMLS_CC);
     636               0 :                 error = uncompress(net->uncompressed_data->data, &tmp_complen, compressed_data, net_payload_size);
     637                 : 
     638               0 :                 DBG_INF_FMT("compressed data: decomp_len=%d compressed_size=%d", decompressed_size, net_payload_size);
     639               0 :                 if (error != Z_OK) {
     640               0 :                         DBG_ERR_FMT("Can't uncompress packet, error: %d", error);
     641               0 :                         ret = FAIL;
     642               0 :                         goto end;
     643                 :                 }
     644                 :         } else {
     645               0 :                 DBG_INF_FMT("The server decided not to compress the data. Our job is easy. Copying %u bytes", net_payload_size);
     646               0 :                 net->uncompressed_data = mysqlnd_create_read_buffer(net_payload_size TSRMLS_CC);
     647               0 :                 if (FAIL == conn->net.stream_read(conn, net->uncompressed_data->data, net_payload_size TSRMLS_CC)) {
     648               0 :                         ret = FAIL;
     649               0 :                         goto end;
     650                 :                 }
     651                 :         }
     652               0 : end:
     653               0 :         if (compressed_data) {
     654               0 :                 efree(compressed_data);
     655                 :         }
     656               0 :         DBG_RETURN(ret);
     657                 : }
     658                 : #endif /* MYSQLND_COMPRESSION_ENABLED */
     659                 : 
     660                 : 
     661                 : /* {{{ mysqlnd_real_read */
     662                 : static enum_func_status
     663                 : mysqlnd_real_read(MYSQLND * conn, zend_uchar * buffer, size_t count TSRMLS_DC)
     664          191232 : {
     665          191232 :         size_t to_read = count;
     666          191232 :         zend_uchar * p = buffer;
     667                 : 
     668          191232 :         DBG_ENTER("mysqlnd_real_read");
     669                 : #ifdef MYSQLND_COMPRESSION_ENABLED
     670          191232 :         if (conn->net.compressed) {
     671               0 :                 MYSQLND_NET * net = &conn->net;
     672               0 :                 if (net->uncompressed_data) {
     673               0 :                         size_t to_read_from_buffer = MIN(net->uncompressed_data->bytes_left(net->uncompressed_data), to_read);
     674               0 :                         DBG_INF_FMT("reading %u from uncompressed_data buffer", to_read_from_buffer);
     675               0 :                         if (to_read_from_buffer) {
     676               0 :                                 net->uncompressed_data->read(net->uncompressed_data, to_read_from_buffer, (zend_uchar *) p);
     677               0 :                                 p += to_read_from_buffer;
     678               0 :                                 to_read -= to_read_from_buffer;
     679                 :                         }
     680               0 :                         DBG_INF_FMT("left %u to read", to_read);
     681               0 :                         if (TRUE == net->uncompressed_data->is_empty(net->uncompressed_data)) {
     682                 :                                 /* Everything was consumed. This should never happen here, but for security */
     683               0 :                                 net->uncompressed_data->free(&net->uncompressed_data TSRMLS_CC);
     684                 :                         }
     685                 :                 }
     686               0 :                 if (to_read) {
     687                 :                         zend_uchar net_header[MYSQLND_HEADER_SIZE];
     688                 :                         size_t net_payload_size;
     689                 :                         zend_uchar packet_no;
     690                 : 
     691               0 :                         if (FAIL == conn->net.stream_read(conn, net_header, MYSQLND_HEADER_SIZE TSRMLS_CC)) {
     692               0 :                                 DBG_RETURN(FAIL);
     693                 :                         }
     694               0 :                         net_payload_size = uint3korr(net_header);
     695               0 :                         packet_no = uint1korr(net_header + 3);
     696               0 :                         if (net->compressed_envelope_packet_no != packet_no) {
     697               0 :                                 DBG_ERR_FMT("Transport level: packets out of order. Expected %d received %d. Packet size=%d",
     698                 :                                                         net->compressed_envelope_packet_no, packet_no, net_payload_size);
     699                 : 
     700               0 :                                 php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size="MYSQLND_SZ_T_SPEC,
     701                 :                                                   net->compressed_envelope_packet_no, packet_no, net_payload_size);
     702                 : #if 0
     703                 :                                 *(int *) NULL = 0;
     704                 : #endif
     705               0 :                                 DBG_RETURN(FAIL);
     706                 :                         }
     707               0 :                         net->compressed_envelope_packet_no++;
     708                 : #ifdef MYSQLND_DUMP_HEADER_N_BODY
     709               0 :                         DBG_INF_FMT("HEADER: hwd_packet_no=%d size=%3d", packet_no, net_payload_size);
     710                 : #endif
     711                 :                         /* Now let's read from the wire, decompress it and fill the read buffer */
     712               0 :                         mysqlnd_read_compressed_packet_from_stream_and_fill_read_buffer(conn, net_payload_size TSRMLS_CC);
     713                 : 
     714                 :                         /*
     715                 :                           Now a bit of recursion - read from the read buffer,
     716                 :                           if the data which we have just read from the wire
     717                 :                           is not enough, then the recursive call will try to
     718                 :                           satisfy it until it is satisfied.
     719                 :                         */
     720               0 :                         DBG_RETURN(mysqlnd_real_read(conn, p, to_read TSRMLS_CC));
     721                 :                 }
     722               0 :                 DBG_RETURN(PASS);
     723                 :         }
     724                 : #endif /* MYSQLND_COMPRESSION_ENABLED */
     725          191232 :         DBG_RETURN(conn->net.stream_read(conn, p, to_read TSRMLS_CC));
     726                 : }
     727                 : /* }}} */
     728                 : 
     729                 : 
     730                 : /* {{{ mysqlnd_read_header */
     731                 : static enum_func_status
     732                 : mysqlnd_read_header(MYSQLND * conn, mysqlnd_packet_header * header TSRMLS_DC)
     733           95619 : {
     734           95619 :         MYSQLND_NET *net = &conn->net;
     735                 :         zend_uchar buffer[MYSQLND_HEADER_SIZE];
     736                 : 
     737           95619 :         DBG_ENTER("mysqlnd_read_header_name");
     738           95619 :         DBG_INF_FMT("compressed=%d conn_id=%u", net->compressed, conn->thread_id);
     739           95619 :         if (FAIL == mysqlnd_real_read(conn, buffer, MYSQLND_HEADER_SIZE TSRMLS_CC)) {
     740               4 :                 DBG_RETURN(FAIL);
     741                 :         }
     742                 : 
     743           95615 :         header->size = uint3korr(buffer);
     744           95615 :         header->packet_no = uint1korr(buffer + 3);
     745                 : 
     746                 : #ifdef MYSQLND_DUMP_HEADER_N_BODY
     747           95615 :         DBG_INF_FMT("HEADER: prot_packet_no=%d size=%3d", header->packet_no, header->size);
     748                 : #endif
     749           95615 :         MYSQLND_INC_CONN_STATISTIC_W_VALUE3(&conn->stats,
     750                 :                                                         STAT_BYTES_RECEIVED, MYSQLND_HEADER_SIZE,
     751                 :                                                         STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE,
     752                 :                                                         STAT_PACKETS_RECEIVED, 1);
     753                 : 
     754           95615 :         if (net->compressed || net->packet_no == header->packet_no) {
     755                 :                 /*
     756                 :                   Have to increase the number, so we can send correct number back. It will
     757                 :                   round at 255 as this is unsigned char. The server needs this for simple
     758                 :                   flow control checking.
     759                 :                 */
     760           95613 :                 net->packet_no++;
     761           95613 :                 DBG_RETURN(PASS);
     762                 :         }
     763                 : 
     764               2 :         DBG_ERR_FMT("Logical link: packets out of order. Expected %d received %d. Packet size=%d",
     765                 :                                 net->packet_no, header->packet_no, header->size);
     766                 : 
     767               2 :         php_error(E_WARNING, "Packets out of order. Expected %d received %d. Packet size="MYSQLND_SZ_T_SPEC,
     768                 :                           net->packet_no, header->packet_no, header->size);
     769               2 :         DBG_RETURN(FAIL);
     770                 : }
     771                 : /* }}} */
     772                 : 
     773                 : 
     774                 : /* {{{ mysqlnd_read_body */
     775                 : static enum_func_status
     776                 : mysqlnd_read_body(MYSQLND *conn, mysqlnd_packet_header * header, zend_uchar * store_buf TSRMLS_DC)
     777           95613 : {
     778           95613 :         MYSQLND_NET *net = &conn->net;
     779                 : 
     780           95613 :         DBG_ENTER(mysqlnd_read_body_name);
     781           95613 :         DBG_INF_FMT("chunk_size=%d compression=%d", net->stream->chunk_size, net->compressed);
     782                 : 
     783           95613 :         DBG_RETURN(mysqlnd_real_read(conn, store_buf, header->size TSRMLS_CC));
     784                 : }
     785                 : /* }}} */
     786                 : 
     787                 : 
     788                 : /* {{{ php_mysqlnd_greet_read */
     789                 : static enum_func_status
     790                 : php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC)
     791            1625 : {
     792                 :         zend_uchar buf[2048];
     793            1625 :         zend_uchar *p = buf;
     794            1625 :         zend_uchar *begin = buf;
     795            1625 :         php_mysql_packet_greet *packet= (php_mysql_packet_greet *) _packet;
     796                 : 
     797            1625 :         DBG_ENTER("php_mysqlnd_greet_read");
     798                 : 
     799            1625 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting", PROT_GREET_PACKET);
     800                 : 
     801            1625 :         packet->protocol_version = uint1korr(p);
     802            1625 :         p++;
     803                 : 
     804            1625 :         if (packet->protocol_version == 0xFF) {
     805               0 :                 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
     806                 :                                                                                  packet->error, sizeof(packet->error),
     807                 :                                                                                  &packet->error_no, packet->sqlstate
     808                 :                                                                                  TSRMLS_CC);
     809                 :                 /*
     810                 :                   The server doesn't send sqlstate in the greet packet.
     811                 :                   It's a bug#26426 , so we have to set it correctly ourselves.
     812                 :                   It's probably "Too many connections, which has SQL state 08004".
     813                 :                 */
     814               0 :                 if (packet->error_no == 1040) {
     815               0 :                         memcpy(packet->sqlstate, "08004", MYSQLND_SQLSTATE_LENGTH);
     816                 :                 }
     817               0 :                 DBG_RETURN(PASS);
     818                 :         }
     819                 : 
     820            1625 :         packet->server_version = estrdup((char *)p);
     821            1625 :         p+= strlen(packet->server_version) + 1; /* eat the '\0' */
     822                 : 
     823            1625 :         packet->thread_id = uint4korr(p);
     824            1625 :         p+=4;
     825                 : 
     826            1625 :         memcpy(packet->scramble_buf, p, SCRAMBLE_LENGTH_323);
     827            1625 :         p+= 8;
     828                 : 
     829                 :         /* pad1 */
     830            1625 :         p++;
     831                 : 
     832            1625 :         packet->server_capabilities = uint2korr(p);
     833            1625 :         p+= 2;
     834                 : 
     835            1625 :         packet->charset_no = uint1korr(p);
     836            1625 :         p++;
     837                 : 
     838            1625 :         packet->server_status = uint2korr(p);
     839            1625 :         p+= 2;
     840                 : 
     841                 :         /* pad2 */
     842            1625 :         p+= 13;
     843                 : 
     844            1625 :         if (p - buf < packet->header.size) {
     845                 :                 /* scramble_buf is split into two parts */
     846            1625 :                 memcpy(packet->scramble_buf + SCRAMBLE_LENGTH_323,
     847                 :                                 p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
     848                 :         } else {
     849               0 :                 packet->pre41 = TRUE;
     850                 :         }
     851                 : 
     852            1625 :         DBG_INF_FMT("proto=%d server=%s thread_id=%ld",
     853                 :                                 packet->protocol_version, packet->server_version, packet->thread_id);
     854                 : 
     855            1625 :         DBG_INF_FMT("server_capabilities=%d charset_no=%d server_status=%d",
     856                 :                                 packet->server_capabilities, packet->charset_no, packet->server_status);
     857                 : 
     858            1625 :         if (p - begin > packet->header.size) {
     859               0 :                 DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size);
     860               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
     861                 :                                                  p - begin - packet->header.size);
     862                 :         }
     863                 : 
     864            1625 :         DBG_RETURN(PASS);
     865                 : }
     866                 : /* }}} */
     867                 : 
     868                 : 
     869                 : /* {{{ php_mysqlnd_greet_free_mem */
     870                 : static
     871                 : void php_mysqlnd_greet_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
     872            1634 : {
     873            1634 :         php_mysql_packet_greet *p= (php_mysql_packet_greet *) _packet;
     874            1634 :         if (p->server_version) {
     875            1625 :                 mnd_efree(p->server_version);
     876            1625 :                 p->server_version = NULL;
     877                 :         }
     878            1634 :         if (!alloca) {
     879               0 :                 mnd_efree(p);
     880                 :         }
     881            1634 : }
     882                 : /* }}} */
     883                 : 
     884                 : 
     885                 : #define MYSQLND_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | \
     886                 :                                 CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | \
     887                 :                                 CLIENT_MULTI_RESULTS)
     888                 : 
     889                 : 
     890                 : /* {{{ php_mysqlnd_crypt */
     891                 : static
     892                 : void php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len)
     893            1671 : {
     894            1671 :         const zend_uchar *s1_end = s1 + len;
     895           36762 :         while (s1 < s1_end) {
     896           33420 :                 *buffer++= *s1++ ^ *s2++;
     897                 :         }
     898            1671 : }
     899                 : /* }}} */
     900                 : 
     901                 : 
     902                 : /* {{{ php_mysqlnd_scramble */
     903                 : void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble,
     904                 :                                                   const zend_uchar * const password)
     905            1671 : {
     906                 :         PHP_SHA1_CTX context;
     907                 :         zend_uchar sha1[SHA1_MAX_LENGTH];
     908                 :         zend_uchar sha2[SHA1_MAX_LENGTH];
     909                 : 
     910                 : 
     911                 :         /* Phase 1: hash password */
     912            1671 :         PHP_SHA1Init(&context);
     913            1671 :         PHP_SHA1Update(&context, password, strlen((char *)password));
     914            1671 :         PHP_SHA1Final(sha1, &context);
     915                 : 
     916                 :         /* Phase 2: hash sha1 */
     917            1671 :         PHP_SHA1Init(&context);
     918            1671 :         PHP_SHA1Update(&context, (zend_uchar*)sha1, SHA1_MAX_LENGTH);
     919            1671 :         PHP_SHA1Final(sha2, &context);
     920                 : 
     921                 :         /* Phase 3: hash scramble + sha2 */
     922            1671 :         PHP_SHA1Init(&context);
     923            1671 :         PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH);
     924            1671 :         PHP_SHA1Update(&context, (zend_uchar*)sha2, SHA1_MAX_LENGTH);
     925            1671 :         PHP_SHA1Final(buffer, &context);
     926                 : 
     927                 :         /* let's crypt buffer now */
     928            1671 :         php_mysqlnd_crypt(buffer, (const zend_uchar *)buffer, (const zend_uchar *)sha1, SHA1_MAX_LENGTH);
     929            1671 : }
     930                 : /* }}} */
     931                 : 
     932                 : 
     933                 : /* {{{ php_mysqlnd_auth_write */
     934                 : static
     935                 : size_t php_mysqlnd_auth_write(void *_packet, MYSQLND *conn TSRMLS_DC)
     936            1625 : {
     937                 :         char buffer[1024];
     938            1625 :         register char *p= buffer + MYSQLND_HEADER_SIZE; /* start after the header */
     939                 :         int len;
     940            1625 :         register php_mysql_packet_auth *packet= (php_mysql_packet_auth *) _packet;
     941                 : 
     942            1625 :         DBG_ENTER("php_mysqlnd_auth_write");
     943                 : 
     944            1625 :         packet->client_flags |= MYSQLND_CAPABILITIES;
     945                 : 
     946            1625 :         if (packet->db) {
     947            1625 :                 packet->client_flags |= CLIENT_CONNECT_WITH_DB;
     948                 :         }
     949                 : 
     950            1625 :         if (PG(open_basedir) && strlen(PG(open_basedir))) {
     951               6 :                 packet->client_flags ^= CLIENT_LOCAL_FILES;
     952                 :         }
     953                 : 
     954            1625 :         int4store(p, packet->client_flags);
     955            1625 :         p+= 4;
     956                 : 
     957            1625 :         int4store(p, packet->max_packet_size);
     958            1625 :         p+= 4;
     959                 : 
     960            1625 :         int1store(p, packet->charset_no);
     961            1625 :         p++;
     962                 : 
     963            1625 :         memset(p, 0, 23); /* filler */
     964            1625 :         p+= 23;
     965                 : 
     966            1625 :         len= strlen(packet->user);
     967            1625 :         memcpy(p, packet->user, len);
     968            1625 :         p+= len;
     969            1625 :         *p++ = '\0';
     970                 : 
     971                 :         /* copy scrambled pass*/
     972            3232 :         if (packet->password && packet->password[0]) {
     973                 :                 /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */
     974            1607 :                 int1store(p, 20);
     975            1607 :                 p++;
     976            1607 :                 php_mysqlnd_scramble((zend_uchar*)p, packet->server_scramble_buf, (zend_uchar*)packet->password);
     977            1607 :                 p+= 20;
     978                 :         } else {
     979                 :                 /* Zero length */
     980              18 :                 int1store(p, 0);
     981              18 :                 p++;
     982                 :         }
     983                 : 
     984            1625 :         if (packet->db) {
     985            1625 :                 memcpy(p, packet->db, packet->db_len);
     986            1625 :                 p+= packet->db_len;
     987            1625 :                 *p++= '\0';
     988                 :         }
     989                 :         /* Handle CLIENT_CONNECT_WITH_DB */
     990                 :         /* no \0 for no DB */
     991                 : 
     992            1625 :         DBG_RETURN(mysqlnd_stream_write_w_header(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC));
     993                 : }
     994                 : /* }}} */
     995                 : 
     996                 : 
     997                 : /* {{{ php_mysqlnd_auth_free_mem */
     998                 : static
     999                 : void php_mysqlnd_auth_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
    1000            1634 : {
    1001            1634 :         if (!alloca) {
    1002            1634 :                 mnd_pefree((php_mysql_packet_auth *) _packet, ((php_mysql_packet_auth *)_packet)->header.persistent);
    1003                 :         }
    1004            1634 : }
    1005                 : /* }}} */
    1006                 : 
    1007                 : 
    1008                 : #define OK_BUFFER_SIZE 2048
    1009                 : 
    1010                 : /* {{{ php_mysqlnd_ok_read */
    1011                 : static enum_func_status
    1012                 : php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC)
    1013            1897 : {
    1014                 :         zend_uchar local_buf[OK_BUFFER_SIZE];
    1015            1897 :         size_t buf_len = conn->net.cmd_buffer.buffer? conn->net.cmd_buffer.length : OK_BUFFER_SIZE;
    1016            1897 :         zend_uchar *buf = conn->net.cmd_buffer.buffer? (zend_uchar *) conn->net.cmd_buffer.buffer : local_buf;
    1017            1897 :         zend_uchar *p = buf;
    1018            1897 :         zend_uchar *begin = buf;
    1019                 :         unsigned long i;
    1020            1897 :         register php_mysql_packet_ok *packet= (php_mysql_packet_ok *) _packet;
    1021                 : 
    1022            1897 :         DBG_ENTER("php_mysqlnd_ok_read");
    1023                 : 
    1024            1897 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET);
    1025                 : 
    1026                 :         /* Should be always 0x0 or 0xFF for error */
    1027            1897 :         packet->field_count = uint1korr(p);
    1028            1897 :         p++;
    1029                 : 
    1030            1897 :         if (0xFF == packet->field_count) {
    1031              43 :                 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
    1032                 :                                                                                  packet->error, sizeof(packet->error),
    1033                 :                                                                                  &packet->error_no, packet->sqlstate
    1034                 :                                                                                  TSRMLS_CC);
    1035              43 :                 DBG_RETURN(PASS);
    1036                 :         }
    1037                 :         /* Everything was fine! */
    1038            1854 :         packet->affected_rows  = php_mysqlnd_net_field_length_ll(&p);
    1039            1854 :         packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
    1040                 : 
    1041            1854 :         packet->server_status = uint2korr(p);
    1042            1854 :         p+= 2;
    1043                 : 
    1044            1854 :         packet->warning_count = uint2korr(p);
    1045            1854 :         p+= 2;
    1046                 : 
    1047                 :         /* There is a message */
    1048            1860 :         if (packet->header.size > p - buf && (i = php_mysqlnd_net_field_length(&p))) {
    1049               6 :                 packet->message = estrndup((char *)p, MIN(i, buf_len - (p - begin)));
    1050               6 :                 packet->message_len = i;
    1051                 :         } else {
    1052            1848 :                 packet->message = NULL;
    1053                 :         }
    1054                 : 
    1055            1854 :         DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%d warnings=%d",
    1056                 :                                 packet->affected_rows, packet->last_insert_id, packet->server_status,
    1057                 :                                 packet->warning_count);
    1058                 : 
    1059            1854 :         if (p - begin > packet->header.size) {
    1060               0 :                 DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size);
    1061               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
    1062                 :                                                  p - begin - packet->header.size);
    1063                 :         }
    1064                 : 
    1065            1854 :         DBG_RETURN(PASS);
    1066                 : }
    1067                 : /* }}} */
    1068                 : 
    1069                 : 
    1070                 : /* {{{ php_mysqlnd_ok_free_mem */
    1071                 : static
    1072                 : void php_mysqlnd_ok_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
    1073            1906 : {
    1074            1906 :         php_mysql_packet_ok *p= (php_mysql_packet_ok *) _packet;
    1075            1906 :         if (p->message) {
    1076               6 :                 mnd_efree(p->message);
    1077               6 :                 p->message = NULL;
    1078                 :         }
    1079            1906 :         if (!alloca) {
    1080               0 :                 mnd_pefree(p, p->header.persistent);
    1081                 :         }
    1082            1906 : }
    1083                 : /* }}} */
    1084                 : 
    1085                 : 
    1086                 : /* {{{ php_mysqlnd_eof_read */
    1087                 : static enum_func_status
    1088                 : php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC)
    1089            9308 : {
    1090                 :         /*
    1091                 :           EOF packet is since 4.1 five bytes long,
    1092                 :           but we can get also an error, make it bigger.
    1093                 : 
    1094                 :           Error : error_code + '#' + sqlstate + MYSQLND_ERRMSG_SIZE
    1095                 :         */
    1096            9308 :         php_mysql_packet_eof *packet= (php_mysql_packet_eof *) _packet;
    1097            9308 :         size_t buf_len = conn->net.cmd_buffer.length;
    1098            9308 :         zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
    1099            9308 :         zend_uchar *p = buf;
    1100            9308 :         zend_uchar *begin = buf;
    1101                 : 
    1102            9308 :         DBG_ENTER("php_mysqlnd_eof_read");
    1103                 : 
    1104            9308 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "EOF", PROT_EOF_PACKET);
    1105                 : 
    1106                 :         /* Should be always 0xFE */
    1107            9308 :         packet->field_count = uint1korr(p);
    1108            9308 :         p++;
    1109                 : 
    1110            9308 :         if (0xFF == packet->field_count) {
    1111               2 :                 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
    1112                 :                                                                                  packet->error, sizeof(packet->error),
    1113                 :                                                                                  &packet->error_no, packet->sqlstate
    1114                 :                                                                                  TSRMLS_CC);
    1115               2 :                 DBG_RETURN(PASS);
    1116                 :         }
    1117                 : 
    1118                 :         /*
    1119                 :                 4.1 sends 1 byte EOF packet after metadata of
    1120                 :                 PREPARE/EXECUTE but 5 bytes after the result. This is not
    1121                 :                 according to the Docs@Forge!!!
    1122                 :         */
    1123            9306 :         if (packet->header.size > 1) {
    1124            9306 :                 packet->warning_count = uint2korr(p);
    1125            9306 :                 p+= 2;
    1126            9306 :                 packet->server_status = uint2korr(p);
    1127            9306 :                 p+= 2;
    1128                 :         } else {
    1129               0 :                 packet->warning_count = 0;
    1130               0 :                 packet->server_status = 0;
    1131                 :         }
    1132                 : 
    1133            9306 :         if (p - begin > packet->header.size) {
    1134               0 :                 DBG_ERR_FMT("EOF packet %d bytes shorter than expected", p - begin - packet->header.size);
    1135               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
    1136                 :                                                  p - begin - packet->header.size);
    1137                 :         }
    1138                 : 
    1139            9306 :         DBG_INF_FMT("EOF packet: fields=%d status=%d warnings=%d",
    1140                 :                                 packet->field_count, packet->server_status, packet->warning_count);
    1141                 : 
    1142            9306 :         DBG_RETURN(PASS);
    1143                 : }
    1144                 : /* }}} */
    1145                 : 
    1146                 : 
    1147                 : /* {{{ php_mysqlnd_eof_free_mem */
    1148                 : static
    1149                 : void php_mysqlnd_eof_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
    1150            9308 : {
    1151            9308 :         if (!alloca) {
    1152               0 :                 mnd_pefree(_packet, ((php_mysql_packet_eof *)_packet)->header.persistent);
    1153                 :         }
    1154            9308 : }
    1155                 : /* }}} */
    1156                 : 
    1157                 : 
    1158                 : /* {{{ php_mysqlnd_cmd_write */
    1159                 : size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC)
    1160           29346 : {
    1161                 :         /* Let's have some space, which we can use, if not enough, we will allocate new buffer */
    1162           29346 :         php_mysql_packet_command *packet= (php_mysql_packet_command *) _packet;
    1163           29346 :         MYSQLND_NET *net = &conn->net;
    1164           29346 :         unsigned int error_reporting = EG(error_reporting);
    1165                 :         size_t written;
    1166                 : 
    1167           29346 :         DBG_ENTER("php_mysqlnd_cmd_write");
    1168                 :         /*
    1169                 :           Reset packet_no, or we will get bad handshake!
    1170                 :           Every command starts a new TX and packet numbers are reset to 0.
    1171                 :         */
    1172           29346 :         net->packet_no = 0;
    1173           29346 :         net->compressed_envelope_packet_no = 0; /* this is for the response */
    1174                 : 
    1175           29346 :         if (error_reporting) {
    1176           28838 :                 EG(error_reporting) = 0;
    1177                 :         }
    1178                 : 
    1179           29346 :         MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_PACKETS_SENT_CMD);
    1180                 : 
    1181                 : #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
    1182                 :         php_mysqlnd_consume_uneaten_data(conn, packet->command TSRMLS_CC);
    1183                 : #endif
    1184                 : 
    1185           30962 :         if (!packet->argument || !packet->arg_len) {
    1186                 :                 char buffer[MYSQLND_HEADER_SIZE + 1];
    1187                 : 
    1188            1616 :                 int1store(buffer + MYSQLND_HEADER_SIZE, packet->command);
    1189            1616 :                 written = mysqlnd_stream_write_w_header(conn, buffer, 1 TSRMLS_CC);
    1190                 :         } else {
    1191           27730 :                 size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret;
    1192                 :                 zend_uchar *tmp, *p;
    1193           27730 :                 tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer;
    1194           27730 :                 p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */
    1195                 : 
    1196           27730 :                 int1store(p, packet->command);
    1197           27730 :                 p++;
    1198                 : 
    1199           27730 :                 memcpy(p, packet->argument, packet->arg_len);
    1200                 : 
    1201           27730 :                 ret = mysqlnd_stream_write_w_header(conn, (char *)tmp, tmp_len - MYSQLND_HEADER_SIZE TSRMLS_CC);
    1202           27730 :                 if (tmp != net->cmd_buffer.buffer) {
    1203              30 :                         MYSQLND_INC_CONN_STATISTIC(&conn->stats, STAT_CMD_BUFFER_TOO_SMALL);
    1204              30 :                         mnd_efree(tmp);
    1205                 :                 }
    1206           27730 :                 written = ret;
    1207                 :         }
    1208           29346 :         if (error_reporting) {
    1209                 :                 /* restore error reporting */
    1210           28838 :                 EG(error_reporting) = error_reporting;
    1211                 :         }
    1212           29346 :         DBG_RETURN(written);
    1213                 : }
    1214                 : /* }}} */
    1215                 : 
    1216                 : 
    1217                 : /* {{{ php_mysqlnd_cmd_free_mem */
    1218                 : static
    1219                 : void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
    1220               0 : {
    1221               0 :         if (!alloca) {
    1222               0 :                 mnd_pefree(_packet, ((php_mysql_packet_command *)_packet)->header.persistent);
    1223                 :         }
    1224               0 : }
    1225                 : /* }}} */
    1226                 : 
    1227                 : 
    1228                 : /* {{{ php_mysqlnd_rset_header_read */
    1229                 : static enum_func_status
    1230                 : php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC)
    1231           19124 : {
    1232           19124 :         size_t buf_len = conn->net.cmd_buffer.length;
    1233           19124 :         zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
    1234           19124 :         zend_uchar *p = buf;
    1235           19124 :         zend_uchar *begin = buf;
    1236                 :         size_t len;
    1237           19124 :         php_mysql_packet_rset_header *packet= (php_mysql_packet_rset_header *) _packet;
    1238                 : 
    1239           19124 :         DBG_ENTER("php_mysqlnd_rset_header_read");
    1240                 : 
    1241           19124 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET);
    1242                 : 
    1243                 :         /*
    1244                 :           Don't increment. First byte is 0xFF on error, but otherwise is starting byte
    1245                 :           of encoded sequence for length.
    1246                 :         */
    1247           19121 :         if (*p == 0xFF) {
    1248                 :                 /* Error */
    1249             653 :                 p++;
    1250             653 :                 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
    1251                 :                                                                                  packet->error_info.error, sizeof(packet->error_info.error),
    1252                 :                                                                                  &packet->error_info.error_no, packet->error_info.sqlstate
    1253                 :                                                                                  TSRMLS_CC);
    1254             653 :                 DBG_RETURN(PASS);
    1255                 :         }
    1256                 : 
    1257           18468 :         packet->field_count= php_mysqlnd_net_field_length(&p);
    1258           18468 :         switch (packet->field_count) {
    1259                 :                 case MYSQLND_NULL_LENGTH:
    1260               6 :                         DBG_INF("LOAD LOCAL");
    1261                 :                         /*
    1262                 :                           First byte in the packet is the field count.
    1263                 :                           Thus, the name is size - 1. And we add 1 for a trailing \0.
    1264                 :                         */
    1265               6 :                         len = packet->header.size - 1;
    1266               6 :                         packet->info_or_local_file = mnd_emalloc(len + 1);
    1267               6 :                         memcpy(packet->info_or_local_file, p, len);
    1268               6 :                         packet->info_or_local_file[len] = '\0';
    1269               6 :                         packet->info_or_local_file_len = len;
    1270               6 :                         break;
    1271                 :                 case 0x00:
    1272           12419 :                         DBG_INF("UPSERT");
    1273           12419 :                         packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
    1274           12419 :                         packet->last_insert_id= php_mysqlnd_net_field_length_ll(&p);
    1275           12419 :                         packet->server_status = uint2korr(p);
    1276           12419 :                         p+=2;
    1277           12419 :                         packet->warning_count = uint2korr(p);
    1278           12419 :                         p+=2;
    1279                 :                         /* Check for additional textual data */
    1280           12419 :                         if (packet->header.size  > (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
    1281             365 :                                 packet->info_or_local_file = mnd_emalloc(len + 1);
    1282             365 :                                 memcpy(packet->info_or_local_file, p, len);
    1283             365 :                                 packet->info_or_local_file[len] = '\0';
    1284             365 :                                 packet->info_or_local_file_len = len;
    1285                 :                         }
    1286           12419 :                         DBG_INF_FMT("affected_rows=%llu last_insert_id=%llu server_status=%d warning_count=%d",
    1287                 :                                                 packet->affected_rows, packet->last_insert_id,
    1288                 :                                                 packet->server_status, packet->warning_count);
    1289           12419 :                         break;
    1290                 :                 default:
    1291            6043 :                         DBG_INF("SELECT");
    1292                 :                         /* Result set */
    1293                 :                         break;
    1294                 :         }
    1295           18468 :         if (p - begin > packet->header.size) {
    1296               1 :                 DBG_ERR_FMT("RSET_HEADER packet %d bytes shorter than expected", p - begin - packet->header.size);
    1297               1 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
    1298                 :                                                  p - begin - packet->header.size);
    1299                 :         }
    1300                 : 
    1301           18468 :         DBG_RETURN(PASS);
    1302                 : }
    1303                 : /* }}} */
    1304                 : 
    1305                 : 
    1306                 : /* {{{ php_mysqlnd_rset_header_free_mem */
    1307                 : static
    1308                 : void php_mysqlnd_rset_header_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
    1309           19124 : {
    1310           19124 :         DBG_ENTER("php_mysqlnd_rset_header_free_mem");
    1311           19124 :         php_mysql_packet_rset_header *p= (php_mysql_packet_rset_header *) _packet;
    1312           19124 :         if (p->info_or_local_file) {
    1313             371 :                 mnd_efree(p->info_or_local_file);
    1314             371 :                 p->info_or_local_file = NULL;
    1315                 :         }
    1316           19124 :         if (!alloca) {
    1317               0 :                 mnd_pefree(p, p->header.persistent);
    1318                 :         }
    1319                 :         DBG_VOID_RETURN;
    1320                 : }
    1321                 : /* }}} */
    1322                 : 
    1323                 : static size_t rset_field_offsets[] =
    1324                 : {
    1325                 :         STRUCT_OFFSET(MYSQLND_FIELD, catalog),
    1326                 :         STRUCT_OFFSET(MYSQLND_FIELD, catalog_length),
    1327                 :         STRUCT_OFFSET(MYSQLND_FIELD, db),
    1328                 :         STRUCT_OFFSET(MYSQLND_FIELD, db_length),
    1329                 :         STRUCT_OFFSET(MYSQLND_FIELD, table),
    1330                 :         STRUCT_OFFSET(MYSQLND_FIELD, table_length),
    1331                 :         STRUCT_OFFSET(MYSQLND_FIELD, org_table),
    1332                 :         STRUCT_OFFSET(MYSQLND_FIELD, org_table_length),
    1333                 :         STRUCT_OFFSET(MYSQLND_FIELD, name),
    1334                 :         STRUCT_OFFSET(MYSQLND_FIELD, name_length),
    1335                 :         STRUCT_OFFSET(MYSQLND_FIELD, org_name),
    1336                 :         STRUCT_OFFSET(MYSQLND_FIELD, org_name_length)
    1337                 : };
    1338                 : 
    1339                 : 
    1340                 : /* {{{ php_mysqlnd_rset_field_read */
    1341                 : static enum_func_status
    1342                 : php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC)
    1343           28966 : {
    1344                 :         /* Should be enough for the metadata of a single row */
    1345           28966 :         php_mysql_packet_res_field *packet= (php_mysql_packet_res_field *) _packet;
    1346           28966 :         size_t buf_len = conn->net.cmd_buffer.length, total_len = 0;
    1347           28966 :         zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
    1348           28966 :         zend_uchar *p = buf;
    1349           28966 :         zend_uchar *begin = buf;
    1350                 :         char *root_ptr;
    1351                 :         unsigned long len;
    1352                 :         MYSQLND_FIELD *meta;
    1353           28966 :         unsigned int i, field_count = sizeof(rset_field_offsets)/sizeof(size_t);
    1354                 : 
    1355           28966 :         DBG_ENTER("php_mysqlnd_rset_field_read");
    1356                 : 
    1357           28966 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field", PROT_RSET_FLD_PACKET);
    1358                 : 
    1359           28965 :         if (packet->skip_parsing) {
    1360            1312 :                 DBG_RETURN(PASS);
    1361                 :         }
    1362           27653 :         if (*p == 0xFF) {
    1363                 :                 /* Error */
    1364               1 :                 p++;
    1365               1 :                 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
    1366                 :                                                                                  packet->error_info.error, sizeof(packet->error_info.error),
    1367                 :                                                                                  &packet->error_info.error_no, packet->error_info.sqlstate
    1368                 :                                                                                  TSRMLS_CC);
    1369               1 :                 DBG_ERR_FMT("Server error : (%d) %s", packet->error_info.error_no, packet->error_info.error);
    1370               1 :                 DBG_RETURN(PASS);
    1371           27652 :         } else if (*p == 0xFE && packet->header.size < 8) {
    1372                 :                 /* Premature EOF. That should be COM_FIELD_LIST */
    1373               3 :                 DBG_INF("Premature EOF. That should be COM_FIELD_LIST");
    1374               3 :                 packet->stupid_list_fields_eof = TRUE;
    1375               3 :                 DBG_RETURN(PASS);
    1376                 :         }
    1377                 : 
    1378           27649 :         meta = packet->metadata;
    1379                 : 
    1380          193543 :         for (i = 0; i < field_count; i += 2) {
    1381          165894 :                 len = php_mysqlnd_net_field_length(&p);
    1382          165894 :                 switch ((len)) {
    1383                 :                         case 0:
    1384           22783 :                                 *(char **)(((char*)meta) + rset_field_offsets[i]) = mysqlnd_empty_string;
    1385           22783 :                                 *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0;
    1386           22783 :                                 break;
    1387                 :                         case MYSQLND_NULL_LENGTH:
    1388               0 :                                 goto faulty_or_fake;
    1389                 :                         default:
    1390          143111 :                                 *(char **)(((char *)meta) + rset_field_offsets[i]) = (char *)p;
    1391          143111 :                                 *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len;
    1392          143111 :                                 p += len;
    1393          143111 :                                 total_len += len + 1;
    1394                 :                                 break;
    1395                 :                 }
    1396                 :         }
    1397                 : 
    1398                 :         /* 1 byte filler */
    1399           27649 :         p++;
    1400                 : 
    1401           27649 :         meta->charsetnr = uint2korr(p);
    1402           27649 :         p += 2;
    1403                 : 
    1404           27649 :         meta->length = uint4korr(p);
    1405           27649 :         p += 4;
    1406                 : 
    1407           27649 :         meta->type = uint1korr(p);
    1408           27649 :         p += 1;
    1409                 : 
    1410           27649 :         meta->flags = uint2korr(p);
    1411           27649 :         p += 2;
    1412                 : 
    1413           27649 :         meta->decimals = uint2korr(p);
    1414           27649 :         p += 1;
    1415                 : 
    1416                 :         /* 2 byte filler */
    1417           27649 :         p +=2;
    1418                 : 
    1419                 :         /* Should we set NUM_FLAG (libmysql does it) ? */
    1420           27649 :         if (
    1421                 :                 (meta->type <= MYSQL_TYPE_INT24 &&
    1422                 :                         (meta->type != MYSQL_TYPE_TIMESTAMP || meta->length == 14 || meta->length == 8)
    1423                 :                 ) || meta->type == MYSQL_TYPE_YEAR)
    1424                 :         {
    1425           11504 :                 meta->flags |= NUM_FLAG;
    1426                 :         }
    1427                 : 
    1428                 : 
    1429                 :         /*
    1430                 :           def could be empty, thus don't allocate on the root.
    1431                 :           NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL.
    1432                 :           Otherwise the string is length encoded.
    1433                 :         */
    1434           27649 :         if (packet->header.size > (p - buf) &&
    1435                 :                 (len = php_mysqlnd_net_field_length(&p)) &&
    1436                 :                 len != MYSQLND_NULL_LENGTH)
    1437                 :         {
    1438               3 :                 DBG_INF_FMT("Def found, length %lu", len);
    1439               3 :                 meta->def = mnd_emalloc(len + 1);
    1440               3 :                 memcpy(meta->def, p, len);
    1441               3 :                 meta->def[len] = '\0';
    1442               3 :                 meta->def_length = len;
    1443               3 :                 p += len;
    1444                 :         }
    1445                 : 
    1446           27649 :         if (p - begin > packet->header.size) {
    1447               0 :                 DBG_ERR_FMT("RSET field packet %d bytes shorter than expected", p - begin - packet->header.size);
    1448               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet "MYSQLND_SZ_T_SPEC" bytes "
    1449                 :                                                 "shorter than expected", p - begin - packet->header.size);
    1450                 :         }
    1451                 : 
    1452           27649 :         root_ptr = meta->root = mnd_emalloc(total_len);
    1453           27649 :         meta->root_len = total_len;
    1454                 :         /* Now do allocs */
    1455           27649 :         if (meta->catalog && meta->catalog != mysqlnd_empty_string) {
    1456           27649 :                 len = meta->catalog_length;
    1457           27649 :                 meta->catalog = memcpy(root_ptr, meta->catalog, len);
    1458           27649 :                 *(root_ptr +=len) = '\0';
    1459           27649 :                 root_ptr++;
    1460                 :         }
    1461                 : 
    1462           27649 :         if (meta->db && meta->db != mysqlnd_empty_string) {
    1463           21862 :                 len = meta->db_length;
    1464           21862 :                 meta->db = memcpy(root_ptr, meta->db, len);
    1465           21862 :                 *(root_ptr + len) = '\0';
    1466                 :         }
    1467                 : 
    1468           27649 :         if (meta->table && meta->table != mysqlnd_empty_string) {
    1469           22029 :                 len = meta->table_length;
    1470           22029 :                 meta->table = memcpy(root_ptr, meta->table, len);
    1471           22029 :                 *(root_ptr +=len) = '\0';
    1472           22029 :                 root_ptr++;
    1473                 :         }
    1474                 : 
    1475           27649 :         if (meta->org_table && meta->org_table != mysqlnd_empty_string) {
    1476           21862 :                 len = meta->org_table_length;
    1477           21862 :                 meta->org_table = memcpy(root_ptr, meta->org_table, len);
    1478           21862 :                 *(root_ptr +=len) = '\0';
    1479           21862 :                 root_ptr++;
    1480                 :         }
    1481                 : 
    1482           27649 :         if (meta->name && meta->name != mysqlnd_empty_string) {
    1483           27642 :                 len = meta->name_length;
    1484           27642 :                 meta->name = memcpy(root_ptr, meta->name, len);
    1485           27642 :                 *(root_ptr +=len) = '\0';
    1486           27642 :                 root_ptr++;
    1487                 :         }
    1488                 : 
    1489           27649 :         if (meta->org_name && meta->org_name != mysqlnd_empty_string) {
    1490           22067 :                 len = meta->org_name_length;
    1491           22067 :                 meta->org_name = memcpy(root_ptr, meta->org_name, len);
    1492           22067 :                 *(root_ptr +=len) = '\0';
    1493           22067 :                 root_ptr++;
    1494                 :         }
    1495                 : 
    1496           27649 :         DBG_INF_FMT("FIELD=[%s.%s.%s]", meta->db? meta->db:"*NA*", meta->table? meta->table:"*NA*",
    1497                 :                                 meta->name? meta->name:"*NA*");
    1498                 : 
    1499           27649 :         DBG_RETURN(PASS);
    1500                 : 
    1501               0 : faulty_or_fake:
    1502               0 :         DBG_ERR_FMT("Protocol error. Server sent NULL_LENGTH. The server is faulty");
    1503               0 :         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH."
    1504                 :                                          " The server is faulty");
    1505               0 :         DBG_RETURN(FAIL);
    1506                 : }
    1507                 : /* }}} */
    1508                 : 
    1509                 : 
    1510                 : /* {{{ php_mysqlnd_rset_field_free_mem */
    1511                 : static
    1512                 : void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
    1513            9191 : {
    1514            9191 :         php_mysql_packet_res_field *p= (php_mysql_packet_res_field *) _packet;
    1515                 : 
    1516                 :         /* p->metadata was passed to us as temporal buffer */
    1517            9191 :         if (!alloca) {
    1518               0 :                 mnd_pefree(p, p->header.persistent);
    1519                 :         }
    1520            9191 : }
    1521                 : /* }}} */
    1522                 : 
    1523                 : 
    1524                 : static enum_func_status
    1525                 : php_mysqlnd_read_row_ex(MYSQLND *conn, MYSQLND_MEMORY_POOL * result_set_memory_pool,
    1526                 :                                                 MYSQLND_MEMORY_POOL_CHUNK **buffer,
    1527                 :                                                 uint64_t *data_size, zend_bool persistent_alloc,
    1528                 :                                                 unsigned int prealloc_more_bytes TSRMLS_DC)
    1529           30496 : {
    1530           30496 :         enum_func_status ret = PASS;
    1531                 :         mysqlnd_packet_header header;
    1532           30496 :         zend_uchar *p = NULL;
    1533           30496 :         zend_bool first_iteration = TRUE;
    1534                 : 
    1535           30496 :         DBG_ENTER("php_mysqlnd_read_row_ex");
    1536                 : 
    1537                 :         /*
    1538                 :           To ease the process the server splits everything in packets up to 2^24 - 1.
    1539                 :           Even in the case the payload is evenly divisible by this value, the last
    1540                 :           packet will be empty, namely 0 bytes. Thus, we can read every packet and ask
    1541                 :           for next one if they have 2^24 - 1 sizes. But just read the header of a
    1542                 :           zero-length byte, don't read the body, there is no such.
    1543                 :         */
    1544                 : 
    1545           30496 :         *data_size = prealloc_more_bytes;
    1546                 :         while (1) {
    1547           30496 :                 if (FAIL == mysqlnd_read_header(conn , &header TSRMLS_CC)) {
    1548               1 :                         ret = FAIL;
    1549               1 :                         break;
    1550                 :                 }
    1551                 : 
    1552           30495 :                 *data_size += header.size;
    1553                 : 
    1554           30495 :                 if (first_iteration) {
    1555           30495 :                         first_iteration = FALSE;
    1556                 :                         /*
    1557                 :                           We need a trailing \0 for the last string, in case of text-mode,
    1558                 :                           to be able to implement read-only variables. Thus, we add + 1.
    1559                 :                         */
    1560           30495 :                         *buffer = result_set_memory_pool->get_chunk(result_set_memory_pool, *data_size + 1 TSRMLS_CC);
    1561           30495 :                         p = (*buffer)->ptr;
    1562               0 :                 } else if (!first_iteration) {
    1563                 :                         /* Empty packet after MYSQLND_MAX_PACKET_SIZE packet. That's ok, break */
    1564               0 :                         if (!header.size) {
    1565               0 :                                 break;
    1566                 :                         }
    1567                 : 
    1568                 :                         /*
    1569                 :                           We have to realloc the buffer.
    1570                 : 
    1571                 :                           We need a trailing \0 for the last string, in case of text-mode,
    1572                 :                           to be able to implement read-only variables.
    1573                 :                         */
    1574               0 :                         (*buffer)->resize_chunk((*buffer), *data_size + 1 TSRMLS_CC);
    1575                 :                         /* The position could have changed, recalculate */
    1576               0 :                         p = (*buffer)->ptr + (*data_size - header.size);
    1577                 :                 }
    1578                 : 
    1579           30495 :                 if ((ret = mysqlnd_read_body(conn, &header, p TSRMLS_CC))) {
    1580               0 :                         DBG_ERR("Empty row packet body");
    1581               0 :                         php_error(E_WARNING, "Empty row packet body");
    1582               0 :                         break;
    1583                 :                 }
    1584                 : 
    1585           30495 :                 if (header.size < MYSQLND_MAX_PACKET_SIZE) {
    1586           30495 :                         break;
    1587                 :                 }
    1588               0 :         }
    1589           30496 :         if (ret == FAIL && *buffer) {
    1590               0 :                 (*buffer)->free_chunk((*buffer), TRUE TSRMLS_CC);
    1591               0 :                 *buffer = NULL;
    1592                 :         }
    1593           30496 :         *data_size -= prealloc_more_bytes;
    1594           30496 :         DBG_RETURN(ret);
    1595                 : }
    1596                 : 
    1597                 : 
    1598                 : /* {{{ php_mysqlnd_rowp_read_binary_protocol */
    1599                 : void php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
    1600                 :                                                                                  unsigned int field_count, MYSQLND_FIELD *fields_metadata,
    1601                 :                                                                                  zend_bool persistent,
    1602                 :                                                                                  zend_bool as_unicode, zend_bool as_int_or_float,
    1603                 :                                                                                  MYSQLND_THD_ZVAL_PCACHE * zval_cache,
    1604                 :                                                                                  MYSQLND_STATS * stats TSRMLS_DC)
    1605            5906 : {
    1606                 :         int i;
    1607            5906 :         zend_uchar *p = row_buffer->ptr;
    1608                 :         zend_uchar *null_ptr, bit;
    1609                 :         zval **current_field, **end_field, **start_field;
    1610                 : #ifdef USE_ZVAL_CACHE
    1611                 :         zend_bool allocated;
    1612                 :         void *obj;
    1613                 : #endif
    1614                 : 
    1615            5906 :         DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol");
    1616                 : 
    1617            5906 :         end_field = (current_field = start_field = fields) + field_count;
    1618                 : 
    1619                 : 
    1620                 :         /* skip the first byte, not 0xFE -> 0x0, status */
    1621            5906 :         p++;
    1622            5906 :         null_ptr= p;
    1623            5906 :         p += (field_count + 9)/8;               /* skip null bits */
    1624            5906 :         bit     = 4;                                            /* first 2 bits are reserved */
    1625                 : 
    1626           21702 :         for (i = 0; current_field < end_field; current_field++, i++) {
    1627                 : #ifdef USE_ZVAL_CACHE
    1628                 :                 DBG_INF("Trying to use the zval cache");
    1629                 :                 obj = mysqlnd_palloc_get_zval(zval_cache, &allocated TSRMLS_CC);
    1630                 :                 if (allocated) {
    1631                 :                         *current_field = (zval *) obj;
    1632                 :                 } else {
    1633                 :                         /* It's from the cache, so we can upcast here */
    1634                 :                         *current_field = &((mysqlnd_zval *) obj)->zv;
    1635                 :                         ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_EXT_BUFFER;
    1636                 :                 }
    1637                 : #else
    1638           15796 :                 DBG_INF("Directly creating zval");
    1639           15796 :                 MAKE_STD_ZVAL(*current_field);
    1640                 : #endif
    1641                 : 
    1642           15796 :                 DBG_INF_FMT("Into zval=%p decoding column %d [%s.%s.%s] type=%d field->flags&unsigned=%d flags=%u is_bit=%d as_unicode=%d",
    1643                 :                         *current_field, i,
    1644                 :                         fields_metadata[i].db, fields_metadata[i].table, fields_metadata[i].name, fields_metadata[i].type,
    1645                 :                         fields_metadata[i].flags & UNSIGNED_FLAG, fields_metadata[i].flags, fields_metadata[i].type == MYSQL_TYPE_BIT, as_unicode);
    1646           15796 :                 if (*null_ptr & bit) {
    1647            1151 :                         DBG_INF("It's null");
    1648            1151 :                         ZVAL_NULL(*current_field);
    1649            1151 :                         MYSQLND_INC_CONN_STATISTIC(stats, STAT_BINARY_TYPE_FETCHED_NULL);
    1650                 :                 } else {
    1651           14645 :                         enum_mysqlnd_field_types type = fields_metadata[i].type;
    1652           14645 :                         mysqlnd_ps_fetch_functions[type].func(*current_field, &fields_metadata[i],
    1653                 :                                                                                                   0, &p, as_unicode TSRMLS_CC);
    1654                 : 
    1655           14645 :                         if (MYSQLND_G(collect_statistics)) {
    1656                 :                                 enum_mysqlnd_collected_stats statistic;
    1657           14645 :                                 switch (fields_metadata[i].type) {
    1658               0 :                                         case MYSQL_TYPE_DECIMAL:        statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
    1659              57 :                                         case MYSQL_TYPE_TINY:           statistic = STAT_BINARY_TYPE_FETCHED_INT8; break;
    1660              41 :                                         case MYSQL_TYPE_SHORT:          statistic = STAT_BINARY_TYPE_FETCHED_INT16; break;
    1661            4880 :                                         case MYSQL_TYPE_LONG:           statistic = STAT_BINARY_TYPE_FETCHED_INT32; break;
    1662             114 :                                         case MYSQL_TYPE_FLOAT:          statistic = STAT_BINARY_TYPE_FETCHED_FLOAT; break;
    1663              50 :                                         case MYSQL_TYPE_DOUBLE:         statistic = STAT_BINARY_TYPE_FETCHED_DOUBLE; break;
    1664               0 :                                         case MYSQL_TYPE_NULL:           statistic = STAT_BINARY_TYPE_FETCHED_NULL; break;
    1665              18 :                                         case MYSQL_TYPE_TIMESTAMP:      statistic = STAT_BINARY_TYPE_FETCHED_TIMESTAMP; break;
    1666            2049 :                                         case MYSQL_TYPE_LONGLONG:       statistic = STAT_BINARY_TYPE_FETCHED_INT64; break;
    1667              26 :                                         case MYSQL_TYPE_INT24:          statistic = STAT_BINARY_TYPE_FETCHED_INT24; break;
    1668              20 :                                         case MYSQL_TYPE_DATE:           statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
    1669              20 :                                         case MYSQL_TYPE_TIME:           statistic = STAT_BINARY_TYPE_FETCHED_TIME; break;
    1670              20 :                                         case MYSQL_TYPE_DATETIME:       statistic = STAT_BINARY_TYPE_FETCHED_DATETIME; break;
    1671              18 :                                         case MYSQL_TYPE_YEAR:           statistic = STAT_BINARY_TYPE_FETCHED_YEAR; break;
    1672               0 :                                         case MYSQL_TYPE_NEWDATE:        statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
    1673               0 :                                         case MYSQL_TYPE_VARCHAR:        statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
    1674            1487 :                                         case MYSQL_TYPE_BIT:            statistic = STAT_BINARY_TYPE_FETCHED_BIT; break;
    1675              50 :                                         case MYSQL_TYPE_NEWDECIMAL:     statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
    1676               0 :                                         case MYSQL_TYPE_ENUM:           statistic = STAT_BINARY_TYPE_FETCHED_ENUM; break;
    1677               0 :                                         case MYSQL_TYPE_SET:            statistic = STAT_BINARY_TYPE_FETCHED_SET; break;
    1678               0 :                                         case MYSQL_TYPE_TINY_BLOB:      statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
    1679               0 :                                         case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
    1680               0 :                                         case MYSQL_TYPE_LONG_BLOB:      statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
    1681            1147 :                                         case MYSQL_TYPE_BLOB:           statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
    1682            4350 :                                         case MYSQL_TYPE_VAR_STRING:     statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
    1683             260 :                                         case MYSQL_TYPE_STRING:         statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
    1684              38 :                                         case MYSQL_TYPE_GEOMETRY:       statistic = STAT_BINARY_TYPE_FETCHED_GEOMETRY; break;
    1685               0 :                                         default: statistic = STAT_BINARY_TYPE_FETCHED_OTHER; break;
    1686                 :                                 }
    1687           14645 :                                 MYSQLND_INC_CONN_STATISTIC(stats, statistic);
    1688                 :                         }
    1689                 :                 }
    1690           15796 :                 if (!((bit<<=1) & 255)) {
    1691             151 :                         bit = 1;        /* to the following byte */
    1692             151 :                         null_ptr++;
    1693                 :                 }
    1694                 :         }
    1695                 : 
    1696                 :         DBG_VOID_RETURN;
    1697                 : }
    1698                 : /* }}} */
    1699                 : 
    1700                 : 
    1701                 : /* {{{ php_mysqlnd_rowp_read_text_protocol */
    1702                 : void php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
    1703                 :                                                                                  unsigned int field_count, MYSQLND_FIELD *fields_metadata,
    1704                 :                                                                                  zend_bool persistent,
    1705                 :                                                                                  zend_bool as_unicode, zend_bool as_int_or_float,
    1706                 :                                                                                  MYSQLND_THD_ZVAL_PCACHE * zval_cache,
    1707                 :                                                                                  MYSQLND_STATS * stats TSRMLS_DC)
    1708           17358 : {
    1709                 :         int i;
    1710           17358 :         zend_bool last_field_was_string = FALSE;
    1711                 :         zval **current_field, **end_field, **start_field;
    1712           17358 :         zend_uchar *p = row_buffer->ptr;
    1713           17358 :         size_t data_size = row_buffer->app;
    1714           17358 :         zend_uchar *bit_area = (zend_uchar*) row_buffer->ptr + data_size + 1; /* we allocate from here */
    1715                 : 
    1716           17358 :         DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
    1717                 : 
    1718           17358 :         end_field = (current_field = start_field = fields) + field_count;
    1719           55215 :         for (i = 0; current_field < end_field; current_field++, i++) {
    1720                 :                 /* Don't reverse the order. It is significant!*/
    1721           37857 :                 void *obj = NULL;
    1722           37857 :                 zend_bool allocated = TRUE;
    1723           37857 :                 zend_uchar *this_field_len_pos = p;
    1724                 :                 /* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
    1725           37857 :                 unsigned long len = php_mysqlnd_net_field_length(&p);
    1726                 : 
    1727                 : #ifdef USE_ZVAL_CACHE
    1728                 :                 obj = mysqlnd_palloc_get_zval(zval_cache, &allocated TSRMLS_CC);
    1729                 :                 if (allocated) {
    1730                 :                         *current_field = (zval *) obj;
    1731                 :                 } else {
    1732                 :                         /* It's from the cache, so we can upcast here */
    1733                 :                         *current_field = &((mysqlnd_zval *) obj)->zv;
    1734                 :                         ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_FREE;
    1735                 :                 }
    1736                 : #else
    1737           37857 :                 DBG_INF("Directly creating zval");
    1738           37857 :                 MAKE_STD_ZVAL(*current_field);
    1739                 : #endif
    1740                 : 
    1741           37857 :                 if (current_field > start_field && last_field_was_string) {
    1742                 :                         /*
    1743                 :                           Normal queries:
    1744                 :                           We have to put \0 now to the end of the previous field, if it was
    1745                 :                           a string. IS_NULL doesn't matter. Because we have already read our
    1746                 :                           length, then we can overwrite it in the row buffer.
    1747                 :                           This statement terminates the previous field, not the current one.
    1748                 : 
    1749                 :                           NULL_LENGTH is encoded in one byte, so we can stick a \0 there.
    1750                 :                           Any string's length is encoded in at least one byte, so we can stick
    1751                 :                           a \0 there.
    1752                 :                         */
    1753                 : 
    1754           20442 :                         *this_field_len_pos = '\0';
    1755                 :                 }
    1756                 : 
    1757                 :                 /* NULL or NOT NULL, this is the question! */
    1758           37857 :                 if (len == MYSQLND_NULL_LENGTH) {
    1759             739 :                         ZVAL_NULL(*current_field);
    1760             739 :                         last_field_was_string = FALSE;
    1761                 :                 } else {
    1762                 : #if PHP_MAJOR_VERSION >= 6 || defined(MYSQLND_STRING_TO_INT_CONVERSION)
    1763                 :                         struct st_mysqlnd_perm_bind perm_bind =
    1764           37118 :                                         mysqlnd_ps_fetch_functions[fields_metadata[i].type];
    1765                 : #endif
    1766           37118 :                         if (MYSQLND_G(collect_statistics)) {
    1767                 :                                 enum_mysqlnd_collected_stats statistic;
    1768           37094 :                                 switch (fields_metadata[i].type) {
    1769               0 :                                         case MYSQL_TYPE_DECIMAL:        statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
    1770              35 :                                         case MYSQL_TYPE_TINY:           statistic = STAT_TEXT_TYPE_FETCHED_INT8; break;
    1771              38 :                                         case MYSQL_TYPE_SHORT:          statistic = STAT_TEXT_TYPE_FETCHED_INT16; break;
    1772           16155 :                                         case MYSQL_TYPE_LONG:           statistic = STAT_TEXT_TYPE_FETCHED_INT32; break;
    1773              35 :                                         case MYSQL_TYPE_FLOAT:          statistic = STAT_TEXT_TYPE_FETCHED_FLOAT; break;
    1774              33 :                                         case MYSQL_TYPE_DOUBLE:         statistic = STAT_TEXT_TYPE_FETCHED_DOUBLE; break;
    1775               0 :                                         case MYSQL_TYPE_NULL:           statistic = STAT_TEXT_TYPE_FETCHED_NULL; break;
    1776            1795 :                                         case MYSQL_TYPE_TIMESTAMP:      statistic = STAT_TEXT_TYPE_FETCHED_TIMESTAMP; break;
    1777            1151 :                                         case MYSQL_TYPE_LONGLONG:       statistic = STAT_TEXT_TYPE_FETCHED_INT64; break;
    1778              25 :                                         case MYSQL_TYPE_INT24:          statistic = STAT_TEXT_TYPE_FETCHED_INT24; break;
    1779              19 :                                         case MYSQL_TYPE_DATE:           statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
    1780              19 :                                         case MYSQL_TYPE_TIME:           statistic = STAT_TEXT_TYPE_FETCHED_TIME; break;
    1781              21 :                                         case MYSQL_TYPE_DATETIME:       statistic = STAT_TEXT_TYPE_FETCHED_DATETIME; break;
    1782              16 :                                         case MYSQL_TYPE_YEAR:           statistic = STAT_TEXT_TYPE_FETCHED_YEAR; break;
    1783               0 :                                         case MYSQL_TYPE_NEWDATE:        statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
    1784               0 :                                         case MYSQL_TYPE_VARCHAR:        statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
    1785             462 :                                         case MYSQL_TYPE_BIT:            statistic = STAT_TEXT_TYPE_FETCHED_BIT; break;
    1786              35 :                                         case MYSQL_TYPE_NEWDECIMAL:     statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
    1787               0 :                                         case MYSQL_TYPE_ENUM:           statistic = STAT_TEXT_TYPE_FETCHED_ENUM; break;
    1788               0 :                                         case MYSQL_TYPE_SET:            statistic = STAT_TEXT_TYPE_FETCHED_SET; break;
    1789               0 :                                         case MYSQL_TYPE_TINY_BLOB:      statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
    1790               7 :                                         case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
    1791               0 :                                         case MYSQL_TYPE_LONG_BLOB:      statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
    1792            1926 :                                         case MYSQL_TYPE_BLOB:           statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
    1793            3862 :                                         case MYSQL_TYPE_VAR_STRING:     statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
    1794           11432 :                                         case MYSQL_TYPE_STRING:         statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
    1795              28 :                                         case MYSQL_TYPE_GEOMETRY:       statistic = STAT_TEXT_TYPE_FETCHED_GEOMETRY; break;
    1796               0 :                                         default: statistic = STAT_TEXT_TYPE_FETCHED_OTHER; break;
    1797                 :                                 }
    1798           37094 :                                 MYSQLND_INC_CONN_STATISTIC(stats, statistic);
    1799                 :                         }
    1800                 : 
    1801                 : #ifdef MYSQLND_STRING_TO_INT_CONVERSION
    1802           37118 :                         if (as_int_or_float && perm_bind.php_type == IS_LONG &&
    1803                 :                                 perm_bind.pack_len <= SIZEOF_LONG)
    1804                 :                         {
    1805               0 :                                 zend_uchar save = *(p + len);
    1806                 :                                 /* We have to make it ASCIIZ temporarily */
    1807               0 :                                 *(p + len) = '\0';
    1808               0 :                                 if (perm_bind.pack_len < SIZEOF_LONG)
    1809                 :                                 {
    1810                 :                                         /* direct conversion */
    1811                 :                                         int64_t v =
    1812                 : #ifndef PHP_WIN32
    1813               0 :                                                 atoll((char *) p);
    1814                 : #else
    1815                 :                                                 _atoi64((char *) p);
    1816                 : #endif
    1817               0 :                                         ZVAL_LONG(*current_field, v);
    1818                 :                                 } else {
    1819                 :                                         uint64_t v =
    1820                 : #ifndef PHP_WIN32
    1821               0 :                                                 (uint64_t) atoll((char *) p);
    1822                 : #else
    1823                 :                                                 (uint64_t) _atoi64((char *) p);
    1824                 : #endif
    1825               0 :                                         zend_bool uns = fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE;
    1826                 :                                         /* We have to make it ASCIIZ temporarily */
    1827                 : #if SIZEOF_LONG==8
    1828                 :                                         if (uns == TRUE && v > 9223372036854775807L)
    1829                 : #elif SIZEOF_LONG==4
    1830               0 :                                         if ((uns == TRUE && v > L64(2147483647)) ||
    1831                 :                                                 (uns == FALSE && (( L64(2147483647) < (int64_t) v) ||
    1832                 :                                                 (L64(-2147483648) > (int64_t) v))))
    1833                 : #endif /* SIZEOF */
    1834                 :                                         {
    1835               0 :                                                 ZVAL_STRINGL(*current_field, (char *)p, len, 0);
    1836                 :                                         } else {
    1837               0 :                                                 ZVAL_LONG(*current_field, (int64_t)v);
    1838                 :                                         }
    1839                 :                                 }
    1840               0 :                                 *(p + len) = save;
    1841           37118 :                         } else if (as_int_or_float && perm_bind.php_type == IS_DOUBLE) {
    1842               0 :                                 zend_uchar save = *(p + len);
    1843                 :                                 /* We have to make it ASCIIZ temporarily */
    1844               0 :                                 *(p + len) = '\0';
    1845               0 :                                 ZVAL_DOUBLE(*current_field, atof((char *) p));
    1846               0 :                                 *(p + len) = save;
    1847                 :                         } else
    1848                 : #endif /* MYSQLND_STRING_TO_INT_CONVERSION */
    1849           37118 :                         if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
    1850                 :                                 /*
    1851                 :                                   BIT fields are specially handled. As they come as bit mask, we have
    1852                 :                                   to convert it to human-readable representation. As the bits take
    1853                 :                                   less space in the protocol than the numbers they represent, we don't
    1854                 :                                   have enough space in the packet buffer to overwrite inside.
    1855                 :                                   Thus, a bit more space is pre-allocated at the end of the buffer,
    1856                 :                                   see php_mysqlnd_rowp_read(). And we add the strings at the end.
    1857                 :                                   Definitely not nice, _hackish_ :(, but works.
    1858                 :                                 */
    1859             462 :                                 zend_uchar *start = bit_area;
    1860             462 :                                 ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]),
    1861                 :                                                                                    0, &p, as_unicode, len TSRMLS_CC);
    1862                 :                                 /*
    1863                 :                                   We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
    1864                 :                                   later in this function there will be an advancement.
    1865                 :                                 */
    1866             462 :                                 p -= len;
    1867             462 :                                 if (Z_TYPE_PP(current_field) == IS_LONG) {
    1868             339 :                                         bit_area += 1 + sprintf((char *)start, "%ld", Z_LVAL_PP(current_field));
    1869                 : #if PHP_MAJOR_VERSION >= 6
    1870             339 :                                         if (as_unicode) {
    1871             339 :                                                 ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
    1872                 :                                         } else
    1873                 : #endif
    1874                 :                                         {
    1875               0 :                                                 ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
    1876                 :                                         }
    1877             339 :                                         if (allocated == FALSE) {
    1878               0 :                                                 ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER;
    1879                 :                                         }
    1880             123 :                                 } else if (Z_TYPE_PP(current_field) == IS_STRING){
    1881               0 :                                         memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field));
    1882               0 :                                         bit_area += Z_STRLEN_PP(current_field);
    1883               0 :                                         *bit_area++ = '\0';
    1884               0 :                                         zval_dtor(*current_field);
    1885                 : #if PHP_MAJOR_VERSION >= 6
    1886               0 :                                         if (as_unicode) {
    1887               0 :                                                 ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
    1888                 :                                         } else
    1889                 : #endif
    1890                 :                                         {
    1891               0 :                                                 ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
    1892                 :                                         }
    1893               0 :                                         if (allocated == FALSE) {
    1894               0 :                                                 ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER;
    1895                 :                                         }
    1896                 :                                 }
    1897                 :                                 /*
    1898                 :                                   IS_UNICODE should not be specially handled. In unicode mode
    1899                 :                                   the buffers are not referenced - everything is copied.
    1900                 :                                 */
    1901                 :                         } else
    1902                 : #if PHP_MAJOR_VERSION < 6
    1903                 :                         {
    1904                 :                                 ZVAL_STRINGL(*current_field, (char *)p, len, 0);
    1905                 :                                 if (allocated == FALSE) {
    1906                 :                                         ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER;
    1907                 :                                 }
    1908                 :                         }
    1909                 : #else
    1910                 :                         /*
    1911                 :                           Here we have to convert to UTF16, which means not reusing the buffer.
    1912                 :                           Which in turn means that we can free the buffers once we have
    1913                 :                           stored the result set, if we use store_result().
    1914                 : 
    1915                 :                           Also the destruction of the zvals should not call zval_copy_ctor()
    1916                 :                           because then we will leak.
    1917                 : 
    1918                 :                           I suppose we can use UG(unicode) in mysqlnd.c/mysqlnd_palloc.c when
    1919                 :                           freeing a result set
    1920                 :                           to check if we need to call copy_ctor().
    1921                 : 
    1922                 :                           XXX: Keep in mind that up there there is an open `else` in
    1923                 :                                    #ifdef MYSQLND_STRING_TO_INT_CONVERSION
    1924                 :                                    which will make with this `if` an `else if`.
    1925                 :                         */
    1926           36773 :                         if ((perm_bind.is_possibly_blob == TRUE &&
    1927                 :                                  fields_metadata[i].charsetnr == MYSQLND_BINARY_CHARSET_NR) ||
    1928                 :                                 (!as_unicode && perm_bind.can_ret_as_str_in_uni == TRUE))
    1929                 :                         {
    1930                 :                                 /* BLOB - no conversion please */
    1931             117 :                                 ZVAL_STRINGL(*current_field, (char *)p, len, 0);
    1932                 :                         } else {
    1933           36539 :                                 ZVAL_UTF8_STRINGL(*current_field, (char *)p, len, 0);
    1934                 :                         }
    1935           37118 :                         if (allocated == FALSE) {
    1936                 :                                 /*
    1937                 :                                   The zval cache will check and see that the type is IS_STRING.
    1938                 :                                   In this case it will call copy_ctor(). This is valid when
    1939                 :                                   allocated == TRUE . In this case we can't upcast. Thus for non-PS
    1940                 :                                   point_type doesn't matter much, as the valuable information is
    1941                 :                                   in the type of result set. Still good to set it.
    1942                 :                                 */
    1943               0 :                                 if (Z_TYPE_P(*current_field) == IS_STRING) {
    1944               0 :                                         ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_INT_BUFFER;
    1945                 :                                 } else {
    1946               0 :                                         ((mysqlnd_zval *) obj)->point_type = MYSQLND_POINTS_EXT_BUFFER;
    1947                 :                                 }
    1948                 :                         }
    1949                 : #endif
    1950           37118 :                         p += len;
    1951           37118 :                         last_field_was_string = TRUE;
    1952                 :                 }
    1953                 :         }
    1954           17358 :         if (last_field_was_string) {
    1955                 :                 /* Normal queries: The buffer has one more byte at the end, because we need it */
    1956           16676 :                 row_buffer->ptr[data_size] = '\0';
    1957                 :         }
    1958                 : 
    1959                 :         DBG_VOID_RETURN;
    1960                 : }
    1961                 : /* }}} */
    1962                 : 
    1963                 : 
    1964                 : /* {{{ php_mysqlnd_rowp_read */
    1965                 : /*
    1966                 :   if normal statements => packet->fields is created by this function,
    1967                 :   if PS => packet->fields is passed from outside
    1968                 : */
    1969                 : static enum_func_status
    1970                 : php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC)
    1971           30496 : {
    1972           30496 :         MYSQLND_NET *net = &conn->net;
    1973                 :         zend_uchar *p;
    1974           30496 :         enum_func_status ret = PASS;
    1975           30496 :         size_t old_chunk_size = net->stream->chunk_size;
    1976           30496 :         php_mysql_packet_row *packet= (php_mysql_packet_row *) _packet;
    1977           30496 :         size_t post_alloc_for_bit_fields = 0;
    1978           30496 :         uint64_t data_size = 0;
    1979                 : 
    1980           30496 :         DBG_ENTER("php_mysqlnd_rowp_read");
    1981                 : 
    1982           30496 :         if (!packet->binary_protocol && packet->bit_fields_count) {
    1983                 :                 /* For every field we need terminating \0 */
    1984             965 :                 post_alloc_for_bit_fields =
    1985                 :                         packet->bit_fields_total_len + packet->bit_fields_count;
    1986                 :         }
    1987                 : 
    1988           30496 :         ret = php_mysqlnd_read_row_ex(conn, packet->result_set_memory_pool, &packet->row_buffer, &data_size,
    1989                 :                                                                   packet->persistent_alloc, post_alloc_for_bit_fields
    1990                 :                                                                   TSRMLS_CC);
    1991           30496 :         if (FAIL == ret) {
    1992               1 :                 goto end;
    1993                 :         }
    1994           30495 :         MYSQLND_INC_CONN_STATISTIC_W_VALUE2(&conn->stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
    1995                 :                                                                                 MYSQLND_HEADER_SIZE + packet->header.size,
    1996                 :                                                                                 packet_type_to_statistic_packet_count[PROT_ROW_PACKET],
    1997                 :                                                                                 1);
    1998                 : 
    1999                 :         /* packet->row_buffer->ptr is of size 'data_size + 1' */
    2000           30495 :         packet->header.size = data_size;
    2001           30495 :         packet->row_buffer->app = data_size;
    2002                 : 
    2003           30495 :         if ((*(p = packet->row_buffer->ptr)) == 0xFF) {
    2004                 :                 /*
    2005                 :                    Error message as part of the result set,
    2006                 :                    not good but we should not hang. See:
    2007                 :                    Bug #27876 : SF with cyrillic variable name fails during execution
    2008                 :                 */
    2009               0 :                 ret = FAIL;
    2010               0 :                 php_mysqlnd_read_error_from_line(p + 1, data_size - 1,
    2011                 :                                                                                  packet->error_info.error,
    2012                 :                                                                                  sizeof(packet->error_info.error),
    2013                 :                                                                                  &packet->error_info.error_no,
    2014                 :                                                                                  packet->error_info.sqlstate
    2015                 :                                                                                  TSRMLS_CC);
    2016           36547 :         } else if (*p == 0xFE && data_size < 8) { /* EOF */
    2017            6052 :                 packet->eof = TRUE;
    2018            6052 :                 p++;
    2019            6052 :                 if (data_size > 1) {
    2020            6052 :                         packet->warning_count = uint2korr(p);
    2021            6052 :                         p += 2;
    2022            6052 :                         packet->server_status = uint2korr(p);
    2023                 :                         /* Seems we have 3 bytes reserved for future use */
    2024            6052 :                         DBG_INF_FMT("server_status=%d warning_count=%d",
    2025                 :                                                 packet->server_status, packet->warning_count);
    2026                 :                 }
    2027                 :         } else {
    2028           24443 :                 MYSQLND_INC_CONN_STATISTIC(&conn->stats,
    2029                 :                                                                         packet->binary_protocol? STAT_ROWS_FETCHED_FROM_SERVER_PS:
    2030                 :                                                                                                                          STAT_ROWS_FETCHED_FROM_SERVER_NORMAL);
    2031                 : 
    2032           24443 :                 packet->eof = FALSE;
    2033                 :                 /* packet->field_count is set by the user of the packet */
    2034                 : 
    2035           24443 :                 if (!packet->skip_extraction) {
    2036            3613 :                         if (!packet->fields) {
    2037            3613 :                                 DBG_INF("Allocating packet->fields");
    2038                 :                                 /*
    2039                 :                                   old-API will probably set packet->fields to NULL every time, though for
    2040                 :                                   unbuffered sets it makes not much sense as the zvals in this buffer matter,
    2041                 :                                   not the buffer. Constantly allocating and deallocating brings nothing.
    2042                 : 
    2043                 :                                   For PS - if stmt_store() is performed, thus we don't have a cursor, it will
    2044                 :                                   behave just like old-API buffered. Cursors will behave like a bit different,
    2045                 :                                   but mostly like old-API unbuffered and thus will populate this array with
    2046                 :                                   value.
    2047                 :                                 */
    2048            3613 :                                 packet->fields = (zval **) mnd_pecalloc(packet->field_count, sizeof(zval *),
    2049                 :                                                                                                                 packet->persistent_alloc);
    2050                 :                         }
    2051                 :                 } else {
    2052           20830 :                         MYSQLND_INC_CONN_STATISTIC(&conn->stats,
    2053                 :                                                                                 packet->binary_protocol? STAT_ROWS_SKIPPED_PS:
    2054                 :                                                                                                                                  STAT_ROWS_SKIPPED_NORMAL);
    2055                 :                 }
    2056                 :         }
    2057                 : 
    2058           30496 : end:
    2059           30496 :         net->stream->chunk_size = old_chunk_size;
    2060           30496 :         DBG_RETURN(ret);
    2061                 : }
    2062                 : /* }}} */
    2063                 : 
    2064                 : 
    2065                 : /* {{{ php_mysqlnd_rowp_free_mem */
    2066                 : static
    2067                 : void php_mysqlnd_rowp_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
    2068            6036 : {
    2069                 :         php_mysql_packet_row *p;
    2070                 : 
    2071            6036 :         DBG_ENTER("php_mysqlnd_rowp_free_mem");
    2072            6036 :         p = (php_mysql_packet_row *) _packet;
    2073            6036 :         if (p->row_buffer) {
    2074            6028 :                 p->row_buffer->free_chunk(p->row_buffer, TRUE TSRMLS_CC);
    2075            6028 :                 p->row_buffer = NULL;
    2076                 :         }
    2077            6036 :         DBG_INF_FMT("alloca=%d persistent=%d", (int)alloca, (int)p->header.persistent);
    2078                 :         /*
    2079                 :           Don't free packet->fields :
    2080                 :           - normal queries -> store_result() | fetch_row_unbuffered() will transfer
    2081                 :                 the ownership and NULL it.
    2082                 :           - PS will pass in it the bound variables, we have to use them! and of course
    2083                 :                 not free the array. As it is passed to us, we should not clean it ourselves.
    2084                 :         */
    2085            6036 :         if (!alloca) {
    2086            6036 :                 mnd_pefree(p, p->header.persistent);
    2087                 :         }
    2088                 :         DBG_VOID_RETURN;
    2089                 : }
    2090                 : /* }}} */
    2091                 : 
    2092                 : 
    2093                 : 
    2094                 : /* {{{ php_mysqlnd_stats_read */
    2095                 : static enum_func_status
    2096                 : php_mysqlnd_stats_read(void *_packet, MYSQLND *conn TSRMLS_DC)
    2097               7 : {
    2098               7 :         php_mysql_packet_stats *packet= (php_mysql_packet_stats *) _packet;
    2099               7 :         size_t buf_len = conn->net.cmd_buffer.length;
    2100               7 :         zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
    2101                 : 
    2102               7 :         DBG_ENTER("php_mysqlnd_stats_read");
    2103                 : 
    2104               7 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "statistics", PROT_STATS_PACKET);
    2105                 : 
    2106               7 :         packet->message = mnd_emalloc(packet->header.size + 1);
    2107               7 :         memcpy(packet->message, buf, packet->header.size);
    2108               7 :         packet->message[packet->header.size] = '\0';
    2109               7 :         packet->message_len = packet->header.size;
    2110                 : 
    2111               7 :         DBG_RETURN(PASS);
    2112                 : }
    2113                 : /* }}} */
    2114                 : 
    2115                 : 
    2116                 : /* {{{ php_mysqlnd_stats_free_mem */
    2117                 : static
    2118                 : void php_mysqlnd_stats_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
    2119               7 : {
    2120               7 :         php_mysql_packet_stats *p= (php_mysql_packet_stats *) _packet;
    2121               7 :         if (p->message) {
    2122               0 :                 mnd_efree(p->message);
    2123               0 :                 p->message = NULL;
    2124                 :         }
    2125               7 :         if (!alloca) {
    2126               0 :                 mnd_pefree(p, p->header.persistent);
    2127                 :         }
    2128               7 : }
    2129                 : /* }}} */
    2130                 : 
    2131                 : 
    2132                 : /* 1 + 4 (id) + 2 (field_c) + 2 (param_c) + 1 (filler) + 2 (warnings ) */
    2133                 : #define PREPARE_RESPONSE_SIZE_41 9
    2134                 : #define PREPARE_RESPONSE_SIZE_50 12
    2135                 : 
    2136                 : /* {{{ php_mysqlnd_prepare_read */
    2137                 : static enum_func_status
    2138                 : php_mysqlnd_prepare_read(void *_packet, MYSQLND *conn TSRMLS_DC)
    2139            4133 : {
    2140                 :         /* In case of an error, we should have place to put it */
    2141            4133 :         size_t buf_len = conn->net.cmd_buffer.length;
    2142            4133 :         zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
    2143            4133 :         zend_uchar *p = buf;
    2144            4133 :         zend_uchar *begin = buf;
    2145                 :         unsigned int data_size;
    2146            4133 :         php_mysql_packet_prepare_response *packet= (php_mysql_packet_prepare_response *) _packet;
    2147                 : 
    2148            4133 :         DBG_ENTER("php_mysqlnd_prepare_read");
    2149                 : 
    2150            4133 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET);
    2151                 : 
    2152            4132 :         data_size = packet->header.size;
    2153            4132 :         packet->error_code = uint1korr(p);
    2154            4132 :         p++;
    2155                 : 
    2156            4132 :         if (0xFF == packet->error_code) {
    2157              26 :                 php_mysqlnd_read_error_from_line(p, data_size - 1,
    2158                 :                                                                                  packet->error_info.error,
    2159                 :                                                                                  sizeof(packet->error_info.error),
    2160                 :                                                                                  &packet->error_info.error_no,
    2161                 :                                                                                  packet->error_info.sqlstate
    2162                 :                                                                                  TSRMLS_CC);
    2163              26 :                 DBG_RETURN(PASS);
    2164                 :         }
    2165                 : 
    2166            4106 :         if (data_size != PREPARE_RESPONSE_SIZE_41 &&
    2167                 :                 data_size != PREPARE_RESPONSE_SIZE_50 &&
    2168                 :                 !(data_size > PREPARE_RESPONSE_SIZE_50)) {
    2169               0 :                 DBG_ERR_FMT("Wrong COM_STMT_PREPARE response size. Received %d", data_size);
    2170               0 :                 php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %d", data_size);
    2171               0 :                 DBG_RETURN(FAIL);
    2172                 :         }
    2173                 : 
    2174            4106 :         packet->stmt_id = uint4korr(p);
    2175            4106 :         p += 4;
    2176                 : 
    2177                 :         /* Number of columns in result set */
    2178            4106 :         packet->field_count = uint2korr(p);
    2179            4106 :         p += 2;
    2180                 : 
    2181            4106 :         packet->param_count = uint2korr(p);
    2182            4106 :         p += 2;
    2183                 : 
    2184            4106 :         if (data_size > 9) {
    2185                 :                 /* 0x0 filler sent by the server for 5.0+ clients */
    2186            4106 :                 p++;
    2187                 : 
    2188            4106 :                 packet->warning_count = uint2korr(p);
    2189                 :         }
    2190                 : 
    2191            4106 :         DBG_INF_FMT("Prepare packet read: stmt_id=%d fields=%d params=%d",
    2192                 :                                 packet->stmt_id, packet->field_count, packet->param_count);
    2193                 : 
    2194            4106 :         if (p - begin > packet->header.size) {
    2195               0 :                 DBG_ERR_FMT("PREPARE packet %d bytes shorter than expected", p - begin - packet->header.size);
    2196               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
    2197                 :                                                  p - begin - packet->header.size);
    2198                 :         }
    2199                 : 
    2200            4106 :         DBG_RETURN(PASS);
    2201                 : }
    2202                 : /* }}} */
    2203                 : 
    2204                 : 
    2205                 : /* {{{ php_mysqlnd_prepare_free_mem */
    2206                 : static
    2207                 : void php_mysqlnd_prepare_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
    2208            4106 : {
    2209            4106 :         php_mysql_packet_prepare_response *p= (php_mysql_packet_prepare_response *) _packet;
    2210            4106 :         if (!alloca) {
    2211               0 :                 mnd_pefree(p, p->header.persistent);
    2212                 :         }
    2213            4106 : }
    2214                 : /* }}} */
    2215                 : 
    2216                 : 
    2217                 : /* {{{ php_mysqlnd_chg_user_read */
    2218                 : static enum_func_status
    2219                 : php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC)
    2220              63 : {
    2221                 :         /* There could be an error message */
    2222              63 :         size_t buf_len = conn->net.cmd_buffer.length;
    2223              63 :         zend_uchar *buf = (zend_uchar *) conn->net.cmd_buffer.buffer;
    2224              63 :         zend_uchar *p = buf;
    2225              63 :         zend_uchar *begin = buf;
    2226              63 :         php_mysql_packet_chg_user_resp *packet= (php_mysql_packet_chg_user_resp *) _packet;
    2227                 : 
    2228              63 :         DBG_ENTER("php_mysqlnd_chg_user_read");
    2229                 : 
    2230              63 :         PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "change user response", PROT_CHG_USER_PACKET);
    2231                 : 
    2232                 :         /*
    2233                 :           Don't increment. First byte is 0xFF on error, but otherwise is starting byte
    2234                 :           of encoded sequence for length.
    2235                 :         */
    2236                 : 
    2237                 :         /* Should be always 0x0 or 0xFF for error */
    2238              63 :         packet->field_count= uint1korr(p);
    2239              63 :         p++;
    2240                 : 
    2241              63 :         if (packet->header.size == 1 && buf[0] == 0xFE &&
    2242                 :                 packet->server_capabilities & CLIENT_SECURE_CONNECTION) {
    2243                 :                 /* We don't handle 3.23 authentication */
    2244               0 :                 DBG_RETURN(FAIL);
    2245                 :         }
    2246                 : 
    2247              63 :         if (0xFF == packet->field_count) {
    2248              11 :                 php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
    2249                 :                                                                                  packet->error_info.error,
    2250                 :                                                                                  sizeof(packet->error_info.error),
    2251                 :                                                                                  &packet->error_info.error_no,
    2252                 :                                                                                  packet->error_info.sqlstate
    2253                 :                                                                                  TSRMLS_CC);
    2254                 :         }
    2255              63 :         if (p - begin > packet->header.size) {
    2256               0 :                 DBG_ERR_FMT("CHANGE_USER packet %d bytes shorter than expected", p - begin - packet->header.size);
    2257               0 :                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
    2258                 :                                                  p - begin - packet->header.size);
    2259                 :         }
    2260                 : 
    2261              63 :         DBG_RETURN(PASS);
    2262                 : }
    2263                 : /* }}} */
    2264                 : 
    2265                 : 
    2266                 : /* {{{ php_mysqlnd_chg_user_free_mem */
    2267                 : static
    2268                 : void php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool alloca TSRMLS_DC)
    2269              63 : {
    2270              63 :         if (!alloca) {
    2271               0 :                 mnd_pefree(_packet, ((php_mysql_packet_chg_user_resp *)_packet)->header.persistent);
    2272                 :         }
    2273              63 : }
    2274                 : /* }}} */
    2275                 : 
    2276                 : 
    2277                 : /* {{{ packet_methods
    2278                 :  */
    2279                 : mysqlnd_packet_methods packet_methods[PROT_LAST] =
    2280                 : {
    2281                 :         {
    2282                 :                 sizeof(php_mysql_packet_greet),
    2283                 :                 php_mysqlnd_greet_read,
    2284                 :                 NULL, /* write */
    2285                 :                 php_mysqlnd_greet_free_mem,
    2286                 :         }, /* PROT_GREET_PACKET */
    2287                 :         {
    2288                 :                 sizeof(php_mysql_packet_auth),
    2289                 :                 NULL, /* read */
    2290                 :                 php_mysqlnd_auth_write,
    2291                 :                 php_mysqlnd_auth_free_mem,
    2292                 :         }, /* PROT_AUTH_PACKET */
    2293                 :         {
    2294                 :                 sizeof(php_mysql_packet_ok),
    2295                 :                 php_mysqlnd_ok_read, /* read */
    2296                 :                 NULL, /* write */
    2297                 :                 php_mysqlnd_ok_free_mem,
    2298                 :         }, /* PROT_OK_PACKET */
    2299                 :         {
    2300                 :                 sizeof(php_mysql_packet_eof),
    2301                 :                 php_mysqlnd_eof_read, /* read */
    2302                 :                 NULL, /* write */
    2303                 :                 php_mysqlnd_eof_free_mem,
    2304                 :         }, /* PROT_EOF_PACKET */
    2305                 :         {
    2306                 :                 sizeof(php_mysql_packet_command),
    2307                 :                 NULL, /* read */
    2308                 :                 php_mysqlnd_cmd_write, /* write */
    2309                 :                 php_mysqlnd_cmd_free_mem,
    2310                 :         }, /* PROT_CMD_PACKET */
    2311                 :         {
    2312                 :                 sizeof(php_mysql_packet_rset_header),
    2313                 :                 php_mysqlnd_rset_header_read, /* read */
    2314                 :                 NULL, /* write */
    2315                 :                 php_mysqlnd_rset_header_free_mem,
    2316                 :         }, /* PROT_RSET_HEADER_PACKET */
    2317                 :         {
    2318                 :                 sizeof(php_mysql_packet_res_field),
    2319                 :                 php_mysqlnd_rset_field_read, /* read */
    2320                 :                 NULL, /* write */
    2321                 :                 php_mysqlnd_rset_field_free_mem,
    2322                 :         }, /* PROT_RSET_FLD_PACKET */
    2323                 :         {
    2324                 :                 sizeof(php_mysql_packet_row),
    2325                 :                 php_mysqlnd_rowp_read, /* read */
    2326                 :                 NULL, /* write */
    2327                 :                 php_mysqlnd_rowp_free_mem,
    2328                 :         }, /* PROT_ROW_PACKET */
    2329                 :         {
    2330                 :                 sizeof(php_mysql_packet_stats),
    2331                 :                 php_mysqlnd_stats_read, /* read */
    2332                 :                 NULL, /* write */
    2333                 :                 php_mysqlnd_stats_free_mem,
    2334                 :         }, /* PROT_STATS_PACKET */
    2335                 :         {
    2336                 :                 sizeof(php_mysql_packet_prepare_response),
    2337                 :                 php_mysqlnd_prepare_read, /* read */
    2338                 :                 NULL, /* write */
    2339                 :                 php_mysqlnd_prepare_free_mem,
    2340                 :         }, /* PROT_PREPARE_RESP_PACKET */
    2341                 :         {
    2342                 :                 sizeof(php_mysql_packet_chg_user_resp),
    2343                 :                 php_mysqlnd_chg_user_read, /* read */
    2344                 :                 NULL, /* write */
    2345                 :                 php_mysqlnd_chg_user_free_mem,
    2346                 :         } /* PROT_CHG_USER_PACKET */
    2347                 : };
    2348                 : /* }}} */
    2349                 : 
    2350                 : 
    2351                 : /*
    2352                 :  * Local variables:
    2353                 :  * tab-width: 4
    2354                 :  * c-basic-offset: 4
    2355                 :  * End:
    2356                 :  * vim600: noet sw=4 ts=4 fdm=marker
    2357                 :  * vim<600: noet sw=4 ts=4
    2358                 :  */

Generated by: LTP GCOV extension version 1.5

Generated at Mon, 23 Nov 2009 17:39:33 +0000 (33 hours ago)

Copyright © 2005-2009 The PHP Group
All rights reserved.