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