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