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-21 Instrumented lines: 769
Code covered: 84.0 % Executed lines: 646
Legend: not executed executed

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

Generated by: LTP GCOV extension version 1.5

Generated at Sat, 21 Nov 2009 12:27:03 +0000 (3 days ago)

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