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