root/ext/mysqlnd/mysqlnd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. MYSQLND_METHOD
  2. MYSQLND_METHOD
  3. MYSQLND_METHOD_PRIVATE
  4. MYSQLND_METHOD
  5. MYSQLND_METHOD
  6. mysqlnd_switch_to_ssl_if_needed
  7. MYSQLND_METHOD
  8. mysqlnd_run_authentication
  9. mysqlnd_connect_run_authentication
  10. MYSQLND_METHOD
  11. mysqlnd_connect
  12. MYSQLND_METHOD
  13. mysqlnd_stream_array_check_for_readiness
  14. mysqlnd_stream_array_to_fd_set
  15. mysqlnd_stream_array_from_fd_set
  16. _mysqlnd_poll
  17. MYSQLND_METHOD
  18. MYSQLND_METHOD
  19. MYSQLND_METHOD
  20. mysqlnd_old_escape_string
  21. MYSQLND_METHOD
  22. MYSQLND_METHOD
  23. MYSQLND_METHOD
  24. MYSQLND_METHOD_PRIVATE
  25. MYSQLND_METHOD_PRIVATE
  26. MYSQLND_METHOD_PRIVATE
  27. MYSQLND_METHOD
  28. MYSQLND_METHOD
  29. MYSQLND_METHOD
  30. MYSQLND_METHOD
  31. MYSQLND_METHOD
  32. MYSQLND_METHOD
  33. mysqlnd_get_client_info
  34. mysqlnd_get_client_version
  35. MYSQLND_METHOD
  36. MYSQLND_METHOD
  37. MYSQLND_METHOD
  38. MYSQLND_METHOD
  39. MYSQLND_METHOD
  40. MYSQLND_METHOD
  41. MYSQLND_METHOD
  42. MYSQLND_METHOD
  43. mysqlnd_field_type_name
  44. connect_attr_item_edtor
  45. connect_attr_item_pdtor
  46. MYSQLND_METHOD
  47. MYSQLND_METHOD
  48. mysqlnd_escape_string_for_tx_name_in_comment
  49. MYSQLND_METHOD
  50. MYSQLND_METHOD
  51. MYSQLND_CLASS_METHODS_START
  52. MYSQLND_METHOD_PRIVATE
  53. MYSQLND_CLASS_METHODS_START

   1 /*
   2   +----------------------------------------------------------------------+
   3   | PHP Version 5                                                        |
   4   +----------------------------------------------------------------------+
   5   | Copyright (c) 2006-2016 The PHP Group                                |
   6   +----------------------------------------------------------------------+
   7   | This source file is subject to version 3.01 of the PHP license,      |
   8   | that is bundled with this package in the file LICENSE, and is        |
   9   | available through the world-wide-web at the following url:           |
  10   | http://www.php.net/license/3_01.txt                                  |
  11   | If you did not receive a copy of the PHP license and are unable to   |
  12   | obtain it through the world-wide-web, please send a note to          |
  13   | license@php.net so we can mail you a copy immediately.               |
  14   +----------------------------------------------------------------------+
  15   | Authors: Andrey Hristov <andrey@mysql.com>                           |
  16   |          Ulf Wendel <uwendel@mysql.com>                              |
  17   |          Georg Richter <georg@mysql.com>                             |
  18   +----------------------------------------------------------------------+
  19 */
  20 
  21 /* $Id$ */
  22 #include "php.h"
  23 #include "mysqlnd.h"
  24 #include "mysqlnd_wireprotocol.h"
  25 #include "mysqlnd_priv.h"
  26 #include "mysqlnd_result.h"
  27 #include "mysqlnd_statistics.h"
  28 #include "mysqlnd_charset.h"
  29 #include "mysqlnd_debug.h"
  30 #include "ext/standard/php_smart_str.h"
  31 
  32 /*
  33   TODO :
  34   - Don't bind so tightly the metadata with the result set. This means
  35         that the metadata reading should not expect a MYSQLND_RES pointer, it
  36         does not need it, but return a pointer to the metadata (MYSQLND_FIELD *).
  37         For normal statements we will then just assign it to a member of
  38         MYSQLND_RES. For PS statements, it will stay as part of the statement
  39         (MYSQLND_STMT) between prepare and execute. At execute the new metadata
  40         will be sent by the server, so we will discard the old one and then
  41         finally attach it to the result set. This will make the code more clean,
  42         as a prepared statement won't have anymore stmt->result != NULL, as it
  43         is now, just to have where to store the metadata.
  44 
  45   - Change mysqlnd_simple_command to accept a heap dynamic array of MYSQLND_STRING
  46         terminated by a string with ptr being NULL. Thus, multi-part messages can be
  47         sent to the network like writev() and this can save at least for
  48         mysqlnd_stmt_send_long_data() new malloc. This change will probably make the
  49         code in few other places cleaner.
  50 */
  51 
  52 extern MYSQLND_CHARSET *mysqlnd_charsets;
  53 
  54 
  55 
  56 PHPAPI const char * const mysqlnd_old_passwd  = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. "
  57 "Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will "
  58 "store a new, and more secure, hash value in mysql.user. If this user is used in other scripts executed by PHP 5.2 or earlier you might need to remove the old-passwords "
  59 "flag from your my.cnf file";
  60 
  61 PHPAPI const char * const mysqlnd_server_gone = "MySQL server has gone away";
  62 PHPAPI const char * const mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now";
  63 PHPAPI const char * const mysqlnd_out_of_memory = "Out of memory";
  64 
  65 PHPAPI MYSQLND_STATS *mysqlnd_global_stats = NULL;
  66 
  67 
  68 /* {{{ mysqlnd_conn_data::free_options */
  69 static void
  70 MYSQLND_METHOD(mysqlnd_conn_data, free_options)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
  71 {
  72         zend_bool pers = conn->persistent;
  73 
  74         if (conn->options->charset_name) {
  75                 mnd_pefree(conn->options->charset_name, pers);
  76                 conn->options->charset_name = NULL;
  77         }
  78         if (conn->options->auth_protocol) {
  79                 mnd_pefree(conn->options->auth_protocol, pers);
  80                 conn->options->auth_protocol = NULL;
  81         }
  82         if (conn->options->num_commands) {
  83                 unsigned int i;
  84                 for (i = 0; i < conn->options->num_commands; i++) {
  85                         /* allocated with pestrdup */
  86                         mnd_pefree(conn->options->init_commands[i], pers);
  87                 }
  88                 mnd_pefree(conn->options->init_commands, pers);
  89                 conn->options->init_commands = NULL;
  90         }
  91         if (conn->options->cfg_file) {
  92                 mnd_pefree(conn->options->cfg_file, pers);
  93                 conn->options->cfg_file = NULL;
  94         }
  95         if (conn->options->cfg_section) {
  96                 mnd_pefree(conn->options->cfg_section, pers);
  97                 conn->options->cfg_section = NULL;
  98         }
  99         if (conn->options->connect_attr) {
 100                 zend_hash_destroy(conn->options->connect_attr);
 101                 mnd_pefree(conn->options->connect_attr, pers);
 102                 conn->options->connect_attr = NULL;
 103         }
 104 }
 105 /* }}} */
 106 
 107 
 108 /* {{{ mysqlnd_conn_data::free_contents */
 109 static void
 110 MYSQLND_METHOD(mysqlnd_conn_data, free_contents)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
 111 {
 112         zend_bool pers = conn->persistent;
 113 
 114         DBG_ENTER("mysqlnd_conn_data::free_contents");
 115 
 116         mysqlnd_local_infile_default(conn);
 117         if (conn->current_result) {
 118                 conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
 119                 conn->current_result = NULL;
 120         }
 121 
 122         if (conn->net) {
 123                 conn->net->data->m.free_contents(conn->net TSRMLS_CC);
 124         }
 125 
 126         DBG_INF("Freeing memory of members");
 127 
 128         if (conn->host) {
 129                 mnd_pefree(conn->host, pers);
 130                 conn->host = NULL;
 131         }
 132         if (conn->user) {
 133                 mnd_pefree(conn->user, pers);
 134                 conn->user = NULL;
 135         }
 136         if (conn->passwd) {
 137                 mnd_pefree(conn->passwd, pers);
 138                 conn->passwd = NULL;
 139         }
 140         if (conn->connect_or_select_db) {
 141                 mnd_pefree(conn->connect_or_select_db, pers);
 142                 conn->connect_or_select_db = NULL;
 143         }
 144         if (conn->unix_socket) {
 145                 mnd_pefree(conn->unix_socket, pers);
 146                 conn->unix_socket = NULL;
 147         }
 148         DBG_INF_FMT("scheme=%s", conn->scheme);
 149         if (conn->scheme) {
 150                 mnd_pefree(conn->scheme, pers);
 151                 conn->scheme = NULL;
 152         }
 153         if (conn->server_version) {
 154                 mnd_pefree(conn->server_version, pers);
 155                 conn->server_version = NULL;
 156         }
 157         if (conn->host_info) {
 158                 mnd_pefree(conn->host_info, pers);
 159                 conn->host_info = NULL;
 160         }
 161         if (conn->auth_plugin_data) {
 162                 mnd_pefree(conn->auth_plugin_data, pers);
 163                 conn->auth_plugin_data = NULL;
 164         }
 165         if (conn->last_message) {
 166                 mnd_pefree(conn->last_message, pers);
 167                 conn->last_message = NULL;
 168         }
 169         if (conn->error_info->error_list) {
 170                 zend_llist_clean(conn->error_info->error_list);
 171                 mnd_pefree(conn->error_info->error_list, pers);
 172                 conn->error_info->error_list = NULL;
 173         }
 174         conn->charset = NULL;
 175         conn->greet_charset = NULL;
 176 
 177         DBG_VOID_RETURN;
 178 }
 179 /* }}} */
 180 
 181 
 182 /* {{{ mysqlnd_conn_data::dtor */
 183 static void
 184 MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
 185 {
 186         DBG_ENTER("mysqlnd_conn_data::dtor");
 187         DBG_INF_FMT("conn=%llu", conn->thread_id);
 188 
 189         conn->m->free_contents(conn TSRMLS_CC);
 190         conn->m->free_options(conn TSRMLS_CC);
 191 
 192         if (conn->net) {
 193                 mysqlnd_net_free(conn->net, conn->stats, conn->error_info TSRMLS_CC);
 194                 conn->net = NULL;
 195         }
 196 
 197         if (conn->protocol) {
 198                 mysqlnd_protocol_free(conn->protocol TSRMLS_CC);
 199                 conn->protocol = NULL;
 200         }
 201 
 202         if (conn->stats) {
 203                 mysqlnd_stats_end(conn->stats);
 204         }
 205 
 206         mnd_pefree(conn, conn->persistent);
 207 
 208         DBG_VOID_RETURN;
 209 }
 210 /* }}} */
 211 
 212 
 213 /* {{{ mysqlnd_conn_data::simple_command_handle_response */
 214 static enum_func_status
 215 MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response)(MYSQLND_CONN_DATA * conn, enum mysqlnd_packet_type ok_packet,
 216                                                                                                                          zend_bool silent, enum php_mysqlnd_server_command command,
 217                                                                                                                          zend_bool ignore_upsert_status TSRMLS_DC)
 218 {
 219         enum_func_status ret = FAIL;
 220 
 221         DBG_ENTER("mysqlnd_conn_data::simple_command_handle_response");
 222         DBG_INF_FMT("silent=%u packet=%u command=%s", silent, ok_packet, mysqlnd_command_to_text[command]);
 223 
 224         switch (ok_packet) {
 225                 case PROT_OK_PACKET:{
 226                         MYSQLND_PACKET_OK * ok_response = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
 227                         if (!ok_response) {
 228                                 SET_OOM_ERROR(*conn->error_info);
 229                                 break;
 230                         }
 231                         if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
 232                                 if (!silent) {
 233                                         DBG_ERR_FMT("Error while reading %s's OK packet", mysqlnd_command_to_text[command]);
 234                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet. PID=%u",
 235                                                                          mysqlnd_command_to_text[command], getpid());
 236                                 }
 237                         } else {
 238                                 DBG_INF_FMT("OK from server");
 239                                 if (0xFF == ok_response->field_count) {
 240                                         /* The server signalled error. Set the error */
 241                                         SET_CLIENT_ERROR(*conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
 242                                         ret = FAIL;
 243                                         /*
 244                                           Cover a protocol design error: error packet does not
 245                                           contain the server status. Therefore, the client has no way
 246                                           to find out whether there are more result sets of
 247                                           a multiple-result-set statement pending. Luckily, in 5.0 an
 248                                           error always aborts execution of a statement, wherever it is
 249                                           a multi-statement or a stored procedure, so it should be
 250                                           safe to unconditionally turn off the flag here.
 251                                         */
 252                                         conn->upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
 253                                         SET_ERROR_AFF_ROWS(conn);
 254                                 } else {
 255                                         SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
 256                                                                         ok_response->message, ok_response->message_len,
 257                                                                         conn->persistent);
 258 
 259                                         if (!ignore_upsert_status) {
 260                                                 memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
 261                                                 conn->upsert_status->warning_count = ok_response->warning_count;
 262                                                 conn->upsert_status->server_status = ok_response->server_status;
 263                                                 conn->upsert_status->affected_rows = ok_response->affected_rows;
 264                                                 conn->upsert_status->last_insert_id = ok_response->last_insert_id;
 265                                         }
 266                                 }
 267                         }
 268                         PACKET_FREE(ok_response);
 269                         break;
 270                 }
 271                 case PROT_EOF_PACKET:{
 272                         MYSQLND_PACKET_EOF * ok_response = conn->protocol->m.get_eof_packet(conn->protocol, FALSE TSRMLS_CC);
 273                         if (!ok_response) {
 274                                 SET_OOM_ERROR(*conn->error_info);
 275                                 break;
 276                         }
 277                         if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
 278                                 SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE,
 279                                                                  "Malformed packet");
 280                                 if (!silent) {
 281                                         DBG_ERR_FMT("Error while reading %s's EOF packet", mysqlnd_command_to_text[command]);
 282                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's EOF packet. PID=%d",
 283                                                                          mysqlnd_command_to_text[command], getpid());
 284                                 }
 285                         } else if (0xFF == ok_response->field_count) {
 286                                 /* The server signalled error. Set the error */
 287                                 SET_CLIENT_ERROR(*conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
 288                                 SET_ERROR_AFF_ROWS(conn);
 289                         } else if (0xFE != ok_response->field_count) {
 290                                 SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
 291                                 if (!silent) {
 292                                         DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response->field_count);
 293                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X",
 294                                                                         ok_response->field_count);
 295                                 }
 296                         } else {
 297                                 DBG_INF_FMT("OK from server");
 298                         }
 299                         PACKET_FREE(ok_response);
 300                         break;
 301                 }
 302                 default:
 303                         SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
 304                         php_error_docref(NULL TSRMLS_CC, E_ERROR, "Wrong response packet %u passed to the function", ok_packet);
 305                         break;
 306         }
 307         DBG_INF(ret == PASS ? "PASS":"FAIL");
 308         DBG_RETURN(ret);
 309 }
 310 /* }}} */
 311 
 312 
 313 /* {{{ mysqlnd_conn_data::simple_command_send_request */
 314 static enum_func_status
 315 MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command,
 316                            const zend_uchar * const arg, size_t arg_len, zend_bool silent, zend_bool ignore_upsert_status TSRMLS_DC)
 317 {
 318         enum_func_status ret = PASS;
 319         MYSQLND_PACKET_COMMAND * cmd_packet;
 320 
 321         DBG_ENTER("mysqlnd_conn_data::simple_command_send_request");
 322         DBG_INF_FMT("command=%s silent=%u", mysqlnd_command_to_text[command], silent);
 323         DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
 324         DBG_INF_FMT("sending %u bytes", arg_len + 1); /* + 1 is for the command */
 325 
 326         switch (CONN_GET_STATE(conn)) {
 327                 case CONN_READY:
 328                         break;
 329                 case CONN_QUIT_SENT:
 330                         SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
 331                         DBG_ERR("Server is gone");
 332                         DBG_RETURN(FAIL);
 333                 default:
 334                         SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
 335                         DBG_ERR_FMT("Command out of sync. State=%u", CONN_GET_STATE(conn));
 336                         DBG_RETURN(FAIL);
 337         }
 338 
 339         SET_ERROR_AFF_ROWS(conn);
 340         SET_EMPTY_ERROR(*conn->error_info);
 341 
 342         cmd_packet = conn->protocol->m.get_command_packet(conn->protocol, FALSE TSRMLS_CC);
 343         if (!cmd_packet) {
 344                 SET_OOM_ERROR(*conn->error_info);
 345                 DBG_RETURN(FAIL);
 346         }
 347 
 348         cmd_packet->command = command;
 349         if (arg && arg_len) {
 350                 cmd_packet->argument = arg;
 351                 cmd_packet->arg_len  = arg_len;
 352         }
 353 
 354         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_COM_QUIT + command - 1 /* because of COM_SLEEP */ );
 355 
 356         if (! PACKET_WRITE(cmd_packet, conn)) {
 357                 if (!silent) {
 358                         DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]);
 359                         php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid());
 360                 }
 361                 CONN_SET_STATE(conn, CONN_QUIT_SENT);
 362                 conn->m->send_close(conn TSRMLS_CC);
 363                 DBG_ERR("Server is gone");
 364                 ret = FAIL;
 365         }
 366         PACKET_FREE(cmd_packet);
 367         DBG_RETURN(ret);
 368 }
 369 /* }}} */
 370 
 371 
 372 /* {{{ mysqlnd_conn_data::simple_command */
 373 static enum_func_status
 374 MYSQLND_METHOD(mysqlnd_conn_data, simple_command)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command,
 375                            const zend_uchar * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent,
 376                            zend_bool ignore_upsert_status TSRMLS_DC)
 377 {
 378         enum_func_status ret;
 379         DBG_ENTER("mysqlnd_conn_data::simple_command");
 380 
 381         ret = conn->m->simple_command_send_request(conn, command, arg, arg_len, silent, ignore_upsert_status TSRMLS_CC);
 382         if (PASS == ret && ok_packet != PROT_LAST) {
 383                 ret = conn->m->simple_command_handle_response(conn, ok_packet, silent, command, ignore_upsert_status TSRMLS_CC);
 384         }
 385 
 386         DBG_INF(ret == PASS ? "PASS":"FAIL");
 387         DBG_RETURN(ret);
 388 }
 389 /* }}} */
 390 
 391 
 392 /* {{{ mysqlnd_conn_data::set_server_option */
 393 static enum_func_status
 394 MYSQLND_METHOD(mysqlnd_conn_data, set_server_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_server_option option TSRMLS_DC)
 395 {
 396         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_server_option);
 397         zend_uchar buffer[2];
 398         enum_func_status ret = FAIL;
 399         DBG_ENTER("mysqlnd_conn_data::set_server_option");
 400         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
 401 
 402                 int2store(buffer, (unsigned int) option);
 403                 ret = conn->m->simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer), PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC);
 404 
 405                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
 406         }
 407         DBG_RETURN(ret);
 408 }
 409 /* }}} */
 410 
 411 
 412 /* {{{ mysqlnd_conn_data::restart_psession */
 413 static enum_func_status
 414 MYSQLND_METHOD(mysqlnd_conn_data, restart_psession)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
 415 {
 416         DBG_ENTER("mysqlnd_conn_data::restart_psession");
 417         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_REUSED);
 418         /* Free here what should not be seen by the next script */
 419         if (conn->last_message) {
 420                 mnd_pefree(conn->last_message, conn->persistent);
 421                 conn->last_message = NULL;
 422         }
 423         DBG_RETURN(PASS);
 424 }
 425 /* }}} */
 426 
 427 
 428 /* {{{ mysqlnd_conn_data::end_psession */
 429 static enum_func_status
 430 MYSQLND_METHOD(mysqlnd_conn_data, end_psession)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
 431 {
 432         DBG_ENTER("mysqlnd_conn_data::end_psession");
 433         DBG_RETURN(PASS);
 434 }
 435 /* }}} */
 436 
 437 
 438 /* {{{ mysqlnd_switch_to_ssl_if_needed */
 439 static enum_func_status
 440 mysqlnd_switch_to_ssl_if_needed(
 441                         MYSQLND_CONN_DATA * conn,
 442                         const MYSQLND_PACKET_GREET * const greet_packet,
 443                         const MYSQLND_OPTIONS * const options,
 444                         unsigned long mysql_flags
 445                         TSRMLS_DC
 446                 )
 447 {
 448         enum_func_status ret = FAIL;
 449         const MYSQLND_CHARSET * charset;
 450         MYSQLND_PACKET_AUTH * auth_packet;
 451         DBG_ENTER("mysqlnd_switch_to_ssl_if_needed");
 452         DBG_INF_FMT("client_capability_flags=%lu", mysql_flags);
 453         DBG_INF_FMT("CLIENT_LONG_PASSWORD=      %d", mysql_flags & CLIENT_LONG_PASSWORD? 1:0);
 454         DBG_INF_FMT("CLIENT_FOUND_ROWS=         %d", mysql_flags & CLIENT_FOUND_ROWS? 1:0);
 455         DBG_INF_FMT("CLIENT_LONG_FLAG=          %d", mysql_flags & CLIENT_LONG_FLAG? 1:0);
 456         DBG_INF_FMT("CLIENT_NO_SCHEMA=          %d", mysql_flags & CLIENT_NO_SCHEMA? 1:0);
 457         DBG_INF_FMT("CLIENT_COMPRESS=           %d", mysql_flags & CLIENT_COMPRESS? 1:0);
 458         DBG_INF_FMT("CLIENT_ODBC=                       %d", mysql_flags & CLIENT_ODBC? 1:0);
 459         DBG_INF_FMT("CLIENT_LOCAL_FILES=        %d", mysql_flags & CLIENT_LOCAL_FILES? 1:0);
 460         DBG_INF_FMT("CLIENT_IGNORE_SPACE=       %d", mysql_flags & CLIENT_IGNORE_SPACE? 1:0);
 461         DBG_INF_FMT("CLIENT_PROTOCOL_41=        %d", mysql_flags & CLIENT_PROTOCOL_41? 1:0);
 462         DBG_INF_FMT("CLIENT_INTERACTIVE=        %d", mysql_flags & CLIENT_INTERACTIVE? 1:0);
 463         DBG_INF_FMT("CLIENT_SSL=                        %d", mysql_flags & CLIENT_SSL? 1:0);
 464         DBG_INF_FMT("CLIENT_IGNORE_SIGPIPE=     %d", mysql_flags & CLIENT_IGNORE_SIGPIPE? 1:0);
 465         DBG_INF_FMT("CLIENT_TRANSACTIONS=       %d", mysql_flags & CLIENT_TRANSACTIONS? 1:0);
 466         DBG_INF_FMT("CLIENT_RESERVED=           %d", mysql_flags & CLIENT_RESERVED? 1:0);
 467         DBG_INF_FMT("CLIENT_SECURE_CONNECTION=%d", mysql_flags & CLIENT_SECURE_CONNECTION? 1:0);
 468         DBG_INF_FMT("CLIENT_MULTI_STATEMENTS=%d", mysql_flags & CLIENT_MULTI_STATEMENTS? 1:0);
 469         DBG_INF_FMT("CLIENT_MULTI_RESULTS=      %d", mysql_flags & CLIENT_MULTI_RESULTS? 1:0);
 470         DBG_INF_FMT("CLIENT_PS_MULTI_RESULTS=%d", mysql_flags & CLIENT_PS_MULTI_RESULTS? 1:0);
 471         DBG_INF_FMT("CLIENT_CONNECT_ATTRS=      %d", mysql_flags & CLIENT_PLUGIN_AUTH? 1:0);
 472         DBG_INF_FMT("CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA=     %d", mysql_flags & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA? 1:0);
 473         DBG_INF_FMT("CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS=       %d", mysql_flags & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS? 1:0);
 474         DBG_INF_FMT("CLIENT_SESSION_TRACK=              %d", mysql_flags & CLIENT_SESSION_TRACK? 1:0);
 475         DBG_INF_FMT("CLIENT_SSL_DONT_VERIFY_SERVER_CERT=        %d", mysql_flags & CLIENT_SSL_DONT_VERIFY_SERVER_CERT? 1:0);
 476         DBG_INF_FMT("CLIENT_SSL_VERIFY_SERVER_CERT=     %d", mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? 1:0);
 477         DBG_INF_FMT("CLIENT_REMEMBER_OPTIONS=           %d", mysql_flags & CLIENT_REMEMBER_OPTIONS? 1:0);
 478 
 479         auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
 480         if (!auth_packet) {
 481                 SET_OOM_ERROR(*conn->error_info);
 482                 goto end;
 483         }
 484         auth_packet->client_flags = mysql_flags;
 485         auth_packet->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
 486 
 487         if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
 488                 auth_packet->charset_no = charset->nr;
 489         } else {
 490                 auth_packet->charset_no = greet_packet->charset_no;
 491         }
 492 
 493 #ifdef MYSQLND_SSL_SUPPORTED
 494         if (mysql_flags & CLIENT_SSL) {
 495                 zend_bool server_has_ssl = (greet_packet->server_capabilities & CLIENT_SSL)? TRUE:FALSE;
 496                 if (server_has_ssl == FALSE) {
 497                         goto close_conn;
 498                 } else {
 499                         enum mysqlnd_ssl_peer verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT?
 500                                                                                                 MYSQLND_SSL_PEER_VERIFY:
 501                                                                                                 (mysql_flags & CLIENT_SSL_DONT_VERIFY_SERVER_CERT?
 502                                                                                                         MYSQLND_SSL_PEER_DONT_VERIFY:
 503                                                                                                         MYSQLND_SSL_PEER_DEFAULT);
 504                         DBG_INF("Switching to SSL");
 505                         if (!PACKET_WRITE(auth_packet, conn)) {
 506                                 goto close_conn;
 507                         }
 508 
 509                         conn->net->data->m.set_client_option(conn->net, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify TSRMLS_CC);
 510 
 511                         if (FAIL == conn->net->data->m.enable_ssl(conn->net TSRMLS_CC)) {
 512                                 goto end;
 513                         }
 514                 }
 515         }
 516 #else
 517         auth_packet->client_flags &= ~CLIENT_SSL;
 518         if (!PACKET_WRITE(auth_packet, conn)) {
 519                 goto close_conn;
 520         }
 521 #endif
 522         ret = PASS;
 523 end:
 524         PACKET_FREE(auth_packet);
 525         DBG_RETURN(ret);
 526 
 527 close_conn:
 528         CONN_SET_STATE(conn, CONN_QUIT_SENT);
 529         conn->m->send_close(conn TSRMLS_CC);
 530         SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
 531         PACKET_FREE(auth_packet);
 532         DBG_RETURN(ret);
 533 }
 534 /* }}} */
 535 
 536 
 537 /* {{{ mysqlnd_conn_data::fetch_auth_plugin_by_name */
 538 static struct st_mysqlnd_authentication_plugin *
 539 MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name)(const char * const requested_protocol TSRMLS_DC)
 540 {
 541         struct st_mysqlnd_authentication_plugin * auth_plugin;
 542         char * plugin_name = NULL;
 543         DBG_ENTER("mysqlnd_conn_data::fetch_auth_plugin_by_name");
 544 
 545         mnd_sprintf(&plugin_name, 0, "auth_plugin_%s", requested_protocol);
 546         DBG_INF_FMT("looking for %s auth plugin", plugin_name);
 547         auth_plugin = mysqlnd_plugin_find(plugin_name);
 548         mnd_sprintf_free(plugin_name);
 549 
 550         DBG_RETURN(auth_plugin);
 551 }
 552 /* }}} */
 553 
 554 
 555 /* {{{ mysqlnd_run_authentication */
 556 static enum_func_status
 557 mysqlnd_run_authentication(
 558                         MYSQLND_CONN_DATA * conn,
 559                         const char * const user,
 560                         const char * const passwd,
 561                         const size_t passwd_len,
 562                         const char * const db,
 563                         const size_t db_len,
 564                         const zend_uchar * const auth_plugin_data,
 565                         const size_t auth_plugin_data_len,
 566                         const char * const auth_protocol,
 567                         unsigned int charset_no,
 568                         const MYSQLND_OPTIONS * const options,
 569                         unsigned long mysql_flags,
 570                         zend_bool silent,
 571                         zend_bool is_change_user
 572                         TSRMLS_DC)
 573 {
 574         enum_func_status ret = FAIL;
 575         zend_bool first_call = TRUE;
 576 
 577         char * switch_to_auth_protocol = NULL;
 578         size_t switch_to_auth_protocol_len = 0;
 579         char * requested_protocol = NULL;
 580         zend_uchar * plugin_data;
 581         size_t plugin_data_len;
 582 
 583         DBG_ENTER("mysqlnd_run_authentication");
 584 
 585         plugin_data_len = auth_plugin_data_len;
 586         plugin_data = mnd_emalloc(plugin_data_len + 1);
 587         if (!plugin_data) {
 588                 goto end;
 589         }
 590         memcpy(plugin_data, auth_plugin_data, plugin_data_len);
 591         plugin_data[plugin_data_len] = '\0';
 592 
 593         requested_protocol = mnd_pestrdup(auth_protocol? auth_protocol : MYSQLND_DEFAULT_AUTH_PROTOCOL, FALSE);
 594         if (!requested_protocol) {
 595                 goto end;
 596         }
 597 
 598         do {
 599                 struct st_mysqlnd_authentication_plugin * auth_plugin = conn->m->fetch_auth_plugin_by_name(requested_protocol TSRMLS_CC);
 600 
 601                 if (!auth_plugin) {
 602                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "The server requested authentication method unknown to the client [%s]", requested_protocol);
 603                         SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "The server requested authentication method unknown to the client");
 604                         goto end;
 605                 }
 606                 DBG_INF("plugin found");
 607 
 608                 {
 609                         zend_uchar * switch_to_auth_protocol_data = NULL;
 610                         size_t switch_to_auth_protocol_data_len = 0;
 611                         zend_uchar * scrambled_data = NULL;
 612                         size_t scrambled_data_len = 0;
 613 
 614                         switch_to_auth_protocol = NULL;
 615                         switch_to_auth_protocol_len = 0;
 616 
 617                         if (conn->auth_plugin_data) {
 618                                 mnd_pefree(conn->auth_plugin_data, conn->persistent);
 619                                 conn->auth_plugin_data = NULL;
 620                         }
 621                         conn->auth_plugin_data_len = plugin_data_len;
 622                         conn->auth_plugin_data = mnd_pemalloc(conn->auth_plugin_data_len, conn->persistent);
 623                         if (!conn->auth_plugin_data) {
 624                                 SET_OOM_ERROR(*conn->error_info);
 625                                 goto end;
 626                         }
 627                         memcpy(conn->auth_plugin_data, plugin_data, plugin_data_len);
 628 
 629                         DBG_INF_FMT("salt(%d)=[%.*s]", plugin_data_len, plugin_data_len, plugin_data);
 630                         /* The data should be allocated with malloc() */
 631                         scrambled_data =
 632                                 auth_plugin->methods.get_auth_data(NULL, &scrambled_data_len, conn, user, passwd, passwd_len,
 633                                                                                                    plugin_data, plugin_data_len, options, &conn->net->data->options, mysql_flags TSRMLS_CC);
 634                         if (conn->error_info->error_no) {
 635                                 goto end;       
 636                         }
 637                         if (FALSE == is_change_user) {
 638                                 ret = mysqlnd_auth_handshake(conn, user, passwd, passwd_len, db, db_len, options, mysql_flags,
 639                                                                                         charset_no,
 640                                                                                         first_call,
 641                                                                                         requested_protocol,
 642                                                                                         scrambled_data, scrambled_data_len,
 643                                                                                         &switch_to_auth_protocol, &switch_to_auth_protocol_len,
 644                                                                                         &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
 645                                                                                         TSRMLS_CC);
 646                         } else {
 647                                 ret = mysqlnd_auth_change_user(conn, user, strlen(user), passwd, passwd_len, db, db_len, silent,
 648                                                                                            first_call,
 649                                                                                            requested_protocol,
 650                                                                                            scrambled_data, scrambled_data_len,
 651                                                                                            &switch_to_auth_protocol, &switch_to_auth_protocol_len,
 652                                                                                            &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
 653                                                                                            TSRMLS_CC);                          
 654                         }
 655                         first_call = FALSE;
 656                         free(scrambled_data);
 657 
 658                         DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
 659                         if (requested_protocol && switch_to_auth_protocol) {
 660                                 mnd_efree(requested_protocol);
 661                                 requested_protocol = switch_to_auth_protocol;
 662                         }
 663 
 664                         if (plugin_data) {
 665                                 mnd_efree(plugin_data);
 666                         }
 667                         plugin_data_len = switch_to_auth_protocol_data_len;
 668                         plugin_data = switch_to_auth_protocol_data;
 669                 }
 670                 DBG_INF_FMT("conn->error_info->error_no = %d", conn->error_info->error_no);
 671         } while (ret == FAIL && conn->error_info->error_no == 0 && switch_to_auth_protocol != NULL);
 672                 
 673         if (ret == PASS) {
 674                 DBG_INF_FMT("saving requested_protocol=%s", requested_protocol);
 675                 conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol TSRMLS_CC);
 676         }
 677 end:
 678         if (plugin_data) {
 679                 mnd_efree(plugin_data);
 680         }
 681         if (requested_protocol) {
 682                 mnd_efree(requested_protocol);
 683         }
 684 
 685         DBG_RETURN(ret);
 686 }
 687 /* }}} */
 688 
 689 
 690 /* {{{ mysqlnd_connect_run_authentication */
 691 static enum_func_status
 692 mysqlnd_connect_run_authentication(
 693                         MYSQLND_CONN_DATA * conn,
 694                         const char * const user,
 695                         const char * const passwd,
 696                         const char * const db,
 697                         size_t db_len,
 698                         size_t passwd_len,
 699                         const MYSQLND_PACKET_GREET * const greet_packet,
 700                         const MYSQLND_OPTIONS * const options,
 701                         unsigned long mysql_flags
 702                         TSRMLS_DC)
 703 {
 704         enum_func_status ret = FAIL;
 705         DBG_ENTER("mysqlnd_connect_run_authentication");
 706 
 707         ret = mysqlnd_switch_to_ssl_if_needed(conn, greet_packet, options, mysql_flags TSRMLS_CC);
 708         if (PASS == ret) {
 709                 ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, db_len,
 710                                                                                  greet_packet->auth_plugin_data, greet_packet->auth_plugin_data_len, greet_packet->auth_protocol,
 711                                                                                  greet_packet->charset_no, options, mysql_flags, FALSE /*silent*/, FALSE/*is_change*/ TSRMLS_CC);
 712         }
 713         DBG_RETURN(ret);
 714 }
 715 /* }}} */
 716 
 717 
 718 /* {{{ mysqlnd_conn_data::execute_init_commands */
 719 static enum_func_status
 720 MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
 721 {
 722         enum_func_status ret = PASS;
 723 
 724         DBG_ENTER("mysqlnd_conn_data::execute_init_commands");
 725         if (conn->options->init_commands) {
 726                 unsigned int current_command = 0;
 727                 for (; current_command < conn->options->num_commands; ++current_command) {
 728                         const char * const command = conn->options->init_commands[current_command];
 729                         if (command) {
 730                                 MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_EXECUTED_COUNT);
 731                                 if (PASS != conn->m->query(conn, command, strlen(command) TSRMLS_CC)) {
 732                                         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_FAILED_COUNT);
 733                                         ret = FAIL;
 734                                         break;
 735                                 }
 736                                 if (conn->last_query_type == QUERY_SELECT) {
 737                                         MYSQLND_RES * result = conn->m->use_result(conn, 0 TSRMLS_CC);
 738                                         if (result) {
 739                                                 result->m.free_result(result, TRUE TSRMLS_CC);
 740                                         }
 741                                 }
 742                         }
 743                 }
 744         }
 745         DBG_RETURN(ret);
 746 }
 747 /* }}} */
 748 
 749 
 750 /* {{{ mysqlnd_conn_data::get_updated_connect_flags */
 751 static unsigned int
 752 MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA * conn, unsigned int mysql_flags TSRMLS_DC)
 753 {
 754         MYSQLND_NET * net = conn->net;
 755 
 756         DBG_ENTER("mysqlnd_conn_data::get_updated_connect_flags");
 757         /* we allow load data local infile by default */
 758         mysql_flags |= MYSQLND_CAPABILITIES;
 759 
 760         mysql_flags |= conn->options->flags; /* use the flags from set_client_option() */
 761 
 762 #ifndef MYSQLND_COMPRESSION_ENABLED
 763         if (mysql_flags & CLIENT_COMPRESS) {
 764                 mysql_flags &= ~CLIENT_COMPRESS;
 765         }
 766 #else
 767         if (net && net->data->options.flags & MYSQLND_NET_FLAG_USE_COMPRESSION) {
 768                 mysql_flags |= CLIENT_COMPRESS;
 769         }
 770 #endif
 771 #ifndef MYSQLND_SSL_SUPPORTED
 772         if (mysql_flags & CLIENT_SSL) {
 773                 mysql_flags &= ~CLIENT_SSL;
 774         }
 775 #else
 776         if (net && (net->data->options.ssl_key || net->data->options.ssl_cert ||
 777                 net->data->options.ssl_ca || net->data->options.ssl_capath || net->data->options.ssl_cipher))
 778         {
 779                 mysql_flags |= CLIENT_SSL;
 780         }
 781 #endif
 782 
 783         DBG_RETURN(mysql_flags);
 784 }
 785 /* }}} */
 786 
 787 
 788 /* {{{ mysqlnd_conn_data::connect_handshake */
 789 static enum_func_status
 790 MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn,
 791                                                 const char * const host, const char * const user,
 792                                                 const char * const passwd, const unsigned int passwd_len,
 793                                                 const char * const db, const unsigned int db_len,
 794                                                 const unsigned int mysql_flags TSRMLS_DC)
 795 {
 796         MYSQLND_PACKET_GREET * greet_packet;
 797         MYSQLND_NET * net = conn->net;
 798 
 799         DBG_ENTER("mysqlnd_conn_data::connect_handshake");
 800 
 801         greet_packet = conn->protocol->m.get_greet_packet(conn->protocol, FALSE TSRMLS_CC);
 802         if (!greet_packet) {
 803                 SET_OOM_ERROR(*conn->error_info);
 804                 DBG_RETURN(FAIL); /* OOM */
 805         }
 806 
 807         if (FAIL == net->data->m.connect_ex(conn->net, conn->scheme, conn->scheme_len, conn->persistent,
 808                                                                                 conn->stats, conn->error_info TSRMLS_CC))
 809         {
 810                 goto err;
 811         }
 812 
 813         DBG_INF_FMT("stream=%p", net->data->m.get_stream(net TSRMLS_CC));
 814 
 815         if (FAIL == PACKET_READ(greet_packet, conn)) {
 816                 DBG_ERR("Error while reading greeting packet");
 817                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading greeting packet. PID=%d", getpid());
 818                 goto err;
 819         } else if (greet_packet->error_no) {
 820                 DBG_ERR_FMT("errorno=%u error=%s", greet_packet->error_no, greet_packet->error);
 821                 SET_CLIENT_ERROR(*conn->error_info, greet_packet->error_no, greet_packet->sqlstate, greet_packet->error);
 822                 goto err;
 823         } else if (greet_packet->pre41) {
 824                 DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", greet_packet->server_version);
 825                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 "
 826                                                 " is not supported. Server is %-.32s", greet_packet->server_version);
 827                 SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
 828                                                  "Connecting to 3.22, 3.23 & 4.0 servers is not supported");
 829                 goto err;
 830         }
 831 
 832         conn->thread_id                 = greet_packet->thread_id;
 833         conn->protocol_version  = greet_packet->protocol_version;
 834         conn->server_version    = mnd_pestrdup(greet_packet->server_version, conn->persistent);
 835 
 836         conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no);
 837         if (!conn->greet_charset) {
 838                 php_error_docref(NULL TSRMLS_CC, E_WARNING,
 839                         "Server sent charset (%d) unknown to the client. Please, report to the developers", greet_packet->charset_no);
 840                 SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
 841                         "Server sent charset unknown to the client. Please, report to the developers");
 842                 goto err;
 843         }
 844 
 845         conn->client_flag                       = mysql_flags;
 846         conn->server_capabilities       = greet_packet->server_capabilities;
 847 
 848         if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, (size_t) passwd_len,
 849                                                                                                    greet_packet, conn->options, mysql_flags TSRMLS_CC))
 850         {
 851                 goto err;
 852         }
 853         memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
 854         conn->upsert_status->warning_count = 0;
 855         conn->upsert_status->server_status = greet_packet->server_status;
 856         conn->upsert_status->affected_rows = 0;
 857 
 858         PACKET_FREE(greet_packet);
 859         DBG_RETURN(PASS);
 860 err:
 861         conn->client_flag = 0;
 862         conn->server_capabilities = 0;
 863         PACKET_FREE(greet_packet);
 864         DBG_RETURN(FAIL);
 865 }
 866 /* }}} */
 867 
 868 
 869 /* {{{ mysqlnd_conn_data::connect */
 870 static enum_func_status
 871 MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn,
 872                                                  const char *host, const char *user,
 873                                                  const char *passwd, unsigned int passwd_len,
 874                                                  const char *db, unsigned int db_len,
 875                                                  unsigned int port,
 876                                                  const char *socket_or_pipe,
 877                                                  unsigned int mysql_flags
 878                                                  TSRMLS_DC)
 879 {
 880         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, connect);
 881         size_t host_len;
 882         zend_bool unix_socket = FALSE;
 883         zend_bool named_pipe = FALSE;
 884         zend_bool reconnect = FALSE;
 885         zend_bool saved_compression = FALSE;
 886         zend_bool local_tx_started = FALSE;
 887         MYSQLND_NET * net = conn->net;
 888 
 889         DBG_ENTER("mysqlnd_conn_data::connect");
 890         DBG_INF_FMT("conn=%p", conn);
 891 
 892         if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
 893                 goto err;
 894         }
 895         local_tx_started = TRUE;
 896 
 897         SET_EMPTY_ERROR(*conn->error_info);
 898         SET_ERROR_AFF_ROWS(conn);
 899 
 900         DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u persistent=%u state=%u",
 901                                 host?host:"", user?user:"", db?db:"", port, mysql_flags,
 902                                 conn? conn->persistent:0, conn? CONN_GET_STATE(conn):-1);
 903 
 904         if (CONN_GET_STATE(conn) > CONN_ALLOCED && CONN_GET_STATE(conn) ) {
 905                 DBG_INF("Connecting on a connected handle.");
 906 
 907                 if (CONN_GET_STATE(conn) < CONN_QUIT_SENT) {
 908                         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CLOSE_IMPLICIT);
 909                         reconnect = TRUE;
 910                         conn->m->send_close(conn TSRMLS_CC);
 911                 }
 912 
 913                 conn->m->free_contents(conn TSRMLS_CC);
 914                 MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
 915                 if (conn->persistent) {
 916                         MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
 917                 }
 918                 /* Now reconnect using the same handle */
 919                 if (net->data->compressed) {
 920                         /*
 921                           we need to save the state. As we will re-connect, net->compressed should be off, or
 922                           we will look for a compression header as part of the greet message, but there will
 923                           be none.
 924                         */
 925                         saved_compression = TRUE;
 926                         net->data->compressed = FALSE;
 927                 }
 928                 if (net->data->ssl) {
 929                         net->data->ssl = FALSE;
 930                 }
 931         } else {
 932                 unsigned int max_allowed_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
 933                 conn->m->set_client_option(conn, MYSQLND_OPT_MAX_ALLOWED_PACKET, (char *)&max_allowed_size TSRMLS_CC);
 934         }
 935 
 936         if (!host || !host[0]) {
 937                 host = "localhost";
 938         }
 939         if (!user) {
 940                 DBG_INF_FMT("no user given, using empty string");
 941                 user = "";
 942         }
 943         if (!passwd) {
 944                 DBG_INF_FMT("no password given, using empty string");
 945                 passwd = "";
 946                 passwd_len = 0;
 947         }
 948         if (!db) {
 949                 DBG_INF_FMT("no db given, using empty string");
 950                 db = "";
 951                 db_len = 0;
 952         } else {
 953                 mysql_flags |= CLIENT_CONNECT_WITH_DB;  
 954         }
 955 
 956         host_len = strlen(host);
 957         {
 958                 char * transport = NULL;
 959                 int transport_len;
 960 #ifndef PHP_WIN32
 961                 if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) {
 962                         DBG_INF_FMT("socket=%s", socket_or_pipe? socket_or_pipe:"n/a");
 963                         if (!socket_or_pipe) {
 964                                 socket_or_pipe = "/tmp/mysql.sock";
 965                         }
 966                         transport_len = mnd_sprintf(&transport, 0, "unix://%s", socket_or_pipe);
 967                         unix_socket = TRUE;
 968 #else
 969                 if (host_len == sizeof(".") - 1 && host[0] == '.') {
 970                         /* named pipe in socket */
 971                         if (!socket_or_pipe) {
 972                                 socket_or_pipe = "\\\\.\\pipe\\MySQL";
 973                         }
 974                         transport_len = mnd_sprintf(&transport, 0, "pipe://%s", socket_or_pipe);
 975                         named_pipe = TRUE;
 976 #endif
 977                 } else {
 978                         if (!port) {
 979                                 port = 3306;
 980                         }
 981                         transport_len = mnd_sprintf(&transport, 0, "tcp://%s:%u", host, port);
 982                 }
 983                 if (!transport) {
 984                         SET_OOM_ERROR(*conn->error_info);
 985                         goto err; /* OOM */
 986                 }
 987                 DBG_INF_FMT("transport=%s conn->scheme=%s", transport, conn->scheme);
 988                 conn->scheme = mnd_pestrndup(transport, transport_len, conn->persistent);
 989                 conn->scheme_len = transport_len;
 990                 mnd_sprintf_free(transport);
 991                 transport = NULL;
 992                 if (!conn->scheme) {
 993                         goto err; /* OOM */
 994                 }
 995         }
 996 
 997         mysql_flags = conn->m->get_updated_connect_flags(conn, mysql_flags TSRMLS_CC);
 998 
 999         if (FAIL == conn->m->connect_handshake(conn, host, user, passwd, passwd_len, db, db_len, mysql_flags TSRMLS_CC)) {
1000                 goto err;
1001         }
1002 
1003         {
1004                 CONN_SET_STATE(conn, CONN_READY);
1005 
1006                 if (saved_compression) {
1007                         net->data->compressed = TRUE;
1008                 }
1009                 /*
1010                   If a connect on a existing handle is performed and mysql_flags is
1011                   passed which doesn't CLIENT_COMPRESS, then we need to overwrite the value
1012                   which we set based on saved_compression.
1013                 */
1014                 net->data->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
1015 
1016                 conn->user                              = mnd_pestrdup(user, conn->persistent);
1017                 conn->user_len                  = strlen(conn->user);
1018                 conn->passwd                    = mnd_pestrndup(passwd, passwd_len, conn->persistent);
1019                 conn->passwd_len                = passwd_len;
1020                 conn->port                              = port;
1021                 conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
1022                 conn->connect_or_select_db_len = db_len;
1023 
1024                 if (!conn->user || !conn->passwd || !conn->connect_or_select_db) {
1025                         SET_OOM_ERROR(*conn->error_info);
1026                         goto err; /* OOM */
1027                 }
1028 
1029                 if (!unix_socket && !named_pipe) {
1030                         conn->host = mnd_pestrdup(host, conn->persistent);
1031                         if (!conn->host) {
1032                                 SET_OOM_ERROR(*conn->error_info);
1033                                 goto err; /* OOM */
1034                         }
1035                         conn->host_len = strlen(conn->host);
1036                         {
1037                                 char *p;
1038                                 mnd_sprintf(&p, 0, "%s via TCP/IP", conn->host);
1039                                 if (!p) {
1040                                         SET_OOM_ERROR(*conn->error_info);
1041                                         goto err; /* OOM */
1042                                 }
1043                                 conn->host_info =  mnd_pestrdup(p, conn->persistent);
1044                                 mnd_sprintf_free(p);
1045                                 if (!conn->host_info) {
1046                                         SET_OOM_ERROR(*conn->error_info);
1047                                         goto err; /* OOM */
1048                                 }
1049                         }
1050                 } else {
1051                         conn->unix_socket = mnd_pestrdup(socket_or_pipe, conn->persistent);
1052                         if (unix_socket) {
1053                                 conn->host_info = mnd_pestrdup("Localhost via UNIX socket", conn->persistent);
1054                         } else if (named_pipe) {
1055                                 char *p;
1056                                 mnd_sprintf(&p, 0, "%s via named pipe", conn->unix_socket);
1057                                 if (!p) {
1058                                         SET_OOM_ERROR(*conn->error_info);
1059                                         goto err; /* OOM */
1060                                 }
1061                                 conn->host_info =  mnd_pestrdup(p, conn->persistent);
1062                                 mnd_sprintf_free(p);
1063                                 if (!conn->host_info) {
1064                                         SET_OOM_ERROR(*conn->error_info);
1065                                         goto err; /* OOM */
1066                                 }
1067                         } else {
1068                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Impossible. Should be either socket or a pipe. Report a bug!");
1069                         }
1070                         if (!conn->unix_socket || !conn->host_info) {
1071                                 SET_OOM_ERROR(*conn->error_info);
1072                                 goto err; /* OOM */
1073                         }
1074                         conn->unix_socket_len = strlen(conn->unix_socket);
1075                 }
1076                 conn->max_packet_size   = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
1077                 /* todo: check if charset is available */
1078 
1079                 SET_EMPTY_ERROR(*conn->error_info);
1080 
1081                 mysqlnd_local_infile_default(conn);
1082 
1083                 if (FAIL == conn->m->execute_init_commands(conn TSRMLS_CC)) {
1084                         goto err;
1085                 }
1086 
1087                 MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_CONNECT_SUCCESS, 1, STAT_OPENED_CONNECTIONS, 1);
1088                 if (reconnect) {
1089                         MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT);
1090                 }
1091                 if (conn->persistent) {
1092                         MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PCONNECT_SUCCESS, 1, STAT_OPENED_PERSISTENT_CONNECTIONS, 1);
1093                 }
1094 
1095                 DBG_INF_FMT("connection_id=%llu", conn->thread_id);
1096 
1097                 conn->m->local_tx_end(conn, this_func, PASS TSRMLS_CC);
1098                 DBG_RETURN(PASS);
1099         }
1100 err:
1101 
1102         DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme);
1103         if (!conn->error_info->error_no) {
1104                 SET_CLIENT_ERROR(*conn->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, conn->error_info->error? conn->error_info->error:"Unknown error");
1105                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%u] %.128s (trying to connect via %s)",
1106                                                  conn->error_info->error_no, conn->error_info->error, conn->scheme);
1107         }
1108 
1109         conn->m->free_contents(conn TSRMLS_CC);
1110         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE);
1111         if (TRUE == local_tx_started) {
1112                 conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
1113         }
1114 
1115         DBG_RETURN(FAIL);
1116 }
1117 /* }}} */
1118 
1119 
1120 /* {{{ mysqlnd_conn::connect */
1121 static enum_func_status
1122 MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
1123                                                  const char * host, const char * user,
1124                                                  const char * passwd, unsigned int passwd_len,
1125                                                  const char * db, unsigned int db_len,
1126                                                  unsigned int port,
1127                                                  const char * socket_or_pipe,
1128                                                  unsigned int mysql_flags
1129                                                  TSRMLS_DC)
1130 {
1131         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, connect);
1132         enum_func_status ret = FAIL;
1133         MYSQLND_CONN_DATA * conn = conn_handle->data;
1134 
1135         DBG_ENTER("mysqlnd_conn::connect");
1136 
1137         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1138                 mysqlnd_options4(conn_handle, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "mysqlnd");
1139                 ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC);
1140 
1141                 conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
1142         }
1143         DBG_RETURN(ret);
1144 }
1145 /* }}} */
1146 
1147 
1148 /* {{{ mysqlnd_connect */
1149 PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn_handle,
1150                                                  const char * host, const char * user,
1151                                                  const char * passwd, unsigned int passwd_len,
1152                                                  const char * db, unsigned int db_len,
1153                                                  unsigned int port,
1154                                                  const char * socket_or_pipe,
1155                                                  unsigned int mysql_flags,
1156                                                  unsigned int client_api_flags
1157                                                  TSRMLS_DC)
1158 {
1159         enum_func_status ret = FAIL;
1160         zend_bool self_alloced = FALSE;
1161 
1162         DBG_ENTER("mysqlnd_connect");
1163         DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags);
1164 
1165         if (!conn_handle) {
1166                 self_alloced = TRUE;
1167                 if (!(conn_handle = mysqlnd_init(client_api_flags, FALSE))) {
1168                         /* OOM */
1169                         DBG_RETURN(NULL);
1170                 }
1171         }
1172 
1173         ret = conn_handle->m->connect(conn_handle, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC);
1174 
1175         if (ret == FAIL) {
1176                 if (self_alloced) {
1177                         /*
1178                           We have alloced, thus there are no references to this
1179                           object - we are free to kill it!
1180                         */
1181                         conn_handle->m->dtor(conn_handle TSRMLS_CC);
1182                 }
1183                 DBG_RETURN(NULL);
1184         }
1185         DBG_RETURN(conn_handle);
1186 }
1187 /* }}} */
1188 
1189 
1190 /* {{{ mysqlnd_conn_data::query */
1191 /*
1192   If conn->error_info->error_no is not zero, then we had an error.
1193   Still the result from the query is PASS
1194 */
1195 static enum_func_status
1196 MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len TSRMLS_DC)
1197 {
1198         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, query);
1199         enum_func_status ret = FAIL;
1200         DBG_ENTER("mysqlnd_conn_data::query");
1201         DBG_INF_FMT("conn=%p conn=%llu query=%s", conn, conn->thread_id, query);
1202 
1203         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1204                 if (PASS == conn->m->send_query(conn, query, query_len TSRMLS_CC) &&
1205                         PASS == conn->m->reap_query(conn TSRMLS_CC))
1206                 {
1207                         ret = PASS;
1208                         if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
1209                                 MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
1210                         }
1211                 }
1212                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1213         }
1214         DBG_RETURN(ret);
1215 }
1216 /* }}} */
1217 
1218 
1219 /* {{{ mysqlnd_conn_data::send_query */
1220 static enum_func_status
1221 MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len TSRMLS_DC)
1222 {
1223         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, send_query);
1224         enum_func_status ret = FAIL;
1225         DBG_ENTER("mysqlnd_conn_data::send_query");
1226         DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
1227         DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
1228 
1229         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1230                 ret = conn->m->simple_command(conn, COM_QUERY, (zend_uchar *) query, query_len,
1231                                                                                          PROT_LAST /* we will handle the OK packet*/,
1232                                                                                          FALSE, FALSE TSRMLS_CC);
1233                 if (PASS == ret) {
1234                         CONN_SET_STATE(conn, CONN_QUERY_SENT);
1235                 }
1236                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1237         }
1238         DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
1239         DBG_RETURN(ret);
1240 }
1241 /* }}} */
1242 
1243 
1244 /* {{{ mysqlnd_conn_data::reap_query */
1245 static enum_func_status
1246 MYSQLND_METHOD(mysqlnd_conn_data, reap_query)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
1247 {
1248         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, reap_query);
1249         enum_mysqlnd_connection_state state = CONN_GET_STATE(conn);
1250         enum_func_status ret = FAIL;
1251         DBG_ENTER("mysqlnd_conn_data::reap_query");
1252         DBG_INF_FMT("conn=%llu", conn->thread_id);
1253 
1254         DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
1255         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1256                 if (state <= CONN_READY || state == CONN_QUIT_SENT) {
1257                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not opened, clear or has been closed");
1258                         DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state);
1259                         DBG_RETURN(ret);
1260                 }
1261                 ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC);
1262 
1263                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1264         }
1265         DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
1266         DBG_RETURN(ret);
1267 }
1268 /* }}} */
1269 
1270 
1271 #include "php_network.h"
1272 
1273 /* {{{ mysqlnd_stream_array_to_fd_set */
1274 MYSQLND ** mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array TSRMLS_DC)
1275 {
1276         int cnt = 0;
1277         MYSQLND **p = conn_array, **p_p;
1278         MYSQLND **ret = NULL;
1279 
1280         while (*p) {
1281                 if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
1282                         cnt++;
1283                 }
1284                 p++;
1285         }
1286         if (cnt) {
1287                 MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
1288                 p_p = p = conn_array;
1289                 while (*p) {
1290                         if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
1291                                 *ret_p = *p;
1292                                 *p = NULL;
1293                                 ret_p++;
1294                         } else {
1295                                 *p_p = *p;
1296                                 p_p++;
1297                         }
1298                         p++;
1299                 }
1300                 *ret_p = NULL;
1301         }
1302         return ret;
1303 }
1304 /* }}} */
1305 
1306 
1307 /* {{{ mysqlnd_stream_array_to_fd_set */
1308 static int mysqlnd_stream_array_to_fd_set(MYSQLND ** conn_array, fd_set * fds, php_socket_t * max_fd TSRMLS_DC)
1309 {
1310         php_socket_t this_fd;
1311         php_stream *stream = NULL;
1312         unsigned int cnt = 0;
1313         MYSQLND **p = conn_array;
1314         DBG_ENTER("mysqlnd_stream_array_to_fd_set");
1315 
1316         while (*p) {
1317                 /* get the fd.
1318                  * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
1319                  * when casting.  It is only used here so that the buffered data warning
1320                  * is not displayed.
1321                  * */
1322                 stream = (*p)->data->net->data->m.get_stream((*p)->data->net TSRMLS_CC);
1323                 DBG_INF_FMT("conn=%llu stream=%p", (*p)->data->thread_id, stream);
1324                 if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
1325                                                                                 (void*)&this_fd, 1) && this_fd >= 0) {
1326 
1327                         PHP_SAFE_FD_SET(this_fd, fds);
1328 
1329                         if (this_fd > *max_fd) {
1330                                 *max_fd = this_fd;
1331                         }
1332                         cnt++;
1333                 }
1334                 p++;
1335         }
1336         DBG_RETURN(cnt ? 1 : 0);
1337 }
1338 /* }}} */
1339 
1340 
1341 /* {{{ mysqlnd_stream_array_from_fd_set */
1342 static int mysqlnd_stream_array_from_fd_set(MYSQLND ** conn_array, fd_set * fds TSRMLS_DC)
1343 {
1344         php_socket_t this_fd;
1345         php_stream *stream = NULL;
1346         int ret = 0;
1347         zend_bool disproportion = FALSE;
1348         MYSQLND **fwd = conn_array, **bckwd = conn_array;
1349         DBG_ENTER("mysqlnd_stream_array_from_fd_set");
1350 
1351         while (*fwd) {
1352                 stream = (*fwd)->data->net->data->m.get_stream((*fwd)->data->net TSRMLS_CC);
1353                 DBG_INF_FMT("conn=%llu stream=%p", (*fwd)->data->thread_id, stream);
1354                 if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
1355                                                                                 (void*)&this_fd, 1) && this_fd >= 0) {
1356                         if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
1357                                 if (disproportion) {
1358                                         *bckwd = *fwd;
1359                                 }
1360                                 bckwd++;
1361                                 fwd++;
1362                                 ret++;
1363                                 continue;
1364                         }
1365                 }
1366                 disproportion = TRUE;
1367                 fwd++;
1368         }
1369         *bckwd = NULL;/* NULL-terminate the list */
1370 
1371         DBG_RETURN(ret);
1372 }
1373 /* }}} */
1374 
1375 
1376 #ifndef PHP_WIN32
1377 #define php_select(m, r, w, e, t)       select(m, r, w, e, t)
1378 #else
1379 #include "win32/select.h"
1380 #endif
1381 
1382 
1383 /* {{{ _mysqlnd_poll */
1384 PHPAPI enum_func_status
1385 _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num TSRMLS_DC)
1386 {
1387         struct timeval  tv;
1388         struct timeval *tv_p = NULL;
1389         fd_set                  rfds, wfds, efds;
1390         php_socket_t    max_fd = 0;
1391         int                             retval, sets = 0;
1392         int                             set_count, max_set_count = 0;
1393 
1394         DBG_ENTER("_mysqlnd_poll");
1395         if (sec < 0 || usec < 0) {
1396                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative values passed for sec and/or usec");
1397                 DBG_RETURN(FAIL);
1398         }
1399 
1400         FD_ZERO(&rfds);
1401         FD_ZERO(&wfds);
1402         FD_ZERO(&efds);
1403 
1404         if (r_array != NULL) {
1405                 *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array TSRMLS_CC);
1406                 set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd TSRMLS_CC);
1407                 if (set_count > max_set_count) {
1408                         max_set_count = set_count;
1409                 }
1410                 sets += set_count;
1411         }
1412 
1413         if (e_array != NULL) {
1414                 set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd TSRMLS_CC);
1415                 if (set_count > max_set_count) {
1416                         max_set_count = set_count;
1417                 }
1418                 sets += set_count;
1419         }
1420 
1421         if (!sets) {
1422                 php_error_docref(NULL TSRMLS_CC, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
1423                 DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
1424                 DBG_RETURN(FAIL);
1425         }
1426 
1427         PHP_SAFE_MAX_FD(max_fd, max_set_count);
1428 
1429         /* Solaris + BSD do not like microsecond values which are >= 1 sec */
1430         if (usec > 999999) {
1431                 tv.tv_sec = sec + (usec / 1000000);
1432                 tv.tv_usec = usec % 1000000;
1433         } else {
1434                 tv.tv_sec = sec;
1435                 tv.tv_usec = usec;
1436         }
1437 
1438         tv_p = &tv;
1439 
1440         retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
1441 
1442         if (retval == -1) {
1443                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
1444                                                 errno, strerror(errno), max_fd);
1445                 DBG_RETURN(FAIL);
1446         }
1447 
1448         if (r_array != NULL) {
1449                 mysqlnd_stream_array_from_fd_set(r_array, &rfds TSRMLS_CC);
1450         }
1451         if (e_array != NULL) {
1452                 mysqlnd_stream_array_from_fd_set(e_array, &efds TSRMLS_CC);
1453         }
1454 
1455         *desc_num = retval;
1456         DBG_RETURN(PASS);
1457 }
1458 /* }}} */
1459 
1460 
1461 /*
1462   COM_FIELD_LIST is special, different from a SHOW FIELDS FROM :
1463   - There is no result set header - status from the command, which
1464     impacts us to allocate big chunk of memory for reading the metadata.
1465   - The EOF packet is consumed by the metadata packet reader.
1466 */
1467 
1468 /* {{{ mysqlnd_conn_data::list_fields */
1469 MYSQLND_RES *
1470 MYSQLND_METHOD(mysqlnd_conn_data, list_fields)(MYSQLND_CONN_DATA * conn, const char *table, const char *achtung_wild TSRMLS_DC)
1471 {
1472         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_fields);
1473         /* db + \0 + wild + \0 (for wild) */
1474         zend_uchar buff[MYSQLND_MAX_ALLOWED_DB_LEN * 2 + 1 + 1], *p;
1475         size_t table_len, wild_len;
1476         MYSQLND_RES * result = NULL;
1477         DBG_ENTER("mysqlnd_conn_data::list_fields");
1478         DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:"");
1479 
1480         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1481                 do {
1482                         p = buff;
1483                         if (table && (table_len = strlen(table))) {
1484                                 size_t to_copy = MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN);
1485                                 memcpy(p, table, to_copy);
1486                                 p += to_copy;
1487                                 *p++ = '\0';
1488                         }
1489 
1490                         if (achtung_wild && (wild_len = strlen(achtung_wild))) {
1491                                 size_t to_copy = MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN);
1492                                 memcpy(p, achtung_wild, to_copy);
1493                                 p += to_copy;
1494                                 *p++ = '\0';
1495                         }
1496 
1497                         if (PASS != conn->m->simple_command(conn, COM_FIELD_LIST, buff, p - buff,
1498                                                                                            PROT_LAST /* we will handle the OK packet*/,
1499                                                                                            FALSE, TRUE TSRMLS_CC)) {
1500                                 conn->m->local_tx_end(conn, 0, FAIL TSRMLS_CC);
1501                                 break;
1502                         }
1503 
1504                         /*
1505                            Prepare for the worst case.
1506                            MyISAM goes to 2500 BIT columns, double it for safety.
1507                         */
1508                         result = conn->m->result_init(5000, conn->persistent TSRMLS_CC);
1509                         if (!result) {
1510                                 break;
1511                         }
1512 
1513                         if (FAIL == result->m.read_result_metadata(result, conn TSRMLS_CC)) {
1514                                 DBG_ERR("Error occurred while reading metadata");
1515                                 result->m.free_result(result, TRUE TSRMLS_CC);
1516                                 result = NULL;
1517                                 break;
1518                         }
1519 
1520                         result->type = MYSQLND_RES_NORMAL;
1521                         result->unbuf = mysqlnd_result_unbuffered_init(result->field_count, FALSE, result->persistent TSRMLS_CC);
1522                         if (!result->unbuf) {
1523                                 /* OOM */
1524                                 SET_OOM_ERROR(*conn->error_info);
1525                                 result->m.free_result(result, TRUE TSRMLS_CC);
1526                                 result = NULL;
1527                                 break;
1528                         }
1529                         result->unbuf->eof_reached = TRUE;
1530                 } while (0);
1531                 conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
1532         }
1533 
1534         DBG_RETURN(result);
1535 }
1536 /* }}} */
1537 
1538 
1539 /* {{{ mysqlnd_conn_data::list_method */
1540 MYSQLND_RES *
1541 MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const char * query, const char *achtung_wild, char *par1 TSRMLS_DC)
1542 {
1543         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_method);
1544         char * show_query = NULL;
1545         size_t show_query_len;
1546         MYSQLND_RES * result = NULL;
1547 
1548         DBG_ENTER("mysqlnd_conn_data::list_method");
1549         DBG_INF_FMT("conn=%llu query=%s wild=%u", conn->thread_id, query, achtung_wild);
1550 
1551         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1552                 if (par1) {
1553                         if (achtung_wild) {
1554                                 show_query_len = mnd_sprintf(&show_query, 0, query, par1, achtung_wild);
1555                         } else {
1556                                 show_query_len = mnd_sprintf(&show_query, 0, query, par1);
1557                         }
1558                 } else {
1559                         if (achtung_wild) {
1560                                 show_query_len = mnd_sprintf(&show_query, 0, query, achtung_wild);
1561                         } else {
1562                                 show_query_len = strlen(show_query = (char *)query);
1563                         }
1564                 }
1565 
1566                 if (PASS == conn->m->query(conn, show_query, show_query_len TSRMLS_CC)) {
1567                         result = conn->m->store_result(conn, MYSQLND_STORE_NO_COPY TSRMLS_CC);
1568                 }
1569                 if (show_query != query) {
1570                         mnd_sprintf_free(show_query);
1571                 }
1572                 conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
1573         }
1574         DBG_RETURN(result);
1575 }
1576 /* }}} */
1577 
1578 
1579 /* {{{ mysqlnd_conn_data::errno */
1580 static unsigned int
1581 MYSQLND_METHOD(mysqlnd_conn_data, errno)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1582 {
1583         return conn->error_info->error_no;
1584 }
1585 /* }}} */
1586 
1587 
1588 /* {{{ mysqlnd_conn_data::error */
1589 static const char *
1590 MYSQLND_METHOD(mysqlnd_conn_data, error)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1591 {
1592         return conn->error_info->error;
1593 }
1594 /* }}} */
1595 
1596 
1597 /* {{{ mysqlnd_conn_data::sqlstate */
1598 static const char *
1599 MYSQLND_METHOD(mysqlnd_conn_data, sqlstate)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1600 {
1601         return conn->error_info->sqlstate[0] ? conn->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
1602 }
1603 /* }}} */
1604 
1605 
1606 /* {{{ mysqlnd_old_escape_string */
1607 PHPAPI ulong
1608 mysqlnd_old_escape_string(char * newstr, const char * escapestr, size_t escapestr_len TSRMLS_DC)
1609 {
1610         DBG_ENTER("mysqlnd_old_escape_string");
1611         DBG_RETURN(mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), newstr, escapestr, escapestr_len TSRMLS_CC));
1612 }
1613 /* }}} */
1614 
1615 
1616 /* {{{ mysqlnd_conn_data::ssl_set */
1617 static enum_func_status
1618 MYSQLND_METHOD(mysqlnd_conn_data, ssl_set)(MYSQLND_CONN_DATA * const conn, const char * key, const char * const cert,
1619                                                                           const char * const ca, const char * const capath, const char * const cipher TSRMLS_DC)
1620 {
1621         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ssl_set);
1622         enum_func_status ret = FAIL;
1623         MYSQLND_NET * net = conn->net;
1624         DBG_ENTER("mysqlnd_conn_data::ssl_set");
1625 
1626         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1627                 ret = (PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_KEY, key TSRMLS_CC) &&
1628                         PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CERT, cert TSRMLS_CC) &&
1629                         PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CA, ca TSRMLS_CC) &&
1630                         PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CAPATH, capath TSRMLS_CC) &&
1631                         PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CIPHER, cipher TSRMLS_CC)) ? PASS : FAIL;
1632 
1633                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1634         }
1635         DBG_RETURN(ret);
1636 }
1637 /* }}} */
1638 
1639 
1640 /* {{{ mysqlnd_conn_data::escape_string */
1641 static ulong
1642 MYSQLND_METHOD(mysqlnd_conn_data, escape_string)(MYSQLND_CONN_DATA * const conn, char * newstr, const char * escapestr, size_t escapestr_len TSRMLS_DC)
1643 {
1644         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, escape_string);
1645         ulong ret = FAIL;
1646         DBG_ENTER("mysqlnd_conn_data::escape_string");
1647         DBG_INF_FMT("conn=%llu", conn->thread_id);
1648 
1649         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1650                 DBG_INF_FMT("server_status=%u", conn->upsert_status->server_status);
1651                 if (conn->upsert_status->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
1652                         ret = mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC);
1653                 } else {
1654                         ret = mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC);
1655                 }
1656                 conn->m->local_tx_end(conn, this_func, PASS TSRMLS_CC);
1657         }
1658         DBG_RETURN(ret);
1659 }
1660 /* }}} */
1661 
1662 
1663 /* {{{ mysqlnd_conn_data::dump_debug_info */
1664 static enum_func_status
1665 MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1666 {
1667         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, server_dump_debug_information);
1668         enum_func_status ret = FAIL;
1669         DBG_ENTER("mysqlnd_conn_data::dump_debug_info");
1670         DBG_INF_FMT("conn=%llu", conn->thread_id);
1671         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1672                 ret = conn->m->simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC);
1673 
1674                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1675         }
1676 
1677         DBG_RETURN(ret);
1678 }
1679 /* }}} */
1680 
1681 
1682 /* {{{ mysqlnd_conn_data::select_db */
1683 static enum_func_status
1684 MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, unsigned int db_len TSRMLS_DC)
1685 {
1686         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, select_db);
1687         enum_func_status ret = FAIL;
1688 
1689         DBG_ENTER("mysqlnd_conn_data::select_db");
1690         DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
1691 
1692         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1693                 ret = conn->m->simple_command(conn, COM_INIT_DB, (zend_uchar*) db, db_len, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
1694                 /*
1695                   The server sends 0 but libmysql doesn't read it and has established
1696                   a protocol of giving back -1. Thus we have to follow it :(
1697                 */
1698                 SET_ERROR_AFF_ROWS(conn);
1699                 if (ret == PASS) {
1700                         if (conn->connect_or_select_db) {
1701                                 mnd_pefree(conn->connect_or_select_db, conn->persistent);
1702                         }
1703                         conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
1704                         conn->connect_or_select_db_len = db_len;
1705                         if (!conn->connect_or_select_db) {
1706                                 /* OOM */
1707                                 SET_OOM_ERROR(*conn->error_info);
1708                                 ret = FAIL;
1709                         }
1710                 }
1711                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1712         }
1713         DBG_RETURN(ret);
1714 }
1715 /* }}} */
1716 
1717 
1718 /* {{{ mysqlnd_conn_data::ping */
1719 static enum_func_status
1720 MYSQLND_METHOD(mysqlnd_conn_data, ping)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1721 {
1722         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ping);
1723         enum_func_status ret = FAIL;
1724 
1725         DBG_ENTER("mysqlnd_conn_data::ping");
1726         DBG_INF_FMT("conn=%llu", conn->thread_id);
1727 
1728         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1729                 ret = conn->m->simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, TRUE, TRUE TSRMLS_CC);
1730                 /*
1731                   The server sends 0 but libmysql doesn't read it and has established
1732                   a protocol of giving back -1. Thus we have to follow it :(
1733                 */
1734                 SET_ERROR_AFF_ROWS(conn);
1735 
1736                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1737         }
1738         DBG_INF_FMT("ret=%u", ret);
1739         DBG_RETURN(ret);
1740 }
1741 /* }}} */
1742 
1743 
1744 /* {{{ mysqlnd_conn_data::statistic */
1745 static enum_func_status
1746 MYSQLND_METHOD(mysqlnd_conn_data, statistic)(MYSQLND_CONN_DATA * conn, char **message, unsigned int * message_len TSRMLS_DC)
1747 {
1748         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, get_server_statistics);
1749         enum_func_status ret = FAIL;
1750         MYSQLND_PACKET_STATS * stats_header;
1751 
1752         DBG_ENTER("mysqlnd_conn_data::statistic");
1753         DBG_INF_FMT("conn=%llu", conn->thread_id);
1754 
1755         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1756                 do {
1757                         ret = conn->m->simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE, TRUE TSRMLS_CC);
1758                         if (FAIL == ret) {
1759                                 break;
1760                         }
1761                         stats_header = conn->protocol->m.get_stats_packet(conn->protocol, FALSE TSRMLS_CC);
1762                         if (!stats_header) {
1763                                 SET_OOM_ERROR(*conn->error_info);
1764                                 break;
1765                         }
1766 
1767                         if (PASS == (ret = PACKET_READ(stats_header, conn))) {
1768                                 /* will be freed by Zend, thus don't use the mnd_ allocator */
1769                                 *message = estrndup(stats_header->message, stats_header->message_len);
1770                                 *message_len = stats_header->message_len;
1771                                 DBG_INF(*message);
1772                         }
1773                         PACKET_FREE(stats_header);
1774                 } while (0);
1775 
1776                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1777         }
1778         DBG_RETURN(ret);
1779 }
1780 /* }}} */
1781 
1782 
1783 /* {{{ mysqlnd_conn_data::kill */
1784 static enum_func_status
1785 MYSQLND_METHOD(mysqlnd_conn_data, kill)(MYSQLND_CONN_DATA * conn, unsigned int pid TSRMLS_DC)
1786 {
1787         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, kill_connection);
1788         enum_func_status ret = FAIL;
1789         zend_uchar buff[4];
1790 
1791         DBG_ENTER("mysqlnd_conn_data::kill");
1792         DBG_INF_FMT("conn=%llu pid=%u", conn->thread_id, pid);
1793 
1794         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1795                 int4store(buff, pid);
1796 
1797                 /* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */
1798                 if (pid != conn->thread_id) {
1799                         ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
1800                         /*
1801                           The server sends 0 but libmysql doesn't read it and has established
1802                           a protocol of giving back -1. Thus we have to follow it :(
1803                         */
1804                         SET_ERROR_AFF_ROWS(conn);
1805                 } else if (PASS == (ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_LAST, FALSE, TRUE TSRMLS_CC))) {
1806                         CONN_SET_STATE(conn, CONN_QUIT_SENT);
1807                         conn->m->send_close(conn TSRMLS_CC);
1808                 }
1809 
1810                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1811         }
1812         DBG_RETURN(ret);
1813 }
1814 /* }}} */
1815 
1816 
1817 /* {{{ mysqlnd_conn_data::set_charset */
1818 static enum_func_status
1819 MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, const char * const csname TSRMLS_DC)
1820 {
1821         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_charset);
1822         enum_func_status ret = FAIL;
1823         const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
1824 
1825         DBG_ENTER("mysqlnd_conn_data::set_charset");
1826         DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname);
1827 
1828         if (!charset) {
1829                 SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
1830                                                  "Invalid characterset or character set not supported");
1831                 DBG_RETURN(ret);
1832         }
1833 
1834         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1835                 char * query;
1836                 size_t query_len = mnd_sprintf(&query, 0, "SET NAMES %s", csname);
1837 
1838                 if (FAIL == (ret = conn->m->query(conn, query, query_len TSRMLS_CC))) {
1839                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error executing query");
1840                 } else if (conn->error_info->error_no) {
1841                         ret = FAIL;
1842                 } else {
1843                         conn->charset = charset;
1844                 }
1845                 mnd_sprintf_free(query);
1846 
1847                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1848         }
1849 
1850         DBG_INF(ret == PASS? "PASS":"FAIL");
1851         DBG_RETURN(ret);
1852 }
1853 /* }}} */
1854 
1855 
1856 /* {{{ mysqlnd_conn_data::refresh */
1857 static enum_func_status
1858 MYSQLND_METHOD(mysqlnd_conn_data, refresh)(MYSQLND_CONN_DATA * const conn, uint8_t options TSRMLS_DC)
1859 {
1860         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, refresh_server);
1861         enum_func_status ret = FAIL;
1862         zend_uchar bits[1];
1863         DBG_ENTER("mysqlnd_conn_data::refresh");
1864         DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
1865 
1866         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1867                 int1store(bits, options);
1868 
1869                 ret = conn->m->simple_command(conn, COM_REFRESH, bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
1870 
1871                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1872         }
1873         DBG_RETURN(ret);
1874 }
1875 /* }}} */
1876 
1877 
1878 /* {{{ mysqlnd_conn_data::shutdown */
1879 static enum_func_status
1880 MYSQLND_METHOD(mysqlnd_conn_data, shutdown)(MYSQLND_CONN_DATA * const conn, uint8_t level TSRMLS_DC)
1881 {
1882         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, shutdown_server);
1883         enum_func_status ret = FAIL;
1884         zend_uchar bits[1];
1885         DBG_ENTER("mysqlnd_conn_data::shutdown");
1886         DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
1887 
1888         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1889                 int1store(bits, level);
1890 
1891                 ret = conn->m->simple_command(conn, COM_SHUTDOWN, bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
1892 
1893                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1894         }
1895         DBG_RETURN(ret);
1896 }
1897 /* }}} */
1898 
1899 
1900 /* {{{ mysqlnd_send_close */
1901 static enum_func_status
1902 MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1903 {
1904         enum_func_status ret = PASS;
1905         MYSQLND_NET * net = conn->net;
1906         php_stream * net_stream = net->data->m.get_stream(net TSRMLS_CC);
1907         enum mysqlnd_connection_state state;
1908 
1909         DBG_ENTER("mysqlnd_send_close");
1910         DBG_INF_FMT("conn=%llu net->data->stream->abstract=%p", conn->thread_id, net_stream? net_stream->abstract:NULL);
1911 
1912         if (CONN_GET_STATE(conn) >= CONN_READY) {
1913                 MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
1914                 if (conn->persistent) {
1915                         MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
1916                 }
1917         }
1918         state = CONN_GET_STATE(conn);
1919         DBG_INF_FMT("state=%u", state);
1920         switch (state) {
1921                 case CONN_READY:
1922                         DBG_INF("Connection clean, sending COM_QUIT");
1923                         if (net_stream) {
1924                                 ret = conn->m->simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, TRUE, TRUE TSRMLS_CC);
1925                                 net->data->m.close_stream(net, conn->stats, conn->error_info TSRMLS_CC);
1926                         }
1927                         CONN_SET_STATE(conn, CONN_QUIT_SENT);
1928                         break;
1929                 case CONN_SENDING_LOAD_DATA:
1930                         /*
1931                           Don't send COM_QUIT if we are in a middle of a LOAD DATA or we
1932                           will crash (assert) a debug server.
1933                         */
1934                 case CONN_NEXT_RESULT_PENDING:
1935                 case CONN_QUERY_SENT:
1936                 case CONN_FETCHING_DATA:
1937                         MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE);
1938                         DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme);
1939                         /*
1940                           Do nothing, the connection will be brutally closed
1941                           and the server will catch it and free close from its side.
1942                         */
1943                         /* Fall-through */
1944                 case CONN_ALLOCED:
1945                         /*
1946                           Allocated but not connected or there was failure when trying
1947                           to connect with pre-allocated connect.
1948 
1949                           Fall-through
1950                         */
1951                         CONN_SET_STATE(conn, CONN_QUIT_SENT);
1952                         /* Fall-through */
1953                 case CONN_QUIT_SENT:
1954                         /* The user has killed its own connection */
1955                         net->data->m.close_stream(net, conn->stats, conn->error_info TSRMLS_CC);
1956                         break;
1957         }
1958 
1959         DBG_RETURN(ret);
1960 }
1961 /* }}} */
1962 
1963 
1964 /* {{{ mysqlnd_conn_data::get_reference */
1965 static MYSQLND_CONN_DATA *
1966 MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1967 {
1968         DBG_ENTER("mysqlnd_conn_data::get_reference");
1969         ++conn->refcount;
1970         DBG_INF_FMT("conn=%llu new_refcount=%u", conn->thread_id, conn->refcount);
1971         DBG_RETURN(conn);
1972 }
1973 /* }}} */
1974 
1975 
1976 /* {{{ mysqlnd_conn_data::free_reference */
1977 static enum_func_status
1978 MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1979 {
1980         enum_func_status ret = PASS;
1981         DBG_ENTER("mysqlnd_conn_data::free_reference");
1982         DBG_INF_FMT("conn=%llu old_refcount=%u", conn->thread_id, conn->refcount);
1983         if (!(--conn->refcount)) {
1984                 /*
1985                   No multithreading issues as we don't share the connection :)
1986                   This will free the object too, of course because references has
1987                   reached zero.
1988                 */
1989                 ret = conn->m->send_close(conn TSRMLS_CC);
1990                 conn->m->dtor(conn TSRMLS_CC);
1991         }
1992         DBG_RETURN(ret);
1993 }
1994 /* }}} */
1995 
1996 
1997 /* {{{ mysqlnd_conn_data::get_state */
1998 static enum mysqlnd_connection_state
1999 MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2000 {
2001         DBG_ENTER("mysqlnd_conn_data::get_state");
2002         DBG_RETURN(conn->state);
2003 }
2004 /* }}} */
2005 
2006 
2007 /* {{{ mysqlnd_conn_data::set_state */
2008 static void
2009 MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC)
2010 {
2011         DBG_ENTER("mysqlnd_conn_data::set_state");
2012         DBG_INF_FMT("New state=%u", new_state);
2013         conn->state = new_state;
2014         DBG_VOID_RETURN;
2015 }
2016 /* }}} */
2017 
2018 
2019 /* {{{ mysqlnd_conn_data::field_count */
2020 static unsigned int
2021 MYSQLND_METHOD(mysqlnd_conn_data, field_count)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2022 {
2023         return conn->field_count;
2024 }
2025 /* }}} */
2026 
2027 
2028 /* {{{ mysqlnd_conn_data::server_status */
2029 static unsigned int
2030 MYSQLND_METHOD(mysqlnd_conn_data, server_status)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2031 {
2032         return conn->upsert_status->server_status;
2033 }
2034 /* }}} */
2035 
2036 
2037 /* {{{ mysqlnd_conn_data::insert_id */
2038 static uint64_t
2039 MYSQLND_METHOD(mysqlnd_conn_data, insert_id)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2040 {
2041         return conn->upsert_status->last_insert_id;
2042 }
2043 /* }}} */
2044 
2045 
2046 /* {{{ mysqlnd_conn_data::affected_rows */
2047 static uint64_t
2048 MYSQLND_METHOD(mysqlnd_conn_data, affected_rows)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2049 {
2050         return conn->upsert_status->affected_rows;
2051 }
2052 /* }}} */
2053 
2054 
2055 /* {{{ mysqlnd_conn_data::warning_count */
2056 static unsigned int
2057 MYSQLND_METHOD(mysqlnd_conn_data, warning_count)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2058 {
2059         return conn->upsert_status->warning_count;
2060 }
2061 /* }}} */
2062 
2063 
2064 /* {{{ mysqlnd_conn_data::info */
2065 static const char *
2066 MYSQLND_METHOD(mysqlnd_conn_data, info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2067 {
2068         return conn->last_message;
2069 }
2070 /* }}} */
2071 
2072 /* {{{ mysqlnd_get_client_info */
2073 PHPAPI const char * mysqlnd_get_client_info()
2074 {
2075         return MYSQLND_VERSION;
2076 }
2077 /* }}} */
2078 
2079 
2080 /* {{{ mysqlnd_get_client_version */
2081 PHPAPI unsigned int mysqlnd_get_client_version()
2082 {
2083         return MYSQLND_VERSION_ID;
2084 }
2085 /* }}} */
2086 
2087 
2088 /* {{{ mysqlnd_conn_data::get_server_info */
2089 static const char *
2090 MYSQLND_METHOD(mysqlnd_conn_data, get_server_info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2091 {
2092         return conn->server_version;
2093 }
2094 /* }}} */
2095 
2096 
2097 /* {{{ mysqlnd_conn_data::get_host_info */
2098 static const char *
2099 MYSQLND_METHOD(mysqlnd_conn_data, get_host_info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2100 {
2101         return conn->host_info;
2102 }
2103 /* }}} */
2104 
2105 
2106 /* {{{ mysqlnd_conn_data::get_proto_info */
2107 static unsigned int
2108 MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2109 {
2110         return conn->protocol_version;
2111 }
2112 /* }}} */
2113 
2114 
2115 /* {{{ mysqlnd_conn_data::charset_name */
2116 static const char *
2117 MYSQLND_METHOD(mysqlnd_conn_data, charset_name)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2118 {
2119         return conn->charset->name;
2120 }
2121 /* }}} */
2122 
2123 
2124 /* {{{ mysqlnd_conn_data::thread_id */
2125 static uint64_t
2126 MYSQLND_METHOD(mysqlnd_conn_data, thread_id)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2127 {
2128         return conn->thread_id;
2129 }
2130 /* }}} */
2131 
2132 
2133 /* {{{ mysqlnd_conn_data::get_server_version */
2134 static unsigned long
2135 MYSQLND_METHOD(mysqlnd_conn_data, get_server_version)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2136 {
2137         long major, minor, patch;
2138         char *p;
2139 
2140         if (!(p = conn->server_version)) {
2141                 return 0;
2142         }
2143 
2144         major = strtol(p, &p, 10);
2145         p += 1; /* consume the dot */
2146         minor = strtol(p, &p, 10);
2147         p += 1; /* consume the dot */
2148         patch = strtol(p, &p, 10);
2149 
2150         return (unsigned long)(major * 10000L + (unsigned long)(minor * 100L + patch));
2151 }
2152 /* }}} */
2153 
2154 
2155 /* {{{ mysqlnd_conn_data::more_results */
2156 static zend_bool
2157 MYSQLND_METHOD(mysqlnd_conn_data, more_results)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2158 {
2159         DBG_ENTER("mysqlnd_conn_data::more_results");
2160         /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
2161         DBG_RETURN(conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
2162 }
2163 /* }}} */
2164 
2165 
2166 /* {{{ mysqlnd_conn_data::next_result */
2167 static enum_func_status
2168 MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2169 {
2170         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, next_result);
2171         enum_func_status ret = FAIL;
2172 
2173         DBG_ENTER("mysqlnd_conn_data::next_result");
2174         DBG_INF_FMT("conn=%llu", conn->thread_id);
2175 
2176         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2177                 do {
2178                         if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING) {
2179                                 break;
2180                         }
2181 
2182                         SET_EMPTY_ERROR(*conn->error_info);
2183                         SET_ERROR_AFF_ROWS(conn);
2184                         /*
2185                           We are sure that there is a result set, since conn->state is set accordingly
2186                           in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered()
2187                         */
2188                         if (FAIL == (ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC))) {
2189                                 /*
2190                                   There can be an error in the middle of a multi-statement, which will cancel the multi-statement.
2191                                   So there are no more results and we should just return FALSE, error_no has been set
2192                                 */
2193                                 if (!conn->error_info->error_no) {
2194                                         DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__);
2195                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error. PID=%d", getpid());
2196                                         CONN_SET_STATE(conn, CONN_QUIT_SENT);
2197                                         conn->m->send_close(conn TSRMLS_CC);
2198                                 } else {
2199                                         DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info->error_no, conn->error_info->error);
2200                                 }
2201                                 break;
2202                         }
2203                         if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
2204                                 MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
2205                         }
2206                 } while (0);
2207                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
2208         }
2209 
2210         DBG_RETURN(ret);
2211 }
2212 /* }}} */
2213 
2214 
2215 /* {{{ mysqlnd_field_type_name */
2216 PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type)
2217 {
2218         switch(field_type) {
2219                 case FIELD_TYPE_JSON:
2220                         return "json";
2221                 case FIELD_TYPE_STRING:
2222                 case FIELD_TYPE_VAR_STRING:
2223                         return "string";
2224                 case FIELD_TYPE_TINY:
2225                 case FIELD_TYPE_SHORT:
2226                 case FIELD_TYPE_LONG:
2227                 case FIELD_TYPE_LONGLONG:
2228                 case FIELD_TYPE_INT24:
2229                         return "int";
2230                 case FIELD_TYPE_FLOAT:
2231                 case FIELD_TYPE_DOUBLE:
2232                 case FIELD_TYPE_DECIMAL:
2233                 case FIELD_TYPE_NEWDECIMAL:
2234                         return "real";
2235                 case FIELD_TYPE_TIMESTAMP:
2236                         return "timestamp";
2237                 case FIELD_TYPE_YEAR:
2238                         return "year";
2239                 case FIELD_TYPE_DATE:
2240                 case FIELD_TYPE_NEWDATE:
2241                         return "date";
2242                 case FIELD_TYPE_TIME:
2243                         return "time";
2244                 case FIELD_TYPE_SET:
2245                         return "set";
2246                 case FIELD_TYPE_ENUM:
2247                         return "enum";
2248                 case FIELD_TYPE_GEOMETRY:
2249                         return "geometry";
2250                 case FIELD_TYPE_DATETIME:
2251                         return "datetime";
2252                 case FIELD_TYPE_TINY_BLOB:
2253                 case FIELD_TYPE_MEDIUM_BLOB:
2254                 case FIELD_TYPE_LONG_BLOB:
2255                 case FIELD_TYPE_BLOB:
2256                         return "blob";
2257                 case FIELD_TYPE_NULL:
2258                         return "null";
2259                 case FIELD_TYPE_BIT:
2260                         return "bit";
2261                 default:
2262                         return "unknown";
2263         }
2264 }
2265 /* }}} */
2266 
2267 
2268 /* {{{ mysqlnd_conn_data::change_user */
2269 static enum_func_status
2270 MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
2271                                                                                   const char * user,
2272                                                                                   const char * passwd,
2273                                                                                   const char * db,
2274                                                                                   zend_bool silent,
2275                                                                                   size_t passwd_len
2276                                                                                   TSRMLS_DC)
2277 {
2278         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, change_user);
2279         enum_func_status ret = FAIL;
2280 
2281         DBG_ENTER("mysqlnd_conn_data::change_user");
2282         DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%u",
2283                                 conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 );
2284 
2285         if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2286                 goto end;
2287         }
2288 
2289         SET_EMPTY_ERROR(*conn->error_info);
2290         SET_ERROR_AFF_ROWS(conn);
2291 
2292         if (!user) {
2293                 user = "";
2294         }
2295         if (!passwd) {
2296                 passwd = "";
2297         }
2298         if (!db) {
2299                 db = "";
2300         }
2301 
2302         /* XXX: passwords that have \0 inside work during auth, but in this case won't work with change user */
2303         ret = mysqlnd_run_authentication(conn, user, passwd, strlen(passwd), db, strlen(db),
2304                                                                         conn->auth_plugin_data, conn->auth_plugin_data_len, conn->options->auth_protocol,
2305                                                                         0 /*charset not used*/, conn->options, conn->server_capabilities, silent, TRUE/*is_change*/ TSRMLS_CC);
2306 
2307         /*
2308           Here we should close all statements. Unbuffered queries should not be a
2309           problem as we won't allow sending COM_CHANGE_USER.
2310         */
2311         conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);  
2312 end:
2313         DBG_INF(ret == PASS? "PASS":"FAIL");
2314         DBG_RETURN(ret);
2315 }
2316 /* }}} */
2317 
2318 
2319 /* {{{ mysqlnd_conn_data::set_client_option */
2320 static enum_func_status
2321 MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const conn,
2322                                                                                                 enum mysqlnd_option option,
2323                                                                                                 const char * const value
2324                                                                                                 TSRMLS_DC)
2325 {
2326         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option);
2327         enum_func_status ret = PASS;
2328         DBG_ENTER("mysqlnd_conn_data::set_client_option");
2329         DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
2330 
2331         if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2332                 goto end;
2333         }
2334         switch (option) {
2335                 case MYSQL_OPT_COMPRESS:
2336 #ifdef WHEN_SUPPORTED_BY_MYSQLI
2337                 case MYSQL_OPT_READ_TIMEOUT:
2338                 case MYSQL_OPT_WRITE_TIMEOUT:
2339 #endif
2340                 case MYSQLND_OPT_SSL_KEY:
2341                 case MYSQLND_OPT_SSL_CERT:
2342                 case MYSQLND_OPT_SSL_CA:
2343                 case MYSQLND_OPT_SSL_CAPATH:
2344                 case MYSQLND_OPT_SSL_CIPHER:
2345                 case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
2346                 case MYSQL_OPT_CONNECT_TIMEOUT:
2347                 case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
2348                 case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
2349                 case MYSQL_SERVER_PUBLIC_KEY:
2350                         ret = conn->net->data->m.set_client_option(conn->net, option, value TSRMLS_CC);
2351                         break;
2352 #ifdef MYSQLND_STRING_TO_INT_CONVERSION
2353                 case MYSQLND_OPT_INT_AND_FLOAT_NATIVE:
2354                         conn->options->int_and_float_native = *(unsigned int*) value;
2355                         break;
2356 #endif
2357                 case MYSQL_OPT_LOCAL_INFILE:
2358                         if (value && (*(unsigned int*) value) ? 1 : 0) {
2359                                 conn->options->flags |= CLIENT_LOCAL_FILES;
2360                         } else {
2361                                 conn->options->flags &= ~CLIENT_LOCAL_FILES;
2362                         }
2363                         break;
2364                 case MYSQL_INIT_COMMAND:
2365                 {
2366                         char ** new_init_commands;
2367                         char * new_command;
2368                         /* when num_commands is 0, then realloc will be effectively a malloc call, internally */
2369                         /* Don't assign to conn->options->init_commands because in case of OOM we will lose the pointer and leak */
2370                         new_init_commands = mnd_perealloc(conn->options->init_commands, sizeof(char *) * (conn->options->num_commands + 1), conn->persistent);
2371                         if (!new_init_commands) {
2372                                 goto oom;
2373                         }
2374                         conn->options->init_commands = new_init_commands;
2375                         new_command = mnd_pestrdup(value, conn->persistent);
2376                         if (!new_command) {
2377                                 goto oom;
2378                         }
2379                         conn->options->init_commands[conn->options->num_commands] = new_command;
2380                         ++conn->options->num_commands;
2381                         break;
2382                 }
2383                 case MYSQL_READ_DEFAULT_FILE:
2384                 case MYSQL_READ_DEFAULT_GROUP:
2385 #ifdef WHEN_SUPPORTED_BY_MYSQLI
2386                 case MYSQL_SET_CLIENT_IP:
2387                 case MYSQL_REPORT_DATA_TRUNCATION:
2388 #endif
2389                         /* currently not supported. Todo!! */
2390                         break;
2391                 case MYSQL_SET_CHARSET_NAME:
2392                 {
2393                         char * new_charset_name;
2394                         if (!mysqlnd_find_charset_name(value)) {
2395                                 SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Unknown character set");
2396                                 ret = FAIL;
2397                                 break;
2398                         }
2399 
2400                         new_charset_name = mnd_pestrdup(value, conn->persistent);
2401                         if (!new_charset_name) {
2402                                 goto oom;
2403                         }
2404                         if (conn->options->charset_name) {
2405                                 mnd_pefree(conn->options->charset_name, conn->persistent);
2406                         }
2407                         conn->options->charset_name = new_charset_name;
2408                         DBG_INF_FMT("charset=%s", conn->options->charset_name);
2409                         break;
2410                 }
2411                 case MYSQL_OPT_NAMED_PIPE:
2412                         conn->options->protocol = MYSQL_PROTOCOL_PIPE;
2413                         break;
2414                 case MYSQL_OPT_PROTOCOL:
2415                         if (*(unsigned int*) value < MYSQL_PROTOCOL_LAST) {
2416                                 conn->options->protocol = *(unsigned int*) value;
2417                         }
2418                         break;
2419 #ifdef WHEN_SUPPORTED_BY_MYSQLI
2420                 case MYSQL_SET_CHARSET_DIR:
2421                 case MYSQL_OPT_RECONNECT:
2422                         /* we don't need external character sets, all character sets are
2423                            compiled in. For compatibility we just ignore this setting.
2424                            Same for protocol, we don't support old protocol */
2425                 case MYSQL_OPT_USE_REMOTE_CONNECTION:
2426                 case MYSQL_OPT_USE_EMBEDDED_CONNECTION:
2427                 case MYSQL_OPT_GUESS_CONNECTION:
2428                         /* todo: throw an error, we don't support embedded */
2429                         break;
2430 #endif
2431                 case MYSQLND_OPT_MAX_ALLOWED_PACKET:
2432                         if (*(unsigned int*) value > (1<<16)) {
2433                                 conn->options->max_allowed_packet = *(unsigned int*) value;
2434                         }
2435                         break;
2436                 case MYSQLND_OPT_AUTH_PROTOCOL:
2437                 {
2438                         char * new_auth_protocol = value? mnd_pestrdup(value, conn->persistent) : NULL;
2439                         if (value && !new_auth_protocol) {
2440                                 goto oom;
2441                         }
2442                         if (conn->options->auth_protocol) {
2443                                 mnd_pefree(conn->options->auth_protocol, conn->persistent);
2444                         }
2445                         conn->options->auth_protocol = new_auth_protocol;
2446                         DBG_INF_FMT("auth_protocol=%s", conn->options->auth_protocol);
2447                         break;
2448                 }
2449                 case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS:
2450                         if (value && (*(unsigned int*) value) ? 1 : 0) {
2451                                 conn->options->flags |= CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
2452                         } else {
2453                                 conn->options->flags &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
2454                         }
2455                         break;
2456                 case MYSQL_OPT_CONNECT_ATTR_RESET:
2457                         if (conn->options->connect_attr) {
2458                                 DBG_INF_FMT("Before reset %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
2459                                 zend_hash_clean(conn->options->connect_attr);
2460                         }
2461                         break;
2462                 case MYSQL_OPT_CONNECT_ATTR_DELETE:
2463                         if (conn->options->connect_attr && value) {
2464                                 DBG_INF_FMT("Before delete %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
2465                                 zend_hash_del(conn->options->connect_attr, value, strlen(value));
2466                                 DBG_INF_FMT("%d left", zend_hash_num_elements(conn->options->connect_attr));
2467                         }
2468                         break;
2469 #ifdef WHEN_SUPPORTED_BY_MYSQLI
2470                 case MYSQL_SHARED_MEMORY_BASE_NAME:
2471                 case MYSQL_OPT_USE_RESULT:
2472                 case MYSQL_SECURE_AUTH:
2473                         /* not sure, todo ? */
2474 #endif
2475                 default:
2476                         ret = FAIL;
2477         }
2478         conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
2479         DBG_RETURN(ret);
2480 oom:
2481         SET_OOM_ERROR(*conn->error_info);
2482         conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
2483 end:
2484         DBG_RETURN(FAIL);
2485 }
2486 /* }}} */
2487 
2488 
2489 /* {{{ connect_attr_item_edtor */
2490 static void
2491 connect_attr_item_edtor(void * pDest)
2492 {
2493 #ifdef ZTS
2494         TSRMLS_FETCH();
2495 #endif
2496         DBG_ENTER("connect_attr_item_edtor");
2497         mnd_efree(*(char **) pDest);
2498         DBG_VOID_RETURN;
2499 }
2500 /* }}} */
2501 
2502 
2503 /* {{{ connect_attr_item_pdtor */
2504 static void
2505 connect_attr_item_pdtor(void * pDest)
2506 {
2507 #ifdef ZTS
2508         TSRMLS_FETCH();
2509 #endif
2510         DBG_ENTER("connect_attr_item_pdtor");
2511         mnd_pefree(*(char **) pDest, 1);
2512         DBG_VOID_RETURN;
2513 }
2514 /* }}} */
2515 
2516 
2517 /* {{{ mysqlnd_conn_data::set_client_option_2d */
2518 static enum_func_status
2519 MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * const conn,
2520                                                                                                                 enum mysqlnd_option option,
2521                                                                                                                 const char * const key,
2522                                                                                                                 const char * const value
2523                                                                                                                 TSRMLS_DC)
2524 {
2525         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option_2d);
2526         enum_func_status ret = PASS;
2527         DBG_ENTER("mysqlnd_conn_data::set_client_option_2d");
2528         DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
2529 
2530         if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2531                 goto end;
2532         }
2533         switch (option) {
2534                 case MYSQL_OPT_CONNECT_ATTR_ADD:
2535                         if (!conn->options->connect_attr) {
2536                                 DBG_INF("Initializing connect_attr hash");
2537                                 conn->options->connect_attr = mnd_pemalloc(sizeof(HashTable), conn->persistent);
2538                                 if (!conn->options->connect_attr) {
2539                                         goto oom;
2540                                 }
2541                                 zend_hash_init(conn->options->connect_attr, 0, NULL, conn->persistent? connect_attr_item_pdtor:connect_attr_item_edtor, conn->persistent);
2542                         }
2543                         DBG_INF_FMT("Adding [%s][%s]", key, value);
2544                         {
2545                                 const char * copyv = mnd_pestrdup(value, conn->persistent);
2546                                 if (!copyv) {
2547                                         goto oom;
2548                                 }
2549                                 zend_hash_update(conn->options->connect_attr, key, strlen(key), &copyv, sizeof(char *), NULL);
2550                         }
2551                         break;
2552                 default:
2553                         ret = FAIL;
2554         }
2555         conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);  
2556         DBG_RETURN(ret);
2557 oom:
2558         SET_OOM_ERROR(*conn->error_info);
2559         conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
2560 end:
2561         DBG_RETURN(FAIL);
2562 }
2563 /* }}} */
2564 
2565 
2566 /* {{{ mysqlnd_conn_data::use_result */
2567 static MYSQLND_RES *
2568 MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC)
2569 {
2570         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, use_result);
2571         MYSQLND_RES * result = NULL;
2572 
2573         DBG_ENTER("mysqlnd_conn_data::use_result");
2574         DBG_INF_FMT("conn=%llu", conn->thread_id);
2575 
2576         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2577                 do {
2578                         if (!conn->current_result) {
2579                                 break;
2580                         }
2581 
2582                         /* Nothing to store for UPSERT/LOAD DATA */
2583                         if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
2584                                 SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
2585                                 DBG_ERR("Command out of sync");
2586                                 break;
2587                         }
2588 
2589                         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS);
2590 
2591                         conn->current_result->conn = conn->m->get_reference(conn TSRMLS_CC);
2592                         result = conn->current_result->m.use_result(conn->current_result, FALSE TSRMLS_CC);
2593 
2594                         if (!result) {
2595                                 conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
2596                         }
2597                         conn->current_result = NULL;
2598                 } while (0);
2599 
2600                 conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
2601         }
2602 
2603         DBG_RETURN(result);
2604 }
2605 /* }}} */
2606 
2607 
2608 /* {{{ mysqlnd_conn_data::store_result */
2609 static MYSQLND_RES *
2610 MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC)
2611 {
2612         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, store_result);
2613         MYSQLND_RES * result = NULL;
2614 
2615         DBG_ENTER("mysqlnd_conn_data::store_result");
2616         DBG_INF_FMT("conn=%llu conn=%p", conn->thread_id, conn);
2617 
2618         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2619                 do {
2620                         unsigned int f = flags;
2621                         if (!conn->current_result) {
2622                                 break;
2623                         }
2624 
2625                         /* Nothing to store for UPSERT/LOAD DATA*/
2626                         if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
2627                                 SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
2628                                 DBG_ERR("Command out of sync");
2629                                 break;
2630                         }
2631 
2632                         MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
2633 
2634                         /* overwrite */
2635                         if ((conn->m->get_client_api_capabilities(conn TSRMLS_CC) & MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA)) {
2636                                 if (MYSQLND_G(fetch_data_copy)) {
2637                                         f &= ~MYSQLND_STORE_NO_COPY;
2638                                         f |= MYSQLND_STORE_COPY;
2639                                 }
2640                         } else {
2641                                 /* if for some reason PDO borks something */
2642                                 if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
2643                                         f |= MYSQLND_STORE_COPY;
2644                                 }
2645                         }
2646                         if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
2647                                 SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Unknown fetch mode");
2648                                 DBG_ERR("Unknown fetch mode");
2649                                 break;                          
2650                         }
2651                         result = conn->current_result->m.store_result(conn->current_result, conn, f TSRMLS_CC);
2652                         if (!result) {
2653                                 conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
2654                         }
2655                         conn->current_result = NULL;
2656                 } while (0);
2657 
2658                 conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
2659         }
2660         DBG_RETURN(result);
2661 }
2662 /* }}} */
2663 
2664 
2665 /* {{{ mysqlnd_conn_data::get_connection_stats */
2666 static void
2667 MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats)(const MYSQLND_CONN_DATA * const conn,
2668                                                                                                    zval * return_value TSRMLS_DC ZEND_FILE_LINE_DC)
2669 {
2670         DBG_ENTER("mysqlnd_conn_data::get_connection_stats");
2671         mysqlnd_fill_stats_hash(conn->stats, mysqlnd_stats_values_names, return_value TSRMLS_CC ZEND_FILE_LINE_CC);
2672         DBG_VOID_RETURN;
2673 }
2674 /* }}} */
2675 
2676 
2677 /* {{{ mysqlnd_conn_data::set_autocommit */
2678 static enum_func_status
2679 MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit)(MYSQLND_CONN_DATA * conn, unsigned int mode TSRMLS_DC)
2680 {
2681         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_autocommit);
2682         enum_func_status ret = FAIL;
2683         DBG_ENTER("mysqlnd_conn_data::set_autocommit");
2684 
2685         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2686                 ret = conn->m->query(conn, (mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", sizeof("SET AUTOCOMMIT=1") - 1 TSRMLS_CC);
2687                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
2688         }
2689 
2690         DBG_RETURN(ret);
2691 }
2692 /* }}} */
2693 
2694 
2695 /* {{{ mysqlnd_conn_data::tx_commit */
2696 static enum_func_status
2697 MYSQLND_METHOD(mysqlnd_conn_data, tx_commit)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
2698 {
2699         return conn->m->tx_commit_or_rollback(conn, TRUE, TRANS_COR_NO_OPT, NULL TSRMLS_CC);
2700 }
2701 /* }}} */
2702 
2703 
2704 /* {{{ mysqlnd_conn_data::tx_rollback */
2705 static enum_func_status
2706 MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
2707 {
2708         return conn->m->tx_commit_or_rollback(conn, FALSE, TRANS_COR_NO_OPT, NULL TSRMLS_CC);
2709 }
2710 /* }}} */
2711 
2712 
2713 /* {{{ mysqlnd_tx_cor_options_to_string */
2714 static void
2715 MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string)(const MYSQLND_CONN_DATA * const conn, smart_str * str, const unsigned int mode TSRMLS_DC)
2716 {
2717         if (mode & TRANS_COR_AND_CHAIN && !(mode & TRANS_COR_AND_NO_CHAIN)) {
2718                 if (str->len) {
2719                         smart_str_appendl(str, " ", sizeof(" ") - 1);
2720                 }
2721                 smart_str_appendl(str, "AND CHAIN", sizeof("AND CHAIN") - 1);
2722         } else if (mode & TRANS_COR_AND_NO_CHAIN && !(mode & TRANS_COR_AND_CHAIN)) {
2723                 if (str->len) {
2724                         smart_str_appendl(str, " ", sizeof(" ") - 1);
2725                 }
2726                 smart_str_appendl(str, "AND NO CHAIN", sizeof("AND NO CHAIN") - 1);
2727         }
2728 
2729         if (mode & TRANS_COR_RELEASE && !(mode & TRANS_COR_NO_RELEASE)) {
2730                 if (str->len) {
2731                         smart_str_appendl(str, " ", sizeof(" ") - 1);
2732                 }
2733                 smart_str_appendl(str, "RELEASE", sizeof("RELEASE") - 1);
2734         } else if (mode & TRANS_COR_NO_RELEASE && !(mode & TRANS_COR_RELEASE)) {
2735                 if (str->len) {
2736                         smart_str_appendl(str, " ", sizeof(" ") - 1);
2737                 }
2738                 smart_str_appendl(str, "NO RELEASE", sizeof("NO RELEASE") - 1);
2739         }
2740         smart_str_0(str);
2741 }
2742 /* }}} */
2743 
2744 
2745 /* {{{ mysqlnd_escape_string_for_tx_name_in_comment */
2746 static char *
2747 mysqlnd_escape_string_for_tx_name_in_comment(const char * const name TSRMLS_DC)
2748 {
2749         char * ret = NULL;
2750         DBG_ENTER("mysqlnd_escape_string_for_tx_name_in_comment");
2751         if (name) {
2752                 zend_bool warned = FALSE;
2753                 const char * p_orig = name;
2754                 char * p_copy;
2755                 p_copy = ret = mnd_emalloc(strlen(name) + 1 + 2 + 2 + 1); /* space, open, close, NullS */
2756                 *p_copy++ = ' ';
2757                 *p_copy++ = '/';
2758                 *p_copy++ = '*';
2759                 while (1) {
2760                         register char v = *p_orig;
2761                         if (v == 0) {
2762                                 break;
2763                         }
2764                         if ((v >= '0' && v <= '9') ||
2765                                 (v >= 'a' && v <= 'z') ||
2766                                 (v >= 'A' && v <= 'Z') ||
2767                                 v == '-' ||
2768                                 v == '_' ||
2769                                 v == ' ' ||
2770                                 v == '=')
2771                         {
2772                                 *p_copy++ = v;
2773                         } else if (warned == FALSE) {
2774                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Transaction name truncated. Must be only [0-9A-Za-z\\-_=]+");
2775                                 warned = TRUE;
2776                         }
2777                         ++p_orig;
2778                 }
2779                 *p_copy++ = '*';
2780                 *p_copy++ = '/';
2781                 *p_copy++ = 0;
2782         }
2783         DBG_RETURN(ret);
2784 }
2785 /* }}} */
2786 
2787 
2788 /* {{{ mysqlnd_conn_data::tx_commit_ex */
2789 static enum_func_status
2790 MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * conn, const zend_bool commit, const unsigned int flags, const char * const name TSRMLS_DC)
2791 {
2792         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_commit_or_rollback);
2793         enum_func_status ret = FAIL;
2794         DBG_ENTER("mysqlnd_conn_data::tx_commit_or_rollback");
2795 
2796         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2797                 do {
2798                         smart_str tmp_str = {0, 0, 0};
2799                         conn->m->tx_cor_options_to_string(conn, &tmp_str, flags TSRMLS_CC);
2800                         smart_str_0(&tmp_str);
2801 
2802 
2803                         {
2804                                 char * query;
2805                                 size_t query_len;
2806                                 char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name TSRMLS_CC);
2807                                 
2808                                 query_len = mnd_sprintf(&query, 0, (commit? "COMMIT%s %s":"ROLLBACK%s %s"),
2809                                                                                 name_esc? name_esc:"", tmp_str.c? tmp_str.c:"");
2810                                 smart_str_free(&tmp_str);
2811                                 if (name_esc) {
2812                                         mnd_efree(name_esc);
2813                                         name_esc = NULL;
2814                                 }
2815                                 if (!query) {
2816                                         SET_OOM_ERROR(*conn->error_info);
2817                                         break;
2818                                 }
2819 
2820                                 ret = conn->m->query(conn, query, query_len TSRMLS_CC);
2821                                 mnd_sprintf_free(query);
2822                         }
2823                 } while (0);
2824                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);  
2825         }
2826 
2827         DBG_RETURN(ret);
2828 }
2829 /* }}} */
2830 
2831 
2832 /* {{{ mysqlnd_conn_data::tx_begin */
2833 static enum_func_status
2834 MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsigned int mode, const char * const name TSRMLS_DC)
2835 {
2836         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_begin);
2837         enum_func_status ret = FAIL;
2838         DBG_ENTER("mysqlnd_conn_data::tx_begin");
2839 
2840         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2841                 do {
2842                         smart_str tmp_str = {0, 0, 0};
2843                         if (mode & TRANS_START_WITH_CONSISTENT_SNAPSHOT) {
2844                                 if (tmp_str.len) {
2845                                         smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
2846                                 }
2847                                 smart_str_appendl(&tmp_str, "WITH CONSISTENT SNAPSHOT", sizeof("WITH CONSISTENT SNAPSHOT") - 1);
2848                         }
2849                         if (mode & (TRANS_START_READ_WRITE | TRANS_START_READ_ONLY)) {
2850                                 unsigned long server_version = conn->m->get_server_version(conn TSRMLS_CC);
2851                                 if (server_version < 50605L) {
2852                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "This server version doesn't support 'READ WRITE' and 'READ ONLY'. Minimum 5.6.5 is required");
2853                                         smart_str_free(&tmp_str);
2854                                         break;
2855                                 } else if (mode & TRANS_START_READ_WRITE) {
2856                                         if (tmp_str.len) {
2857                                                 smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
2858                                         }
2859                                         smart_str_appendl(&tmp_str, "READ WRITE", sizeof("READ WRITE") - 1);
2860                                 } else if (mode & TRANS_START_READ_ONLY) {
2861                                         if (tmp_str.len) {
2862                                                 smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
2863                                         }
2864                                         smart_str_appendl(&tmp_str, "READ ONLY", sizeof("READ ONLY") - 1);
2865                                 }
2866                         }
2867                         smart_str_0(&tmp_str);
2868 
2869                         {
2870                                 char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name TSRMLS_CC);
2871                                 char * query;
2872                                 unsigned int query_len = mnd_sprintf(&query, 0, "START TRANSACTION%s %s", name_esc? name_esc:"", tmp_str.c? tmp_str.c:"");
2873                                 smart_str_free(&tmp_str);
2874                                 if (name_esc) {
2875                                         mnd_efree(name_esc);
2876                                         name_esc = NULL;
2877                                 }
2878                                 if (!query) {
2879                                         SET_OOM_ERROR(*conn->error_info);
2880                                         break;
2881                                 }
2882                                 ret = conn->m->query(conn, query, query_len TSRMLS_CC);
2883                                 mnd_sprintf_free(query);
2884                         }
2885                 } while (0);
2886                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);  
2887         }
2888 
2889         DBG_RETURN(ret);
2890 }
2891 /* }}} */
2892 
2893 
2894 /* {{{ mysqlnd_conn_data::tx_savepoint */
2895 static enum_func_status
2896 MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint)(MYSQLND_CONN_DATA * conn, const char * const name TSRMLS_DC)
2897 {
2898         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_savepoint);
2899         enum_func_status ret = FAIL;
2900         DBG_ENTER("mysqlnd_conn_data::tx_savepoint");
2901 
2902         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2903                 do {
2904                         char * query;
2905                         unsigned int query_len;
2906                         if (!name) {
2907                                 SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
2908                                 break;
2909                         }
2910                         query_len = mnd_sprintf(&query, 0, "SAVEPOINT `%s`", name);
2911                         if (!query) {
2912                                 SET_OOM_ERROR(*conn->error_info);
2913                                 break;
2914                         }
2915                         ret = conn->m->query(conn, query, query_len TSRMLS_CC);
2916                         mnd_sprintf_free(query);
2917                 } while (0);
2918                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);  
2919         }
2920 
2921         DBG_RETURN(ret);
2922 }
2923 /* }}} */
2924 
2925 
2926 /* {{{ mysqlnd_conn_data::tx_savepoint_release */
2927 static enum_func_status
2928 MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn, const char * const name TSRMLS_DC)
2929 {
2930         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_savepoint_release);
2931         enum_func_status ret = FAIL;
2932         DBG_ENTER("mysqlnd_conn_data::tx_savepoint_release");
2933 
2934         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2935                 do {
2936                         char * query;
2937                         unsigned int query_len;
2938                         if (!name) {
2939                                 SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
2940                                 break;
2941                         }
2942                         query_len = mnd_sprintf(&query, 0, "RELEASE SAVEPOINT `%s`", name);
2943                         if (!query) {
2944                                 SET_OOM_ERROR(*conn->error_info);
2945                                 break;
2946                         }
2947                         ret = conn->m->query(conn, query, query_len TSRMLS_CC);
2948                         mnd_sprintf_free(query);
2949                 } while (0);
2950                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);  
2951         }
2952 
2953         DBG_RETURN(ret);
2954 }
2955 /* }}} */
2956 
2957 
2958 /* {{{ mysqlnd_conn_data::negotiate_client_api_capabilities */
2959 static unsigned int
2960 MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC)
2961 {
2962         unsigned int ret = 0;
2963         DBG_ENTER("mysqlnd_conn_data::negotiate_client_api_capabilities");
2964         if (conn) {
2965                 ret = conn->client_api_capabilities;
2966                 conn->client_api_capabilities = flags;
2967         }
2968 
2969         DBG_RETURN(ret);
2970 }
2971 /* }}} */
2972 
2973 
2974 /* {{{ mysqlnd_conn_data::get_client_api_capabilities */
2975 static unsigned int
2976 MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2977 {
2978         DBG_ENTER("mysqlnd_conn_data::get_client_api_capabilities");
2979         DBG_RETURN(conn? conn->client_api_capabilities : 0);
2980 }
2981 /* }}} */
2982 
2983 
2984 /* {{{ mysqlnd_conn_data::local_tx_start */
2985 static enum_func_status
2986 MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, size_t this_func TSRMLS_DC)
2987 {
2988         enum_func_status ret = PASS;
2989         DBG_ENTER("mysqlnd_conn_data::local_tx_start");
2990         DBG_RETURN(ret);
2991 }
2992 /* }}} */
2993 
2994 
2995 /* {{{ mysqlnd_conn_data::local_tx_end */
2996 static enum_func_status
2997 MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, size_t this_func, enum_func_status status TSRMLS_DC)
2998 {
2999         DBG_ENTER("mysqlnd_conn_data::local_tx_end");
3000         DBG_RETURN(status);
3001 }
3002 /* }}} */
3003 
3004 
3005 /* {{{ mysqlnd_conn_data::init */
3006 static enum_func_status
3007 MYSQLND_METHOD(mysqlnd_conn_data, init)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
3008 {
3009         DBG_ENTER("mysqlnd_conn_data::init");
3010         mysqlnd_stats_init(&conn->stats, STAT_LAST);
3011         SET_ERROR_AFF_ROWS(conn);
3012 
3013         conn->net = mysqlnd_net_init(conn->persistent, conn->stats, conn->error_info TSRMLS_CC);
3014         conn->protocol = mysqlnd_protocol_init(conn->persistent TSRMLS_CC);
3015 
3016         DBG_RETURN(conn->stats && conn->net && conn->protocol? PASS:FAIL);
3017 }
3018 /* }}} */
3019 
3020 
3021 MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
3022 
3023 
3024 MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
3025         MYSQLND_METHOD(mysqlnd_conn_data, init),
3026         MYSQLND_METHOD(mysqlnd_conn_data, connect),
3027 
3028         MYSQLND_METHOD(mysqlnd_conn_data, escape_string),
3029         MYSQLND_METHOD(mysqlnd_conn_data, set_charset),
3030         MYSQLND_METHOD(mysqlnd_conn_data, query),
3031         MYSQLND_METHOD(mysqlnd_conn_data, send_query),
3032         MYSQLND_METHOD(mysqlnd_conn_data, reap_query),
3033         MYSQLND_METHOD(mysqlnd_conn_data, use_result),
3034         MYSQLND_METHOD(mysqlnd_conn_data, store_result),
3035         MYSQLND_METHOD(mysqlnd_conn_data, next_result),
3036         MYSQLND_METHOD(mysqlnd_conn_data, more_results),
3037 
3038         _mysqlnd_stmt_init,
3039 
3040         MYSQLND_METHOD(mysqlnd_conn_data, shutdown),
3041         MYSQLND_METHOD(mysqlnd_conn_data, refresh),
3042 
3043         MYSQLND_METHOD(mysqlnd_conn_data, ping),
3044         MYSQLND_METHOD(mysqlnd_conn_data, kill),
3045         MYSQLND_METHOD(mysqlnd_conn_data, select_db),
3046         MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info),
3047         MYSQLND_METHOD(mysqlnd_conn_data, change_user),
3048 
3049         MYSQLND_METHOD(mysqlnd_conn_data, errno),
3050         MYSQLND_METHOD(mysqlnd_conn_data, error),
3051         MYSQLND_METHOD(mysqlnd_conn_data, sqlstate),
3052         MYSQLND_METHOD(mysqlnd_conn_data, thread_id),
3053 
3054         MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats),
3055 
3056         MYSQLND_METHOD(mysqlnd_conn_data, get_server_version),
3057         MYSQLND_METHOD(mysqlnd_conn_data, get_server_info),
3058         MYSQLND_METHOD(mysqlnd_conn_data, statistic),
3059         MYSQLND_METHOD(mysqlnd_conn_data, get_host_info),
3060         MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info),
3061         MYSQLND_METHOD(mysqlnd_conn_data, info),
3062         MYSQLND_METHOD(mysqlnd_conn_data, charset_name),
3063         MYSQLND_METHOD(mysqlnd_conn_data, list_fields),
3064         MYSQLND_METHOD(mysqlnd_conn_data, list_method),
3065 
3066         MYSQLND_METHOD(mysqlnd_conn_data, insert_id),
3067         MYSQLND_METHOD(mysqlnd_conn_data, affected_rows),
3068         MYSQLND_METHOD(mysqlnd_conn_data, warning_count),
3069         MYSQLND_METHOD(mysqlnd_conn_data, field_count),
3070 
3071         MYSQLND_METHOD(mysqlnd_conn_data, server_status),
3072 
3073         MYSQLND_METHOD(mysqlnd_conn_data, set_server_option),
3074         MYSQLND_METHOD(mysqlnd_conn_data, set_client_option),
3075         MYSQLND_METHOD(mysqlnd_conn_data, free_contents),
3076         MYSQLND_METHOD(mysqlnd_conn_data, free_options),
3077 
3078         MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor),
3079 
3080         mysqlnd_query_read_result_set_header,
3081 
3082         MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference),
3083         MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference),
3084         MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state),
3085         MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state),
3086 
3087         MYSQLND_METHOD(mysqlnd_conn_data, simple_command),
3088         MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response),
3089         MYSQLND_METHOD(mysqlnd_conn_data, restart_psession),
3090         MYSQLND_METHOD(mysqlnd_conn_data, end_psession),
3091         MYSQLND_METHOD(mysqlnd_conn_data, send_close),
3092 
3093         MYSQLND_METHOD(mysqlnd_conn_data, ssl_set),
3094         mysqlnd_result_init,
3095         MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit),
3096         MYSQLND_METHOD(mysqlnd_conn_data, tx_commit),
3097         MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback),
3098         MYSQLND_METHOD(mysqlnd_conn_data, tx_begin),
3099         MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback),
3100         MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string),
3101         MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint),
3102         MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release),
3103 
3104         MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start),
3105         MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end),
3106         MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands),
3107         MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags),
3108         MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake),
3109         MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request),
3110         MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name),
3111 
3112         MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d),
3113 
3114         MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities),
3115         MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)
3116 MYSQLND_CLASS_METHODS_END;
3117 
3118 
3119 /* {{{ mysqlnd_conn::get_reference */
3120 static MYSQLND *
3121 MYSQLND_METHOD(mysqlnd_conn, clone_object)(MYSQLND * const conn TSRMLS_DC)
3122 {
3123         MYSQLND * ret;
3124         DBG_ENTER("mysqlnd_conn::get_reference");
3125         ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).clone_connection_object(conn TSRMLS_CC);
3126         DBG_RETURN(ret);
3127 }
3128 /* }}} */
3129 
3130 
3131 /* {{{ mysqlnd_conn_data::dtor */
3132 static void
3133 MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn TSRMLS_DC)
3134 {
3135         DBG_ENTER("mysqlnd_conn::dtor");
3136         DBG_INF_FMT("conn=%llu", conn->data->thread_id);
3137 
3138         conn->data->m->free_reference(conn->data TSRMLS_CC);
3139 
3140         mnd_pefree(conn, conn->persistent);
3141 
3142         DBG_VOID_RETURN;
3143 }
3144 /* }}} */
3145 
3146 
3147 /* {{{ mysqlnd_conn_data::close */
3148 static enum_func_status
3149 MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, enum_connection_close_type close_type TSRMLS_DC)
3150 {
3151         size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_methods, close);
3152         MYSQLND_CONN_DATA * conn = conn_handle->data;
3153         enum_func_status ret = FAIL;
3154 
3155         DBG_ENTER("mysqlnd_conn::close");
3156         DBG_INF_FMT("conn=%llu", conn->thread_id);
3157 
3158         if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
3159                 if (CONN_GET_STATE(conn) >= CONN_READY) {
3160                         static enum_mysqlnd_collected_stats close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
3161                                 STAT_CLOSE_EXPLICIT,
3162                                 STAT_CLOSE_IMPLICIT,
3163                                 STAT_CLOSE_DISCONNECT
3164                         };
3165                         MYSQLND_INC_CONN_STATISTIC(conn->stats, close_type_to_stat_map[close_type]);
3166                 }
3167 
3168                 /*
3169                   Close now, free_reference will try,
3170                   if we are last, but that's not a problem.
3171                 */
3172                 ret = conn->m->send_close(conn TSRMLS_CC);
3173 
3174                 /* do it after free_reference/dtor and we might crash */
3175                 conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
3176 
3177                 conn_handle->m->dtor(conn_handle TSRMLS_CC);
3178         }
3179         DBG_RETURN(ret);
3180 }
3181 /* }}} */
3182 
3183 
3184 MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
3185         MYSQLND_METHOD(mysqlnd_conn, connect),
3186         MYSQLND_METHOD(mysqlnd_conn, clone_object),
3187         MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor),
3188         MYSQLND_METHOD(mysqlnd_conn, close)
3189 MYSQLND_CLASS_METHODS_END;
3190 
3191 
3192 /* {{{ _mysqlnd_init */
3193 PHPAPI MYSQLND *
3194 _mysqlnd_init(unsigned int flags, zend_bool persistent TSRMLS_DC)
3195 {
3196         MYSQLND * ret;
3197         DBG_ENTER("mysqlnd_init");
3198         ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_connection(persistent TSRMLS_CC);
3199         if (ret && ret->data) {
3200                 ret->data->m->negotiate_client_api_capabilities(ret->data, flags TSRMLS_CC);
3201         }
3202         DBG_RETURN(ret);
3203 }
3204 /* }}} */
3205 
3206 /*
3207  * Local variables:
3208  * tab-width: 4
3209  * c-basic-offset: 4
3210  * End:
3211  * vim600: noet sw=4 ts=4 fdm=marker
3212  * vim<600: noet sw=4 ts=4
3213  */

/* [<][>][^][v][top][bottom][index][help] */