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