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