Annotation of embedaddon/php/ext/mysqlnd/mysqlnd.c, revision 1.1.1.1
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 2006-2012 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 321634 2012-01-01 13:15:04Z felipe $ */
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 */
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
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
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
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
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
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
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
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
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
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
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: /* we allow load data local infile by default */
684: mysql_flags |= CLIENT_LOCAL_FILES | CLIENT_PS_MULTI_RESULTS;
685: mysql_flags |= MYSQLND_CAPABILITIES;
686:
687: if (db) {
688: mysql_flags |= CLIENT_CONNECT_WITH_DB;
689: }
690:
691: if (PG(open_basedir) && strlen(PG(open_basedir))) {
692: mysql_flags ^= CLIENT_LOCAL_FILES;
693: }
694:
695: #ifndef MYSQLND_COMPRESSION_ENABLED
696: if (mysql_flags & CLIENT_COMPRESS) {
697: mysql_flags &= ~CLIENT_COMPRESS;
698: }
699: #else
700: if (conn->net->options.flags & MYSQLND_NET_FLAG_USE_COMPRESSION) {
701: mysql_flags |= CLIENT_COMPRESS;
702: }
703: #endif
704: #ifndef MYSQLND_SSL_SUPPORTED
705: if (mysql_flags & CLIENT_SSL) {
706: mysql_flags &= ~CLIENT_SSL;
707: }
708: #else
709: if (conn->net->options.ssl_key || conn->net->options.ssl_cert ||
710: conn->net->options.ssl_ca || conn->net->options.ssl_capath || conn->net->options.ssl_cipher)
711: {
712: mysql_flags |= CLIENT_SSL;
713: }
714: #endif
715:
716: if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, greet_packet, &conn->options, mysql_flags TSRMLS_CC)) {
717: goto err;
718: }
719:
720: {
721: CONN_SET_STATE(conn, CONN_READY);
722:
723: if (saved_compression) {
724: conn->net->compressed = TRUE;
725: }
726: /*
727: If a connect on a existing handle is performed and mysql_flags is
728: passed which doesn't CLIENT_COMPRESS, then we need to overwrite the value
729: which we set based on saved_compression.
730: */
731: conn->net->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
732:
733: conn->user = mnd_pestrdup(user, conn->persistent);
734: conn->user_len = strlen(conn->user);
735: conn->passwd = mnd_pestrndup(passwd, passwd_len, conn->persistent);
736: conn->passwd_len = passwd_len;
737: conn->port = port;
738: conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
739: conn->connect_or_select_db_len = db_len;
740:
741: if (!conn->user || !conn->passwd || !conn->connect_or_select_db) {
742: SET_OOM_ERROR(conn->error_info);
743: goto err; /* OOM */
744: }
745:
746: if (!unix_socket) {
747: conn->host = mnd_pestrdup(host, conn->persistent);
748: if (!conn->host) {
749: SET_OOM_ERROR(conn->error_info);
750: goto err; /* OOM */
751: }
752: conn->host_len = strlen(conn->host);
753: {
754: char *p;
755: spprintf(&p, 0, "%s via TCP/IP", conn->host);
756: if (!p) {
757: SET_OOM_ERROR(conn->error_info);
758: goto err; /* OOM */
759: }
760: conn->host_info = mnd_pestrdup(p, conn->persistent);
761: efree(p); /* allocated by spprintf */
762: if (!conn->host_info) {
763: SET_OOM_ERROR(conn->error_info);
764: goto err; /* OOM */
765: }
766: }
767: } else {
768: conn->unix_socket = mnd_pestrdup(socket_or_pipe, conn->persistent);
769: conn->host_info = mnd_pestrdup("Localhost via UNIX socket", conn->persistent);
770: if (!conn->unix_socket || !conn->host_info) {
771: SET_OOM_ERROR(conn->error_info);
772: goto err; /* OOM */
773: }
774: conn->unix_socket_len = strlen(conn->unix_socket);
775: }
776: conn->client_flag = mysql_flags;
777: conn->max_packet_size = MYSQLND_ASSEBLED_PACKET_MAX_SIZE;
778: /* todo: check if charset is available */
779: conn->server_capabilities = greet_packet->server_capabilities;
780: conn->upsert_status.warning_count = 0;
781: conn->upsert_status.server_status = greet_packet->server_status;
782: conn->upsert_status.affected_rows = 0;
783:
784: SET_EMPTY_ERROR(conn->error_info);
785:
786: mysqlnd_local_infile_default(conn);
787:
788: #if MYSQLND_UNICODE
789: {
790: unsigned int as_unicode = 1;
791: conn->m->set_client_option(conn, MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE, (char *)&as_unicode TSRMLS_CC);
792: DBG_INF("unicode set");
793: }
794: #endif
795: if (conn->options.init_commands) {
796: unsigned int current_command = 0;
797: for (; current_command < conn->options.num_commands; ++current_command) {
798: const char * const command = conn->options.init_commands[current_command];
799: if (command) {
800: MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_EXECUTED_COUNT);
801: if (PASS != conn->m->query(conn, command, strlen(command) TSRMLS_CC)) {
802: MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_FAILED_COUNT);
803: goto err;
804: }
805: if (conn->last_query_type == QUERY_SELECT) {
806: MYSQLND_RES * result = conn->m->use_result(conn TSRMLS_CC);
807: if (result) {
808: result->m.free_result(result, TRUE TSRMLS_CC);
809: }
810: }
811: }
812: }
813: }
814:
815:
816: MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_CONNECT_SUCCESS, 1, STAT_OPENED_CONNECTIONS, 1);
817: if (reconnect) {
818: MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT);
819: }
820: if (conn->persistent) {
821: MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PCONNECT_SUCCESS, 1, STAT_OPENED_PERSISTENT_CONNECTIONS, 1);
822: }
823:
824: DBG_INF_FMT("connection_id=%llu", conn->thread_id);
825:
826: PACKET_FREE(greet_packet);
827:
828: DBG_RETURN(PASS);
829: }
830: err:
831: PACKET_FREE(greet_packet);
832:
833: if (errstr) {
834: DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", errcode, errstr, conn->scheme);
835: SET_CLIENT_ERROR(conn->error_info, errcode? errcode:CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, errstr);
836: php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%u] %.128s (trying to connect via %s)", errcode, errstr, conn->scheme);
837: /* no mnd_ since we don't allocate it */
838: efree(errstr);
839: }
840: conn->m->free_contents(conn TSRMLS_CC);
841: MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE);
842:
843: DBG_RETURN(FAIL);
844: }
845: /* }}} */
846:
847:
848: /* {{{ mysqlnd_connect */
849: PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn,
850: const char *host, const char *user,
851: const char *passwd, unsigned int passwd_len,
852: const char *db, unsigned int db_len,
853: unsigned int port,
854: const char *socket_or_pipe,
855: unsigned int mysql_flags
856: TSRMLS_DC)
857: {
858: enum_func_status ret = FAIL;
859: zend_bool self_alloced = FALSE;
860:
861: DBG_ENTER("mysqlnd_connect");
862: DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags);
863:
864: if (!conn) {
865: self_alloced = TRUE;
866: if (!(conn = mysqlnd_init(FALSE))) {
867: /* OOM */
868: DBG_RETURN(NULL);
869: }
870: }
871:
872: ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC);
873:
874: if (ret == FAIL) {
875: if (self_alloced) {
876: /*
877: We have alloced, thus there are no references to this
878: object - we are free to kill it!
879: */
880: conn->m->dtor(conn TSRMLS_CC);
881: }
882: DBG_RETURN(NULL);
883: }
884: DBG_RETURN(conn);
885: }
886: /* }}} */
887:
888:
889: /* {{{ mysqlnd_conn::change_user */
890: static enum_func_status
891: MYSQLND_METHOD(mysqlnd_conn, change_user)(MYSQLND * const conn,
892: const char *user,
893: const char *passwd,
894: const char *db,
895: zend_bool silent TSRMLS_DC)
896: {
897: size_t user_len;
898: enum_func_status ret = FAIL;
899: MYSQLND_PACKET_CHG_USER_RESPONSE * chg_user_resp;
900: char buffer[MYSQLND_MAX_ALLOWED_USER_LEN + 1 + 1 + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 2 /* charset*/ + 2];
901: char *p = buffer;
902: const MYSQLND_CHARSET * old_cs = conn->charset;
903:
904: DBG_ENTER("mysqlnd_conn::change_user");
905: DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%u",
906: conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 );
907:
908: SET_ERROR_AFF_ROWS(conn);
909:
910: if (!user) {
911: user = "";
912: }
913: if (!passwd) {
914: passwd = "";
915: }
916: if (!db) {
917: db = "";
918: }
919:
920: /* 1. user ASCIIZ */
921: user_len = MIN(strlen(user), MYSQLND_MAX_ALLOWED_USER_LEN);
922: memcpy(p, user, user_len);
923: p += user_len;
924: *p++ = '\0';
925:
926: /* 2. password SCRAMBLE_LENGTH followed by the scramble or \0 */
927: if (passwd[0]) {
928: *p++ = SCRAMBLE_LENGTH;
929: php_mysqlnd_scramble((unsigned char *)p, conn->scramble, (unsigned char *)passwd);
930: p += SCRAMBLE_LENGTH;
931: } else {
932: *p++ = '\0';
933: }
934:
935: /* 3. db ASCIIZ */
936: if (db[0]) {
937: size_t db_len = MIN(strlen(db), MYSQLND_MAX_ALLOWED_DB_LEN);
938: memcpy(p, db, db_len);
939: p += db_len;
940: }
941: *p++ = '\0';
942:
943: /*
944: 4. request the current charset, or it will be reset to the system one.
945: 5.0 doesn't support it. Support added in 5.1.23 by fixing the following bug :
946: Bug #30472 libmysql doesn't reset charset, insert_id after succ. mysql_change_user() call
947: */
948: if (mysqlnd_get_server_version(conn) >= 50123) {
949: int2store(p, conn->charset->nr);
950: p+=2;
951: }
952:
953: if (PASS != conn->m->simple_command(conn, COM_CHANGE_USER, buffer, p - buffer,
954: PROT_LAST /* we will handle the OK packet*/,
955: silent, TRUE TSRMLS_CC)) {
956: DBG_RETURN(FAIL);
957: }
958:
959: chg_user_resp = conn->protocol->m.get_change_user_response_packet(conn->protocol, FALSE TSRMLS_CC);
960: if (!chg_user_resp) {
961: SET_OOM_ERROR(conn->error_info);
962: goto end;
963: }
964: ret = PACKET_READ(chg_user_resp, conn);
965: conn->error_info = chg_user_resp->error_info;
966:
967: if (conn->error_info.error_no) {
968: ret = FAIL;
969: /*
970: COM_CHANGE_USER is broken in 5.1. At least in 5.1.15 and 5.1.14, 5.1.11 is immune.
971: bug#25371 mysql_change_user() triggers "packets out of sync"
972: When it gets fixed, there should be one more check here
973: */
974: if (mysqlnd_get_server_version(conn) > 50113L && mysqlnd_get_server_version(conn) < 50118L) {
975: MYSQLND_PACKET_OK * redundant_error_packet = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
976: if (redundant_error_packet) {
977: PACKET_READ(redundant_error_packet, conn);
978: PACKET_FREE(redundant_error_packet);
979: DBG_INF_FMT("Server is %u, buggy, sends two ERR messages", mysqlnd_get_server_version(conn));
980: } else {
981: SET_OOM_ERROR(conn->error_info);
982: }
983: }
984: }
985: if (ret == PASS) {
986: char * tmp = NULL;
987: /* if we get conn->user as parameter and then we first free it, then estrndup it, we will crash */
988: tmp = mnd_pestrndup(user, user_len, conn->persistent);
989: if (conn->user) {
990: mnd_pefree(conn->user, conn->persistent);
991: }
992: conn->user = tmp;
993:
994: tmp = mnd_pestrdup(passwd, conn->persistent);
995: if (conn->passwd) {
996: mnd_pefree(conn->passwd, conn->persistent);
997: }
998: conn->passwd = tmp;
999:
1000: if (conn->last_message) {
1001: mnd_pefree(conn->last_message, conn->persistent);
1002: conn->last_message = NULL;
1003: }
1004: memset(&conn->upsert_status, 0, sizeof(conn->upsert_status));
1005: /* set charset for old servers */
1006: if (mysqlnd_get_server_version(conn) < 50123) {
1007: ret = conn->m->set_charset(conn, old_cs->name TSRMLS_CC);
1008: }
1009: } else if (ret == FAIL && chg_user_resp->server_asked_323_auth == TRUE) {
1010: /* old authentication with new server !*/
1011: DBG_ERR(mysqlnd_old_passwd);
1012: SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, mysqlnd_old_passwd);
1013: }
1014: end:
1015: PACKET_FREE(chg_user_resp);
1016:
1017: /*
1018: Here we should close all statements. Unbuffered queries should not be a
1019: problem as we won't allow sending COM_CHANGE_USER.
1020: */
1021: DBG_INF(ret == PASS? "PASS":"FAIL");
1022: DBG_RETURN(ret);
1023: }
1024: /* }}} */
1025:
1026:
1027: /* {{{ mysqlnd_conn::query */
1028: /*
1029: If conn->error_info.error_no is not zero, then we had an error.
1030: Still the result from the query is PASS
1031: */
1032: static enum_func_status
1033: MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND * conn, const char * query, unsigned int query_len TSRMLS_DC)
1034: {
1035: enum_func_status ret = FAIL;
1036: DBG_ENTER("mysqlnd_conn::query");
1037: DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
1038:
1039: if (PASS == conn->m->send_query(conn, query, query_len TSRMLS_CC) &&
1040: PASS == conn->m->reap_query(conn TSRMLS_CC))
1041: {
1042: ret = PASS;
1043: if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status.affected_rows) {
1044: MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status.affected_rows);
1045: }
1046: }
1047: DBG_RETURN(ret);
1048: }
1049: /* }}} */
1050:
1051:
1052: /* {{{ mysqlnd_conn::send_query */
1053: static enum_func_status
1054: MYSQLND_METHOD(mysqlnd_conn, send_query)(MYSQLND * conn, const char * query, unsigned int query_len TSRMLS_DC)
1055: {
1056: enum_func_status ret;
1057: DBG_ENTER("mysqlnd_conn::send_query");
1058: DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
1059:
1060: ret = conn->m->simple_command(conn, COM_QUERY, query, query_len,
1061: PROT_LAST /* we will handle the OK packet*/,
1062: FALSE, FALSE TSRMLS_CC);
1063: if (PASS == ret) {
1064: CONN_SET_STATE(conn, CONN_QUERY_SENT);
1065: }
1066: DBG_RETURN(ret);
1067: }
1068: /* }}} */
1069:
1070:
1071: /* {{{ mysqlnd_conn::reap_query */
1072: static enum_func_status
1073: MYSQLND_METHOD(mysqlnd_conn, reap_query)(MYSQLND * conn TSRMLS_DC)
1074: {
1075: enum_mysqlnd_connection_state state = CONN_GET_STATE(conn);
1076: DBG_ENTER("mysqlnd_conn::reap_query");
1077: DBG_INF_FMT("conn=%llu", conn->thread_id);
1078:
1079: if (state <= CONN_READY || state == CONN_QUIT_SENT) {
1080: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not opened, clear or has been closed");
1081: DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state);
1082: DBG_RETURN(FAIL);
1083: }
1084: /*
1085: Here read the result set. We don't do it in simple_command because it need
1086: information from the ok packet. We will fetch it ourselves.
1087: */
1088: DBG_RETURN(conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC));
1089: }
1090: /* }}} */
1091:
1092:
1093: #include "php_network.h"
1094:
1095: MYSQLND ** mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array TSRMLS_DC)
1096: {
1097: int cnt = 0;
1098: MYSQLND **p = conn_array, **p_p;
1099: MYSQLND **ret = NULL;
1100:
1101: while (*p) {
1102: if (CONN_GET_STATE(*p) <= CONN_READY || CONN_GET_STATE(*p) == CONN_QUIT_SENT) {
1103: cnt++;
1104: }
1105: p++;
1106: }
1107: if (cnt) {
1108: MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
1109: p_p = p = conn_array;
1110: while (*p) {
1111: if (CONN_GET_STATE(*p) <= CONN_READY || CONN_GET_STATE(*p) == CONN_QUIT_SENT) {
1112: *ret_p = *p;
1113: *p = NULL;
1114: ret_p++;
1115: } else {
1116: *p_p = *p;
1117: p_p++;
1118: }
1119: p++;
1120: }
1121: *ret_p = NULL;
1122: }
1123: return ret;
1124: }
1125:
1126:
1127: /* {{{ stream_select mysqlnd_stream_array_to_fd_set functions */
1128: static int mysqlnd_stream_array_to_fd_set(MYSQLND **conn_array, fd_set *fds, php_socket_t *max_fd TSRMLS_DC)
1129: {
1130: php_socket_t this_fd;
1131: int cnt = 0;
1132: MYSQLND **p = conn_array;
1133:
1134: while (*p) {
1135: /* get the fd.
1136: * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
1137: * when casting. It is only used here so that the buffered data warning
1138: * is not displayed.
1139: * */
1140: if (SUCCESS == php_stream_cast((*p)->net->stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
1141: (void*)&this_fd, 1) && this_fd >= 0) {
1142:
1143: PHP_SAFE_FD_SET(this_fd, fds);
1144:
1145: if (this_fd > *max_fd) {
1146: *max_fd = this_fd;
1147: }
1148: cnt++;
1149: }
1150: p++;
1151: }
1152: return cnt ? 1 : 0;
1153: }
1154:
1155: static int mysqlnd_stream_array_from_fd_set(MYSQLND **conn_array, fd_set *fds TSRMLS_DC)
1156: {
1157: php_socket_t this_fd;
1158: int ret = 0;
1159: zend_bool disproportion = FALSE;
1160:
1161:
1162: MYSQLND **fwd = conn_array, **bckwd = conn_array;
1163:
1164: while (*fwd) {
1165: if (SUCCESS == php_stream_cast((*fwd)->net->stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
1166: (void*)&this_fd, 1) && this_fd >= 0) {
1167: if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
1168: if (disproportion) {
1169: *bckwd = *fwd;
1170: }
1171: bckwd++;
1172: fwd++;
1173: ret++;
1174: continue;
1175: }
1176: }
1177: disproportion = TRUE;
1178: fwd++;
1179: }
1180: *bckwd = NULL;/* NULL-terminate the list */
1181:
1182: return ret;
1183: }
1184: /* }}} */
1185:
1186: #ifndef PHP_WIN32
1187: #define php_select(m, r, w, e, t) select(m, r, w, e, t)
1188: #else
1189: #include "win32/select.h"
1190: #endif
1191:
1192: /* {{{ _mysqlnd_poll */
1193: PHPAPI enum_func_status
1194: _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, uint * desc_num TSRMLS_DC)
1195: {
1196:
1197: struct timeval tv;
1198: struct timeval *tv_p = NULL;
1199: fd_set rfds, wfds, efds;
1200: php_socket_t max_fd = 0;
1201: int retval, sets = 0;
1202: int set_count, max_set_count = 0;
1203: DBG_ENTER("mysqlnd_poll");
1204:
1205: if (sec < 0 || usec < 0) {
1206: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative values passed for sec and/or usec");
1207: DBG_RETURN(FAIL);
1208: }
1209:
1210: *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array TSRMLS_CC);
1211:
1212: FD_ZERO(&rfds);
1213: FD_ZERO(&wfds);
1214: FD_ZERO(&efds);
1215:
1216: if (r_array != NULL) {
1217: set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd TSRMLS_CC);
1218: if (set_count > max_set_count) {
1219: max_set_count = set_count;
1220: }
1221: sets += set_count;
1222: }
1223:
1224: if (e_array != NULL) {
1225: set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd TSRMLS_CC);
1226: if (set_count > max_set_count) {
1227: max_set_count = set_count;
1228: }
1229: sets += set_count;
1230: }
1231:
1232: if (!sets) {
1233: php_error_docref(NULL TSRMLS_CC, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
1234: DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
1235: DBG_RETURN(FAIL);
1236: }
1237:
1238: PHP_SAFE_MAX_FD(max_fd, max_set_count);
1239:
1240: /* Solaris + BSD do not like microsecond values which are >= 1 sec */
1241: if (usec > 999999) {
1242: tv.tv_sec = sec + (usec / 1000000);
1243: tv.tv_usec = usec % 1000000;
1244: } else {
1245: tv.tv_sec = sec;
1246: tv.tv_usec = usec;
1247: }
1248:
1249: tv_p = &tv;
1250:
1251: retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
1252:
1253: if (retval == -1) {
1254: php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
1255: errno, strerror(errno), max_fd);
1256: DBG_RETURN(FAIL);
1257: }
1258:
1259: if (r_array != NULL) {
1260: mysqlnd_stream_array_from_fd_set(r_array, &rfds TSRMLS_CC);
1261: }
1262: if (e_array != NULL) {
1263: mysqlnd_stream_array_from_fd_set(e_array, &efds TSRMLS_CC);
1264: }
1265:
1266: *desc_num = retval;
1267:
1268: DBG_RETURN(PASS);
1269: }
1270: /* }}} */
1271:
1272:
1273: /*
1274: COM_FIELD_LIST is special, different from a SHOW FIELDS FROM :
1275: - There is no result set header - status from the command, which
1276: impacts us to allocate big chunk of memory for reading the metadata.
1277: - The EOF packet is consumed by the metadata packet reader.
1278: */
1279:
1280: /* {{{ mysqlnd_conn::list_fields */
1281: MYSQLND_RES *
1282: MYSQLND_METHOD(mysqlnd_conn, list_fields)(MYSQLND * conn, const char *table, const char *achtung_wild TSRMLS_DC)
1283: {
1284: /* db + \0 + wild + \0 (for wild) */
1285: char buff[MYSQLND_MAX_ALLOWED_DB_LEN * 2 + 1 + 1], *p;
1286: size_t table_len, wild_len;
1287: MYSQLND_RES *result = NULL;
1288: DBG_ENTER("mysqlnd_conn::list_fields");
1289: DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:"");
1290:
1291: p = buff;
1292: if (table && (table_len = strlen(table))) {
1293: size_t to_copy = MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN);
1294: memcpy(p, table, to_copy);
1295: p += to_copy;
1296: *p++ = '\0';
1297: }
1298:
1299: if (achtung_wild && (wild_len = strlen(achtung_wild))) {
1300: size_t to_copy = MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN);
1301: memcpy(p, achtung_wild, to_copy);
1302: p += to_copy;
1303: *p++ = '\0';
1304: }
1305:
1306: if (PASS != conn->m->simple_command(conn, COM_FIELD_LIST, buff, p - buff,
1307: PROT_LAST /* we will handle the OK packet*/,
1308: FALSE, TRUE TSRMLS_CC)) {
1309: DBG_RETURN(NULL);
1310: }
1311:
1312: /*
1313: Prepare for the worst case.
1314: MyISAM goes to 2500 BIT columns, double it for safety.
1315: */
1316: result = conn->m->result_init(5000, conn->persistent TSRMLS_CC);
1317: if (!result) {
1318: DBG_RETURN(NULL);
1319: }
1320:
1321: if (FAIL == result->m.read_result_metadata(result, conn TSRMLS_CC)) {
1322: DBG_ERR("Error ocurred while reading metadata");
1323: result->m.free_result(result, TRUE TSRMLS_CC);
1324: DBG_RETURN(NULL);
1325: }
1326:
1327: result->type = MYSQLND_RES_NORMAL;
1328: result->m.fetch_row = result->m.fetch_row_normal_unbuffered;
1329: result->unbuf = mnd_ecalloc(1, sizeof(MYSQLND_RES_UNBUFFERED));
1330: if (!result->unbuf) {
1331: /* OOM */
1332: SET_OOM_ERROR(conn->error_info);
1333: result->m.free_result(result, TRUE TSRMLS_CC);
1334: DBG_RETURN(NULL);
1335: }
1336: result->unbuf->eof_reached = TRUE;
1337:
1338: DBG_RETURN(result);
1339: }
1340: /* }}} */
1341:
1342:
1343: /* {{{ mysqlnd_conn::list_method */
1344: MYSQLND_RES *
1345: MYSQLND_METHOD(mysqlnd_conn, list_method)(MYSQLND * conn, const char * query, const char *achtung_wild, char *par1 TSRMLS_DC)
1346: {
1347: char *show_query = NULL;
1348: size_t show_query_len;
1349: MYSQLND_RES *result = NULL;
1350:
1351: DBG_ENTER("mysqlnd_conn::list_method");
1352: DBG_INF_FMT("conn=%llu query=%s wild=%u", conn->thread_id, query, achtung_wild);
1353:
1354: if (par1) {
1355: if (achtung_wild) {
1356: show_query_len = spprintf(&show_query, 0, query, par1, achtung_wild);
1357: } else {
1358: show_query_len = spprintf(&show_query, 0, query, par1);
1359: }
1360: } else {
1361: if (achtung_wild) {
1362: show_query_len = spprintf(&show_query, 0, query, achtung_wild);
1363: } else {
1364: show_query_len = strlen(show_query = (char *)query);
1365: }
1366: }
1367:
1368: if (PASS == conn->m->query(conn, show_query, show_query_len TSRMLS_CC)) {
1369: result = conn->m->store_result(conn TSRMLS_CC);
1370: }
1371: if (show_query != query) {
1372: efree(show_query); /* allocated by spprintf */
1373: }
1374: DBG_RETURN(result);
1375: }
1376: /* }}} */
1377:
1378:
1379: /* {{{ mysqlnd_conn::errno */
1380: static unsigned int
1381: MYSQLND_METHOD(mysqlnd_conn, errno)(const MYSQLND * const conn TSRMLS_DC)
1382: {
1383: return conn->error_info.error_no;
1384: }
1385: /* }}} */
1386:
1387:
1388: /* {{{ mysqlnd_conn::error */
1389: static const char *
1390: MYSQLND_METHOD(mysqlnd_conn, error)(const MYSQLND * const conn TSRMLS_DC)
1391: {
1392: return conn->error_info.error;
1393: }
1394: /* }}} */
1395:
1396:
1397: /* {{{ mysqlnd_conn::sqlstate */
1398: static const char *
1399: MYSQLND_METHOD(mysqlnd_conn, sqlstate)(const MYSQLND * const conn TSRMLS_DC)
1400: {
1401: return conn->error_info.sqlstate[0] ? conn->error_info.sqlstate:MYSQLND_SQLSTATE_NULL;
1402: }
1403: /* }}} */
1404:
1405:
1406: /* {{{ mysqlnd_old_escape_string */
1407: PHPAPI ulong mysqlnd_old_escape_string(char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC)
1408: {
1409: DBG_ENTER("mysqlnd_old_escape_string");
1410: DBG_RETURN(mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), newstr, escapestr, escapestr_len TSRMLS_CC));
1411: }
1412: /* }}} */
1413:
1414: /* {{{ mysqlnd_conn::ssl_set */
1415: static enum_func_status
1416: 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)
1417: {
1418: return (PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_KEY, key TSRMLS_CC) &&
1419: PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CERT, cert TSRMLS_CC) &&
1420: PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CA, ca TSRMLS_CC) &&
1421: PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CAPATH, capath TSRMLS_CC) &&
1422: PASS == conn->net->m.set_client_option(conn->net, MYSQLND_OPT_SSL_CIPHER, cipher TSRMLS_CC)) ? PASS : FAIL;
1423: }
1424: /* }}} */
1425:
1426:
1427: /* {{{ mysqlnd_conn::escape_string */
1428: static ulong
1429: MYSQLND_METHOD(mysqlnd_conn, escape_string)(MYSQLND * const conn, char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC)
1430: {
1431: DBG_ENTER("mysqlnd_conn::escape_string");
1432: DBG_INF_FMT("conn=%llu", conn->thread_id);
1433: if (conn->upsert_status.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
1434: DBG_RETURN(mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC));
1435: }
1436: DBG_RETURN(mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC));
1437: }
1438: /* }}} */
1439:
1440:
1441: /* {{{ mysqlnd_conn::dump_debug_info */
1442: static enum_func_status
1443: MYSQLND_METHOD(mysqlnd_conn, dump_debug_info)(MYSQLND * const conn TSRMLS_DC)
1444: {
1445: DBG_ENTER("mysqlnd_conn::dump_debug_info");
1446: DBG_INF_FMT("conn=%llu", conn->thread_id);
1447: DBG_RETURN(conn->m->simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC));
1448: }
1449: /* }}} */
1450:
1451:
1452: /* {{{ mysqlnd_conn::select_db */
1453: static enum_func_status
1454: MYSQLND_METHOD(mysqlnd_conn, select_db)(MYSQLND * const conn, const char * const db, unsigned int db_len TSRMLS_DC)
1455: {
1456: enum_func_status ret;
1457:
1458: DBG_ENTER("mysqlnd_conn::select_db");
1459: DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
1460:
1461: ret = conn->m->simple_command(conn, COM_INIT_DB, db, db_len, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
1462: /*
1463: The server sends 0 but libmysql doesn't read it and has established
1464: a protocol of giving back -1. Thus we have to follow it :(
1465: */
1466: SET_ERROR_AFF_ROWS(conn);
1467: if (ret == PASS) {
1468: if (conn->connect_or_select_db) {
1469: mnd_pefree(conn->connect_or_select_db, conn->persistent);
1470: }
1471: conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
1472: conn->connect_or_select_db_len = db_len;
1473: if (!conn->connect_or_select_db) {
1474: /* OOM */
1475: SET_OOM_ERROR(conn->error_info);
1476: ret = FAIL;
1477: }
1478: }
1479: DBG_RETURN(ret);
1480: }
1481: /* }}} */
1482:
1483:
1484: /* {{{ mysqlnd_conn::ping */
1485: static enum_func_status
1486: MYSQLND_METHOD(mysqlnd_conn, ping)(MYSQLND * const conn TSRMLS_DC)
1487: {
1488: enum_func_status ret;
1489:
1490: DBG_ENTER("mysqlnd_conn::ping");
1491: DBG_INF_FMT("conn=%llu", conn->thread_id);
1492:
1493: ret = conn->m->simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, TRUE, TRUE TSRMLS_CC);
1494: /*
1495: The server sends 0 but libmysql doesn't read it and has established
1496: a protocol of giving back -1. Thus we have to follow it :(
1497: */
1498: SET_ERROR_AFF_ROWS(conn);
1499:
1500: DBG_INF_FMT("ret=%u", ret);
1501: DBG_RETURN(ret);
1502: }
1503: /* }}} */
1504:
1505:
1506: /* {{{ mysqlnd_conn::statistic */
1507: static enum_func_status
1508: MYSQLND_METHOD(mysqlnd_conn, statistic)(MYSQLND * conn, char **message, unsigned int * message_len TSRMLS_DC)
1509: {
1510: enum_func_status ret;
1511: MYSQLND_PACKET_STATS * stats_header;
1512:
1513: DBG_ENTER("mysqlnd_conn::statistic");
1514: DBG_INF_FMT("conn=%llu", conn->thread_id);
1515:
1516: ret = conn->m->simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE, TRUE TSRMLS_CC);
1517: if (FAIL == ret) {
1518: DBG_RETURN(FAIL);
1519: }
1520: stats_header = conn->protocol->m.get_stats_packet(conn->protocol, FALSE TSRMLS_CC);
1521: if (!stats_header) {
1522: SET_OOM_ERROR(conn->error_info);
1523: DBG_RETURN(FAIL);
1524: }
1525:
1526: if (FAIL == (ret = PACKET_READ(stats_header, conn))) {
1527: DBG_RETURN(FAIL);
1528: }
1529: /* will be freed by Zend, thus don't use the mnd_ allocator */
1530: *message = estrndup(stats_header->message, stats_header->message_len);
1531: *message_len = stats_header->message_len;
1532: PACKET_FREE(stats_header);
1533:
1534: DBG_INF(*message);
1535: DBG_RETURN(PASS);
1536: }
1537: /* }}} */
1538:
1539:
1540: /* {{{ mysqlnd_conn::kill */
1541: static enum_func_status
1542: MYSQLND_METHOD(mysqlnd_conn, kill)(MYSQLND * conn, unsigned int pid TSRMLS_DC)
1543: {
1544: enum_func_status ret;
1545: char buff[4];
1546:
1547: DBG_ENTER("mysqlnd_conn::kill");
1548: DBG_INF_FMT("conn=%llu pid=%u", conn->thread_id, pid);
1549:
1550: int4store(buff, pid);
1551:
1552: /* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */
1553: if (pid != conn->thread_id) {
1554: ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
1555: /*
1556: The server sends 0 but libmysql doesn't read it and has established
1557: a protocol of giving back -1. Thus we have to follow it :(
1558: */
1559: SET_ERROR_AFF_ROWS(conn);
1560: } else if (PASS == (ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_LAST, FALSE, TRUE TSRMLS_CC))) {
1561: CONN_SET_STATE(conn, CONN_QUIT_SENT);
1562: }
1563: DBG_RETURN(ret);
1564: }
1565: /* }}} */
1566:
1567:
1568: /* {{{ mysqlnd_conn::set_charset */
1569: static enum_func_status
1570: MYSQLND_METHOD(mysqlnd_conn, set_charset)(MYSQLND * const conn, const char * const csname TSRMLS_DC)
1571: {
1572: enum_func_status ret = PASS;
1573: char * query;
1574: size_t query_len;
1575: const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
1576:
1577: DBG_ENTER("mysqlnd_conn::set_charset");
1578: DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname);
1579:
1580: if (!charset) {
1581: SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
1582: "Invalid characterset or character set not supported");
1583: DBG_RETURN(FAIL);
1584: }
1585:
1586: query_len = spprintf(&query, 0, "SET NAMES %s", csname);
1587:
1588: if (FAIL == conn->m->query(conn, query, query_len TSRMLS_CC)) {
1589: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error executing query");
1590: } else if (conn->error_info.error_no) {
1591: ret = FAIL;
1592: } else {
1593: conn->charset = charset;
1594: }
1595: efree(query); /* allocated by spprintf */
1596:
1597: DBG_INF(ret == PASS? "PASS":"FAIL");
1598: DBG_RETURN(ret);
1599: }
1600: /* }}} */
1601:
1602:
1603: /* {{{ mysqlnd_conn::refresh */
1604: static enum_func_status
1605: MYSQLND_METHOD(mysqlnd_conn, refresh)(MYSQLND * const conn, uint8_t options TSRMLS_DC)
1606: {
1607: zend_uchar bits[1];
1608: DBG_ENTER("mysqlnd_conn::refresh");
1609: DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
1610:
1611: int1store(bits, options);
1612:
1613: DBG_RETURN(conn->m->simple_command(conn, COM_REFRESH, (char *)bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC));
1614: }
1615: /* }}} */
1616:
1617:
1618: /* {{{ mysqlnd_conn::shutdown */
1619: static enum_func_status
1620: MYSQLND_METHOD(mysqlnd_conn, shutdown)(MYSQLND * const conn, uint8_t level TSRMLS_DC)
1621: {
1622: zend_uchar bits[1];
1623: DBG_ENTER("mysqlnd_conn::shutdown");
1624: DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
1625:
1626: int1store(bits, level);
1627:
1628: DBG_RETURN(conn->m->simple_command(conn, COM_SHUTDOWN, (char *)bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC));
1629: }
1630: /* }}} */
1631:
1632:
1633: /* {{{ mysqlnd_send_close */
1634: static enum_func_status
1635: MYSQLND_METHOD(mysqlnd_conn, send_close)(MYSQLND * const conn TSRMLS_DC)
1636: {
1637: enum_func_status ret = PASS;
1638:
1639: DBG_ENTER("mysqlnd_send_close");
1640: DBG_INF_FMT("conn=%llu conn->net->stream->abstract=%p",
1641: conn->thread_id, conn->net->stream? conn->net->stream->abstract:NULL);
1642:
1643: switch (CONN_GET_STATE(conn)) {
1644: case CONN_READY:
1645: DBG_INF("Connection clean, sending COM_QUIT");
1646: if (conn->net->stream) {
1647: ret = conn->m->simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, TRUE, TRUE TSRMLS_CC);
1648: }
1649: /* Do nothing */
1650: break;
1651: case CONN_SENDING_LOAD_DATA:
1652: /*
1653: Don't send COM_QUIT if we are in a middle of a LOAD DATA or we
1654: will crash (assert) a debug server.
1655: */
1656: case CONN_NEXT_RESULT_PENDING:
1657: case CONN_QUERY_SENT:
1658: case CONN_FETCHING_DATA:
1659: MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE);
1660: DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme);
1661: /*
1662: Do nothing, the connection will be brutally closed
1663: and the server will catch it and free close from its side.
1664: */
1665: case CONN_ALLOCED:
1666: /*
1667: Allocated but not connected or there was failure when trying
1668: to connect with pre-allocated connect.
1669:
1670: Fall-through
1671: */
1672: case CONN_QUIT_SENT:
1673: /* The user has killed its own connection */
1674: break;
1675: }
1676: /*
1677: We hold one reference, and every other object which needs the
1678: connection does increase it by 1.
1679: */
1680: CONN_SET_STATE(conn, CONN_QUIT_SENT);
1681:
1682: DBG_RETURN(ret);
1683: }
1684: /* }}} */
1685:
1686:
1687: /* {{{ mysqlnd_conn::close */
1688: static enum_func_status
1689: MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn, enum_connection_close_type close_type TSRMLS_DC)
1690: {
1691: enum_func_status ret = PASS;
1692: static enum_mysqlnd_collected_stats
1693: close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
1694: STAT_CLOSE_EXPLICIT,
1695: STAT_CLOSE_IMPLICIT,
1696: STAT_CLOSE_DISCONNECT
1697: };
1698: enum_mysqlnd_collected_stats statistic = close_type_to_stat_map[close_type];
1699:
1700: DBG_ENTER("mysqlnd_conn::close");
1701: DBG_INF_FMT("conn=%llu", conn->thread_id);
1702:
1703: if (conn->state >= CONN_READY) {
1704: MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic);
1705: MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
1706: if (conn->persistent) {
1707: MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
1708: }
1709: }
1710:
1711: /*
1712: Close now, free_reference will try,
1713: if we are last, but that's not a problem.
1714: */
1715: ret = conn->m->send_close(conn TSRMLS_CC);
1716:
1717: ret = conn->m->free_reference(conn TSRMLS_CC);
1718:
1719: DBG_RETURN(ret);
1720: }
1721: /* }}} */
1722:
1723:
1724: /* {{{ mysqlnd_conn::get_reference */
1725: static MYSQLND *
1726: MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference)(MYSQLND * const conn TSRMLS_DC)
1727: {
1728: DBG_ENTER("mysqlnd_conn::get_reference");
1729: ++conn->refcount;
1730: DBG_INF_FMT("conn=%llu new_refcount=%u", conn->thread_id, conn->refcount);
1731: DBG_RETURN(conn);
1732: }
1733: /* }}} */
1734:
1735:
1736: /* {{{ mysqlnd_conn::free_reference */
1737: static enum_func_status
1738: MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference)(MYSQLND * const conn TSRMLS_DC)
1739: {
1740: enum_func_status ret = PASS;
1741: DBG_ENTER("mysqlnd_conn::free_reference");
1742: DBG_INF_FMT("conn=%llu old_refcount=%u", conn->thread_id, conn->refcount);
1743: if (!(--conn->refcount)) {
1744: /*
1745: No multithreading issues as we don't share the connection :)
1746: This will free the object too, of course because references has
1747: reached zero.
1748: */
1749: ret = conn->m->send_close(conn TSRMLS_CC);
1750: conn->m->dtor(conn TSRMLS_CC);
1751: }
1752: DBG_RETURN(ret);
1753: }
1754: /* }}} */
1755:
1756:
1757: /* {{{ mysqlnd_conn::get_state */
1758: static enum mysqlnd_connection_state
1759: MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_state)(MYSQLND * const conn TSRMLS_DC)
1760: {
1761: DBG_ENTER("mysqlnd_conn::get_state");
1762: DBG_RETURN(conn->state);
1763: }
1764: /* }}} */
1765:
1766:
1767: /* {{{ mysqlnd_conn::set_state */
1768: static void
1769: MYSQLND_METHOD_PRIVATE(mysqlnd_conn, set_state)(MYSQLND * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC)
1770: {
1771: DBG_ENTER("mysqlnd_conn::set_state");
1772: DBG_INF_FMT("New state=%u", new_state);
1773: conn->state = new_state;
1774: DBG_VOID_RETURN;
1775: }
1776: /* }}} */
1777:
1778:
1779: /* {{{ mysqlnd_conn::field_count */
1780: static unsigned int
1781: MYSQLND_METHOD(mysqlnd_conn, field_count)(const MYSQLND * const conn TSRMLS_DC)
1782: {
1783: return conn->field_count;
1784: }
1785: /* }}} */
1786:
1787:
1788: /* {{{ mysqlnd_conn::insert_id */
1789: static uint64_t
1790: MYSQLND_METHOD(mysqlnd_conn, insert_id)(const MYSQLND * const conn TSRMLS_DC)
1791: {
1792: return conn->upsert_status.last_insert_id;
1793: }
1794: /* }}} */
1795:
1796:
1797: /* {{{ mysqlnd_conn::affected_rows */
1798: static uint64_t
1799: MYSQLND_METHOD(mysqlnd_conn, affected_rows)(const MYSQLND * const conn TSRMLS_DC)
1800: {
1801: return conn->upsert_status.affected_rows;
1802: }
1803: /* }}} */
1804:
1805:
1806: /* {{{ mysqlnd_conn::warning_count */
1807: static unsigned int
1808: MYSQLND_METHOD(mysqlnd_conn, warning_count)(const MYSQLND * const conn TSRMLS_DC)
1809: {
1810: return conn->upsert_status.warning_count;
1811: }
1812: /* }}} */
1813:
1814:
1815: /* {{{ mysqlnd_conn::info */
1816: static const char *
1817: MYSQLND_METHOD(mysqlnd_conn, info)(const MYSQLND * const conn TSRMLS_DC)
1818: {
1819: return conn->last_message;
1820: }
1821: /* }}} */
1822:
1823: #if !defined(MYSQLND_USE_OPTIMISATIONS) || MYSQLND_USE_OPTIMISATIONS == 0
1824: /* {{{ mysqlnd_get_client_info */
1825: PHPAPI const char * mysqlnd_get_client_info()
1826: {
1827: return MYSQLND_VERSION;
1828: }
1829: /* }}} */
1830:
1831:
1832: /* {{{ mysqlnd_get_client_version */
1833: PHPAPI unsigned int mysqlnd_get_client_version()
1834: {
1835: return MYSQLND_VERSION_ID;
1836: }
1837: /* }}} */
1838: #endif
1839:
1840: /* {{{ mysqlnd_conn::get_server_info */
1841: static const char *
1842: MYSQLND_METHOD(mysqlnd_conn, get_server_info)(const MYSQLND * const conn TSRMLS_DC)
1843: {
1844: return conn->server_version;
1845: }
1846: /* }}} */
1847:
1848:
1849: /* {{{ mysqlnd_conn::get_host_info */
1850: static const char *
1851: MYSQLND_METHOD(mysqlnd_conn, get_host_info)(const MYSQLND * const conn TSRMLS_DC)
1852: {
1853: return conn->host_info;
1854: }
1855: /* }}} */
1856:
1857:
1858: /* {{{ mysqlnd_conn::get_proto_info */
1859: static unsigned int
1860: MYSQLND_METHOD(mysqlnd_conn, get_proto_info)(const MYSQLND *const conn TSRMLS_DC)
1861: {
1862: return conn->protocol_version;
1863: }
1864: /* }}} */
1865:
1866:
1867: /* {{{ mysqlnd_conn::charset_name */
1868: static const char *
1869: MYSQLND_METHOD(mysqlnd_conn, charset_name)(const MYSQLND * const conn TSRMLS_DC)
1870: {
1871: return conn->charset->name;
1872: }
1873: /* }}} */
1874:
1875:
1876: /* {{{ mysqlnd_conn::thread_id */
1877: static uint64_t
1878: MYSQLND_METHOD(mysqlnd_conn, thread_id)(const MYSQLND * const conn TSRMLS_DC)
1879: {
1880: return conn->thread_id;
1881: }
1882: /* }}} */
1883:
1884:
1885: /* {{{ mysqlnd_conn::get_server_version */
1886: static unsigned long
1887: MYSQLND_METHOD(mysqlnd_conn, get_server_version)(const MYSQLND * const conn TSRMLS_DC)
1888: {
1889: long major, minor, patch;
1890: char *p;
1891:
1892: if (!(p = conn->server_version)) {
1893: return 0;
1894: }
1895:
1896: major = strtol(p, &p, 10);
1897: p += 1; /* consume the dot */
1898: minor = strtol(p, &p, 10);
1899: p += 1; /* consume the dot */
1900: patch = strtol(p, &p, 10);
1901:
1902: return (unsigned long)(major * 10000L + (unsigned long)(minor * 100L + patch));
1903: }
1904: /* }}} */
1905:
1906:
1907: /* {{{ mysqlnd_conn::more_results */
1908: static zend_bool
1909: MYSQLND_METHOD(mysqlnd_conn, more_results)(const MYSQLND * const conn TSRMLS_DC)
1910: {
1911: DBG_ENTER("mysqlnd_conn::more_results");
1912: /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
1913: DBG_RETURN(conn->upsert_status.server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
1914: }
1915: /* }}} */
1916:
1917:
1918: /* {{{ mysqlnd_conn::next_result */
1919: static enum_func_status
1920: MYSQLND_METHOD(mysqlnd_conn, next_result)(MYSQLND * const conn TSRMLS_DC)
1921: {
1922: enum_func_status ret;
1923:
1924: DBG_ENTER("mysqlnd_conn::next_result");
1925: DBG_INF_FMT("conn=%llu", conn->thread_id);
1926:
1927: if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING) {
1928: DBG_RETURN(FAIL);
1929: }
1930:
1931: SET_EMPTY_ERROR(conn->error_info);
1932: SET_ERROR_AFF_ROWS(conn);
1933: /*
1934: We are sure that there is a result set, since conn->state is set accordingly
1935: in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered()
1936: */
1937: if (FAIL == (ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC))) {
1938: /*
1939: There can be an error in the middle of a multi-statement, which will cancel the multi-statement.
1940: So there are no more results and we should just return FALSE, error_no has been set
1941: */
1942: if (!conn->error_info.error_no) {
1943: DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__);
1944: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error. PID=%d", getpid());
1945: CONN_SET_STATE(conn, CONN_QUIT_SENT);
1946: } else {
1947: DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info.error_no, conn->error_info.error);
1948: }
1949: }
1950: if (ret == PASS && conn->last_query_type == QUERY_UPSERT && conn->upsert_status.affected_rows) {
1951: MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status.affected_rows);
1952: }
1953:
1954: DBG_RETURN(ret);
1955: }
1956: /* }}} */
1957:
1958:
1959: /* {{{ mysqlnd_field_type_name */
1960: PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type)
1961: {
1962: switch(field_type) {
1963: case FIELD_TYPE_STRING:
1964: case FIELD_TYPE_VAR_STRING:
1965: return "string";
1966: case FIELD_TYPE_TINY:
1967: case FIELD_TYPE_SHORT:
1968: case FIELD_TYPE_LONG:
1969: case FIELD_TYPE_LONGLONG:
1970: case FIELD_TYPE_INT24:
1971: return "int";
1972: case FIELD_TYPE_FLOAT:
1973: case FIELD_TYPE_DOUBLE:
1974: case FIELD_TYPE_DECIMAL:
1975: case FIELD_TYPE_NEWDECIMAL:
1976: return "real";
1977: case FIELD_TYPE_TIMESTAMP:
1978: return "timestamp";
1979: case FIELD_TYPE_YEAR:
1980: return "year";
1981: case FIELD_TYPE_DATE:
1982: case FIELD_TYPE_NEWDATE:
1983: return "date";
1984: case FIELD_TYPE_TIME:
1985: return "time";
1986: case FIELD_TYPE_SET:
1987: return "set";
1988: case FIELD_TYPE_ENUM:
1989: return "enum";
1990: case FIELD_TYPE_GEOMETRY:
1991: return "geometry";
1992: case FIELD_TYPE_DATETIME:
1993: return "datetime";
1994: case FIELD_TYPE_TINY_BLOB:
1995: case FIELD_TYPE_MEDIUM_BLOB:
1996: case FIELD_TYPE_LONG_BLOB:
1997: case FIELD_TYPE_BLOB:
1998: return "blob";
1999: case FIELD_TYPE_NULL:
2000: return "null";
2001: case FIELD_TYPE_BIT:
2002: return "bit";
2003: default:
2004: return "unknown";
2005: }
2006: }
2007: /* }}} */
2008:
2009:
2010: /* {{{ mysqlnd_conn::set_client_option */
2011: static enum_func_status
2012: MYSQLND_METHOD(mysqlnd_conn, set_client_option)(MYSQLND * const conn,
2013: enum mysqlnd_option option,
2014: const char * const value
2015: TSRMLS_DC)
2016: {
2017: enum_func_status ret = PASS;
2018: DBG_ENTER("mysqlnd_conn::set_client_option");
2019: DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
2020: switch (option) {
2021: case MYSQL_OPT_COMPRESS:
2022: #ifdef WHEN_SUPPORTED_BY_MYSQLI
2023: case MYSQL_OPT_READ_TIMEOUT:
2024: case MYSQL_OPT_WRITE_TIMEOUT:
2025: #endif
2026: case MYSQLND_OPT_SSL_KEY:
2027: case MYSQLND_OPT_SSL_CERT:
2028: case MYSQLND_OPT_SSL_CA:
2029: case MYSQLND_OPT_SSL_CAPATH:
2030: case MYSQLND_OPT_SSL_CIPHER:
2031: case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
2032: case MYSQL_OPT_CONNECT_TIMEOUT:
2033: case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
2034: case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
2035: ret = conn->net->m.set_client_option(conn->net, option, value TSRMLS_CC);
2036: break;
2037: #if MYSQLND_UNICODE
2038: case MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE:
2039: conn->options.numeric_and_datetime_as_unicode = *(unsigned int*) value;
2040: break;
2041: #endif
2042: #ifdef MYSQLND_STRING_TO_INT_CONVERSION
2043: case MYSQLND_OPT_INT_AND_FLOAT_NATIVE:
2044: DBG_INF("MYSQLND_OPT_INT_AND_FLOAT_NATIVE");
2045: conn->options.int_and_float_native = *(unsigned int*) value;
2046: break;
2047: #endif
2048: case MYSQL_OPT_LOCAL_INFILE:
2049: DBG_INF("MYSQL_OPT_LOCAL_INFILE");
2050: if (!value || (*(unsigned int*) value) ? 1 : 0) {
2051: conn->options.flags |= CLIENT_LOCAL_FILES;
2052: } else {
2053: conn->options.flags &= ~CLIENT_LOCAL_FILES;
2054: }
2055: break;
2056: case MYSQL_INIT_COMMAND:
2057: {
2058: char ** new_init_commands;
2059: char * new_command;
2060: DBG_INF("MYSQL_INIT_COMMAND");
2061: DBG_INF_FMT("command=%s", value);
2062: /* when num_commands is 0, then realloc will be effectively a malloc call, internally */
2063: /* Don't assign to conn->options.init_commands because in case of OOM we will lose the pointer and leak */
2064: new_init_commands = mnd_perealloc(conn->options.init_commands, sizeof(char *) * (conn->options.num_commands + 1), conn->persistent);
2065: if (!new_init_commands) {
2066: goto oom;
2067: }
2068: conn->options.init_commands = new_init_commands;
2069: new_command = mnd_pestrdup(value, conn->persistent);
2070: if (!new_command) {
2071: goto oom;
2072: }
2073: conn->options.init_commands[conn->options.num_commands] = new_command;
2074: ++conn->options.num_commands;
2075: break;
2076: }
2077: case MYSQL_READ_DEFAULT_FILE:
2078: case MYSQL_READ_DEFAULT_GROUP:
2079: #ifdef WHEN_SUPPORTED_BY_MYSQLI
2080: case MYSQL_SET_CLIENT_IP:
2081: case MYSQL_REPORT_DATA_TRUNCATION:
2082: #endif
2083: /* currently not supported. Todo!! */
2084: break;
2085: case MYSQL_SET_CHARSET_NAME:
2086: {
2087: char * new_charset_name = mnd_pestrdup(value, conn->persistent);
2088: DBG_INF("MYSQL_SET_CHARSET_NAME");
2089: if (!new_charset_name) {
2090: goto oom;
2091: }
2092: if (conn->options.charset_name) {
2093: mnd_pefree(conn->options.charset_name, conn->persistent);
2094: }
2095: conn->options.charset_name = new_charset_name;
2096: DBG_INF_FMT("charset=%s", conn->options.charset_name);
2097: break;
2098: }
2099: case MYSQL_OPT_NAMED_PIPE:
2100: conn->options.protocol = MYSQL_PROTOCOL_PIPE;
2101: break;
2102: case MYSQL_OPT_PROTOCOL:
2103: if (*(unsigned int*) value < MYSQL_PROTOCOL_LAST) {
2104: conn->options.protocol = *(unsigned int*) value;
2105: }
2106: break;
2107: #ifdef WHEN_SUPPORTED_BY_MYSQLI
2108: case MYSQL_SET_CHARSET_DIR:
2109: case MYSQL_OPT_RECONNECT:
2110: /* we don't need external character sets, all character sets are
2111: compiled in. For compatibility we just ignore this setting.
2112: Same for protocol, we don't support old protocol */
2113: case MYSQL_OPT_USE_REMOTE_CONNECTION:
2114: case MYSQL_OPT_USE_EMBEDDED_CONNECTION:
2115: case MYSQL_OPT_GUESS_CONNECTION:
2116: /* todo: throw an error, we don't support embedded */
2117: break;
2118: #endif
2119:
2120: #ifdef WHEN_SUPPORTED_BY_MYSQLI
2121: case MYSQL_SHARED_MEMORY_BASE_NAME:
2122: case MYSQL_OPT_USE_RESULT:
2123: case MYSQL_SECURE_AUTH:
2124: /* not sure, todo ? */
2125: #endif
2126: default:
2127: ret = FAIL;
2128: }
2129: DBG_RETURN(ret);
2130: oom:
2131: SET_OOM_ERROR(conn->error_info);
2132: DBG_RETURN(FAIL);
2133: }
2134: /* }}} */
2135:
2136:
2137: /* {{{ mysqlnd_conn::use_result */
2138: static MYSQLND_RES *
2139: MYSQLND_METHOD(mysqlnd_conn, use_result)(MYSQLND * const conn TSRMLS_DC)
2140: {
2141: MYSQLND_RES * result;
2142:
2143: DBG_ENTER("mysqlnd_conn::use_result");
2144: DBG_INF_FMT("conn=%llu", conn->thread_id);
2145:
2146: if (!conn->current_result) {
2147: DBG_RETURN(NULL);
2148: }
2149:
2150: /* Nothing to store for UPSERT/LOAD DATA */
2151: if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
2152: SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
2153: mysqlnd_out_of_sync);
2154: DBG_ERR("Command out of sync");
2155: DBG_RETURN(NULL);
2156: }
2157:
2158: MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS);
2159:
2160: conn->current_result->conn = conn->m->get_reference(conn TSRMLS_CC);
2161: result = conn->current_result->m.use_result(conn->current_result, FALSE TSRMLS_CC);
2162:
2163: if (!result) {
2164: conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
2165: }
2166: conn->current_result = NULL;
2167:
2168: DBG_RETURN(result);
2169: }
2170: /* }}} */
2171:
2172:
2173: /* {{{ mysqlnd_conn::store_result */
2174: static MYSQLND_RES *
2175: MYSQLND_METHOD(mysqlnd_conn, store_result)(MYSQLND * const conn TSRMLS_DC)
2176: {
2177: MYSQLND_RES *result;
2178:
2179: DBG_ENTER("mysqlnd_conn::store_result");
2180: DBG_INF_FMT("conn=%llu", conn->thread_id);
2181:
2182: if (!conn->current_result) {
2183: DBG_RETURN(NULL);
2184: }
2185:
2186: /* Nothing to store for UPSERT/LOAD DATA*/
2187: if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
2188: SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE,
2189: mysqlnd_out_of_sync);
2190: DBG_ERR("Command out of sync");
2191: DBG_RETURN(NULL);
2192: }
2193:
2194: MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
2195:
2196: result = conn->current_result->m.store_result(conn->current_result, conn, FALSE TSRMLS_CC);
2197: if (!result) {
2198: conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
2199: }
2200: conn->current_result = NULL;
2201: DBG_RETURN(result);
2202: }
2203: /* }}} */
2204:
2205:
2206: /* {{{ mysqlnd_conn::get_connection_stats */
2207: static void
2208: MYSQLND_METHOD(mysqlnd_conn, get_connection_stats)(const MYSQLND * const conn,
2209: zval *return_value
2210: TSRMLS_DC ZEND_FILE_LINE_DC)
2211: {
2212: DBG_ENTER("mysqlnd_conn::get_connection_stats");
2213: DBG_INF_FMT("conn=%llu", conn->thread_id);
2214: mysqlnd_fill_stats_hash(conn->stats, mysqlnd_stats_values_names, return_value TSRMLS_CC ZEND_FILE_LINE_CC);
2215: DBG_VOID_RETURN;
2216: }
2217: /* }}} */
2218:
2219:
2220: /* {{{ mysqlnd_conn::set_autocommit */
2221: static enum_func_status
2222: MYSQLND_METHOD(mysqlnd_conn, set_autocommit)(MYSQLND * conn, unsigned int mode TSRMLS_DC)
2223: {
2224: enum_func_status ret;
2225: DBG_ENTER("mysqlnd_conn::set_autocommit");
2226: ret = conn->m->query(conn, (mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", sizeof("SET AUTOCOMMIT=1") - 1 TSRMLS_CC);
2227: DBG_RETURN(ret);
2228: }
2229: /* }}} */
2230:
2231:
2232: /* {{{ mysqlnd_conn::tx_commit */
2233: static enum_func_status
2234: MYSQLND_METHOD(mysqlnd_conn, tx_commit)(MYSQLND * conn TSRMLS_DC)
2235: {
2236: enum_func_status ret;
2237: DBG_ENTER("mysqlnd_conn::tx_commit");
2238: ret = conn->m->query(conn, "COMMIT", sizeof("COMMIT") - 1 TSRMLS_CC);
2239: DBG_RETURN(ret);
2240: }
2241: /* }}} */
2242:
2243:
2244: /* {{{ mysqlnd_conn::tx_rollback */
2245: static enum_func_status
2246: MYSQLND_METHOD(mysqlnd_conn, tx_rollback)(MYSQLND * conn TSRMLS_DC)
2247: {
2248: enum_func_status ret;
2249: DBG_ENTER("mysqlnd_conn::tx_rollback");
2250: ret = conn->m->query(conn, "ROLLBACK", sizeof("ROLLBACK") - 1 TSRMLS_CC);
2251: DBG_RETURN(ret);
2252: }
2253: /* }}} */
2254:
2255:
2256:
2257: MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND * const conn TSRMLS_DC);
2258: static enum_func_status MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC);
2259:
2260: static
2261: MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
2262: MYSQLND_METHOD(mysqlnd_conn, init),
2263: MYSQLND_METHOD(mysqlnd_conn, connect),
2264:
2265: MYSQLND_METHOD(mysqlnd_conn, escape_string),
2266: MYSQLND_METHOD(mysqlnd_conn, set_charset),
2267: MYSQLND_METHOD(mysqlnd_conn, query),
2268: MYSQLND_METHOD(mysqlnd_conn, send_query),
2269: MYSQLND_METHOD(mysqlnd_conn, reap_query),
2270: MYSQLND_METHOD(mysqlnd_conn, use_result),
2271: MYSQLND_METHOD(mysqlnd_conn, store_result),
2272: MYSQLND_METHOD(mysqlnd_conn, next_result),
2273: MYSQLND_METHOD(mysqlnd_conn, more_results),
2274:
2275: _mysqlnd_stmt_init,
2276:
2277: MYSQLND_METHOD(mysqlnd_conn, shutdown),
2278: MYSQLND_METHOD(mysqlnd_conn, refresh),
2279:
2280: MYSQLND_METHOD(mysqlnd_conn, ping),
2281: MYSQLND_METHOD(mysqlnd_conn, kill),
2282: MYSQLND_METHOD(mysqlnd_conn, select_db),
2283: MYSQLND_METHOD(mysqlnd_conn, dump_debug_info),
2284: MYSQLND_METHOD(mysqlnd_conn, change_user),
2285:
2286: MYSQLND_METHOD(mysqlnd_conn, errno),
2287: MYSQLND_METHOD(mysqlnd_conn, error),
2288: MYSQLND_METHOD(mysqlnd_conn, sqlstate),
2289: MYSQLND_METHOD(mysqlnd_conn, thread_id),
2290:
2291: MYSQLND_METHOD(mysqlnd_conn, get_connection_stats),
2292:
2293: MYSQLND_METHOD(mysqlnd_conn, get_server_version),
2294: MYSQLND_METHOD(mysqlnd_conn, get_server_info),
2295: MYSQLND_METHOD(mysqlnd_conn, statistic),
2296: MYSQLND_METHOD(mysqlnd_conn, get_host_info),
2297: MYSQLND_METHOD(mysqlnd_conn, get_proto_info),
2298: MYSQLND_METHOD(mysqlnd_conn, info),
2299: MYSQLND_METHOD(mysqlnd_conn, charset_name),
2300: MYSQLND_METHOD(mysqlnd_conn, list_fields),
2301: MYSQLND_METHOD(mysqlnd_conn, list_method),
2302:
2303: MYSQLND_METHOD(mysqlnd_conn, insert_id),
2304: MYSQLND_METHOD(mysqlnd_conn, affected_rows),
2305: MYSQLND_METHOD(mysqlnd_conn, warning_count),
2306: MYSQLND_METHOD(mysqlnd_conn, field_count),
2307:
2308: MYSQLND_METHOD(mysqlnd_conn, set_server_option),
2309: MYSQLND_METHOD(mysqlnd_conn, set_client_option),
2310: MYSQLND_METHOD(mysqlnd_conn, free_contents),
2311: MYSQLND_METHOD(mysqlnd_conn, free_options),
2312: MYSQLND_METHOD(mysqlnd_conn, close),
2313:
2314: MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor),
2315:
2316: mysqlnd_query_read_result_set_header,
2317:
2318: MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_reference),
2319: MYSQLND_METHOD_PRIVATE(mysqlnd_conn, free_reference),
2320: MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_state),
2321: MYSQLND_METHOD_PRIVATE(mysqlnd_conn, set_state),
2322:
2323: MYSQLND_METHOD(mysqlnd_conn, simple_command),
2324: MYSQLND_METHOD(mysqlnd_conn, simple_command_handle_response),
2325: MYSQLND_METHOD(mysqlnd_conn, restart_psession),
2326: MYSQLND_METHOD(mysqlnd_conn, end_psession),
2327: MYSQLND_METHOD(mysqlnd_conn, send_close),
2328:
2329: MYSQLND_METHOD(mysqlnd_conn, ssl_set),
2330: mysqlnd_result_init
2331: #ifdef AUTOCOMMIT_TX_COMMIT_ROLLBACK
2332: ,MYSQLND_METHOD(mysqlnd_conn, set_autocommit),
2333: MYSQLND_METHOD(mysqlnd_conn, tx_commit),
2334: MYSQLND_METHOD(mysqlnd_conn, tx_rollback)
2335: #endif
2336: MYSQLND_CLASS_METHODS_END;
2337:
2338:
2339: /* {{{ mysqlnd_conn::init */
2340: static enum_func_status
2341: MYSQLND_METHOD(mysqlnd_conn, init)(MYSQLND * conn TSRMLS_DC)
2342: {
2343: DBG_ENTER("mysqlnd_conn::init");
2344: mysqlnd_stats_init(&conn->stats, STAT_LAST);
2345: SET_ERROR_AFF_ROWS(conn);
2346:
2347: conn->net = mysqlnd_net_init(conn->persistent TSRMLS_CC);
2348: conn->protocol = mysqlnd_protocol_init(conn->persistent TSRMLS_CC);
2349:
2350: DBG_RETURN(conn->net && conn->protocol? PASS:FAIL);
2351: }
2352: /* }}} */
2353:
2354:
2355: /* {{{ mysqlnd_init */
2356: PHPAPI MYSQLND * _mysqlnd_init(zend_bool persistent TSRMLS_DC)
2357: {
2358: size_t alloc_size = sizeof(MYSQLND) + mysqlnd_plugin_count() * sizeof(void *);
2359: MYSQLND *ret;
2360:
2361: DBG_ENTER("mysqlnd_init");
2362: DBG_INF_FMT("persistent=%u", persistent);
2363: ret = mnd_pecalloc(1, alloc_size, persistent);
2364: if (!ret) {
2365: DBG_RETURN(NULL);
2366: }
2367:
2368: ret->persistent = persistent;
2369: ret->m = mysqlnd_conn_methods;
2370: CONN_SET_STATE(ret, CONN_ALLOCED);
2371: ret->m->get_reference(ret TSRMLS_CC);
2372:
2373: if (PASS != ret->m->init(ret TSRMLS_CC)) {
2374: ret->m->dtor(ret TSRMLS_CC);
2375: ret = NULL;
2376: }
2377:
2378: DBG_RETURN(ret);
2379: }
2380: /* }}} */
2381:
2382:
2383: /* {{{ mysqlnd_library_init */
2384: PHPAPI void mysqlnd_library_init(TSRMLS_D)
2385: {
2386: if (mysqlnd_library_initted == FALSE) {
2387: mysqlnd_library_initted = TRUE;
2388: mysqlnd_conn_methods = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_conn);
2389: _mysqlnd_init_ps_subsystem();
2390: /* Should be calloc, as mnd_calloc will reference LOCK_access*/
2391: mysqlnd_stats_init(&mysqlnd_global_stats, STAT_LAST);
2392: }
2393: }
2394: /* }}} */
2395:
2396: /* {{{ mysqlnd_conn_get_methods */
2397: PHPAPI struct st_mysqlnd_conn_methods * mysqlnd_conn_get_methods()
2398: {
2399: return mysqlnd_conn_methods;
2400: }
2401: /* }}} */
2402:
2403: /* {{{ mysqlnd_conn_set_methods */
2404: PHPAPI void mysqlnd_conn_set_methods(struct st_mysqlnd_conn_methods *methods)
2405: {
2406: mysqlnd_conn_methods = methods;
2407: }
2408: /* }}} */
2409:
2410:
2411: static unsigned int mysqlnd_plugins_counter = 0;
2412:
2413: /* {{{ mysqlnd_plugin_register */
2414: PHPAPI unsigned int mysqlnd_plugin_register()
2415: {
2416: return mysqlnd_plugins_counter++;
2417: }
2418: /* }}} */
2419:
2420:
2421: /* {{{ mysqlnd_plugin_count */
2422: PHPAPI unsigned int mysqlnd_plugin_count()
2423: {
2424: return mysqlnd_plugins_counter;
2425: }
2426: /* }}} */
2427:
2428:
2429: /* {{{ _mysqlnd_plugin_get_plugin_connection_data */
2430: PHPAPI void ** _mysqlnd_plugin_get_plugin_connection_data(const MYSQLND * conn, unsigned int plugin_id TSRMLS_DC)
2431: {
2432: DBG_ENTER("_mysqlnd_plugin_get_plugin_connection_data");
2433: DBG_INF_FMT("plugin_id=%u", plugin_id);
2434: if (!conn || plugin_id >= mysqlnd_plugin_count()) {
2435: return NULL;
2436: }
2437: DBG_RETURN((void *)((char *)conn + sizeof(MYSQLND) + plugin_id * sizeof(void *)));
2438: }
2439: /* }}} */
2440:
2441: /*
2442: * Local variables:
2443: * tab-width: 4
2444: * c-basic-offset: 4
2445: * End:
2446: * vim600: noet sw=4 ts=4 fdm=marker
2447: * vim<600: noet sw=4 ts=4
2448: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>