Back to index

php5  5.3.10
mysqlnd_wireprotocol.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | PHP Version 5                                                        |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 2006-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt                                  |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Authors: Georg Richter <georg@mysql.com>                             |
00016   |          Andrey Hristov <andrey@mysql.com>                           |
00017   |          Ulf Wendel <uwendel@mysql.com>                              |
00018   +----------------------------------------------------------------------+
00019 */
00020 #include "php.h"
00021 #include "php_globals.h"
00022 #include "mysqlnd.h"
00023 #include "mysqlnd_priv.h"
00024 #include "mysqlnd_wireprotocol.h"
00025 #include "mysqlnd_statistics.h"
00026 #include "mysqlnd_debug.h"
00027 #include "ext/standard/sha1.h"
00028 #include "zend_ini.h"
00029 
00030 #define MYSQLND_SILENT 1
00031 
00032 #define MYSQLND_DUMP_HEADER_N_BODY
00033 
00034 #define       PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_size, packet_type_as_text, packet_type) \
00035        { \
00036               DBG_INF_FMT("buf=%p size=%u", (buf), (buf_size)); \
00037               if (FAIL == mysqlnd_read_header((conn), &((packet)->header) TSRMLS_CC)) {\
00038                      CONN_SET_STATE(conn, CONN_QUIT_SENT); \
00039                      SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
00040                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
00041                      DBG_ERR_FMT("Can't read %s's header", (packet_type_as_text)); \
00042                      DBG_RETURN(FAIL);\
00043               }\
00044               if ((buf_size) < (packet)->header.size) { \
00045                      DBG_ERR_FMT("Packet buffer %u wasn't big enough %u, %u bytes will be unread", \
00046                                           (buf_size), (packet)->header.size, (packet)->header.size - (buf_size)); \
00047                                           DBG_RETURN(FAIL); \
00048               }\
00049               if (FAIL == conn->net->m.receive((conn), (buf), (packet)->header.size TSRMLS_CC)) { \
00050                      CONN_SET_STATE(conn, CONN_QUIT_SENT); \
00051                      SET_CLIENT_ERROR(conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);\
00052                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", mysqlnd_server_gone); \
00053                      DBG_ERR_FMT("Empty '%s' packet body", (packet_type_as_text)); \
00054                      DBG_RETURN(FAIL);\
00055               } \
00056               MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[packet_type], \
00057                                                                              MYSQLND_HEADER_SIZE + (packet)->header.size, \
00058                                                                              packet_type_to_statistic_packet_count[packet_type], \
00059                                                                              1); \
00060        }
00061 
00062 
00063 #define BAIL_IF_NO_MORE_DATA \
00064        if ((size_t)(p - begin) > packet->header.size) { \
00065               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Premature end of data (mysqlnd_wireprotocol.c:%u)", __LINE__); \
00066               goto premature_end; \
00067        } \
00068 
00069 
00070 static const char *unknown_sqlstate= "HY000";
00071 
00072 const char * const mysqlnd_empty_string = "";
00073 
00074 /* Used in mysqlnd_debug.c */
00075 const char mysqlnd_read_header_name[]     = "mysqlnd_read_header";
00076 const char mysqlnd_read_body_name[]              = "mysqlnd_read_body";
00077 
00078 
00079 #define ERROR_MARKER 0xFF
00080 #define EODATA_MARKER 0xFE
00081 
00082 /* {{{ mysqlnd_command_to_text
00083  */
00084 const char * const mysqlnd_command_to_text[COM_END] =
00085 {
00086   "SLEEP", "QUIT", "INIT_DB", "QUERY", "FIELD_LIST",
00087   "CREATE_DB", "DROP_DB", "REFRESH", "SHUTDOWN", "STATISTICS",
00088   "PROCESS_INFO", "CONNECT", "PROCESS_KILL", "DEBUG", "PING",
00089   "TIME", "DELAYED_INSERT", "CHANGE_USER", "BINLOG_DUMP",
00090   "TABLE_DUMP", "CONNECT_OUT", "REGISTER_SLAVE",
00091   "STMT_PREPARE", "STMT_EXECUTE", "STMT_SEND_LONG_DATA", "STMT_CLOSE",
00092   "STMT_RESET", "SET_OPTION", "STMT_FETCH", "DAEMON"
00093 };
00094 /* }}} */
00095 
00096 
00097 
00098 static enum_mysqlnd_collected_stats packet_type_to_statistic_byte_count[PROT_LAST] =
00099 {
00100        STAT_LAST,
00101        STAT_LAST,
00102        STAT_BYTES_RECEIVED_OK,
00103        STAT_BYTES_RECEIVED_EOF,
00104        STAT_LAST,
00105        STAT_BYTES_RECEIVED_RSET_HEADER,
00106        STAT_BYTES_RECEIVED_RSET_FIELD_META,
00107        STAT_BYTES_RECEIVED_RSET_ROW,
00108        STAT_BYTES_RECEIVED_PREPARE_RESPONSE,
00109        STAT_BYTES_RECEIVED_CHANGE_USER,
00110 };
00111 
00112 static enum_mysqlnd_collected_stats packet_type_to_statistic_packet_count[PROT_LAST] =
00113 {
00114        STAT_LAST,
00115        STAT_LAST,
00116        STAT_PACKETS_RECEIVED_OK,
00117        STAT_PACKETS_RECEIVED_EOF,
00118        STAT_LAST,
00119        STAT_PACKETS_RECEIVED_RSET_HEADER,
00120        STAT_PACKETS_RECEIVED_RSET_FIELD_META,
00121        STAT_PACKETS_RECEIVED_RSET_ROW,
00122        STAT_PACKETS_RECEIVED_PREPARE_RESPONSE,
00123        STAT_PACKETS_RECEIVED_CHANGE_USER,
00124 };
00125 
00126 
00127 /* {{{ php_mysqlnd_net_field_length
00128    Get next field's length */
00129 unsigned long
00130 php_mysqlnd_net_field_length(zend_uchar **packet)
00131 {
00132        register zend_uchar *p= (zend_uchar *)*packet;
00133 
00134        if (*p < 251) {
00135               (*packet)++;
00136               return (unsigned long) *p;
00137        }
00138 
00139        switch (*p) {
00140               case 251:
00141                      (*packet)++;
00142                      return MYSQLND_NULL_LENGTH;
00143               case 252:
00144                      (*packet) += 3;
00145                      return (unsigned long) uint2korr(p+1);
00146               case 253:
00147                      (*packet) += 4;
00148                      return (unsigned long) uint3korr(p+1);
00149               default:
00150                      (*packet) += 9;
00151                      return (unsigned long) uint4korr(p+1);
00152        }
00153 }
00154 /* }}} */
00155 
00156 
00157 /* {{{ php_mysqlnd_net_field_length_ll
00158    Get next field's length */
00159 uint64_t
00160 php_mysqlnd_net_field_length_ll(zend_uchar **packet)
00161 {
00162        register zend_uchar *p= (zend_uchar *)*packet;
00163 
00164        if (*p < 251) {
00165               (*packet)++;
00166               return (uint64_t) *p;
00167        }
00168 
00169        switch (*p) {
00170               case 251:
00171                      (*packet)++;
00172                      return (uint64_t) MYSQLND_NULL_LENGTH;
00173               case 252:
00174                      (*packet) += 3;
00175                      return (uint64_t) uint2korr(p + 1);
00176               case 253:
00177                      (*packet) += 4;
00178                      return (uint64_t) uint3korr(p + 1);
00179               default:
00180                      (*packet) += 9;
00181                      return (uint64_t) uint8korr(p + 1);
00182        }
00183 }
00184 /* }}} */
00185 
00186 
00187 /* {{{ php_mysqlnd_net_store_length */
00188 zend_uchar *
00189 php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length)
00190 {
00191        if (length < (uint64_t) L64(251)) {
00192               *packet = (zend_uchar) length;
00193               return packet + 1;
00194        }
00195 
00196        if (length < (uint64_t) L64(65536)) {
00197               *packet++ = 252;
00198               int2store(packet,(unsigned int) length);
00199               return packet + 2;
00200        }
00201 
00202        if (length < (uint64_t) L64(16777216)) {
00203               *packet++ = 253;
00204               int3store(packet,(ulong) length);
00205               return packet + 3;
00206        }
00207        *packet++ = 254;
00208        int8store(packet, length);
00209        return packet + 8;
00210 }
00211 /* }}} */
00212 
00213 
00214 /* {{{ php_mysqlnd_read_error_from_line */
00215 static enum_func_status
00216 php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len,
00217                                                         char *error, int error_buf_len,
00218                                                         unsigned int *error_no, char *sqlstate TSRMLS_DC)
00219 {
00220        zend_uchar *p = buf;
00221        int error_msg_len= 0;
00222 
00223        DBG_ENTER("php_mysqlnd_read_error_from_line");
00224 
00225        *error_no = CR_UNKNOWN_ERROR;
00226        memcpy(sqlstate, unknown_sqlstate, MYSQLND_SQLSTATE_LENGTH);
00227 
00228        if (buf_len > 2) {
00229               *error_no = uint2korr(p);
00230               p+= 2;
00231               /*
00232                 sqlstate is following. No need to check for buf_left_len as we checked > 2 above,
00233                 if it was >=2 then we would need a check
00234               */
00235               if (*p == '#') {
00236                      ++p;
00237                      if ((buf_len - (p - buf)) >= MYSQLND_SQLSTATE_LENGTH) {
00238                             memcpy(sqlstate, p, MYSQLND_SQLSTATE_LENGTH);
00239                             p+= MYSQLND_SQLSTATE_LENGTH;
00240                      } else {
00241                             goto end;
00242                      }
00243               }
00244               if ((buf_len - (p - buf)) > 0) {
00245                      error_msg_len = MIN((int)((buf_len - (p - buf))), (int) (error_buf_len - 1));
00246                      memcpy(error, p, error_msg_len);
00247               }
00248        }
00249 end:
00250        sqlstate[MYSQLND_SQLSTATE_LENGTH] = '\0';
00251        error[error_msg_len]= '\0';
00252 
00253        DBG_RETURN(FAIL);
00254 }
00255 /* }}} */
00256 
00257 
00258 /* {{{ mysqlnd_read_header */
00259 static enum_func_status
00260 mysqlnd_read_header(MYSQLND * conn, MYSQLND_PACKET_HEADER * header TSRMLS_DC)
00261 {
00262        MYSQLND_NET * net = conn->net;
00263        zend_uchar buffer[MYSQLND_HEADER_SIZE];
00264 
00265        DBG_ENTER("mysqlnd_read_header_name");
00266        DBG_INF_FMT("compressed=%u conn_id=%u", net->compressed, conn->thread_id);
00267        if (FAIL == net->m.receive(conn, buffer, MYSQLND_HEADER_SIZE TSRMLS_CC)) {
00268               DBG_RETURN(FAIL);
00269        }
00270 
00271        header->size = uint3korr(buffer);
00272        header->packet_no = uint1korr(buffer + 3);
00273 
00274 #ifdef MYSQLND_DUMP_HEADER_N_BODY
00275        DBG_INF_FMT("HEADER: prot_packet_no=%u size=%3u", header->packet_no, header->size);
00276 #endif
00277        MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats,
00278                                                  STAT_PROTOCOL_OVERHEAD_IN, MYSQLND_HEADER_SIZE,
00279                                                  STAT_PACKETS_RECEIVED, 1);
00280 
00281        if (net->compressed || net->packet_no == header->packet_no) {
00282               /*
00283                 Have to increase the number, so we can send correct number back. It will
00284                 round at 255 as this is unsigned char. The server needs this for simple
00285                 flow control checking.
00286               */
00287               net->packet_no++;
00288               DBG_RETURN(PASS);
00289        }
00290 
00291        DBG_ERR_FMT("Logical link: packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
00292                             net->packet_no, header->packet_no, header->size);
00293 
00294        php_error(E_WARNING, "Packets out of order. Expected %u received %u. Packet size="MYSQLND_SZ_T_SPEC,
00295                        net->packet_no, header->packet_no, header->size);
00296        DBG_RETURN(FAIL);
00297 }
00298 /* }}} */
00299 
00300 
00301 /* {{{ php_mysqlnd_greet_read */
00302 static enum_func_status
00303 php_mysqlnd_greet_read(void *_packet, MYSQLND *conn TSRMLS_DC)
00304 {
00305        zend_uchar buf[2048];
00306        zend_uchar *p = buf;
00307        zend_uchar *begin = buf;
00308        MYSQLND_PACKET_GREET *packet= (MYSQLND_PACKET_GREET *) _packet;
00309 
00310        DBG_ENTER("php_mysqlnd_greet_read");
00311 
00312        PACKET_READ_HEADER_AND_BODY(packet, conn, buf, sizeof(buf), "greeting", PROT_GREET_PACKET);
00313        BAIL_IF_NO_MORE_DATA;
00314 
00315        packet->protocol_version = uint1korr(p);
00316        p++;
00317        BAIL_IF_NO_MORE_DATA;
00318 
00319        if (ERROR_MARKER == packet->protocol_version) {
00320               php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
00321                                                                        packet->error, sizeof(packet->error),
00322                                                                        &packet->error_no, packet->sqlstate
00323                                                                        TSRMLS_CC);
00324               /*
00325                 The server doesn't send sqlstate in the greet packet.
00326                 It's a bug#26426 , so we have to set it correctly ourselves.
00327                 It's probably "Too many connections, which has SQL state 08004".
00328               */
00329               if (packet->error_no == 1040) {
00330                      memcpy(packet->sqlstate, "08004", MYSQLND_SQLSTATE_LENGTH);
00331               }
00332               DBG_RETURN(PASS);
00333        }
00334 
00335        packet->server_version = estrdup((char *)p);
00336        p+= strlen(packet->server_version) + 1; /* eat the '\0' */
00337        BAIL_IF_NO_MORE_DATA;
00338 
00339        packet->thread_id = uint4korr(p);
00340        p+=4;
00341        BAIL_IF_NO_MORE_DATA;
00342 
00343        memcpy(packet->scramble_buf, p, SCRAMBLE_LENGTH_323);
00344        p+= 8;
00345        BAIL_IF_NO_MORE_DATA;
00346 
00347        /* pad1 */
00348        p++;
00349        BAIL_IF_NO_MORE_DATA;
00350 
00351        packet->server_capabilities = uint2korr(p);
00352        p+= 2;
00353        BAIL_IF_NO_MORE_DATA;
00354 
00355        packet->charset_no = uint1korr(p);
00356        p++;
00357        BAIL_IF_NO_MORE_DATA;
00358 
00359        packet->server_status = uint2korr(p);
00360        p+= 2;
00361        BAIL_IF_NO_MORE_DATA;
00362 
00363        /* pad2 */
00364        p+= 13;
00365        BAIL_IF_NO_MORE_DATA;
00366 
00367        if ((size_t) (p - buf) < packet->header.size) {
00368               /* scramble_buf is split into two parts */
00369               memcpy(packet->scramble_buf + SCRAMBLE_LENGTH_323,
00370                             p, SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323);
00371        } else {
00372               packet->pre41 = TRUE;
00373        }
00374 
00375        DBG_INF_FMT("proto=%u server=%s thread_id=%u",
00376                             packet->protocol_version, packet->server_version, packet->thread_id);
00377 
00378        DBG_INF_FMT("server_capabilities=%u charset_no=%u server_status=%i",
00379                             packet->server_capabilities, packet->charset_no, packet->server_status);
00380 
00381        DBG_RETURN(PASS);
00382 premature_end:
00383        DBG_ERR_FMT("GREET packet %d bytes shorter than expected", p - begin - packet->header.size);
00384        php_error_docref(NULL TSRMLS_CC, E_WARNING, "GREET packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
00385                                     p - begin - packet->header.size);
00386        DBG_RETURN(FAIL);
00387 }
00388 /* }}} */
00389 
00390 
00391 /* {{{ php_mysqlnd_greet_free_mem */
00392 static
00393 void php_mysqlnd_greet_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
00394 {
00395        MYSQLND_PACKET_GREET *p= (MYSQLND_PACKET_GREET *) _packet;
00396        if (p->server_version) {
00397               efree(p->server_version);
00398               p->server_version = NULL;
00399        }
00400        if (!stack_allocation) {
00401               mnd_pefree(p, p->header.persistent);
00402        }
00403 }
00404 /* }}} */
00405 
00406 
00407 /* {{{ php_mysqlnd_crypt */
00408 static void
00409 php_mysqlnd_crypt(zend_uchar *buffer, const zend_uchar *s1, const zend_uchar *s2, size_t len)
00410 {
00411        const zend_uchar *s1_end = s1 + len;
00412        while (s1 < s1_end) {
00413               *buffer++= *s1++ ^ *s2++;
00414        }
00415 }
00416 /* }}} */
00417 
00418 
00419 /* {{{ php_mysqlnd_scramble */
00420 void php_mysqlnd_scramble(zend_uchar * const buffer, const zend_uchar * const scramble, const zend_uchar * const password)
00421 {
00422        PHP_SHA1_CTX context;
00423        zend_uchar sha1[SHA1_MAX_LENGTH];
00424        zend_uchar sha2[SHA1_MAX_LENGTH];
00425 
00426 
00427        /* Phase 1: hash password */
00428        PHP_SHA1Init(&context);
00429        PHP_SHA1Update(&context, password, strlen((char *)password));
00430        PHP_SHA1Final(sha1, &context);
00431 
00432        /* Phase 2: hash sha1 */
00433        PHP_SHA1Init(&context);
00434        PHP_SHA1Update(&context, (zend_uchar*)sha1, SHA1_MAX_LENGTH);
00435        PHP_SHA1Final(sha2, &context);
00436 
00437        /* Phase 3: hash scramble + sha2 */
00438        PHP_SHA1Init(&context);
00439        PHP_SHA1Update(&context, scramble, SCRAMBLE_LENGTH);
00440        PHP_SHA1Update(&context, (zend_uchar*)sha2, SHA1_MAX_LENGTH);
00441        PHP_SHA1Final(buffer, &context);
00442 
00443        /* let's crypt buffer now */
00444        php_mysqlnd_crypt(buffer, (const zend_uchar *)buffer, (const zend_uchar *)sha1, SHA1_MAX_LENGTH);
00445 }
00446 /* }}} */
00447 
00448 
00449 #define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SHA1_MAX_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 128)
00450 
00451 /* {{{ php_mysqlnd_auth_write */
00452 static
00453 size_t php_mysqlnd_auth_write(void *_packet, MYSQLND * conn TSRMLS_DC)
00454 {
00455        char buffer[AUTH_WRITE_BUFFER_LEN];
00456        register char *p= buffer + MYSQLND_HEADER_SIZE; /* start after the header */
00457        int len;
00458        register MYSQLND_PACKET_AUTH *packet= (MYSQLND_PACKET_AUTH *) _packet;
00459 
00460        DBG_ENTER("php_mysqlnd_auth_write");
00461 
00462        int4store(p, packet->client_flags);
00463        p+= 4;
00464 
00465        int4store(p, packet->max_packet_size);
00466        p+= 4;
00467 
00468        int1store(p, packet->charset_no);
00469        p++;
00470 
00471        memset(p, 0, 23); /* filler */
00472        p+= 23;
00473 
00474        if (!packet->send_half_packet) {
00475               len = MIN(strlen(packet->user), MYSQLND_MAX_ALLOWED_USER_LEN);
00476               memcpy(p, packet->user, len);
00477               p+= len;
00478               *p++ = '\0';
00479 
00480               /* copy scrambled pass*/
00481               if (packet->password && packet->password[0]) {
00482                      /* In 4.1 we use CLIENT_SECURE_CONNECTION and thus the len of the buf should be passed */
00483                      int1store(p, SHA1_MAX_LENGTH);
00484                      p++;
00485                      php_mysqlnd_scramble((zend_uchar*)p, packet->server_scramble_buf, (zend_uchar*)packet->password);
00486                      p+= SHA1_MAX_LENGTH;
00487               } else {
00488                      /* Zero length */
00489                      int1store(p, 0);
00490                      p++;
00491               }
00492 
00493               if (packet->db) {
00494                      size_t real_db_len = MIN(MYSQLND_MAX_ALLOWED_DB_LEN, packet->db_len);
00495                      memcpy(p, packet->db, real_db_len);
00496                      p+= real_db_len;
00497                      *p++= '\0';
00498               }
00499               /* Handle CLIENT_CONNECT_WITH_DB */
00500               /* no \0 for no DB */
00501        }
00502 
00503        DBG_RETURN(conn->net->m.send(conn, buffer, p - buffer - MYSQLND_HEADER_SIZE TSRMLS_CC));
00504 }
00505 /* }}} */
00506 
00507 
00508 /* {{{ php_mysqlnd_auth_free_mem */
00509 static
00510 void php_mysqlnd_auth_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
00511 {
00512        if (!stack_allocation) {
00513               MYSQLND_PACKET_AUTH * p = (MYSQLND_PACKET_AUTH *) _packet;
00514               mnd_pefree(p, p->header.persistent);
00515        }
00516 }
00517 /* }}} */
00518 
00519 
00520 #define OK_BUFFER_SIZE 2048
00521 
00522 /* {{{ php_mysqlnd_ok_read */
00523 static enum_func_status
00524 php_mysqlnd_ok_read(void *_packet, MYSQLND *conn TSRMLS_DC)
00525 {
00526        zend_uchar local_buf[OK_BUFFER_SIZE];
00527        size_t buf_len = conn->net->cmd_buffer.buffer? conn->net->cmd_buffer.length : OK_BUFFER_SIZE;
00528        zend_uchar *buf = conn->net->cmd_buffer.buffer? (zend_uchar *) conn->net->cmd_buffer.buffer : local_buf;
00529        zend_uchar *p = buf;
00530        zend_uchar *begin = buf;
00531        unsigned long i;
00532        register MYSQLND_PACKET_OK *packet= (MYSQLND_PACKET_OK *) _packet;
00533 
00534        DBG_ENTER("php_mysqlnd_ok_read");
00535 
00536        PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "OK", PROT_OK_PACKET);
00537        BAIL_IF_NO_MORE_DATA;
00538 
00539        /* Should be always 0x0 or ERROR_MARKER for error */
00540        packet->field_count = uint1korr(p);
00541        p++;
00542        BAIL_IF_NO_MORE_DATA;
00543 
00544        if (ERROR_MARKER == packet->field_count) {
00545               php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
00546                                                                        packet->error, sizeof(packet->error),
00547                                                                        &packet->error_no, packet->sqlstate
00548                                                                        TSRMLS_CC);
00549               DBG_RETURN(PASS);
00550        }
00551        /* Everything was fine! */
00552        packet->affected_rows  = php_mysqlnd_net_field_length_ll(&p);
00553        BAIL_IF_NO_MORE_DATA;
00554 
00555        packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
00556        BAIL_IF_NO_MORE_DATA;
00557 
00558        packet->server_status = uint2korr(p);
00559        p+= 2;
00560        BAIL_IF_NO_MORE_DATA;
00561 
00562        packet->warning_count = uint2korr(p);
00563        p+= 2;
00564        BAIL_IF_NO_MORE_DATA;
00565 
00566        /* There is a message */
00567        if (packet->header.size > (size_t) (p - buf) && (i = php_mysqlnd_net_field_length(&p))) {
00568               packet->message_len = MIN(i, buf_len - (p - begin));
00569               packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
00570        } else {
00571               packet->message = NULL;
00572               packet->message_len = 0;
00573        }
00574 
00575        DBG_INF_FMT("OK packet: aff_rows=%lld last_ins_id=%ld server_status=%u warnings=%u",
00576                             packet->affected_rows, packet->last_insert_id, packet->server_status,
00577                             packet->warning_count);
00578 
00579        BAIL_IF_NO_MORE_DATA;
00580 
00581        DBG_RETURN(PASS);
00582 premature_end:
00583        DBG_ERR_FMT("OK packet %d bytes shorter than expected", p - begin - packet->header.size);
00584        php_error_docref(NULL TSRMLS_CC, E_WARNING, "OK packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
00585                                     p - begin - packet->header.size);
00586        DBG_RETURN(FAIL);
00587 }
00588 /* }}} */
00589 
00590 
00591 /* {{{ php_mysqlnd_ok_free_mem */
00592 static void
00593 php_mysqlnd_ok_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
00594 {
00595        MYSQLND_PACKET_OK *p= (MYSQLND_PACKET_OK *) _packet;
00596        if (p->message) {
00597               mnd_efree(p->message);
00598               p->message = NULL;
00599        }
00600        if (!stack_allocation) {
00601               mnd_pefree(p, p->header.persistent);
00602        }
00603 }
00604 /* }}} */
00605 
00606 
00607 /* {{{ php_mysqlnd_eof_read */
00608 static enum_func_status
00609 php_mysqlnd_eof_read(void *_packet, MYSQLND *conn TSRMLS_DC)
00610 {
00611        /*
00612          EOF packet is since 4.1 five bytes long,
00613          but we can get also an error, make it bigger.
00614 
00615          Error : error_code + '#' + sqlstate + MYSQLND_ERRMSG_SIZE
00616        */
00617        MYSQLND_PACKET_EOF *packet= (MYSQLND_PACKET_EOF *) _packet;
00618        size_t buf_len = conn->net->cmd_buffer.length;
00619        zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
00620        zend_uchar *p = buf;
00621        zend_uchar *begin = buf;
00622 
00623        DBG_ENTER("php_mysqlnd_eof_read");
00624 
00625        PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "EOF", PROT_EOF_PACKET);
00626        BAIL_IF_NO_MORE_DATA;
00627 
00628        /* Should be always EODATA_MARKER */
00629        packet->field_count = uint1korr(p);
00630        p++;
00631        BAIL_IF_NO_MORE_DATA;
00632 
00633        if (ERROR_MARKER == packet->field_count) {
00634               php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
00635                                                                        packet->error, sizeof(packet->error),
00636                                                                        &packet->error_no, packet->sqlstate
00637                                                                        TSRMLS_CC);
00638               DBG_RETURN(PASS);
00639        }
00640 
00641        /*
00642               4.1 sends 1 byte EOF packet after metadata of
00643               PREPARE/EXECUTE but 5 bytes after the result. This is not
00644               according to the Docs@Forge!!!
00645        */
00646        if (packet->header.size > 1) {
00647               packet->warning_count = uint2korr(p);
00648               p+= 2;
00649               BAIL_IF_NO_MORE_DATA;
00650 
00651               packet->server_status = uint2korr(p);
00652               p+= 2;
00653               BAIL_IF_NO_MORE_DATA;
00654        } else {
00655               packet->warning_count = 0;
00656               packet->server_status = 0;
00657        }
00658 
00659        BAIL_IF_NO_MORE_DATA;
00660 
00661        DBG_INF_FMT("EOF packet: fields=%u status=%u warnings=%u",
00662                             packet->field_count, packet->server_status, packet->warning_count);
00663 
00664        DBG_RETURN(PASS);
00665 premature_end:
00666        DBG_ERR_FMT("EOF packet %d bytes shorter than expected", p - begin - packet->header.size);
00667        php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
00668                                     p - begin - packet->header.size);
00669        DBG_RETURN(FAIL);
00670 }
00671 /* }}} */
00672 
00673 
00674 /* {{{ php_mysqlnd_eof_free_mem */
00675 static
00676 void php_mysqlnd_eof_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
00677 {
00678        if (!stack_allocation) {
00679               mnd_pefree(_packet, ((MYSQLND_PACKET_EOF *)_packet)->header.persistent);
00680        }
00681 }
00682 /* }}} */
00683 
00684 
00685 /* {{{ php_mysqlnd_cmd_write */
00686 size_t php_mysqlnd_cmd_write(void *_packet, MYSQLND *conn TSRMLS_DC)
00687 {
00688        /* Let's have some space, which we can use, if not enough, we will allocate new buffer */
00689        MYSQLND_PACKET_COMMAND *packet= (MYSQLND_PACKET_COMMAND *) _packet;
00690        MYSQLND_NET *net = conn->net;
00691        unsigned int error_reporting = EG(error_reporting);
00692        size_t written = 0;
00693 
00694        DBG_ENTER("php_mysqlnd_cmd_write");
00695        /*
00696          Reset packet_no, or we will get bad handshake!
00697          Every command starts a new TX and packet numbers are reset to 0.
00698        */
00699        net->packet_no = 0;
00700        net->compressed_envelope_packet_no = 0; /* this is for the response */
00701 
00702        if (error_reporting) {
00703               EG(error_reporting) = 0;
00704        }
00705 
00706        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_PACKETS_SENT_CMD);
00707 
00708 #ifdef MYSQLND_DO_WIRE_CHECK_BEFORE_COMMAND
00709        net->m.consume_uneaten_data(net, packet->command TSRMLS_CC);
00710 #endif
00711 
00712        if (!packet->argument || !packet->arg_len) {
00713               char buffer[MYSQLND_HEADER_SIZE + 1];
00714 
00715               int1store(buffer + MYSQLND_HEADER_SIZE, packet->command);
00716               written = conn->net->m.send(conn, buffer, 1 TSRMLS_CC);
00717        } else {
00718               size_t tmp_len = packet->arg_len + 1 + MYSQLND_HEADER_SIZE, ret;
00719               zend_uchar *tmp, *p;
00720               tmp = (tmp_len > net->cmd_buffer.length)? mnd_emalloc(tmp_len):net->cmd_buffer.buffer;
00721               if (!tmp) {
00722                      goto end;
00723               }
00724               p = tmp + MYSQLND_HEADER_SIZE; /* skip the header */
00725 
00726               int1store(p, packet->command);
00727               p++;
00728 
00729               memcpy(p, packet->argument, packet->arg_len);
00730 
00731               ret = conn->net->m.send(conn, (char *)tmp, tmp_len - MYSQLND_HEADER_SIZE TSRMLS_CC);
00732               if (tmp != net->cmd_buffer.buffer) {
00733                      MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CMD_BUFFER_TOO_SMALL);
00734                      mnd_efree(tmp);
00735               }
00736               written = ret;
00737        }
00738 end:
00739        if (error_reporting) {
00740               /* restore error reporting */
00741               EG(error_reporting) = error_reporting;
00742        }
00743        DBG_RETURN(written);
00744 }
00745 /* }}} */
00746 
00747 
00748 /* {{{ php_mysqlnd_cmd_free_mem */
00749 static
00750 void php_mysqlnd_cmd_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
00751 {
00752        if (!stack_allocation) {
00753               MYSQLND_PACKET_COMMAND * p = (MYSQLND_PACKET_COMMAND *) _packet;
00754               mnd_pefree(p, p->header.persistent);
00755        }
00756 }
00757 /* }}} */
00758 
00759 
00760 /* {{{ php_mysqlnd_rset_header_read */
00761 static enum_func_status
00762 php_mysqlnd_rset_header_read(void *_packet, MYSQLND *conn TSRMLS_DC)
00763 {
00764        enum_func_status ret = PASS;
00765        size_t buf_len = conn->net->cmd_buffer.length;
00766        zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
00767        zend_uchar *p = buf;
00768        zend_uchar *begin = buf;
00769        size_t len;
00770        MYSQLND_PACKET_RSET_HEADER *packet= (MYSQLND_PACKET_RSET_HEADER *) _packet;
00771 
00772        DBG_ENTER("php_mysqlnd_rset_header_read");
00773 
00774        PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "resultset header", PROT_RSET_HEADER_PACKET);
00775        BAIL_IF_NO_MORE_DATA;
00776 
00777        /*
00778          Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte
00779          of encoded sequence for length.
00780        */
00781        if (ERROR_MARKER == *p) {
00782               /* Error */
00783               p++;
00784               BAIL_IF_NO_MORE_DATA;
00785               php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
00786                                                                        packet->error_info.error, sizeof(packet->error_info.error),
00787                                                                        &packet->error_info.error_no, packet->error_info.sqlstate
00788                                                                        TSRMLS_CC);
00789               DBG_RETURN(PASS);
00790        }
00791 
00792        packet->field_count = php_mysqlnd_net_field_length(&p);
00793        BAIL_IF_NO_MORE_DATA;
00794 
00795        switch (packet->field_count) {
00796               case MYSQLND_NULL_LENGTH:
00797                      DBG_INF("LOAD LOCAL");
00798                      /*
00799                        First byte in the packet is the field count.
00800                        Thus, the name is size - 1. And we add 1 for a trailing \0.
00801                        Because we have BAIL_IF_NO_MORE_DATA before the switch, we are guaranteed
00802                        that packet->header.size is > 0. Which means that len can't underflow, that
00803                        would lead to 0 byte allocation but 2^32 or 2^64 bytes copied.
00804                      */
00805                      len = packet->header.size - 1;
00806                      packet->info_or_local_file = mnd_emalloc(len + 1);
00807                      if (packet->info_or_local_file) {
00808                             memcpy(packet->info_or_local_file, p, len);
00809                             packet->info_or_local_file[len] = '\0';
00810                             packet->info_or_local_file_len = len;
00811                      } else {
00812                             SET_OOM_ERROR(conn->error_info);
00813                             ret = FAIL;
00814                      }
00815                      break;
00816               case 0x00:
00817                      DBG_INF("UPSERT");
00818                      packet->affected_rows = php_mysqlnd_net_field_length_ll(&p);
00819                      BAIL_IF_NO_MORE_DATA;
00820 
00821                      packet->last_insert_id = php_mysqlnd_net_field_length_ll(&p);
00822                      BAIL_IF_NO_MORE_DATA;
00823 
00824                      packet->server_status = uint2korr(p);
00825                      p+=2;
00826                      BAIL_IF_NO_MORE_DATA;
00827 
00828                      packet->warning_count = uint2korr(p);
00829                      p+=2;
00830                      BAIL_IF_NO_MORE_DATA;
00831                      /* Check for additional textual data */
00832                      if (packet->header.size  > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
00833                             packet->info_or_local_file = mnd_emalloc(len + 1);
00834                             if (packet->info_or_local_file) {
00835                                    memcpy(packet->info_or_local_file, p, len);
00836                                    packet->info_or_local_file[len] = '\0';
00837                                    packet->info_or_local_file_len = len;
00838                             } else {
00839                                    SET_OOM_ERROR(conn->error_info);
00840                                    ret = FAIL;
00841                             }
00842                      }
00843                      DBG_INF_FMT("affected_rows=%llu last_insert_id=%llu server_status=%u warning_count=%u",
00844                                           packet->affected_rows, packet->last_insert_id,
00845                                           packet->server_status, packet->warning_count);
00846                      break;
00847               default:
00848                      DBG_INF("SELECT");
00849                      /* Result set */
00850                      break;
00851        }
00852        BAIL_IF_NO_MORE_DATA;
00853 
00854        DBG_RETURN(ret);
00855 premature_end:
00856        DBG_ERR_FMT("RSET_HEADER packet %d bytes shorter than expected", p - begin - packet->header.size);
00857        php_error_docref(NULL TSRMLS_CC, E_WARNING, "RSET_HEADER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
00858                                     p - begin - packet->header.size);
00859        DBG_RETURN(FAIL);
00860 }
00861 /* }}} */
00862 
00863 
00864 /* {{{ php_mysqlnd_rset_header_free_mem */
00865 static
00866 void php_mysqlnd_rset_header_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
00867 {
00868        MYSQLND_PACKET_RSET_HEADER *p= (MYSQLND_PACKET_RSET_HEADER *) _packet;
00869        DBG_ENTER("php_mysqlnd_rset_header_free_mem");
00870        if (p->info_or_local_file) {
00871               mnd_efree(p->info_or_local_file);
00872               p->info_or_local_file = NULL;
00873        }
00874        if (!stack_allocation) {
00875               mnd_pefree(p, p->header.persistent);
00876        }
00877        DBG_VOID_RETURN;
00878 }
00879 /* }}} */
00880 
00881 static size_t rset_field_offsets[] =
00882 {
00883        STRUCT_OFFSET(MYSQLND_FIELD, catalog),
00884        STRUCT_OFFSET(MYSQLND_FIELD, catalog_length),
00885        STRUCT_OFFSET(MYSQLND_FIELD, db),
00886        STRUCT_OFFSET(MYSQLND_FIELD, db_length),
00887        STRUCT_OFFSET(MYSQLND_FIELD, table),
00888        STRUCT_OFFSET(MYSQLND_FIELD, table_length),
00889        STRUCT_OFFSET(MYSQLND_FIELD, org_table),
00890        STRUCT_OFFSET(MYSQLND_FIELD, org_table_length),
00891        STRUCT_OFFSET(MYSQLND_FIELD, name),
00892        STRUCT_OFFSET(MYSQLND_FIELD, name_length),
00893        STRUCT_OFFSET(MYSQLND_FIELD, org_name),
00894        STRUCT_OFFSET(MYSQLND_FIELD, org_name_length)
00895 };
00896 
00897 
00898 /* {{{ php_mysqlnd_rset_field_read */
00899 static enum_func_status
00900 php_mysqlnd_rset_field_read(void *_packet, MYSQLND *conn TSRMLS_DC)
00901 {
00902        /* Should be enough for the metadata of a single row */
00903        MYSQLND_PACKET_RES_FIELD *packet= (MYSQLND_PACKET_RES_FIELD *) _packet;
00904        size_t buf_len = conn->net->cmd_buffer.length, total_len = 0;
00905        zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
00906        zend_uchar *p = buf;
00907        zend_uchar *begin = buf;
00908        char *root_ptr;
00909        unsigned long len;
00910        MYSQLND_FIELD *meta;
00911        unsigned int i, field_count = sizeof(rset_field_offsets)/sizeof(size_t);
00912 
00913        DBG_ENTER("php_mysqlnd_rset_field_read");
00914 
00915        PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "field", PROT_RSET_FLD_PACKET);
00916 
00917        if (packet->skip_parsing) {
00918               DBG_RETURN(PASS);
00919        }
00920 
00921        BAIL_IF_NO_MORE_DATA;
00922        if (ERROR_MARKER == *p) {
00923               /* Error */
00924               p++;
00925               BAIL_IF_NO_MORE_DATA;
00926               php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
00927                                                                        packet->error_info.error, sizeof(packet->error_info.error),
00928                                                                        &packet->error_info.error_no, packet->error_info.sqlstate
00929                                                                        TSRMLS_CC);
00930               DBG_ERR_FMT("Server error : (%u) %s", packet->error_info.error_no, packet->error_info.error);
00931               DBG_RETURN(PASS);
00932        } else if (EODATA_MARKER == *p && packet->header.size < 8) {
00933               /* Premature EOF. That should be COM_FIELD_LIST */
00934               DBG_INF("Premature EOF. That should be COM_FIELD_LIST");
00935               packet->stupid_list_fields_eof = TRUE;
00936               DBG_RETURN(PASS);
00937        }
00938 
00939        meta = packet->metadata;
00940 
00941        for (i = 0; i < field_count; i += 2) {
00942               len = php_mysqlnd_net_field_length(&p);
00943               BAIL_IF_NO_MORE_DATA;
00944               switch ((len)) {
00945                      case 0:
00946                             *(const char **)(((char*)meta) + rset_field_offsets[i]) = mysqlnd_empty_string;
00947                             *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = 0;
00948                             break;
00949                      case MYSQLND_NULL_LENGTH:
00950                             goto faulty_or_fake;
00951                      default:
00952                             *(const char **)(((char *)meta) + rset_field_offsets[i]) = (const char *)p;
00953                             *(unsigned int *)(((char*)meta) + rset_field_offsets[i+1]) = len;
00954                             p += len;
00955                             total_len += len + 1;
00956                             break;
00957               }
00958               BAIL_IF_NO_MORE_DATA;
00959        }
00960 
00961        /* 1 byte filler */
00962        p++;
00963        BAIL_IF_NO_MORE_DATA;
00964 
00965        meta->charsetnr = uint2korr(p);
00966        p += 2;
00967        BAIL_IF_NO_MORE_DATA;
00968 
00969        meta->length = uint4korr(p);
00970        p += 4;
00971        BAIL_IF_NO_MORE_DATA;
00972 
00973        meta->type = uint1korr(p);
00974        p += 1;
00975        BAIL_IF_NO_MORE_DATA;
00976 
00977        meta->flags = uint2korr(p);
00978        p += 2;
00979        BAIL_IF_NO_MORE_DATA;
00980 
00981        meta->decimals = uint2korr(p);
00982        p += 1;
00983        BAIL_IF_NO_MORE_DATA;
00984 
00985        /* 2 byte filler */
00986        p +=2;
00987        BAIL_IF_NO_MORE_DATA;
00988 
00989        /* Should we set NUM_FLAG (libmysql does it) ? */
00990        if (
00991               (meta->type <= MYSQL_TYPE_INT24 &&
00992                      (meta->type != MYSQL_TYPE_TIMESTAMP || meta->length == 14 || meta->length == 8)
00993               ) || meta->type == MYSQL_TYPE_YEAR)
00994        {
00995               meta->flags |= NUM_FLAG;
00996        }
00997 
00998 
00999        /*
01000          def could be empty, thus don't allocate on the root.
01001          NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL.
01002          Otherwise the string is length encoded.
01003        */
01004        if (packet->header.size > (size_t) (p - buf) &&
01005               (len = php_mysqlnd_net_field_length(&p)) &&
01006               len != MYSQLND_NULL_LENGTH)
01007        {
01008               BAIL_IF_NO_MORE_DATA;
01009               DBG_INF_FMT("Def found, length %lu, persistent=%u", len, packet->persistent_alloc);
01010               meta->def = mnd_pemalloc(len + 1, packet->persistent_alloc);
01011               if (!meta->def) {
01012                      SET_OOM_ERROR(conn->error_info);
01013                      DBG_RETURN(FAIL);
01014               }
01015               memcpy(meta->def, p, len);
01016               meta->def[len] = '\0';
01017               meta->def_length = len;
01018               p += len;
01019        }
01020 
01021        DBG_INF_FMT("allocing root. persistent=%u", packet->persistent_alloc);
01022        root_ptr = meta->root = mnd_pemalloc(total_len, packet->persistent_alloc);
01023        if (!root_ptr) {
01024               SET_OOM_ERROR(conn->error_info);
01025               DBG_RETURN(FAIL);
01026        }
01027 
01028        meta->root_len = total_len;
01029        /* Now do allocs */
01030        if (meta->catalog && meta->catalog != mysqlnd_empty_string) {
01031               len = meta->catalog_length;
01032               meta->catalog = memcpy(root_ptr, meta->catalog, len);
01033               *(root_ptr +=len) = '\0';
01034               root_ptr++;
01035        }
01036 
01037        if (meta->db && meta->db != mysqlnd_empty_string) {
01038               len = meta->db_length;
01039               meta->db = memcpy(root_ptr, meta->db, len);
01040               *(root_ptr +=len) = '\0';
01041               root_ptr++;
01042        }
01043 
01044        if (meta->table && meta->table != mysqlnd_empty_string) {
01045               len = meta->table_length;
01046               meta->table = memcpy(root_ptr, meta->table, len);
01047               *(root_ptr +=len) = '\0';
01048               root_ptr++;
01049        }
01050 
01051        if (meta->org_table && meta->org_table != mysqlnd_empty_string) {
01052               len = meta->org_table_length;
01053               meta->org_table = memcpy(root_ptr, meta->org_table, len);
01054               *(root_ptr +=len) = '\0';
01055               root_ptr++;
01056        }
01057 
01058        if (meta->name && meta->name != mysqlnd_empty_string) {
01059               len = meta->name_length;
01060               meta->name = memcpy(root_ptr, meta->name, len);
01061               *(root_ptr +=len) = '\0';
01062               root_ptr++;
01063        }
01064 
01065        if (meta->org_name && meta->org_name != mysqlnd_empty_string) {
01066               len = meta->org_name_length;
01067               meta->org_name = memcpy(root_ptr, meta->org_name, len);
01068               *(root_ptr +=len) = '\0';
01069               root_ptr++;
01070        }
01071 
01072        DBG_INF_FMT("FIELD=[%s.%s.%s]", meta->db? meta->db:"*NA*", meta->table? meta->table:"*NA*",
01073                             meta->name? meta->name:"*NA*");
01074 
01075        DBG_RETURN(PASS);
01076 
01077 faulty_or_fake:
01078        DBG_ERR_FMT("Protocol error. Server sent NULL_LENGTH. The server is faulty");
01079        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol error. Server sent NULL_LENGTH."
01080                                     " The server is faulty");
01081        DBG_RETURN(FAIL);
01082 premature_end:
01083        DBG_ERR_FMT("RSET field packet %d bytes shorter than expected", p - begin - packet->header.size);
01084        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Result set field packet "MYSQLND_SZ_T_SPEC" bytes "
01085                                    "shorter than expected", p - begin - packet->header.size);
01086        DBG_RETURN(FAIL);
01087 }
01088 /* }}} */
01089 
01090 
01091 /* {{{ php_mysqlnd_rset_field_free_mem */
01092 static
01093 void php_mysqlnd_rset_field_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
01094 {
01095        MYSQLND_PACKET_RES_FIELD *p= (MYSQLND_PACKET_RES_FIELD *) _packet;
01096        /* p->metadata was passed to us as temporal buffer */
01097        if (!stack_allocation) {
01098               mnd_pefree(p, p->header.persistent);
01099        }
01100 }
01101 /* }}} */
01102 
01103 
01104 /* {{{ php_mysqlnd_read_row_ex */
01105 static enum_func_status
01106 php_mysqlnd_read_row_ex(MYSQLND * conn, MYSQLND_MEMORY_POOL * result_set_memory_pool,
01107                                           MYSQLND_MEMORY_POOL_CHUNK **buffer,
01108                                           size_t *data_size, zend_bool persistent_alloc,
01109                                           unsigned int prealloc_more_bytes TSRMLS_DC)
01110 {
01111        enum_func_status ret = PASS;
01112        MYSQLND_PACKET_HEADER header;
01113        zend_uchar *p = NULL;
01114        zend_bool first_iteration = TRUE;
01115 
01116        DBG_ENTER("php_mysqlnd_read_row_ex");
01117 
01118        /*
01119          To ease the process the server splits everything in packets up to 2^24 - 1.
01120          Even in the case the payload is evenly divisible by this value, the last
01121          packet will be empty, namely 0 bytes. Thus, we can read every packet and ask
01122          for next one if they have 2^24 - 1 sizes. But just read the header of a
01123          zero-length byte, don't read the body, there is no such.
01124        */
01125 
01126        *data_size = prealloc_more_bytes;
01127        while (1) {
01128               if (FAIL == mysqlnd_read_header(conn , &header TSRMLS_CC)) {
01129                      ret = FAIL;
01130                      break;
01131               }
01132 
01133               *data_size += header.size;
01134 
01135               if (first_iteration) {
01136                      first_iteration = FALSE;
01137                      /*
01138                        We need a trailing \0 for the last string, in case of text-mode,
01139                        to be able to implement read-only variables. Thus, we add + 1.
01140                      */
01141                      *buffer = result_set_memory_pool->get_chunk(result_set_memory_pool, *data_size + 1 TSRMLS_CC);
01142                      if (!*buffer) {
01143                             ret = FAIL;
01144                             break;
01145                      }
01146                      p = (*buffer)->ptr;
01147               } else if (!first_iteration) {
01148                      /* Empty packet after MYSQLND_MAX_PACKET_SIZE packet. That's ok, break */
01149                      if (!header.size) {
01150                             break;
01151                      }
01152 
01153                      /*
01154                        We have to realloc the buffer.
01155 
01156                        We need a trailing \0 for the last string, in case of text-mode,
01157                        to be able to implement read-only variables.
01158                      */
01159                      if (FAIL == (*buffer)->resize_chunk((*buffer), *data_size + 1 TSRMLS_CC)) {
01160                             SET_OOM_ERROR(conn->error_info);
01161                             ret = FAIL;
01162                             break;
01163                      }
01164                      /* The position could have changed, recalculate */
01165                      p = (*buffer)->ptr + (*data_size - header.size);
01166               }
01167 
01168               if (PASS != (ret = conn->net->m.receive(conn, p, header.size TSRMLS_CC))) {
01169                      DBG_ERR("Empty row packet body");
01170                      php_error(E_WARNING, "Empty row packet body");
01171                      break;
01172               }
01173 
01174               if (header.size < MYSQLND_MAX_PACKET_SIZE) {
01175                      break;
01176               }
01177        }
01178        if (ret == FAIL && *buffer) {
01179               (*buffer)->free_chunk((*buffer) TSRMLS_CC);
01180               *buffer = NULL;
01181        }
01182        *data_size -= prealloc_more_bytes;
01183        DBG_RETURN(ret);
01184 }
01185 /* }}} */
01186 
01187 
01188 /* {{{ php_mysqlnd_rowp_read_binary_protocol */
01189 enum_func_status
01190 php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
01191                                                                  unsigned int field_count, MYSQLND_FIELD *fields_metadata,
01192                                                                  zend_bool persistent,
01193                                                                  zend_bool as_unicode, zend_bool as_int_or_float,
01194                                                                  MYSQLND_STATS * stats TSRMLS_DC)
01195 {
01196        unsigned int i;
01197        zend_uchar *p = row_buffer->ptr;
01198        zend_uchar *null_ptr, bit;
01199        zval **current_field, **end_field, **start_field;
01200 
01201        DBG_ENTER("php_mysqlnd_rowp_read_binary_protocol");
01202 
01203        if (!fields) {
01204               DBG_RETURN(FAIL);
01205        }
01206 
01207        end_field = (start_field = fields) + field_count;
01208 
01209        /* skip the first byte, not EODATA_MARKER -> 0x0, status */
01210        p++;
01211        null_ptr= p;
01212        p += (field_count + 9)/8;   /* skip null bits */
01213        bit    = 4;                               /* first 2 bits are reserved */
01214 
01215        for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
01216               DBG_INF("Directly creating zval");
01217               MAKE_STD_ZVAL(*current_field);
01218               if (!*current_field) {
01219                      DBG_RETURN(FAIL);
01220               }
01221        }
01222 
01223        for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
01224               enum_mysqlnd_collected_stats statistic;
01225               zend_uchar * orig_p = p;
01226 
01227               DBG_INF_FMT("Into zval=%p decoding column %u [%s.%s.%s] type=%u field->flags&unsigned=%u flags=%u is_bit=%u as_unicode=%u",
01228                      *current_field, i,
01229                      fields_metadata[i].db, fields_metadata[i].table, fields_metadata[i].name, fields_metadata[i].type,
01230                      fields_metadata[i].flags & UNSIGNED_FLAG, fields_metadata[i].flags, fields_metadata[i].type == MYSQL_TYPE_BIT, as_unicode);
01231               if (*null_ptr & bit) {
01232                      DBG_INF("It's null");
01233                      ZVAL_NULL(*current_field);
01234                      statistic = STAT_BINARY_TYPE_FETCHED_NULL;
01235               } else {
01236                      enum_mysqlnd_field_types type = fields_metadata[i].type;
01237                      mysqlnd_ps_fetch_functions[type].func(*current_field, &fields_metadata[i], 0, &p, as_unicode TSRMLS_CC);
01238 
01239                      if (MYSQLND_G(collect_statistics)) {
01240                             switch (fields_metadata[i].type) {
01241                                    case MYSQL_TYPE_DECIMAL:    statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
01242                                    case MYSQL_TYPE_TINY:              statistic = STAT_BINARY_TYPE_FETCHED_INT8; break;
01243                                    case MYSQL_TYPE_SHORT:             statistic = STAT_BINARY_TYPE_FETCHED_INT16; break;
01244                                    case MYSQL_TYPE_LONG:              statistic = STAT_BINARY_TYPE_FETCHED_INT32; break;
01245                                    case MYSQL_TYPE_FLOAT:             statistic = STAT_BINARY_TYPE_FETCHED_FLOAT; break;
01246                                    case MYSQL_TYPE_DOUBLE:            statistic = STAT_BINARY_TYPE_FETCHED_DOUBLE; break;
01247                                    case MYSQL_TYPE_NULL:              statistic = STAT_BINARY_TYPE_FETCHED_NULL; break;
01248                                    case MYSQL_TYPE_TIMESTAMP:  statistic = STAT_BINARY_TYPE_FETCHED_TIMESTAMP; break;
01249                                    case MYSQL_TYPE_LONGLONG:   statistic = STAT_BINARY_TYPE_FETCHED_INT64; break;
01250                                    case MYSQL_TYPE_INT24:             statistic = STAT_BINARY_TYPE_FETCHED_INT24; break;
01251                                    case MYSQL_TYPE_DATE:              statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
01252                                    case MYSQL_TYPE_TIME:              statistic = STAT_BINARY_TYPE_FETCHED_TIME; break;
01253                                    case MYSQL_TYPE_DATETIME:   statistic = STAT_BINARY_TYPE_FETCHED_DATETIME; break;
01254                                    case MYSQL_TYPE_YEAR:              statistic = STAT_BINARY_TYPE_FETCHED_YEAR; break;
01255                                    case MYSQL_TYPE_NEWDATE:    statistic = STAT_BINARY_TYPE_FETCHED_DATE; break;
01256                                    case MYSQL_TYPE_VARCHAR:    statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
01257                                    case MYSQL_TYPE_BIT:        statistic = STAT_BINARY_TYPE_FETCHED_BIT; break;
01258                                    case MYSQL_TYPE_NEWDECIMAL: statistic = STAT_BINARY_TYPE_FETCHED_DECIMAL; break;
01259                                    case MYSQL_TYPE_ENUM:              statistic = STAT_BINARY_TYPE_FETCHED_ENUM; break;
01260                                    case MYSQL_TYPE_SET:        statistic = STAT_BINARY_TYPE_FETCHED_SET; break;
01261                                    case MYSQL_TYPE_TINY_BLOB:  statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
01262                                    case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
01263                                    case MYSQL_TYPE_LONG_BLOB:  statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
01264                                    case MYSQL_TYPE_BLOB:              statistic = STAT_BINARY_TYPE_FETCHED_BLOB; break;
01265                                    case MYSQL_TYPE_VAR_STRING: statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
01266                                    case MYSQL_TYPE_STRING:            statistic = STAT_BINARY_TYPE_FETCHED_STRING; break;
01267                                    case MYSQL_TYPE_GEOMETRY:   statistic = STAT_BINARY_TYPE_FETCHED_GEOMETRY; break;
01268                                    default: statistic = STAT_BINARY_TYPE_FETCHED_OTHER; break;
01269                             }
01270                      }
01271               }
01272               MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1,
01273                                                                       STAT_BYTES_RECEIVED_PURE_DATA_PS,
01274                                                                       (Z_TYPE_PP(current_field) == IS_STRING)?
01275                                                                              Z_STRLEN_PP(current_field) : (p - orig_p));
01276 
01277               if (!((bit<<=1) & 255)) {
01278                      bit = 1;      /* to the following byte */
01279                      null_ptr++;
01280               }
01281        }
01282 
01283        DBG_RETURN(PASS);
01284 }
01285 /* }}} */
01286 
01287 
01288 /* {{{ php_mysqlnd_rowp_read_text_protocol */
01289 enum_func_status
01290 php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields,
01291                                                                unsigned int field_count, MYSQLND_FIELD *fields_metadata,
01292                                                                zend_bool persistent,
01293                                                                zend_bool as_unicode, zend_bool as_int_or_float,
01294                                                                MYSQLND_STATS * stats TSRMLS_DC)
01295 {
01296        unsigned int i;
01297        zend_bool last_field_was_string = FALSE;
01298        zval **current_field, **end_field, **start_field;
01299        zend_uchar *p = row_buffer->ptr;
01300        size_t data_size = row_buffer->app;
01301        zend_uchar *bit_area = (zend_uchar*) row_buffer->ptr + data_size + 1; /* we allocate from here */
01302 
01303        DBG_ENTER("php_mysqlnd_rowp_read_text_protocol");
01304 
01305        if (!fields) {
01306               DBG_RETURN(FAIL);
01307        }
01308 
01309        end_field = (start_field = fields) + field_count;
01310 
01311        for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
01312               DBG_INF("Directly creating zval");
01313               MAKE_STD_ZVAL(*current_field);
01314               if (!*current_field) {
01315                      DBG_RETURN(FAIL);
01316               }
01317        }
01318 
01319        for (i = 0, current_field = start_field; current_field < end_field; current_field++, i++) {
01320               /* Don't reverse the order. It is significant!*/
01321               zend_uchar *this_field_len_pos = p;
01322               /* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */
01323               unsigned long len = php_mysqlnd_net_field_length(&p);
01324 
01325               if (current_field > start_field && last_field_was_string) {
01326                      /*
01327                        Normal queries:
01328                        We have to put \0 now to the end of the previous field, if it was
01329                        a string. IS_NULL doesn't matter. Because we have already read our
01330                        length, then we can overwrite it in the row buffer.
01331                        This statement terminates the previous field, not the current one.
01332 
01333                        NULL_LENGTH is encoded in one byte, so we can stick a \0 there.
01334                        Any string's length is encoded in at least one byte, so we can stick
01335                        a \0 there.
01336                      */
01337 
01338                      *this_field_len_pos = '\0';
01339               }
01340 
01341               /* NULL or NOT NULL, this is the question! */
01342               if (len == MYSQLND_NULL_LENGTH) {
01343                      ZVAL_NULL(*current_field);
01344                      last_field_was_string = FALSE;
01345               } else {
01346 #if MYSQLND_UNICODE || defined(MYSQLND_STRING_TO_INT_CONVERSION)
01347                      struct st_mysqlnd_perm_bind perm_bind =
01348                                    mysqlnd_ps_fetch_functions[fields_metadata[i].type];
01349 #endif
01350                      if (MYSQLND_G(collect_statistics)) {
01351                             enum_mysqlnd_collected_stats statistic;
01352                             switch (fields_metadata[i].type) {
01353                                    case MYSQL_TYPE_DECIMAL:    statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
01354                                    case MYSQL_TYPE_TINY:              statistic = STAT_TEXT_TYPE_FETCHED_INT8; break;
01355                                    case MYSQL_TYPE_SHORT:             statistic = STAT_TEXT_TYPE_FETCHED_INT16; break;
01356                                    case MYSQL_TYPE_LONG:              statistic = STAT_TEXT_TYPE_FETCHED_INT32; break;
01357                                    case MYSQL_TYPE_FLOAT:             statistic = STAT_TEXT_TYPE_FETCHED_FLOAT; break;
01358                                    case MYSQL_TYPE_DOUBLE:            statistic = STAT_TEXT_TYPE_FETCHED_DOUBLE; break;
01359                                    case MYSQL_TYPE_NULL:              statistic = STAT_TEXT_TYPE_FETCHED_NULL; break;
01360                                    case MYSQL_TYPE_TIMESTAMP:  statistic = STAT_TEXT_TYPE_FETCHED_TIMESTAMP; break;
01361                                    case MYSQL_TYPE_LONGLONG:   statistic = STAT_TEXT_TYPE_FETCHED_INT64; break;
01362                                    case MYSQL_TYPE_INT24:             statistic = STAT_TEXT_TYPE_FETCHED_INT24; break;
01363                                    case MYSQL_TYPE_DATE:              statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
01364                                    case MYSQL_TYPE_TIME:              statistic = STAT_TEXT_TYPE_FETCHED_TIME; break;
01365                                    case MYSQL_TYPE_DATETIME:   statistic = STAT_TEXT_TYPE_FETCHED_DATETIME; break;
01366                                    case MYSQL_TYPE_YEAR:              statistic = STAT_TEXT_TYPE_FETCHED_YEAR; break;
01367                                    case MYSQL_TYPE_NEWDATE:    statistic = STAT_TEXT_TYPE_FETCHED_DATE; break;
01368                                    case MYSQL_TYPE_VARCHAR:    statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
01369                                    case MYSQL_TYPE_BIT:        statistic = STAT_TEXT_TYPE_FETCHED_BIT; break;
01370                                    case MYSQL_TYPE_NEWDECIMAL: statistic = STAT_TEXT_TYPE_FETCHED_DECIMAL; break;
01371                                    case MYSQL_TYPE_ENUM:              statistic = STAT_TEXT_TYPE_FETCHED_ENUM; break;
01372                                    case MYSQL_TYPE_SET:        statistic = STAT_TEXT_TYPE_FETCHED_SET; break;
01373                                    case MYSQL_TYPE_TINY_BLOB:  statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
01374                                    case MYSQL_TYPE_MEDIUM_BLOB:statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
01375                                    case MYSQL_TYPE_LONG_BLOB:  statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
01376                                    case MYSQL_TYPE_BLOB:              statistic = STAT_TEXT_TYPE_FETCHED_BLOB; break;
01377                                    case MYSQL_TYPE_VAR_STRING: statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
01378                                    case MYSQL_TYPE_STRING:            statistic = STAT_TEXT_TYPE_FETCHED_STRING; break;
01379                                    case MYSQL_TYPE_GEOMETRY:   statistic = STAT_TEXT_TYPE_FETCHED_GEOMETRY; break;
01380                                    default: statistic = STAT_TEXT_TYPE_FETCHED_OTHER; break;
01381                             }
01382                             MYSQLND_INC_CONN_STATISTIC_W_VALUE2(stats, statistic, 1, STAT_BYTES_RECEIVED_PURE_DATA_TEXT, len);
01383                      }
01384 #ifdef MYSQLND_STRING_TO_INT_CONVERSION
01385                      if (as_int_or_float && perm_bind.php_type == IS_LONG) {
01386                             zend_uchar save = *(p + len);
01387                             /* We have to make it ASCIIZ temporarily */
01388                             *(p + len) = '\0';
01389                             if (perm_bind.pack_len < SIZEOF_LONG) {
01390                                    /* direct conversion */
01391                                    int64_t v =
01392 #ifndef PHP_WIN32
01393                                           atoll((char *) p);
01394 #else
01395                                           _atoi64((char *) p);
01396 #endif
01397                                    ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
01398                             } else {
01399                                    uint64_t v =
01400 #ifndef PHP_WIN32
01401                                           (uint64_t) atoll((char *) p);
01402 #else
01403                                           (uint64_t) _atoi64((char *) p);
01404 #endif
01405                                    zend_bool uns = fields_metadata[i].flags & UNSIGNED_FLAG? TRUE:FALSE;
01406                                    /* We have to make it ASCIIZ temporarily */
01407 #if SIZEOF_LONG==8
01408                                    if (uns == TRUE && v > 9223372036854775807L)
01409 #elif SIZEOF_LONG==4
01410                                    if ((uns == TRUE && v > L64(2147483647)) ||
01411                                           (uns == FALSE && (( L64(2147483647) < (int64_t) v) ||
01412                                           (L64(-2147483648) > (int64_t) v))))
01413 #else
01414 #error Need fix for this architecture
01415 #endif /* SIZEOF */
01416                                    {
01417                                           ZVAL_STRINGL(*current_field, (char *)p, len, 0);
01418                                    } else {
01419                                           ZVAL_LONG(*current_field, (long) v); /* the cast is safe */
01420                                    }
01421                             }
01422                             *(p + len) = save;
01423                      } else if (as_int_or_float && perm_bind.php_type == IS_DOUBLE) {
01424                             zend_uchar save = *(p + len);
01425                             /* We have to make it ASCIIZ temporarily */
01426                             *(p + len) = '\0';
01427                             ZVAL_DOUBLE(*current_field, atof((char *) p));
01428                             *(p + len) = save;
01429                      } else
01430 #endif /* MYSQLND_STRING_TO_INT_CONVERSION */
01431                      if (fields_metadata[i].type == MYSQL_TYPE_BIT) {
01432                             /*
01433                               BIT fields are specially handled. As they come as bit mask, we have
01434                               to convert it to human-readable representation. As the bits take
01435                               less space in the protocol than the numbers they represent, we don't
01436                               have enough space in the packet buffer to overwrite inside.
01437                               Thus, a bit more space is pre-allocated at the end of the buffer,
01438                               see php_mysqlnd_rowp_read(). And we add the strings at the end.
01439                               Definitely not nice, _hackish_ :(, but works.
01440                             */
01441                             zend_uchar *start = bit_area;
01442                             ps_fetch_from_1_to_8_bytes(*current_field, &(fields_metadata[i]), 0, &p, as_unicode, len TSRMLS_CC);
01443                             /*
01444                               We have advanced in ps_fetch_from_1_to_8_bytes. We should go back because
01445                               later in this function there will be an advancement.
01446                             */
01447                             p -= len;
01448                             if (Z_TYPE_PP(current_field) == IS_LONG) {
01449                                    bit_area += 1 + sprintf((char *)start, "%ld", Z_LVAL_PP(current_field));
01450 #if MYSQLND_UNICODE
01451                                    if (as_unicode) {
01452                                           ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
01453                                    } else
01454 #endif
01455                                    {
01456                                           ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
01457                                    }
01458                             } else if (Z_TYPE_PP(current_field) == IS_STRING){
01459                                    memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field));
01460                                    bit_area += Z_STRLEN_PP(current_field);
01461                                    *bit_area++ = '\0';
01462                                    zval_dtor(*current_field);
01463 #if MYSQLND_UNICODE
01464                                    if (as_unicode) {
01465                                           ZVAL_UTF8_STRINGL(*current_field, start, bit_area - start - 1, 0);
01466                                    } else
01467 #endif
01468                                    {
01469                                           ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0);
01470                                    }
01471                             }
01472                             /*
01473                               IS_UNICODE should not be specially handled. In unicode mode
01474                               the buffers are not referenced - everything is copied.
01475                             */
01476                      } else
01477 #if MYSQLND_UNICODE == 0
01478                      {
01479                             ZVAL_STRINGL(*current_field, (char *)p, len, 0);
01480                      }
01481 #else
01482                      /*
01483                        Here we have to convert to UTF16, which means not reusing the buffer.
01484                        Which in turn means that we can free the buffers once we have
01485                        stored the result set, if we use store_result().
01486 
01487                        Also the destruction of the zvals should not call zval_copy_ctor()
01488                        because then we will leak.
01489 
01490                        XXX: Keep in mind that up there there is an open `else` in
01491                        #ifdef MYSQLND_STRING_TO_INT_CONVERSION
01492                        which will make with this `if` an `else if`.
01493                      */
01494                      if ((perm_bind.is_possibly_blob == TRUE &&
01495                              fields_metadata[i].charsetnr == MYSQLND_BINARY_CHARSET_NR) ||
01496                             (!as_unicode && perm_bind.can_ret_as_str_in_uni == TRUE))
01497                      {
01498                             /* BLOB - no conversion please */
01499                             ZVAL_STRINGL(*current_field, (char *)p, len, 0);
01500                      } else {
01501                             ZVAL_UTF8_STRINGL(*current_field, (char *)p, len, 0);
01502                      }
01503 #endif
01504                      p += len;
01505                      last_field_was_string = TRUE;
01506               }
01507        }
01508        if (last_field_was_string) {
01509               /* Normal queries: The buffer has one more byte at the end, because we need it */
01510               row_buffer->ptr[data_size] = '\0';
01511        }
01512 
01513        DBG_RETURN(PASS);
01514 }
01515 /* }}} */
01516 
01517 
01518 /* {{{ php_mysqlnd_rowp_read */
01519 /*
01520   if normal statements => packet->fields is created by this function,
01521   if PS => packet->fields is passed from outside
01522 */
01523 static enum_func_status
01524 php_mysqlnd_rowp_read(void *_packet, MYSQLND *conn TSRMLS_DC)
01525 {
01526        MYSQLND_NET *net = conn->net;
01527        zend_uchar *p;
01528        enum_func_status ret = PASS;
01529        size_t old_chunk_size = net->stream->chunk_size;
01530        MYSQLND_PACKET_ROW *packet= (MYSQLND_PACKET_ROW *) _packet;
01531        size_t post_alloc_for_bit_fields = 0;
01532        size_t data_size = 0;
01533 
01534        DBG_ENTER("php_mysqlnd_rowp_read");
01535 
01536        if (!packet->binary_protocol && packet->bit_fields_count) {
01537               /* For every field we need terminating \0 */
01538               post_alloc_for_bit_fields = packet->bit_fields_total_len + packet->bit_fields_count;
01539        }
01540 
01541        ret = php_mysqlnd_read_row_ex(conn, packet->result_set_memory_pool, &packet->row_buffer, &data_size,
01542                                                           packet->persistent_alloc, post_alloc_for_bit_fields
01543                                                           TSRMLS_CC);
01544        if (FAIL == ret) {
01545               goto end;
01546        }
01547        MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, packet_type_to_statistic_byte_count[PROT_ROW_PACKET],
01548                                                                       MYSQLND_HEADER_SIZE + packet->header.size,
01549                                                                       packet_type_to_statistic_packet_count[PROT_ROW_PACKET],
01550                                                                       1);
01551 
01552        /* packet->row_buffer->ptr is of size 'data_size + 1' */
01553        packet->header.size = data_size;
01554        packet->row_buffer->app = data_size;
01555 
01556        if (ERROR_MARKER == (*(p = packet->row_buffer->ptr))) {
01557               /*
01558                  Error message as part of the result set,
01559                  not good but we should not hang. See:
01560                  Bug #27876 : SF with cyrillic variable name fails during execution
01561               */
01562               ret = FAIL;
01563               php_mysqlnd_read_error_from_line(p + 1, data_size - 1,
01564                                                                        packet->error_info.error,
01565                                                                        sizeof(packet->error_info.error),
01566                                                                        &packet->error_info.error_no,
01567                                                                        packet->error_info.sqlstate
01568                                                                        TSRMLS_CC);
01569        } else if (EODATA_MARKER == *p && data_size < 8) { /* EOF */
01570               packet->eof = TRUE;
01571               p++;
01572               if (data_size > 1) {
01573                      packet->warning_count = uint2korr(p);
01574                      p += 2;
01575                      packet->server_status = uint2korr(p);
01576                      /* Seems we have 3 bytes reserved for future use */
01577                      DBG_INF_FMT("server_status=%u warning_count=%u", packet->server_status, packet->warning_count);
01578               }
01579        } else {
01580               MYSQLND_INC_CONN_STATISTIC(conn->stats,
01581                                                                packet->binary_protocol? STAT_ROWS_FETCHED_FROM_SERVER_PS:
01582                                                                                                           STAT_ROWS_FETCHED_FROM_SERVER_NORMAL);
01583 
01584               packet->eof = FALSE;
01585               /* packet->field_count is set by the user of the packet */
01586 
01587               if (!packet->skip_extraction) {
01588                      if (!packet->fields) {
01589                             DBG_INF("Allocating packet->fields");
01590                             /*
01591                               old-API will probably set packet->fields to NULL every time, though for
01592                               unbuffered sets it makes not much sense as the zvals in this buffer matter,
01593                               not the buffer. Constantly allocating and deallocating brings nothing.
01594 
01595                               For PS - if stmt_store() is performed, thus we don't have a cursor, it will
01596                               behave just like old-API buffered. Cursors will behave like a bit different,
01597                               but mostly like old-API unbuffered and thus will populate this array with
01598                               value.
01599                             */
01600                             packet->fields = (zval **) mnd_pecalloc(packet->field_count, sizeof(zval *),
01601                                                                                                   packet->persistent_alloc);
01602                      }
01603               } else {
01604                      MYSQLND_INC_CONN_STATISTIC(conn->stats,
01605                                                                       packet->binary_protocol? STAT_ROWS_SKIPPED_PS:
01606                                                                                                                  STAT_ROWS_SKIPPED_NORMAL);
01607               }
01608        }
01609 
01610 end:
01611        net->stream->chunk_size = old_chunk_size;
01612        DBG_RETURN(ret);
01613 }
01614 /* }}} */
01615 
01616 
01617 /* {{{ php_mysqlnd_rowp_free_mem */
01618 static void
01619 php_mysqlnd_rowp_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
01620 {
01621        MYSQLND_PACKET_ROW *p;
01622 
01623        DBG_ENTER("php_mysqlnd_rowp_free_mem");
01624        p = (MYSQLND_PACKET_ROW *) _packet;
01625        if (p->row_buffer) {
01626               p->row_buffer->free_chunk(p->row_buffer TSRMLS_CC);
01627               p->row_buffer = NULL;
01628        }
01629        DBG_INF_FMT("stack_allocation=%u persistent=%u", (int)stack_allocation, (int)p->header.persistent);
01630        /*
01631          Don't free packet->fields :
01632          - normal queries -> store_result() | fetch_row_unbuffered() will transfer
01633            the ownership and NULL it.
01634          - PS will pass in it the bound variables, we have to use them! and of course
01635            not free the array. As it is passed to us, we should not clean it ourselves.
01636        */
01637        if (!stack_allocation) {
01638               mnd_pefree(p, p->header.persistent);
01639        }
01640        DBG_VOID_RETURN;
01641 }
01642 /* }}} */
01643 
01644 
01645 /* {{{ php_mysqlnd_stats_read */
01646 static enum_func_status
01647 php_mysqlnd_stats_read(void *_packet, MYSQLND *conn TSRMLS_DC)
01648 {
01649        MYSQLND_PACKET_STATS *packet= (MYSQLND_PACKET_STATS *) _packet;
01650        size_t buf_len = conn->net->cmd_buffer.length;
01651        zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
01652 
01653        DBG_ENTER("php_mysqlnd_stats_read");
01654 
01655        PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "statistics", PROT_STATS_PACKET);
01656 
01657        packet->message = mnd_emalloc(packet->header.size + 1);
01658        memcpy(packet->message, buf, packet->header.size);
01659        packet->message[packet->header.size] = '\0';
01660        packet->message_len = packet->header.size;
01661 
01662        DBG_RETURN(PASS);
01663 }
01664 /* }}} */
01665 
01666 
01667 /* {{{ php_mysqlnd_stats_free_mem */
01668 static
01669 void php_mysqlnd_stats_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
01670 {
01671        MYSQLND_PACKET_STATS *p= (MYSQLND_PACKET_STATS *) _packet;
01672        if (p->message) {
01673               mnd_efree(p->message);
01674               p->message = NULL;
01675        }
01676        if (!stack_allocation) {
01677               mnd_pefree(p, p->header.persistent);
01678        }
01679 }
01680 /* }}} */
01681 
01682 
01683 /* 1 + 4 (id) + 2 (field_c) + 2 (param_c) + 1 (filler) + 2 (warnings ) */
01684 #define PREPARE_RESPONSE_SIZE_41 9
01685 #define PREPARE_RESPONSE_SIZE_50 12
01686 
01687 /* {{{ php_mysqlnd_prepare_read */
01688 static enum_func_status
01689 php_mysqlnd_prepare_read(void *_packet, MYSQLND *conn TSRMLS_DC)
01690 {
01691        /* In case of an error, we should have place to put it */
01692        size_t buf_len = conn->net->cmd_buffer.length;
01693        zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
01694        zend_uchar *p = buf;
01695        zend_uchar *begin = buf;
01696        unsigned int data_size;
01697        MYSQLND_PACKET_PREPARE_RESPONSE *packet= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
01698 
01699        DBG_ENTER("php_mysqlnd_prepare_read");
01700 
01701        PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "prepare", PROT_PREPARE_RESP_PACKET);
01702        BAIL_IF_NO_MORE_DATA;
01703 
01704        data_size = packet->header.size;
01705        packet->error_code = uint1korr(p);
01706        p++;
01707        BAIL_IF_NO_MORE_DATA;
01708 
01709        if (ERROR_MARKER == packet->error_code) {
01710               php_mysqlnd_read_error_from_line(p, data_size - 1,
01711                                                                        packet->error_info.error,
01712                                                                        sizeof(packet->error_info.error),
01713                                                                        &packet->error_info.error_no,
01714                                                                        packet->error_info.sqlstate
01715                                                                        TSRMLS_CC);
01716               DBG_RETURN(PASS);
01717        }
01718 
01719        if (data_size != PREPARE_RESPONSE_SIZE_41 &&
01720               data_size != PREPARE_RESPONSE_SIZE_50 &&
01721               !(data_size > PREPARE_RESPONSE_SIZE_50)) {
01722               DBG_ERR_FMT("Wrong COM_STMT_PREPARE response size. Received %u", data_size);
01723               php_error(E_WARNING, "Wrong COM_STMT_PREPARE response size. Received %u", data_size);
01724               DBG_RETURN(FAIL);
01725        }
01726 
01727        packet->stmt_id = uint4korr(p);
01728        p += 4;
01729        BAIL_IF_NO_MORE_DATA;
01730 
01731        /* Number of columns in result set */
01732        packet->field_count = uint2korr(p);
01733        p += 2;
01734        BAIL_IF_NO_MORE_DATA;
01735 
01736        packet->param_count = uint2korr(p);
01737        p += 2;
01738        BAIL_IF_NO_MORE_DATA;
01739 
01740        if (data_size > 9) {
01741               /* 0x0 filler sent by the server for 5.0+ clients */
01742               p++;
01743               BAIL_IF_NO_MORE_DATA;
01744 
01745               packet->warning_count = uint2korr(p);
01746        }
01747 
01748        DBG_INF_FMT("Prepare packet read: stmt_id=%u fields=%u params=%u",
01749                             packet->stmt_id, packet->field_count, packet->param_count);
01750 
01751        BAIL_IF_NO_MORE_DATA;
01752 
01753        DBG_RETURN(PASS);
01754 premature_end:
01755        DBG_ERR_FMT("PREPARE packet %d bytes shorter than expected", p - begin - packet->header.size);
01756        php_error_docref(NULL TSRMLS_CC, E_WARNING, "PREPARE packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
01757                                     p - begin - packet->header.size);
01758        DBG_RETURN(FAIL);
01759 }
01760 /* }}} */
01761 
01762 
01763 /* {{{ php_mysqlnd_prepare_free_mem */
01764 static void
01765 php_mysqlnd_prepare_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
01766 {
01767        MYSQLND_PACKET_PREPARE_RESPONSE *p= (MYSQLND_PACKET_PREPARE_RESPONSE *) _packet;
01768        if (!stack_allocation) {
01769               mnd_pefree(p, p->header.persistent);
01770        }
01771 }
01772 /* }}} */
01773 
01774 
01775 /* {{{ php_mysqlnd_chg_user_read */
01776 static enum_func_status
01777 php_mysqlnd_chg_user_read(void *_packet, MYSQLND *conn TSRMLS_DC)
01778 {
01779        /* There could be an error message */
01780        size_t buf_len = conn->net->cmd_buffer.length;
01781        zend_uchar *buf = (zend_uchar *) conn->net->cmd_buffer.buffer;
01782        zend_uchar *p = buf;
01783        zend_uchar *begin = buf;
01784        MYSQLND_PACKET_CHG_USER_RESPONSE *packet= (MYSQLND_PACKET_CHG_USER_RESPONSE *) _packet;
01785 
01786        DBG_ENTER("php_mysqlnd_chg_user_read");
01787 
01788        PACKET_READ_HEADER_AND_BODY(packet, conn, buf, buf_len, "change user response", PROT_CHG_USER_RESP_PACKET);
01789        BAIL_IF_NO_MORE_DATA;
01790 
01791        /*
01792          Don't increment. First byte is ERROR_MARKER on error, but otherwise is starting byte
01793          of encoded sequence for length.
01794        */
01795 
01796        /* Should be always 0x0 or ERROR_MARKER for error */
01797        packet->field_count= uint1korr(p);
01798        p++;
01799 
01800        if (packet->header.size == 1 && buf[0] == EODATA_MARKER && packet->server_capabilities & CLIENT_SECURE_CONNECTION) {
01801               /* We don't handle 3.23 authentication */
01802               packet->server_asked_323_auth = TRUE;
01803               DBG_RETURN(FAIL);
01804        }
01805 
01806        if (ERROR_MARKER == packet->field_count) {
01807               php_mysqlnd_read_error_from_line(p, packet->header.size - 1,
01808                                                                        packet->error_info.error,
01809                                                                        sizeof(packet->error_info.error),
01810                                                                        &packet->error_info.error_no,
01811                                                                        packet->error_info.sqlstate
01812                                                                        TSRMLS_CC);
01813        }
01814        BAIL_IF_NO_MORE_DATA;
01815 
01816        DBG_RETURN(PASS);
01817 premature_end:
01818        DBG_ERR_FMT("CHANGE_USER packet %d bytes shorter than expected", p - begin - packet->header.size);
01819        php_error_docref(NULL TSRMLS_CC, E_WARNING, "CHANGE_USER packet "MYSQLND_SZ_T_SPEC" bytes shorter than expected",
01820                                            p - begin - packet->header.size);
01821        DBG_RETURN(FAIL);
01822 }
01823 /* }}} */
01824 
01825 
01826 /* {{{ php_mysqlnd_chg_user_free_mem */
01827 static void
01828 php_mysqlnd_chg_user_free_mem(void *_packet, zend_bool stack_allocation TSRMLS_DC)
01829 {
01830        if (!stack_allocation) {
01831               mnd_pefree(_packet, ((MYSQLND_PACKET_CHG_USER_RESPONSE *)_packet)->header.persistent);
01832        }
01833 }
01834 /* }}} */
01835 
01836 
01837 /* {{{ packet_methods */
01838 static
01839 mysqlnd_packet_methods packet_methods[PROT_LAST] =
01840 {
01841        {
01842               sizeof(MYSQLND_PACKET_GREET),
01843               php_mysqlnd_greet_read,
01844               NULL, /* write */
01845               php_mysqlnd_greet_free_mem,
01846        }, /* PROT_GREET_PACKET */
01847        {
01848               sizeof(MYSQLND_PACKET_AUTH),
01849               NULL, /* read */
01850               php_mysqlnd_auth_write,
01851               php_mysqlnd_auth_free_mem,
01852        }, /* PROT_AUTH_PACKET */
01853        {
01854               sizeof(MYSQLND_PACKET_OK),
01855               php_mysqlnd_ok_read, /* read */
01856               NULL, /* write */
01857               php_mysqlnd_ok_free_mem,
01858        }, /* PROT_OK_PACKET */
01859        {
01860               sizeof(MYSQLND_PACKET_EOF),
01861               php_mysqlnd_eof_read, /* read */
01862               NULL, /* write */
01863               php_mysqlnd_eof_free_mem,
01864        }, /* PROT_EOF_PACKET */
01865        {
01866               sizeof(MYSQLND_PACKET_COMMAND),
01867               NULL, /* read */
01868               php_mysqlnd_cmd_write, /* write */
01869               php_mysqlnd_cmd_free_mem,
01870        }, /* PROT_CMD_PACKET */
01871        {
01872               sizeof(MYSQLND_PACKET_RSET_HEADER),
01873               php_mysqlnd_rset_header_read, /* read */
01874               NULL, /* write */
01875               php_mysqlnd_rset_header_free_mem,
01876        }, /* PROT_RSET_HEADER_PACKET */
01877        {
01878               sizeof(MYSQLND_PACKET_RES_FIELD),
01879               php_mysqlnd_rset_field_read, /* read */
01880               NULL, /* write */
01881               php_mysqlnd_rset_field_free_mem,
01882        }, /* PROT_RSET_FLD_PACKET */
01883        {
01884               sizeof(MYSQLND_PACKET_ROW),
01885               php_mysqlnd_rowp_read, /* read */
01886               NULL, /* write */
01887               php_mysqlnd_rowp_free_mem,
01888        }, /* PROT_ROW_PACKET */
01889        {
01890               sizeof(MYSQLND_PACKET_STATS),
01891               php_mysqlnd_stats_read, /* read */
01892               NULL, /* write */
01893               php_mysqlnd_stats_free_mem,
01894        }, /* PROT_STATS_PACKET */
01895        {
01896               sizeof(MYSQLND_PACKET_PREPARE_RESPONSE),
01897               php_mysqlnd_prepare_read, /* read */
01898               NULL, /* write */
01899               php_mysqlnd_prepare_free_mem,
01900        }, /* PROT_PREPARE_RESP_PACKET */
01901        {
01902               sizeof(MYSQLND_PACKET_CHG_USER_RESPONSE),
01903               php_mysqlnd_chg_user_read, /* read */
01904               NULL, /* write */
01905               php_mysqlnd_chg_user_free_mem,
01906        } /* PROT_CHG_USER_RESP_PACKET */
01907 };
01908 /* }}} */
01909 
01910 
01911 /* {{{ mysqlnd_protocol::get_greet_packet */
01912 static struct st_mysqlnd_packet_greet *
01913 MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
01914 {
01915        struct st_mysqlnd_packet_greet * packet = mnd_pecalloc(1, packet_methods[PROT_GREET_PACKET].struct_size, persistent);
01916        DBG_ENTER("mysqlnd_protocol::get_greet_packet");
01917        if (packet) {
01918               packet->header.m = &packet_methods[PROT_GREET_PACKET];
01919               packet->header.persistent = persistent;
01920        }
01921        DBG_RETURN(packet);
01922 }
01923 /* }}} */
01924 
01925 
01926 /* {{{ mysqlnd_protocol::get_auth_packet */
01927 static struct st_mysqlnd_packet_auth *
01928 MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
01929 {
01930        struct st_mysqlnd_packet_auth * packet = mnd_pecalloc(1, packet_methods[PROT_AUTH_PACKET].struct_size, persistent);
01931        DBG_ENTER("mysqlnd_protocol::get_auth_packet");
01932        if (packet) {
01933               packet->header.m = &packet_methods[PROT_AUTH_PACKET];
01934               packet->header.persistent = persistent;
01935        }
01936        DBG_RETURN(packet);
01937 }
01938 /* }}} */
01939 
01940 
01941 /* {{{ mysqlnd_protocol::get_ok_packet */
01942 static struct st_mysqlnd_packet_ok *
01943 MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
01944 {
01945        struct st_mysqlnd_packet_ok * packet = mnd_pecalloc(1, packet_methods[PROT_OK_PACKET].struct_size, persistent);
01946        DBG_ENTER("mysqlnd_protocol::get_ok_packet");
01947        if (packet) {
01948               packet->header.m = &packet_methods[PROT_OK_PACKET];
01949               packet->header.persistent = persistent;
01950        }
01951        DBG_RETURN(packet);
01952 }
01953 /* }}} */
01954 
01955 
01956 /* {{{ mysqlnd_protocol::get_eof_packet */
01957 static struct st_mysqlnd_packet_eof *
01958 MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
01959 {
01960        struct st_mysqlnd_packet_eof * packet = mnd_pecalloc(1, packet_methods[PROT_EOF_PACKET].struct_size, persistent);
01961        DBG_ENTER("mysqlnd_protocol::get_eof_packet");
01962        if (packet) {
01963               packet->header.m = &packet_methods[PROT_EOF_PACKET];
01964               packet->header.persistent = persistent;
01965        }
01966        DBG_RETURN(packet);
01967 }
01968 /* }}} */
01969 
01970 
01971 /* {{{ mysqlnd_protocol::get_command_packet */
01972 static struct st_mysqlnd_packet_command *
01973 MYSQLND_METHOD(mysqlnd_protocol, get_command_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
01974 {
01975        struct st_mysqlnd_packet_command * packet = mnd_pecalloc(1, packet_methods[PROT_CMD_PACKET].struct_size, persistent);
01976        DBG_ENTER("mysqlnd_protocol::get_command_packet");
01977        if (packet) {
01978               packet->header.m = &packet_methods[PROT_CMD_PACKET];
01979               packet->header.persistent = persistent;
01980        }
01981        DBG_RETURN(packet);
01982 }
01983 /* }}} */
01984 
01985 
01986 /* {{{ mysqlnd_protocol::get_rset_packet */
01987 static struct st_mysqlnd_packet_rset_header *
01988 MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
01989 {
01990        struct st_mysqlnd_packet_rset_header * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_HEADER_PACKET].struct_size, persistent);
01991        DBG_ENTER("mysqlnd_protocol::get_rset_header_packet");
01992        if (packet) {
01993               packet->header.m = &packet_methods[PROT_RSET_HEADER_PACKET];
01994               packet->header.persistent = persistent;
01995        }
01996        DBG_RETURN(packet);
01997 }
01998 /* }}} */
01999 
02000 
02001 /* {{{ mysqlnd_protocol::get_result_field_packet */
02002 static struct st_mysqlnd_packet_res_field *
02003 MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
02004 {
02005        struct st_mysqlnd_packet_res_field * packet = mnd_pecalloc(1, packet_methods[PROT_RSET_FLD_PACKET].struct_size, persistent);
02006        DBG_ENTER("mysqlnd_protocol::get_result_field_packet");
02007        if (packet) {
02008               packet->header.m = &packet_methods[PROT_RSET_FLD_PACKET];
02009               packet->header.persistent = persistent;
02010        }
02011        DBG_RETURN(packet);
02012 }
02013 /* }}} */
02014 
02015 
02016 /* {{{ mysqlnd_protocol::get_row_packet */
02017 static struct st_mysqlnd_packet_row *
02018 MYSQLND_METHOD(mysqlnd_protocol, get_row_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
02019 {
02020        struct st_mysqlnd_packet_row * packet = mnd_pecalloc(1, packet_methods[PROT_ROW_PACKET].struct_size, persistent);
02021        DBG_ENTER("mysqlnd_protocol::get_row_packet");
02022        if (packet) {
02023               packet->header.m = &packet_methods[PROT_ROW_PACKET];
02024               packet->header.persistent = persistent;
02025        }
02026        DBG_RETURN(packet);
02027 }
02028 /* }}} */
02029 
02030 
02031 /* {{{ mysqlnd_protocol::get_stats_packet */
02032 static struct st_mysqlnd_packet_stats *
02033 MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
02034 {
02035        struct st_mysqlnd_packet_stats * packet = mnd_pecalloc(1, packet_methods[PROT_STATS_PACKET].struct_size, persistent);
02036        DBG_ENTER("mysqlnd_protocol::get_stats_packet");
02037        if (packet) {
02038               packet->header.m = &packet_methods[PROT_STATS_PACKET];
02039               packet->header.persistent = persistent;
02040        }
02041        DBG_RETURN(packet);
02042 }
02043 /* }}} */
02044 
02045 
02046 /* {{{ mysqlnd_protocol::get_prepare_response_packet */
02047 static struct st_mysqlnd_packet_prepare_response *
02048 MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
02049 {
02050        struct st_mysqlnd_packet_prepare_response * packet = mnd_pecalloc(1, packet_methods[PROT_PREPARE_RESP_PACKET].struct_size, persistent);
02051        DBG_ENTER("mysqlnd_protocol::get_prepare_response_packet");
02052        if (packet) {
02053               packet->header.m = &packet_methods[PROT_PREPARE_RESP_PACKET];
02054               packet->header.persistent = persistent;
02055        }
02056        DBG_RETURN(packet);
02057 }
02058 /* }}} */
02059 
02060 
02061 /* {{{ mysqlnd_protocol::get_change_user_response_packet */
02062 static struct st_mysqlnd_packet_chg_user_resp*
02063 MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)(MYSQLND_PROTOCOL * const protocol, zend_bool persistent TSRMLS_DC)
02064 {
02065        struct st_mysqlnd_packet_chg_user_resp * packet = mnd_pecalloc(1, packet_methods[PROT_CHG_USER_RESP_PACKET].struct_size, persistent);
02066        DBG_ENTER("mysqlnd_protocol::get_change_user_response_packet");
02067        if (packet) {
02068               packet->header.m = &packet_methods[PROT_CHG_USER_RESP_PACKET];
02069               packet->header.persistent = persistent;
02070        }
02071        DBG_RETURN(packet);
02072 }
02073 /* }}} */
02074 
02075 
02076 static
02077 MYSQLND_CLASS_METHODS_START(mysqlnd_protocol)
02078        MYSQLND_METHOD(mysqlnd_protocol, get_greet_packet),
02079        MYSQLND_METHOD(mysqlnd_protocol, get_auth_packet),
02080        MYSQLND_METHOD(mysqlnd_protocol, get_ok_packet),
02081        MYSQLND_METHOD(mysqlnd_protocol, get_command_packet),
02082        MYSQLND_METHOD(mysqlnd_protocol, get_eof_packet),
02083        MYSQLND_METHOD(mysqlnd_protocol, get_rset_header_packet),
02084        MYSQLND_METHOD(mysqlnd_protocol, get_result_field_packet),
02085        MYSQLND_METHOD(mysqlnd_protocol, get_row_packet),
02086        MYSQLND_METHOD(mysqlnd_protocol, get_stats_packet),
02087        MYSQLND_METHOD(mysqlnd_protocol, get_prepare_response_packet),
02088        MYSQLND_METHOD(mysqlnd_protocol, get_change_user_response_packet)
02089 MYSQLND_CLASS_METHODS_END;
02090 
02091 
02092 /* {{{ mysqlnd_protocol_init */
02093 PHPAPI MYSQLND_PROTOCOL *
02094 mysqlnd_protocol_init(zend_bool persistent TSRMLS_DC)
02095 {
02096        size_t alloc_size = sizeof(MYSQLND_PROTOCOL) + mysqlnd_plugin_count() * sizeof(void *);
02097        MYSQLND_PROTOCOL *ret = mnd_pecalloc(1, alloc_size, persistent);
02098 
02099        DBG_ENTER("mysqlnd_protocol_init");
02100        DBG_INF_FMT("persistent=%u", persistent);
02101        if (ret) {
02102               ret->persistent = persistent;
02103               ret->m = mysqlnd_mysqlnd_protocol_methods;
02104        }
02105 
02106        DBG_RETURN(ret);
02107 }
02108 /* }}} */
02109 
02110 
02111 /* {{{ mysqlnd_protocol_free */
02112 PHPAPI void
02113 mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol TSRMLS_DC)
02114 {
02115        DBG_ENTER("mysqlnd_protocol_free");
02116 
02117        if (protocol) {
02118               zend_bool pers = protocol->persistent;
02119               mnd_pefree(protocol, pers);
02120        }
02121        DBG_VOID_RETURN;
02122 }
02123 /* }}} */
02124 
02125 
02126 /* {{{ _mysqlnd_plugin_get_plugin_protocol_data */
02127 PHPAPI void **
02128 _mysqlnd_plugin_get_plugin_protocol_data(const MYSQLND_PROTOCOL * protocol, unsigned int plugin_id TSRMLS_DC)
02129 {
02130        DBG_ENTER("_mysqlnd_plugin_get_plugin_protocol_data");
02131        DBG_INF_FMT("plugin_id=%u", plugin_id);
02132        if (!protocol || plugin_id >= mysqlnd_plugin_count()) {
02133               return NULL;
02134        }
02135        DBG_RETURN((void *)((char *)protocol + sizeof(MYSQLND_PROTOCOL) + plugin_id * sizeof(void *)));
02136 }
02137 /* }}} */
02138 
02139 
02140 /* {{{ mysqlnd_protocol_get_methods */
02141 PHPAPI struct st_mysqlnd_protocol_methods *
02142 mysqlnd_protocol_get_methods()
02143 {
02144        return &mysqlnd_mysqlnd_protocol_methods;
02145 }
02146 /* }}} */
02147 
02148 
02149 /*
02150  * Local variables:
02151  * tab-width: 4
02152  * c-basic-offset: 4
02153  * End:
02154  * vim600: noet sw=4 ts=4 fdm=marker
02155  * vim<600: noet sw=4 ts=4
02156  */