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