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