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