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