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