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