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