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