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_stream_array_check_for_readiness
  13. mysqlnd_stream_array_to_fd_set
  14. mysqlnd_stream_array_from_fd_set
  15. mysqlnd_poll
  16. MYSQLND_METHOD
  17. MYSQLND_METHOD
  18. MYSQLND_METHOD
  19. mysqlnd_old_escape_string
  20. MYSQLND_METHOD
  21. MYSQLND_METHOD
  22. MYSQLND_METHOD
  23. MYSQLND_METHOD_PRIVATE
  24. MYSQLND_METHOD_PRIVATE
  25. MYSQLND_METHOD_PRIVATE
  26. MYSQLND_METHOD
  27. MYSQLND_METHOD
  28. MYSQLND_METHOD
  29. MYSQLND_METHOD
  30. MYSQLND_METHOD
  31. MYSQLND_METHOD
  32. mysqlnd_get_client_info
  33. mysqlnd_get_client_version
  34. MYSQLND_METHOD
  35. MYSQLND_METHOD
  36. MYSQLND_METHOD
  37. MYSQLND_METHOD
  38. MYSQLND_METHOD
  39. MYSQLND_METHOD
  40. MYSQLND_METHOD
  41. MYSQLND_METHOD
  42. mysqlnd_field_type_name
  43. MYSQLND_METHOD
  44. MYSQLND_METHOD
  45. mysqlnd_escape_string_for_tx_name_in_comment
  46. MYSQLND_METHOD
  47. MYSQLND_METHOD
  48. MYSQLND_CLASS_METHODS_START
  49. MYSQLND_METHOD_PRIVATE
  50. MYSQLND_CLASS_METHODS_START

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

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