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