1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 5                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 2006-2014 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: Andrey Hristov <andrey@mysql.com>                           |
16  |          Ulf Wendel <uwendel@mysql.com>                              |
17  |          Georg Richter <georg@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#include "ext/standard/php_smart_str.h"
31
32/*
33  TODO :
34  - Don't bind so tightly the metadata with the result set. This means
35    that the metadata reading should not expect a MYSQLND_RES pointer, it
36    does not need it, but return a pointer to the metadata (MYSQLND_FIELD *).
37    For normal statements we will then just assign it to a member of
38    MYSQLND_RES. For PS statements, it will stay as part of the statement
39    (MYSQLND_STMT) between prepare and execute. At execute the new metadata
40    will be sent by the server, so we will discard the old one and then
41    finally attach it to the result set. This will make the code more clean,
42    as a prepared statement won't have anymore stmt->result != NULL, as it
43    is now, just to have where to store the metadata.
44
45  - Change mysqlnd_simple_command to accept a heap dynamic array of MYSQLND_STRING
46    terminated by a string with ptr being NULL. Thus, multi-part messages can be
47    sent to the network like writev() and this can save at least for
48    mysqlnd_stmt_send_long_data() new malloc. This change will probably make the
49    code in few other places cleaner.
50*/
51
52extern MYSQLND_CHARSET *mysqlnd_charsets;
53
54
55
56PHPAPI const char * const mysqlnd_old_passwd  = "mysqlnd cannot connect to MySQL 4.1+ using the old insecure authentication. "
57"Please use an administration tool to reset your password with the command SET PASSWORD = PASSWORD('your_existing_password'). This will "
58"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 "
59"flag from your my.cnf file";
60
61PHPAPI const char * const mysqlnd_server_gone = "MySQL server has gone away";
62PHPAPI const char * const mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now";
63PHPAPI const char * const mysqlnd_out_of_memory = "Out of memory";
64
65PHPAPI MYSQLND_STATS *mysqlnd_global_stats = NULL;
66
67
68/* {{{ mysqlnd_conn_data::free_options */
69static void
70MYSQLND_METHOD(mysqlnd_conn_data, free_options)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
71{
72    zend_bool pers = conn->persistent;
73
74    if (conn->options->charset_name) {
75        mnd_pefree(conn->options->charset_name, pers);
76        conn->options->charset_name = NULL;
77    }
78    if (conn->options->auth_protocol) {
79        mnd_pefree(conn->options->auth_protocol, pers);
80        conn->options->auth_protocol = NULL;
81    }
82    if (conn->options->num_commands) {
83        unsigned int i;
84        for (i = 0; i < conn->options->num_commands; i++) {
85            /* allocated with pestrdup */
86            mnd_pefree(conn->options->init_commands[i], pers);
87        }
88        mnd_pefree(conn->options->init_commands, pers);
89        conn->options->init_commands = NULL;
90    }
91    if (conn->options->cfg_file) {
92        mnd_pefree(conn->options->cfg_file, pers);
93        conn->options->cfg_file = NULL;
94    }
95    if (conn->options->cfg_section) {
96        mnd_pefree(conn->options->cfg_section, pers);
97        conn->options->cfg_section = NULL;
98    }
99    if (conn->options->connect_attr) {
100        zend_hash_destroy(conn->options->connect_attr);
101        mnd_pefree(conn->options->connect_attr, pers);
102        conn->options->connect_attr = NULL;
103    }
104}
105/* }}} */
106
107
108/* {{{ mysqlnd_conn_data::free_contents */
109static void
110MYSQLND_METHOD(mysqlnd_conn_data, free_contents)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
111{
112    zend_bool pers = conn->persistent;
113
114    DBG_ENTER("mysqlnd_conn_data::free_contents");
115
116    mysqlnd_local_infile_default(conn);
117    if (conn->current_result) {
118        conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
119        conn->current_result = NULL;
120    }
121
122    if (conn->net) {
123        conn->net->data->m.free_contents(conn->net TSRMLS_CC);
124    }
125
126    DBG_INF("Freeing memory of members");
127
128    if (conn->host) {
129        mnd_pefree(conn->host, pers);
130        conn->host = NULL;
131    }
132    if (conn->user) {
133        mnd_pefree(conn->user, pers);
134        conn->user = NULL;
135    }
136    if (conn->passwd) {
137        mnd_pefree(conn->passwd, pers);
138        conn->passwd = NULL;
139    }
140    if (conn->connect_or_select_db) {
141        mnd_pefree(conn->connect_or_select_db, pers);
142        conn->connect_or_select_db = NULL;
143    }
144    if (conn->unix_socket) {
145        mnd_pefree(conn->unix_socket, pers);
146        conn->unix_socket = NULL;
147    }
148    DBG_INF_FMT("scheme=%s", conn->scheme);
149    if (conn->scheme) {
150        mnd_pefree(conn->scheme, pers);
151        conn->scheme = NULL;
152    }
153    if (conn->server_version) {
154        mnd_pefree(conn->server_version, pers);
155        conn->server_version = NULL;
156    }
157    if (conn->host_info) {
158        mnd_pefree(conn->host_info, pers);
159        conn->host_info = NULL;
160    }
161    if (conn->auth_plugin_data) {
162        mnd_pefree(conn->auth_plugin_data, pers);
163        conn->auth_plugin_data = NULL;
164    }
165    if (conn->last_message) {
166        mnd_pefree(conn->last_message, pers);
167        conn->last_message = NULL;
168    }
169    if (conn->error_info->error_list) {
170        zend_llist_clean(conn->error_info->error_list);
171        mnd_pefree(conn->error_info->error_list, pers);
172        conn->error_info->error_list = NULL;
173    }
174    conn->charset = NULL;
175    conn->greet_charset = NULL;
176
177    DBG_VOID_RETURN;
178}
179/* }}} */
180
181
182/* {{{ mysqlnd_conn_data::dtor */
183static void
184MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
185{
186    DBG_ENTER("mysqlnd_conn_data::dtor");
187    DBG_INF_FMT("conn=%llu", conn->thread_id);
188
189    conn->m->free_contents(conn TSRMLS_CC);
190    conn->m->free_options(conn TSRMLS_CC);
191
192    if (conn->net) {
193        mysqlnd_net_free(conn->net, conn->stats, conn->error_info TSRMLS_CC);
194        conn->net = NULL;
195    }
196
197    if (conn->protocol) {
198        mysqlnd_protocol_free(conn->protocol TSRMLS_CC);
199        conn->protocol = NULL;
200    }
201
202    if (conn->stats) {
203        mysqlnd_stats_end(conn->stats);
204    }
205
206    mnd_pefree(conn, conn->persistent);
207
208    DBG_VOID_RETURN;
209}
210/* }}} */
211
212
213/* {{{ mysqlnd_conn_data::simple_command_handle_response */
214static enum_func_status
215MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response)(MYSQLND_CONN_DATA * conn, enum mysqlnd_packet_type ok_packet,
216                                                             zend_bool silent, enum php_mysqlnd_server_command command,
217                                                             zend_bool ignore_upsert_status TSRMLS_DC)
218{
219    enum_func_status ret = FAIL;
220
221    DBG_ENTER("mysqlnd_conn_data::simple_command_handle_response");
222    DBG_INF_FMT("silent=%u packet=%u command=%s", silent, ok_packet, mysqlnd_command_to_text[command]);
223
224    switch (ok_packet) {
225        case PROT_OK_PACKET:{
226            MYSQLND_PACKET_OK * ok_response = conn->protocol->m.get_ok_packet(conn->protocol, FALSE TSRMLS_CC);
227            if (!ok_response) {
228                SET_OOM_ERROR(*conn->error_info);
229                break;
230            }
231            if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
232                if (!silent) {
233                    DBG_ERR_FMT("Error while reading %s's OK packet", mysqlnd_command_to_text[command]);
234                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's OK packet. PID=%u",
235                                     mysqlnd_command_to_text[command], getpid());
236                }
237            } else {
238                DBG_INF_FMT("OK from server");
239                if (0xFF == ok_response->field_count) {
240                    /* The server signalled error. Set the error */
241                    SET_CLIENT_ERROR(*conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
242                    ret = FAIL;
243                    /*
244                      Cover a protocol design error: error packet does not
245                      contain the server status. Therefore, the client has no way
246                      to find out whether there are more result sets of
247                      a multiple-result-set statement pending. Luckily, in 5.0 an
248                      error always aborts execution of a statement, wherever it is
249                      a multi-statement or a stored procedure, so it should be
250                      safe to unconditionally turn off the flag here.
251                    */
252                    conn->upsert_status->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
253                    SET_ERROR_AFF_ROWS(conn);
254                } else {
255                    SET_NEW_MESSAGE(conn->last_message, conn->last_message_len,
256                                    ok_response->message, ok_response->message_len,
257                                    conn->persistent);
258
259                    if (!ignore_upsert_status) {
260                        memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
261                        conn->upsert_status->warning_count = ok_response->warning_count;
262                        conn->upsert_status->server_status = ok_response->server_status;
263                        conn->upsert_status->affected_rows = ok_response->affected_rows;
264                        conn->upsert_status->last_insert_id = ok_response->last_insert_id;
265                    }
266                }
267            }
268            PACKET_FREE(ok_response);
269            break;
270        }
271        case PROT_EOF_PACKET:{
272            MYSQLND_PACKET_EOF * ok_response = conn->protocol->m.get_eof_packet(conn->protocol, FALSE TSRMLS_CC);
273            if (!ok_response) {
274                SET_OOM_ERROR(*conn->error_info);
275                break;
276            }
277            if (FAIL == (ret = PACKET_READ(ok_response, conn))) {
278                SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE,
279                                 "Malformed packet");
280                if (!silent) {
281                    DBG_ERR_FMT("Error while reading %s's EOF packet", mysqlnd_command_to_text[command]);
282                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading %s's EOF packet. PID=%d",
283                                     mysqlnd_command_to_text[command], getpid());
284                }
285            } else if (0xFF == ok_response->field_count) {
286                /* The server signalled error. Set the error */
287                SET_CLIENT_ERROR(*conn->error_info, ok_response->error_no, ok_response->sqlstate, ok_response->error);
288                SET_ERROR_AFF_ROWS(conn);
289            } else if (0xFE != ok_response->field_count) {
290                SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
291                if (!silent) {
292                    DBG_ERR_FMT("EOF packet expected, field count wasn't 0xFE but 0x%2X", ok_response->field_count);
293                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "EOF packet expected, field count wasn't 0xFE but 0x%2X",
294                                    ok_response->field_count);
295                }
296            } else {
297                DBG_INF_FMT("OK from server");
298            }
299            PACKET_FREE(ok_response);
300            break;
301        }
302        default:
303            SET_CLIENT_ERROR(*conn->error_info, CR_MALFORMED_PACKET, UNKNOWN_SQLSTATE, "Malformed packet");
304            php_error_docref(NULL TSRMLS_CC, E_ERROR, "Wrong response packet %u passed to the function", ok_packet);
305            break;
306    }
307    DBG_INF(ret == PASS ? "PASS":"FAIL");
308    DBG_RETURN(ret);
309}
310/* }}} */
311
312
313/* {{{ mysqlnd_conn_data::simple_command_send_request */
314static enum_func_status
315MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command,
316               const zend_uchar * const arg, size_t arg_len, zend_bool silent, zend_bool ignore_upsert_status TSRMLS_DC)
317{
318    enum_func_status ret = PASS;
319    MYSQLND_PACKET_COMMAND * cmd_packet;
320
321    DBG_ENTER("mysqlnd_conn_data::simple_command_send_request");
322    DBG_INF_FMT("command=%s silent=%u", mysqlnd_command_to_text[command], silent);
323    DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
324    DBG_INF_FMT("sending %u bytes", arg_len + 1); /* + 1 is for the command */
325
326    switch (CONN_GET_STATE(conn)) {
327        case CONN_READY:
328            break;
329        case CONN_QUIT_SENT:
330            SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
331            DBG_ERR("Server is gone");
332            DBG_RETURN(FAIL);
333        default:
334            SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
335            DBG_ERR_FMT("Command out of sync. State=%u", CONN_GET_STATE(conn));
336            DBG_RETURN(FAIL);
337    }
338
339    SET_ERROR_AFF_ROWS(conn);
340    SET_EMPTY_ERROR(*conn->error_info);
341
342    cmd_packet = conn->protocol->m.get_command_packet(conn->protocol, FALSE TSRMLS_CC);
343    if (!cmd_packet) {
344        SET_OOM_ERROR(*conn->error_info);
345        DBG_RETURN(FAIL);
346    }
347
348    cmd_packet->command = command;
349    if (arg && arg_len) {
350        cmd_packet->argument = arg;
351        cmd_packet->arg_len  = arg_len;
352    }
353
354    MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_COM_QUIT + command - 1 /* because of COM_SLEEP */ );
355
356    if (! PACKET_WRITE(cmd_packet, conn)) {
357        if (!silent) {
358            DBG_ERR_FMT("Error while sending %s packet", mysqlnd_command_to_text[command]);
359            php_error(E_WARNING, "Error while sending %s packet. PID=%d", mysqlnd_command_to_text[command], getpid());
360        }
361        CONN_SET_STATE(conn, CONN_QUIT_SENT);
362        conn->m->send_close(conn TSRMLS_CC);
363        DBG_ERR("Server is gone");
364        ret = FAIL;
365    }
366    PACKET_FREE(cmd_packet);
367    DBG_RETURN(ret);
368}
369/* }}} */
370
371
372/* {{{ mysqlnd_conn_data::simple_command */
373static enum_func_status
374MYSQLND_METHOD(mysqlnd_conn_data, simple_command)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command,
375               const zend_uchar * const arg, size_t arg_len, enum mysqlnd_packet_type ok_packet, zend_bool silent,
376               zend_bool ignore_upsert_status TSRMLS_DC)
377{
378    enum_func_status ret;
379    DBG_ENTER("mysqlnd_conn_data::simple_command");
380
381    ret = conn->m->simple_command_send_request(conn, command, arg, arg_len, silent, ignore_upsert_status TSRMLS_CC);
382    if (PASS == ret && ok_packet != PROT_LAST) {
383        ret = conn->m->simple_command_handle_response(conn, ok_packet, silent, command, ignore_upsert_status TSRMLS_CC);
384    }
385
386    DBG_INF(ret == PASS ? "PASS":"FAIL");
387    DBG_RETURN(ret);
388}
389/* }}} */
390
391
392/* {{{ mysqlnd_conn_data::set_server_option */
393static enum_func_status
394MYSQLND_METHOD(mysqlnd_conn_data, set_server_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_server_option option TSRMLS_DC)
395{
396    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_server_option);
397    zend_uchar buffer[2];
398    enum_func_status ret = FAIL;
399    DBG_ENTER("mysqlnd_conn_data::set_server_option");
400    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
401
402        int2store(buffer, (unsigned int) option);
403        ret = conn->m->simple_command(conn, COM_SET_OPTION, buffer, sizeof(buffer), PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC);
404
405        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
406    }
407    DBG_RETURN(ret);
408}
409/* }}} */
410
411
412/* {{{ mysqlnd_conn_data::restart_psession */
413static enum_func_status
414MYSQLND_METHOD(mysqlnd_conn_data, restart_psession)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
415{
416    DBG_ENTER("mysqlnd_conn_data::restart_psession");
417    MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_REUSED);
418    /* Free here what should not be seen by the next script */
419    if (conn->last_message) {
420        mnd_pefree(conn->last_message, conn->persistent);
421        conn->last_message = NULL;
422    }
423    DBG_RETURN(PASS);
424}
425/* }}} */
426
427
428/* {{{ mysqlnd_conn_data::end_psession */
429static enum_func_status
430MYSQLND_METHOD(mysqlnd_conn_data, end_psession)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
431{
432    DBG_ENTER("mysqlnd_conn_data::end_psession");
433    DBG_RETURN(PASS);
434}
435/* }}} */
436
437
438/* {{{ mysqlnd_switch_to_ssl_if_needed */
439static enum_func_status
440mysqlnd_switch_to_ssl_if_needed(
441            MYSQLND_CONN_DATA * conn,
442            const MYSQLND_PACKET_GREET * const greet_packet,
443            const MYSQLND_OPTIONS * const options,
444            unsigned long mysql_flags
445            TSRMLS_DC
446        )
447{
448    enum_func_status ret = FAIL;
449    const MYSQLND_CHARSET * charset;
450    MYSQLND_PACKET_AUTH * auth_packet;
451    DBG_ENTER("mysqlnd_switch_to_ssl_if_needed");
452    DBG_INF_FMT("client_capability_flags=%lu", mysql_flags);
453    DBG_INF_FMT("CLIENT_LONG_PASSWORD=  %d", mysql_flags & CLIENT_LONG_PASSWORD? 1:0);
454    DBG_INF_FMT("CLIENT_FOUND_ROWS=     %d", mysql_flags & CLIENT_FOUND_ROWS? 1:0);
455    DBG_INF_FMT("CLIENT_LONG_FLAG=      %d", mysql_flags & CLIENT_LONG_FLAG? 1:0);
456    DBG_INF_FMT("CLIENT_NO_SCHEMA=      %d", mysql_flags & CLIENT_NO_SCHEMA? 1:0);
457    DBG_INF_FMT("CLIENT_COMPRESS=       %d", mysql_flags & CLIENT_COMPRESS? 1:0);
458    DBG_INF_FMT("CLIENT_ODBC=           %d", mysql_flags & CLIENT_ODBC? 1:0);
459    DBG_INF_FMT("CLIENT_LOCAL_FILES=    %d", mysql_flags & CLIENT_LOCAL_FILES? 1:0);
460    DBG_INF_FMT("CLIENT_IGNORE_SPACE=   %d", mysql_flags & CLIENT_IGNORE_SPACE? 1:0);
461    DBG_INF_FMT("CLIENT_PROTOCOL_41=    %d", mysql_flags & CLIENT_PROTOCOL_41? 1:0);
462    DBG_INF_FMT("CLIENT_INTERACTIVE=    %d", mysql_flags & CLIENT_INTERACTIVE? 1:0);
463    DBG_INF_FMT("CLIENT_SSL=            %d", mysql_flags & CLIENT_SSL? 1:0);
464    DBG_INF_FMT("CLIENT_IGNORE_SIGPIPE= %d", mysql_flags & CLIENT_IGNORE_SIGPIPE? 1:0);
465    DBG_INF_FMT("CLIENT_TRANSACTIONS=   %d", mysql_flags & CLIENT_TRANSACTIONS? 1:0);
466    DBG_INF_FMT("CLIENT_RESERVED=       %d", mysql_flags & CLIENT_RESERVED? 1:0);
467    DBG_INF_FMT("CLIENT_SECURE_CONNECTION=%d", mysql_flags & CLIENT_SECURE_CONNECTION? 1:0);
468    DBG_INF_FMT("CLIENT_MULTI_STATEMENTS=%d", mysql_flags & CLIENT_MULTI_STATEMENTS? 1:0);
469    DBG_INF_FMT("CLIENT_MULTI_RESULTS=  %d", mysql_flags & CLIENT_MULTI_RESULTS? 1:0);
470    DBG_INF_FMT("CLIENT_PS_MULTI_RESULTS=%d", mysql_flags & CLIENT_PS_MULTI_RESULTS? 1:0);
471    DBG_INF_FMT("CLIENT_CONNECT_ATTRS=  %d", mysql_flags & CLIENT_PLUGIN_AUTH? 1:0);
472    DBG_INF_FMT("CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA= %d", mysql_flags & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA? 1:0);
473    DBG_INF_FMT("CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS=   %d", mysql_flags & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS? 1:0);
474    DBG_INF_FMT("CLIENT_SESSION_TRACK=      %d", mysql_flags & CLIENT_SESSION_TRACK? 1:0);
475    DBG_INF_FMT("CLIENT_SSL_VERIFY_SERVER_CERT= %d", mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? 1:0);
476    DBG_INF_FMT("CLIENT_REMEMBER_OPTIONS=       %d", mysql_flags & CLIENT_REMEMBER_OPTIONS? 1:0);
477
478    auth_packet = conn->protocol->m.get_auth_packet(conn->protocol, FALSE TSRMLS_CC);
479    if (!auth_packet) {
480        SET_OOM_ERROR(*conn->error_info);
481        goto end;
482    }
483    auth_packet->client_flags = mysql_flags;
484    auth_packet->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
485
486    if (options->charset_name && (charset = mysqlnd_find_charset_name(options->charset_name))) {
487        auth_packet->charset_no = charset->nr;
488    } else {
489        auth_packet->charset_no = greet_packet->charset_no;
490    }
491
492#ifdef MYSQLND_SSL_SUPPORTED
493    if ((greet_packet->server_capabilities & CLIENT_SSL) && (mysql_flags & CLIENT_SSL)) {
494        zend_bool verify = mysql_flags & CLIENT_SSL_VERIFY_SERVER_CERT? TRUE:FALSE;
495        DBG_INF("Switching to SSL");
496        if (!PACKET_WRITE(auth_packet, conn)) {
497            CONN_SET_STATE(conn, CONN_QUIT_SENT);
498            conn->m->send_close(conn TSRMLS_CC);
499            SET_CLIENT_ERROR(*conn->error_info, CR_SERVER_GONE_ERROR, UNKNOWN_SQLSTATE, mysqlnd_server_gone);
500            goto end;
501        }
502
503        conn->net->data->m.set_client_option(conn->net, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *) &verify TSRMLS_CC);
504
505        if (FAIL == conn->net->data->m.enable_ssl(conn->net TSRMLS_CC)) {
506            goto end;
507        }
508    }
509#endif
510    ret = PASS;
511end:
512    PACKET_FREE(auth_packet);
513    DBG_RETURN(ret);
514}
515/* }}} */
516
517
518/* {{{ mysqlnd_conn_data::fetch_auth_plugin_by_name */
519static struct st_mysqlnd_authentication_plugin *
520MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name)(const char * const requested_protocol TSRMLS_DC)
521{
522    struct st_mysqlnd_authentication_plugin * auth_plugin;
523    char * plugin_name = NULL;
524    DBG_ENTER("mysqlnd_conn_data::fetch_auth_plugin_by_name");
525
526    mnd_sprintf(&plugin_name, 0, "auth_plugin_%s", requested_protocol);
527    DBG_INF_FMT("looking for %s auth plugin", plugin_name);
528    auth_plugin = mysqlnd_plugin_find(plugin_name);
529    mnd_sprintf_free(plugin_name);
530
531    DBG_RETURN(auth_plugin);
532}
533/* }}} */
534
535
536/* {{{ mysqlnd_run_authentication */
537static enum_func_status
538mysqlnd_run_authentication(
539            MYSQLND_CONN_DATA * conn,
540            const char * const user,
541            const char * const passwd,
542            const size_t passwd_len,
543            const char * const db,
544            const size_t db_len,
545            const zend_uchar * const auth_plugin_data,
546            const size_t auth_plugin_data_len,
547            const char * const auth_protocol,
548            unsigned int charset_no,
549            const MYSQLND_OPTIONS * const options,
550            unsigned long mysql_flags,
551            zend_bool silent,
552            zend_bool is_change_user
553            TSRMLS_DC)
554{
555    enum_func_status ret = FAIL;
556    zend_bool first_call = TRUE;
557
558    char * switch_to_auth_protocol = NULL;
559    size_t switch_to_auth_protocol_len = 0;
560    char * requested_protocol = NULL;
561    zend_uchar * plugin_data;
562    size_t plugin_data_len;
563
564    DBG_ENTER("mysqlnd_run_authentication");
565
566    plugin_data_len = auth_plugin_data_len;
567    plugin_data = mnd_emalloc(plugin_data_len + 1);
568    if (!plugin_data) {
569        goto end;
570    }
571    memcpy(plugin_data, auth_plugin_data, plugin_data_len);
572    plugin_data[plugin_data_len] = '\0';
573
574    requested_protocol = mnd_pestrdup(auth_protocol? auth_protocol : MYSQLND_DEFAULT_AUTH_PROTOCOL, FALSE);
575    if (!requested_protocol) {
576        goto end;
577    }
578
579    do {
580        struct st_mysqlnd_authentication_plugin * auth_plugin = conn->m->fetch_auth_plugin_by_name(requested_protocol TSRMLS_CC);
581
582        if (!auth_plugin) {
583            php_error_docref(NULL TSRMLS_CC, E_WARNING, "The server requested authentication method unknown to the client [%s]", requested_protocol);
584            SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, "The server requested authentication method unknown to the client");
585            goto end;
586        }
587        DBG_INF("plugin found");
588
589        {
590            zend_uchar * switch_to_auth_protocol_data = NULL;
591            size_t switch_to_auth_protocol_data_len = 0;
592            zend_uchar * scrambled_data = NULL;
593            size_t scrambled_data_len = 0;
594
595            switch_to_auth_protocol = NULL;
596            switch_to_auth_protocol_len = 0;
597
598            if (conn->auth_plugin_data) {
599                mnd_pefree(conn->auth_plugin_data, conn->persistent);
600                conn->auth_plugin_data = NULL;
601            }
602            conn->auth_plugin_data_len = plugin_data_len;
603            conn->auth_plugin_data = mnd_pemalloc(conn->auth_plugin_data_len, conn->persistent);
604            if (!conn->auth_plugin_data) {
605                SET_OOM_ERROR(*conn->error_info);
606                goto end;
607            }
608            memcpy(conn->auth_plugin_data, plugin_data, plugin_data_len);
609
610            DBG_INF_FMT("salt(%d)=[%.*s]", plugin_data_len, plugin_data_len, plugin_data);
611            /* The data should be allocated with malloc() */
612            scrambled_data =
613                auth_plugin->methods.get_auth_data(NULL, &scrambled_data_len, conn, user, passwd, passwd_len,
614                                                   plugin_data, plugin_data_len, options, &conn->net->data->options, mysql_flags TSRMLS_CC);
615            if (conn->error_info->error_no) {
616                goto end;
617            }
618            if (FALSE == is_change_user) {
619                ret = mysqlnd_auth_handshake(conn, user, passwd, passwd_len, db, db_len, options, mysql_flags,
620                                            charset_no,
621                                            first_call,
622                                            requested_protocol,
623                                            scrambled_data, scrambled_data_len,
624                                            &switch_to_auth_protocol, &switch_to_auth_protocol_len,
625                                            &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
626                                            TSRMLS_CC);
627            } else {
628                ret = mysqlnd_auth_change_user(conn, user, strlen(user), passwd, passwd_len, db, db_len, silent,
629                                               first_call,
630                                               requested_protocol,
631                                               scrambled_data, scrambled_data_len,
632                                               &switch_to_auth_protocol, &switch_to_auth_protocol_len,
633                                               &switch_to_auth_protocol_data, &switch_to_auth_protocol_data_len
634                                               TSRMLS_CC);
635            }
636            first_call = FALSE;
637            free(scrambled_data);
638
639            DBG_INF_FMT("switch_to_auth_protocol=%s", switch_to_auth_protocol? switch_to_auth_protocol:"n/a");
640            if (requested_protocol && switch_to_auth_protocol) {
641                mnd_efree(requested_protocol);
642                requested_protocol = switch_to_auth_protocol;
643            }
644
645            if (plugin_data) {
646                mnd_efree(plugin_data);
647            }
648            plugin_data_len = switch_to_auth_protocol_data_len;
649            plugin_data = switch_to_auth_protocol_data;
650        }
651        DBG_INF_FMT("conn->error_info->error_no = %d", conn->error_info->error_no);
652    } while (ret == FAIL && conn->error_info->error_no == 0 && switch_to_auth_protocol != NULL);
653
654    if (ret == PASS) {
655        DBG_INF_FMT("saving requested_protocol=%s", requested_protocol);
656        conn->m->set_client_option(conn, MYSQLND_OPT_AUTH_PROTOCOL, requested_protocol TSRMLS_CC);
657    }
658end:
659    if (plugin_data) {
660        mnd_efree(plugin_data);
661    }
662    if (requested_protocol) {
663        mnd_efree(requested_protocol);
664    }
665
666    DBG_RETURN(ret);
667}
668/* }}} */
669
670
671/* {{{ mysqlnd_connect_run_authentication */
672static enum_func_status
673mysqlnd_connect_run_authentication(
674            MYSQLND_CONN_DATA * conn,
675            const char * const user,
676            const char * const passwd,
677            const char * const db,
678            size_t db_len,
679            size_t passwd_len,
680            const MYSQLND_PACKET_GREET * const greet_packet,
681            const MYSQLND_OPTIONS * const options,
682            unsigned long mysql_flags
683            TSRMLS_DC)
684{
685    enum_func_status ret = FAIL;
686    DBG_ENTER("mysqlnd_connect_run_authentication");
687
688    ret = mysqlnd_switch_to_ssl_if_needed(conn, greet_packet, options, mysql_flags TSRMLS_CC);
689    if (PASS == ret) {
690        ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, db_len,
691                                         greet_packet->auth_plugin_data, greet_packet->auth_plugin_data_len, greet_packet->auth_protocol,
692                                         greet_packet->charset_no, options, mysql_flags, FALSE /*silent*/, FALSE/*is_change*/ TSRMLS_CC);
693    }
694    DBG_RETURN(ret);
695}
696/* }}} */
697
698
699/* {{{ mysqlnd_conn_data::execute_init_commands */
700static enum_func_status
701MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
702{
703    enum_func_status ret = PASS;
704
705    DBG_ENTER("mysqlnd_conn_data::execute_init_commands");
706    if (conn->options->init_commands) {
707        unsigned int current_command = 0;
708        for (; current_command < conn->options->num_commands; ++current_command) {
709            const char * const command = conn->options->init_commands[current_command];
710            if (command) {
711                MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_EXECUTED_COUNT);
712                if (PASS != conn->m->query(conn, command, strlen(command) TSRMLS_CC)) {
713                    MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_FAILED_COUNT);
714                    ret = FAIL;
715                    break;
716                }
717                if (conn->last_query_type == QUERY_SELECT) {
718                    MYSQLND_RES * result = conn->m->use_result(conn, 0 TSRMLS_CC);
719                    if (result) {
720                        result->m.free_result(result, TRUE TSRMLS_CC);
721                    }
722                }
723            }
724        }
725    }
726    DBG_RETURN(ret);
727}
728/* }}} */
729
730
731/* {{{ mysqlnd_conn_data::get_updated_connect_flags */
732static unsigned int
733MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA * conn, unsigned int mysql_flags TSRMLS_DC)
734{
735    MYSQLND_NET * net = conn->net;
736
737    DBG_ENTER("mysqlnd_conn_data::get_updated_connect_flags");
738    /* we allow load data local infile by default */
739    mysql_flags |= MYSQLND_CAPABILITIES;
740
741    mysql_flags |= conn->options->flags; /* use the flags from set_client_option() */
742
743    if (PG(open_basedir) && strlen(PG(open_basedir))) {
744        mysql_flags ^= CLIENT_LOCAL_FILES;
745    }
746
747#ifndef MYSQLND_COMPRESSION_ENABLED
748    if (mysql_flags & CLIENT_COMPRESS) {
749        mysql_flags &= ~CLIENT_COMPRESS;
750    }
751#else
752    if (net && net->data->options.flags & MYSQLND_NET_FLAG_USE_COMPRESSION) {
753        mysql_flags |= CLIENT_COMPRESS;
754    }
755#endif
756#ifndef MYSQLND_SSL_SUPPORTED
757    if (mysql_flags & CLIENT_SSL) {
758        mysql_flags &= ~CLIENT_SSL;
759    }
760#else
761    if (net && (net->data->options.ssl_key || net->data->options.ssl_cert ||
762        net->data->options.ssl_ca || net->data->options.ssl_capath || net->data->options.ssl_cipher))
763    {
764        mysql_flags |= CLIENT_SSL;
765    }
766#endif
767
768    DBG_RETURN(mysql_flags);
769}
770/* }}} */
771
772
773/* {{{ mysqlnd_conn_data::connect_handshake */
774static enum_func_status
775MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn,
776                        const char * const host, const char * const user,
777                        const char * const passwd, const unsigned int passwd_len,
778                        const char * const db, const unsigned int db_len,
779                        const unsigned int mysql_flags TSRMLS_DC)
780{
781    MYSQLND_PACKET_GREET * greet_packet;
782    MYSQLND_NET * net = conn->net;
783
784    DBG_ENTER("mysqlnd_conn_data::connect_handshake");
785
786    greet_packet = conn->protocol->m.get_greet_packet(conn->protocol, FALSE TSRMLS_CC);
787    if (!greet_packet) {
788        SET_OOM_ERROR(*conn->error_info);
789        DBG_RETURN(FAIL); /* OOM */
790    }
791
792    if (FAIL == net->data->m.connect_ex(conn->net, conn->scheme, conn->scheme_len, conn->persistent,
793                                        conn->stats, conn->error_info TSRMLS_CC))
794    {
795        goto err;
796    }
797
798    DBG_INF_FMT("stream=%p", net->data->m.get_stream(net TSRMLS_CC));
799
800    if (FAIL == PACKET_READ(greet_packet, conn)) {
801        DBG_ERR("Error while reading greeting packet");
802        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while reading greeting packet. PID=%d", getpid());
803        goto err;
804    } else if (greet_packet->error_no) {
805        DBG_ERR_FMT("errorno=%u error=%s", greet_packet->error_no, greet_packet->error);
806        SET_CLIENT_ERROR(*conn->error_info, greet_packet->error_no, greet_packet->sqlstate, greet_packet->error);
807        goto err;
808    } else if (greet_packet->pre41) {
809        DBG_ERR_FMT("Connecting to 3.22, 3.23 & 4.0 is not supported. Server is %-.32s", greet_packet->server_version);
810        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connecting to 3.22, 3.23 & 4.0 "
811                        " is not supported. Server is %-.32s", greet_packet->server_version);
812        SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
813                         "Connecting to 3.22, 3.23 & 4.0 servers is not supported");
814        goto err;
815    }
816
817    conn->thread_id         = greet_packet->thread_id;
818    conn->protocol_version  = greet_packet->protocol_version;
819    conn->server_version    = mnd_pestrdup(greet_packet->server_version, conn->persistent);
820
821    conn->greet_charset = mysqlnd_find_charset_nr(greet_packet->charset_no);
822    if (!conn->greet_charset) {
823        php_error_docref(NULL TSRMLS_CC, E_WARNING,
824            "Server sent charset (%d) unknown to the client. Please, report to the developers", greet_packet->charset_no);
825        SET_CLIENT_ERROR(*conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE,
826            "Server sent charset unknown to the client. Please, report to the developers");
827        goto err;
828    }
829
830    conn->client_flag           = mysql_flags;
831    conn->server_capabilities   = greet_packet->server_capabilities;
832
833    if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, (size_t) passwd_len,
834                                                   greet_packet, conn->options, mysql_flags TSRMLS_CC))
835    {
836        goto err;
837    }
838    memset(conn->upsert_status, 0, sizeof(*conn->upsert_status));
839    conn->upsert_status->warning_count = 0;
840    conn->upsert_status->server_status = greet_packet->server_status;
841    conn->upsert_status->affected_rows = 0;
842
843    PACKET_FREE(greet_packet);
844    DBG_RETURN(PASS);
845err:
846    conn->client_flag = 0;
847    conn->server_capabilities = 0;
848    PACKET_FREE(greet_packet);
849    DBG_RETURN(FAIL);
850}
851/* }}} */
852
853
854/* {{{ mysqlnd_conn_data::connect */
855static enum_func_status
856MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn,
857                         const char *host, const char *user,
858                         const char *passwd, unsigned int passwd_len,
859                         const char *db, unsigned int db_len,
860                         unsigned int port,
861                         const char *socket_or_pipe,
862                         unsigned int mysql_flags
863                         TSRMLS_DC)
864{
865    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, connect);
866    size_t host_len;
867    zend_bool unix_socket = FALSE;
868    zend_bool named_pipe = FALSE;
869    zend_bool reconnect = FALSE;
870    zend_bool saved_compression = FALSE;
871    zend_bool local_tx_started = FALSE;
872    MYSQLND_NET * net = conn->net;
873
874    DBG_ENTER("mysqlnd_conn_data::connect");
875    DBG_INF_FMT("conn=%p", conn);
876
877    if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
878        goto err;
879    }
880    local_tx_started = TRUE;
881
882    SET_EMPTY_ERROR(*conn->error_info);
883    SET_ERROR_AFF_ROWS(conn);
884
885    DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u persistent=%u state=%u",
886                host?host:"", user?user:"", db?db:"", port, mysql_flags,
887                conn? conn->persistent:0, conn? CONN_GET_STATE(conn):-1);
888
889    if (CONN_GET_STATE(conn) > CONN_ALLOCED && CONN_GET_STATE(conn) ) {
890        DBG_INF("Connecting on a connected handle.");
891
892        if (CONN_GET_STATE(conn) < CONN_QUIT_SENT) {
893            MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CLOSE_IMPLICIT);
894            reconnect = TRUE;
895            conn->m->send_close(conn TSRMLS_CC);
896        }
897
898        conn->m->free_contents(conn TSRMLS_CC);
899        MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
900        if (conn->persistent) {
901            MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
902        }
903        /* Now reconnect using the same handle */
904        if (net->data->compressed) {
905            /*
906              we need to save the state. As we will re-connect, net->compressed should be off, or
907              we will look for a compression header as part of the greet message, but there will
908              be none.
909            */
910            saved_compression = TRUE;
911            net->data->compressed = FALSE;
912        }
913        if (net->data->ssl) {
914            net->data->ssl = FALSE;
915        }
916    } else {
917        unsigned int max_allowed_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
918        conn->m->set_client_option(conn, MYSQLND_OPT_MAX_ALLOWED_PACKET, (char *)&max_allowed_size TSRMLS_CC);
919    }
920
921    if (!host || !host[0]) {
922        host = "localhost";
923    }
924    if (!user) {
925        DBG_INF_FMT("no user given, using empty string");
926        user = "";
927    }
928    if (!passwd) {
929        DBG_INF_FMT("no password given, using empty string");
930        passwd = "";
931        passwd_len = 0;
932    }
933    if (!db) {
934        DBG_INF_FMT("no db given, using empty string");
935        db = "";
936        db_len = 0;
937    } else {
938        mysql_flags |= CLIENT_CONNECT_WITH_DB;
939    }
940
941    host_len = strlen(host);
942    {
943        char * transport = NULL;
944        int transport_len;
945#ifndef PHP_WIN32
946        if (host_len == sizeof("localhost") - 1 && !strncasecmp(host, "localhost", host_len)) {
947            DBG_INF_FMT("socket=%s", socket_or_pipe? socket_or_pipe:"n/a");
948            if (!socket_or_pipe) {
949                socket_or_pipe = "/tmp/mysql.sock";
950            }
951            transport_len = mnd_sprintf(&transport, 0, "unix://%s", socket_or_pipe);
952            unix_socket = TRUE;
953#else
954        if (host_len == sizeof(".") - 1 && host[0] == '.') {
955            /* named pipe in socket */
956            if (!socket_or_pipe) {
957                socket_or_pipe = "\\\\.\\pipe\\MySQL";
958            }
959            transport_len = mnd_sprintf(&transport, 0, "pipe://%s", socket_or_pipe);
960            named_pipe = TRUE;
961#endif
962        } else {
963            if (!port) {
964                port = 3306;
965            }
966            transport_len = mnd_sprintf(&transport, 0, "tcp://%s:%u", host, port);
967        }
968        if (!transport) {
969            SET_OOM_ERROR(*conn->error_info);
970            goto err; /* OOM */
971        }
972        DBG_INF_FMT("transport=%s conn->scheme=%s", transport, conn->scheme);
973        conn->scheme = mnd_pestrndup(transport, transport_len, conn->persistent);
974        conn->scheme_len = transport_len;
975        mnd_sprintf_free(transport);
976        transport = NULL;
977        if (!conn->scheme) {
978            goto err; /* OOM */
979        }
980    }
981
982    mysql_flags = conn->m->get_updated_connect_flags(conn, mysql_flags TSRMLS_CC);
983
984    if (FAIL == conn->m->connect_handshake(conn, host, user, passwd, passwd_len, db, db_len, mysql_flags TSRMLS_CC)) {
985        goto err;
986    }
987
988    {
989        CONN_SET_STATE(conn, CONN_READY);
990
991        if (saved_compression) {
992            net->data->compressed = TRUE;
993        }
994        /*
995          If a connect on a existing handle is performed and mysql_flags is
996          passed which doesn't CLIENT_COMPRESS, then we need to overwrite the value
997          which we set based on saved_compression.
998        */
999        net->data->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
1000
1001        conn->user              = mnd_pestrdup(user, conn->persistent);
1002        conn->user_len          = strlen(conn->user);
1003        conn->passwd            = mnd_pestrndup(passwd, passwd_len, conn->persistent);
1004        conn->passwd_len        = passwd_len;
1005        conn->port              = port;
1006        conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
1007        conn->connect_or_select_db_len = db_len;
1008
1009        if (!conn->user || !conn->passwd || !conn->connect_or_select_db) {
1010            SET_OOM_ERROR(*conn->error_info);
1011            goto err; /* OOM */
1012        }
1013
1014        if (!unix_socket && !named_pipe) {
1015            conn->host = mnd_pestrdup(host, conn->persistent);
1016            if (!conn->host) {
1017                SET_OOM_ERROR(*conn->error_info);
1018                goto err; /* OOM */
1019            }
1020            conn->host_len = strlen(conn->host);
1021            {
1022                char *p;
1023                mnd_sprintf(&p, 0, "%s via TCP/IP", conn->host);
1024                if (!p) {
1025                    SET_OOM_ERROR(*conn->error_info);
1026                    goto err; /* OOM */
1027                }
1028                conn->host_info =  mnd_pestrdup(p, conn->persistent);
1029                mnd_sprintf_free(p);
1030                if (!conn->host_info) {
1031                    SET_OOM_ERROR(*conn->error_info);
1032                    goto err; /* OOM */
1033                }
1034            }
1035        } else {
1036            conn->unix_socket = mnd_pestrdup(socket_or_pipe, conn->persistent);
1037            if (unix_socket) {
1038                conn->host_info = mnd_pestrdup("Localhost via UNIX socket", conn->persistent);
1039            } else if (named_pipe) {
1040                char *p;
1041                mnd_sprintf(&p, 0, "%s via named pipe", conn->unix_socket);
1042                if (!p) {
1043                    SET_OOM_ERROR(*conn->error_info);
1044                    goto err; /* OOM */
1045                }
1046                conn->host_info =  mnd_pestrdup(p, conn->persistent);
1047                mnd_sprintf_free(p);
1048                if (!conn->host_info) {
1049                    SET_OOM_ERROR(*conn->error_info);
1050                    goto err; /* OOM */
1051                }
1052            } else {
1053                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Impossible. Should be either socket or a pipe. Report a bug!");
1054            }
1055            if (!conn->unix_socket || !conn->host_info) {
1056                SET_OOM_ERROR(*conn->error_info);
1057                goto err; /* OOM */
1058            }
1059            conn->unix_socket_len = strlen(conn->unix_socket);
1060        }
1061        conn->max_packet_size   = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
1062        /* todo: check if charset is available */
1063
1064        SET_EMPTY_ERROR(*conn->error_info);
1065
1066        mysqlnd_local_infile_default(conn);
1067
1068        if (FAIL == conn->m->execute_init_commands(conn TSRMLS_CC)) {
1069            goto err;
1070        }
1071
1072        MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_CONNECT_SUCCESS, 1, STAT_OPENED_CONNECTIONS, 1);
1073        if (reconnect) {
1074            MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT);
1075        }
1076        if (conn->persistent) {
1077            MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PCONNECT_SUCCESS, 1, STAT_OPENED_PERSISTENT_CONNECTIONS, 1);
1078        }
1079
1080        DBG_INF_FMT("connection_id=%llu", conn->thread_id);
1081
1082        conn->m->local_tx_end(conn, this_func, PASS TSRMLS_CC);
1083        DBG_RETURN(PASS);
1084    }
1085err:
1086
1087    DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme);
1088    if (!conn->error_info->error_no) {
1089        SET_CLIENT_ERROR(*conn->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, conn->error_info->error? conn->error_info->error:"Unknown error");
1090        php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%u] %.128s (trying to connect via %s)",
1091                         conn->error_info->error_no, conn->error_info->error, conn->scheme);
1092    }
1093
1094    conn->m->free_contents(conn TSRMLS_CC);
1095    MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE);
1096    if (TRUE == local_tx_started) {
1097        conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
1098    }
1099
1100    DBG_RETURN(FAIL);
1101}
1102/* }}} */
1103
1104
1105/* {{{ mysqlnd_conn::connect */
1106static enum_func_status
1107MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
1108                         const char * host, const char * user,
1109                         const char * passwd, unsigned int passwd_len,
1110                         const char * db, unsigned int db_len,
1111                         unsigned int port,
1112                         const char * socket_or_pipe,
1113                         unsigned int mysql_flags
1114                         TSRMLS_DC)
1115{
1116    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, connect);
1117    enum_func_status ret = FAIL;
1118    MYSQLND_CONN_DATA * conn = conn_handle->data;
1119
1120    DBG_ENTER("mysqlnd_conn::connect");
1121
1122    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1123        mysqlnd_options4(conn_handle, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "mysqlnd");
1124        ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC);
1125
1126        conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
1127    }
1128    DBG_RETURN(ret);
1129}
1130/* }}} */
1131
1132
1133/* {{{ mysqlnd_connect */
1134PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn_handle,
1135                         const char * host, const char * user,
1136                         const char * passwd, unsigned int passwd_len,
1137                         const char * db, unsigned int db_len,
1138                         unsigned int port,
1139                         const char * socket_or_pipe,
1140                         unsigned int mysql_flags,
1141                         unsigned int client_api_flags
1142                         TSRMLS_DC)
1143{
1144    enum_func_status ret = FAIL;
1145    zend_bool self_alloced = FALSE;
1146
1147    DBG_ENTER("mysqlnd_connect");
1148    DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host?host:"", user?user:"", db?db:"", port, mysql_flags);
1149
1150    if (!conn_handle) {
1151        self_alloced = TRUE;
1152        if (!(conn_handle = mysqlnd_init(client_api_flags, FALSE))) {
1153            /* OOM */
1154            DBG_RETURN(NULL);
1155        }
1156    }
1157
1158    ret = conn_handle->m->connect(conn_handle, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC);
1159
1160    if (ret == FAIL) {
1161        if (self_alloced) {
1162            /*
1163              We have alloced, thus there are no references to this
1164              object - we are free to kill it!
1165            */
1166            conn_handle->m->dtor(conn_handle TSRMLS_CC);
1167        }
1168        DBG_RETURN(NULL);
1169    }
1170    DBG_RETURN(conn_handle);
1171}
1172/* }}} */
1173
1174
1175/* {{{ mysqlnd_conn_data::query */
1176/*
1177  If conn->error_info->error_no is not zero, then we had an error.
1178  Still the result from the query is PASS
1179*/
1180static enum_func_status
1181MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len TSRMLS_DC)
1182{
1183    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, query);
1184    enum_func_status ret = FAIL;
1185    DBG_ENTER("mysqlnd_conn_data::query");
1186    DBG_INF_FMT("conn=%p conn=%llu query=%s", conn, conn->thread_id, query);
1187
1188    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1189        if (PASS == conn->m->send_query(conn, query, query_len TSRMLS_CC) &&
1190            PASS == conn->m->reap_query(conn TSRMLS_CC))
1191        {
1192            ret = PASS;
1193            if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
1194                MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
1195            }
1196        }
1197        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1198    }
1199    DBG_RETURN(ret);
1200}
1201/* }}} */
1202
1203
1204/* {{{ mysqlnd_conn_data::send_query */
1205static enum_func_status
1206MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const char * query, unsigned int query_len TSRMLS_DC)
1207{
1208    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, send_query);
1209    enum_func_status ret = FAIL;
1210    DBG_ENTER("mysqlnd_conn_data::send_query");
1211    DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
1212    DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
1213
1214    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1215        ret = conn->m->simple_command(conn, COM_QUERY, (zend_uchar *) query, query_len,
1216                                             PROT_LAST /* we will handle the OK packet*/,
1217                                             FALSE, FALSE TSRMLS_CC);
1218        if (PASS == ret) {
1219            CONN_SET_STATE(conn, CONN_QUERY_SENT);
1220        }
1221        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1222    }
1223    DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
1224    DBG_RETURN(ret);
1225}
1226/* }}} */
1227
1228
1229/* {{{ mysqlnd_conn_data::reap_query */
1230static enum_func_status
1231MYSQLND_METHOD(mysqlnd_conn_data, reap_query)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
1232{
1233    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, reap_query);
1234    enum_mysqlnd_connection_state state = CONN_GET_STATE(conn);
1235    enum_func_status ret = FAIL;
1236    DBG_ENTER("mysqlnd_conn_data::reap_query");
1237    DBG_INF_FMT("conn=%llu", conn->thread_id);
1238
1239    DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
1240    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1241        if (state <= CONN_READY || state == CONN_QUIT_SENT) {
1242            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not opened, clear or has been closed");
1243            DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state);
1244            DBG_RETURN(ret);
1245        }
1246        ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC);
1247
1248        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1249    }
1250    DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
1251    DBG_RETURN(ret);
1252}
1253/* }}} */
1254
1255
1256#include "php_network.h"
1257
1258/* {{{ mysqlnd_stream_array_to_fd_set */
1259MYSQLND ** mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array TSRMLS_DC)
1260{
1261    int cnt = 0;
1262    MYSQLND **p = conn_array, **p_p;
1263    MYSQLND **ret = NULL;
1264
1265    while (*p) {
1266        if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
1267            cnt++;
1268        }
1269        p++;
1270    }
1271    if (cnt) {
1272        MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
1273        p_p = p = conn_array;
1274        while (*p) {
1275            if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
1276                *ret_p = *p;
1277                *p = NULL;
1278                ret_p++;
1279            } else {
1280                *p_p = *p;
1281                p_p++;
1282            }
1283            p++;
1284        }
1285        *ret_p = NULL;
1286    }
1287    return ret;
1288}
1289/* }}} */
1290
1291
1292/* {{{ mysqlnd_stream_array_to_fd_set */
1293static int mysqlnd_stream_array_to_fd_set(MYSQLND ** conn_array, fd_set * fds, php_socket_t * max_fd TSRMLS_DC)
1294{
1295    php_socket_t this_fd;
1296    php_stream *stream = NULL;
1297    unsigned int cnt = 0;
1298    MYSQLND **p = conn_array;
1299    DBG_ENTER("mysqlnd_stream_array_to_fd_set");
1300
1301    while (*p) {
1302        /* get the fd.
1303         * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
1304         * when casting.  It is only used here so that the buffered data warning
1305         * is not displayed.
1306         * */
1307        stream = (*p)->data->net->data->m.get_stream((*p)->data->net TSRMLS_CC);
1308        DBG_INF_FMT("conn=%llu stream=%p", (*p)->data->thread_id, stream);
1309        if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
1310                                        (void*)&this_fd, 1) && this_fd >= 0) {
1311
1312            PHP_SAFE_FD_SET(this_fd, fds);
1313
1314            if (this_fd > *max_fd) {
1315                *max_fd = this_fd;
1316            }
1317            cnt++;
1318        }
1319        p++;
1320    }
1321    DBG_RETURN(cnt ? 1 : 0);
1322}
1323/* }}} */
1324
1325
1326/* {{{ mysqlnd_stream_array_from_fd_set */
1327static int mysqlnd_stream_array_from_fd_set(MYSQLND ** conn_array, fd_set * fds TSRMLS_DC)
1328{
1329    php_socket_t this_fd;
1330    php_stream *stream = NULL;
1331    int ret = 0;
1332    zend_bool disproportion = FALSE;
1333    MYSQLND **fwd = conn_array, **bckwd = conn_array;
1334    DBG_ENTER("mysqlnd_stream_array_from_fd_set");
1335
1336    while (*fwd) {
1337        stream = (*fwd)->data->net->data->m.get_stream((*fwd)->data->net TSRMLS_CC);
1338        DBG_INF_FMT("conn=%llu stream=%p", (*fwd)->data->thread_id, stream);
1339        if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
1340                                        (void*)&this_fd, 1) && this_fd >= 0) {
1341            if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
1342                if (disproportion) {
1343                    *bckwd = *fwd;
1344                }
1345                bckwd++;
1346                fwd++;
1347                ret++;
1348                continue;
1349            }
1350        }
1351        disproportion = TRUE;
1352        fwd++;
1353    }
1354    *bckwd = NULL;/* NULL-terminate the list */
1355
1356    DBG_RETURN(ret);
1357}
1358/* }}} */
1359
1360
1361#ifndef PHP_WIN32
1362#define php_select(m, r, w, e, t)   select(m, r, w, e, t)
1363#else
1364#include "win32/select.h"
1365#endif
1366
1367
1368/* {{{ _mysqlnd_poll */
1369PHPAPI enum_func_status
1370_mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num TSRMLS_DC)
1371{
1372    struct timeval  tv;
1373    struct timeval *tv_p = NULL;
1374    fd_set          rfds, wfds, efds;
1375    php_socket_t    max_fd = 0;
1376    int             retval, sets = 0;
1377    int             set_count, max_set_count = 0;
1378
1379    DBG_ENTER("_mysqlnd_poll");
1380    if (sec < 0 || usec < 0) {
1381        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative values passed for sec and/or usec");
1382        DBG_RETURN(FAIL);
1383    }
1384
1385    FD_ZERO(&rfds);
1386    FD_ZERO(&wfds);
1387    FD_ZERO(&efds);
1388
1389    if (r_array != NULL) {
1390        *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array TSRMLS_CC);
1391        set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd TSRMLS_CC);
1392        if (set_count > max_set_count) {
1393            max_set_count = set_count;
1394        }
1395        sets += set_count;
1396    }
1397
1398    if (e_array != NULL) {
1399        set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd TSRMLS_CC);
1400        if (set_count > max_set_count) {
1401            max_set_count = set_count;
1402        }
1403        sets += set_count;
1404    }
1405
1406    if (!sets) {
1407        php_error_docref(NULL TSRMLS_CC, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
1408        DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
1409        DBG_RETURN(FAIL);
1410    }
1411
1412    PHP_SAFE_MAX_FD(max_fd, max_set_count);
1413
1414    /* Solaris + BSD do not like microsecond values which are >= 1 sec */
1415    if (usec > 999999) {
1416        tv.tv_sec = sec + (usec / 1000000);
1417        tv.tv_usec = usec % 1000000;
1418    } else {
1419        tv.tv_sec = sec;
1420        tv.tv_usec = usec;
1421    }
1422
1423    tv_p = &tv;
1424
1425    retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
1426
1427    if (retval == -1) {
1428        php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
1429                        errno, strerror(errno), max_fd);
1430        DBG_RETURN(FAIL);
1431    }
1432
1433    if (r_array != NULL) {
1434        mysqlnd_stream_array_from_fd_set(r_array, &rfds TSRMLS_CC);
1435    }
1436    if (e_array != NULL) {
1437        mysqlnd_stream_array_from_fd_set(e_array, &efds TSRMLS_CC);
1438    }
1439
1440    *desc_num = retval;
1441    DBG_RETURN(PASS);
1442}
1443/* }}} */
1444
1445
1446/*
1447  COM_FIELD_LIST is special, different from a SHOW FIELDS FROM :
1448  - There is no result set header - status from the command, which
1449    impacts us to allocate big chunk of memory for reading the metadata.
1450  - The EOF packet is consumed by the metadata packet reader.
1451*/
1452
1453/* {{{ mysqlnd_conn_data::list_fields */
1454MYSQLND_RES *
1455MYSQLND_METHOD(mysqlnd_conn_data, list_fields)(MYSQLND_CONN_DATA * conn, const char *table, const char *achtung_wild TSRMLS_DC)
1456{
1457    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_fields);
1458    /* db + \0 + wild + \0 (for wild) */
1459    zend_uchar buff[MYSQLND_MAX_ALLOWED_DB_LEN * 2 + 1 + 1], *p;
1460    size_t table_len, wild_len;
1461    MYSQLND_RES * result = NULL;
1462    DBG_ENTER("mysqlnd_conn_data::list_fields");
1463    DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:"");
1464
1465    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1466        do {
1467            p = buff;
1468            if (table && (table_len = strlen(table))) {
1469                size_t to_copy = MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN);
1470                memcpy(p, table, to_copy);
1471                p += to_copy;
1472                *p++ = '\0';
1473            }
1474
1475            if (achtung_wild && (wild_len = strlen(achtung_wild))) {
1476                size_t to_copy = MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN);
1477                memcpy(p, achtung_wild, to_copy);
1478                p += to_copy;
1479                *p++ = '\0';
1480            }
1481
1482            if (PASS != conn->m->simple_command(conn, COM_FIELD_LIST, buff, p - buff,
1483                                               PROT_LAST /* we will handle the OK packet*/,
1484                                               FALSE, TRUE TSRMLS_CC)) {
1485                conn->m->local_tx_end(conn, 0, FAIL TSRMLS_CC);
1486                break;
1487            }
1488
1489            /*
1490               Prepare for the worst case.
1491               MyISAM goes to 2500 BIT columns, double it for safety.
1492            */
1493            result = conn->m->result_init(5000, conn->persistent TSRMLS_CC);
1494            if (!result) {
1495                break;
1496            }
1497
1498            if (FAIL == result->m.read_result_metadata(result, conn TSRMLS_CC)) {
1499                DBG_ERR("Error occurred while reading metadata");
1500                result->m.free_result(result, TRUE TSRMLS_CC);
1501                result = NULL;
1502                break;
1503            }
1504
1505            result->type = MYSQLND_RES_NORMAL;
1506            result->unbuf = mysqlnd_result_unbuffered_init(result->field_count, FALSE, result->persistent TSRMLS_CC);
1507            if (!result->unbuf) {
1508                /* OOM */
1509                SET_OOM_ERROR(*conn->error_info);
1510                result->m.free_result(result, TRUE TSRMLS_CC);
1511                result = NULL;
1512                break;
1513            }
1514            result->unbuf->eof_reached = TRUE;
1515        } while (0);
1516        conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
1517    }
1518
1519    DBG_RETURN(result);
1520}
1521/* }}} */
1522
1523
1524/* {{{ mysqlnd_conn_data::list_method */
1525MYSQLND_RES *
1526MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const char * query, const char *achtung_wild, char *par1 TSRMLS_DC)
1527{
1528    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_method);
1529    char * show_query = NULL;
1530    size_t show_query_len;
1531    MYSQLND_RES * result = NULL;
1532
1533    DBG_ENTER("mysqlnd_conn_data::list_method");
1534    DBG_INF_FMT("conn=%llu query=%s wild=%u", conn->thread_id, query, achtung_wild);
1535
1536    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1537        if (par1) {
1538            if (achtung_wild) {
1539                show_query_len = mnd_sprintf(&show_query, 0, query, par1, achtung_wild);
1540            } else {
1541                show_query_len = mnd_sprintf(&show_query, 0, query, par1);
1542            }
1543        } else {
1544            if (achtung_wild) {
1545                show_query_len = mnd_sprintf(&show_query, 0, query, achtung_wild);
1546            } else {
1547                show_query_len = strlen(show_query = (char *)query);
1548            }
1549        }
1550
1551        if (PASS == conn->m->query(conn, show_query, show_query_len TSRMLS_CC)) {
1552            result = conn->m->store_result(conn, MYSQLND_STORE_NO_COPY TSRMLS_CC);
1553        }
1554        if (show_query != query) {
1555            mnd_sprintf_free(show_query);
1556        }
1557        conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
1558    }
1559    DBG_RETURN(result);
1560}
1561/* }}} */
1562
1563
1564/* {{{ mysqlnd_conn_data::errno */
1565static unsigned int
1566MYSQLND_METHOD(mysqlnd_conn_data, errno)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1567{
1568    return conn->error_info->error_no;
1569}
1570/* }}} */
1571
1572
1573/* {{{ mysqlnd_conn_data::error */
1574static const char *
1575MYSQLND_METHOD(mysqlnd_conn_data, error)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1576{
1577    return conn->error_info->error;
1578}
1579/* }}} */
1580
1581
1582/* {{{ mysqlnd_conn_data::sqlstate */
1583static const char *
1584MYSQLND_METHOD(mysqlnd_conn_data, sqlstate)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1585{
1586    return conn->error_info->sqlstate[0] ? conn->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
1587}
1588/* }}} */
1589
1590
1591/* {{{ mysqlnd_old_escape_string */
1592PHPAPI ulong
1593mysqlnd_old_escape_string(char * newstr, const char * escapestr, size_t escapestr_len TSRMLS_DC)
1594{
1595    DBG_ENTER("mysqlnd_old_escape_string");
1596    DBG_RETURN(mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), newstr, escapestr, escapestr_len TSRMLS_CC));
1597}
1598/* }}} */
1599
1600
1601/* {{{ mysqlnd_conn_data::ssl_set */
1602static enum_func_status
1603MYSQLND_METHOD(mysqlnd_conn_data, ssl_set)(MYSQLND_CONN_DATA * const conn, const char * key, const char * const cert,
1604                                      const char * const ca, const char * const capath, const char * const cipher TSRMLS_DC)
1605{
1606    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ssl_set);
1607    enum_func_status ret = FAIL;
1608    MYSQLND_NET * net = conn->net;
1609    DBG_ENTER("mysqlnd_conn_data::ssl_set");
1610
1611    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1612        ret = (PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_KEY, key TSRMLS_CC) &&
1613            PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CERT, cert TSRMLS_CC) &&
1614            PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CA, ca TSRMLS_CC) &&
1615            PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CAPATH, capath TSRMLS_CC) &&
1616            PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CIPHER, cipher TSRMLS_CC)) ? PASS : FAIL;
1617
1618        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1619    }
1620    DBG_RETURN(ret);
1621}
1622/* }}} */
1623
1624
1625/* {{{ mysqlnd_conn_data::escape_string */
1626static ulong
1627MYSQLND_METHOD(mysqlnd_conn_data, escape_string)(MYSQLND_CONN_DATA * const conn, char * newstr, const char * escapestr, size_t escapestr_len TSRMLS_DC)
1628{
1629    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, escape_string);
1630    ulong ret = FAIL;
1631    DBG_ENTER("mysqlnd_conn_data::escape_string");
1632    DBG_INF_FMT("conn=%llu", conn->thread_id);
1633
1634    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1635        DBG_INF_FMT("server_status=%u", conn->upsert_status->server_status);
1636        if (conn->upsert_status->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
1637            ret = mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC);
1638        } else {
1639            ret = mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC);
1640        }
1641        conn->m->local_tx_end(conn, this_func, PASS TSRMLS_CC);
1642    }
1643    DBG_RETURN(ret);
1644}
1645/* }}} */
1646
1647
1648/* {{{ mysqlnd_conn_data::dump_debug_info */
1649static enum_func_status
1650MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1651{
1652    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, server_dump_debug_information);
1653    enum_func_status ret = FAIL;
1654    DBG_ENTER("mysqlnd_conn_data::dump_debug_info");
1655    DBG_INF_FMT("conn=%llu", conn->thread_id);
1656    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1657        ret = conn->m->simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE, TRUE TSRMLS_CC);
1658
1659        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1660    }
1661
1662    DBG_RETURN(ret);
1663}
1664/* }}} */
1665
1666
1667/* {{{ mysqlnd_conn_data::select_db */
1668static enum_func_status
1669MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, unsigned int db_len TSRMLS_DC)
1670{
1671    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, select_db);
1672    enum_func_status ret = FAIL;
1673
1674    DBG_ENTER("mysqlnd_conn_data::select_db");
1675    DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
1676
1677    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1678        ret = conn->m->simple_command(conn, COM_INIT_DB, (zend_uchar*) db, db_len, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
1679        /*
1680          The server sends 0 but libmysql doesn't read it and has established
1681          a protocol of giving back -1. Thus we have to follow it :(
1682        */
1683        SET_ERROR_AFF_ROWS(conn);
1684        if (ret == PASS) {
1685            if (conn->connect_or_select_db) {
1686                mnd_pefree(conn->connect_or_select_db, conn->persistent);
1687            }
1688            conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
1689            conn->connect_or_select_db_len = db_len;
1690            if (!conn->connect_or_select_db) {
1691                /* OOM */
1692                SET_OOM_ERROR(*conn->error_info);
1693                ret = FAIL;
1694            }
1695        }
1696        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1697    }
1698    DBG_RETURN(ret);
1699}
1700/* }}} */
1701
1702
1703/* {{{ mysqlnd_conn_data::ping */
1704static enum_func_status
1705MYSQLND_METHOD(mysqlnd_conn_data, ping)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1706{
1707    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ping);
1708    enum_func_status ret = FAIL;
1709
1710    DBG_ENTER("mysqlnd_conn_data::ping");
1711    DBG_INF_FMT("conn=%llu", conn->thread_id);
1712
1713    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1714        ret = conn->m->simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, TRUE, TRUE TSRMLS_CC);
1715        /*
1716          The server sends 0 but libmysql doesn't read it and has established
1717          a protocol of giving back -1. Thus we have to follow it :(
1718        */
1719        SET_ERROR_AFF_ROWS(conn);
1720
1721        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1722    }
1723    DBG_INF_FMT("ret=%u", ret);
1724    DBG_RETURN(ret);
1725}
1726/* }}} */
1727
1728
1729/* {{{ mysqlnd_conn_data::statistic */
1730static enum_func_status
1731MYSQLND_METHOD(mysqlnd_conn_data, statistic)(MYSQLND_CONN_DATA * conn, char **message, unsigned int * message_len TSRMLS_DC)
1732{
1733    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, get_server_statistics);
1734    enum_func_status ret = FAIL;
1735    MYSQLND_PACKET_STATS * stats_header;
1736
1737    DBG_ENTER("mysqlnd_conn_data::statistic");
1738    DBG_INF_FMT("conn=%llu", conn->thread_id);
1739
1740    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1741        do {
1742            ret = conn->m->simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE, TRUE TSRMLS_CC);
1743            if (FAIL == ret) {
1744                break;
1745            }
1746            stats_header = conn->protocol->m.get_stats_packet(conn->protocol, FALSE TSRMLS_CC);
1747            if (!stats_header) {
1748                SET_OOM_ERROR(*conn->error_info);
1749                break;
1750            }
1751
1752            if (PASS == (ret = PACKET_READ(stats_header, conn))) {
1753                /* will be freed by Zend, thus don't use the mnd_ allocator */
1754                *message = estrndup(stats_header->message, stats_header->message_len);
1755                *message_len = stats_header->message_len;
1756                DBG_INF(*message);
1757            }
1758            PACKET_FREE(stats_header);
1759        } while (0);
1760
1761        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1762    }
1763    DBG_RETURN(ret);
1764}
1765/* }}} */
1766
1767
1768/* {{{ mysqlnd_conn_data::kill */
1769static enum_func_status
1770MYSQLND_METHOD(mysqlnd_conn_data, kill)(MYSQLND_CONN_DATA * conn, unsigned int pid TSRMLS_DC)
1771{
1772    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, kill_connection);
1773    enum_func_status ret = FAIL;
1774    zend_uchar buff[4];
1775
1776    DBG_ENTER("mysqlnd_conn_data::kill");
1777    DBG_INF_FMT("conn=%llu pid=%u", conn->thread_id, pid);
1778
1779    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1780        int4store(buff, pid);
1781
1782        /* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */
1783        if (pid != conn->thread_id) {
1784            ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
1785            /*
1786              The server sends 0 but libmysql doesn't read it and has established
1787              a protocol of giving back -1. Thus we have to follow it :(
1788            */
1789            SET_ERROR_AFF_ROWS(conn);
1790        } else if (PASS == (ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_LAST, FALSE, TRUE TSRMLS_CC))) {
1791            CONN_SET_STATE(conn, CONN_QUIT_SENT);
1792            conn->m->send_close(conn TSRMLS_CC);
1793        }
1794
1795        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1796    }
1797    DBG_RETURN(ret);
1798}
1799/* }}} */
1800
1801
1802/* {{{ mysqlnd_conn_data::set_charset */
1803static enum_func_status
1804MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, const char * const csname TSRMLS_DC)
1805{
1806    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_charset);
1807    enum_func_status ret = FAIL;
1808    const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
1809
1810    DBG_ENTER("mysqlnd_conn_data::set_charset");
1811    DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname);
1812
1813    if (!charset) {
1814        SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
1815                         "Invalid characterset or character set not supported");
1816        DBG_RETURN(ret);
1817    }
1818
1819    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1820        char * query;
1821        size_t query_len = mnd_sprintf(&query, 0, "SET NAMES %s", csname);
1822
1823        if (FAIL == (ret = conn->m->query(conn, query, query_len TSRMLS_CC))) {
1824            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error executing query");
1825        } else if (conn->error_info->error_no) {
1826            ret = FAIL;
1827        } else {
1828            conn->charset = charset;
1829        }
1830        mnd_sprintf_free(query);
1831
1832        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1833    }
1834
1835    DBG_INF(ret == PASS? "PASS":"FAIL");
1836    DBG_RETURN(ret);
1837}
1838/* }}} */
1839
1840
1841/* {{{ mysqlnd_conn_data::refresh */
1842static enum_func_status
1843MYSQLND_METHOD(mysqlnd_conn_data, refresh)(MYSQLND_CONN_DATA * const conn, uint8_t options TSRMLS_DC)
1844{
1845    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, refresh_server);
1846    enum_func_status ret = FAIL;
1847    zend_uchar bits[1];
1848    DBG_ENTER("mysqlnd_conn_data::refresh");
1849    DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
1850
1851    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1852        int1store(bits, options);
1853
1854        ret = conn->m->simple_command(conn, COM_REFRESH, bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
1855
1856        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1857    }
1858    DBG_RETURN(ret);
1859}
1860/* }}} */
1861
1862
1863/* {{{ mysqlnd_conn_data::shutdown */
1864static enum_func_status
1865MYSQLND_METHOD(mysqlnd_conn_data, shutdown)(MYSQLND_CONN_DATA * const conn, uint8_t level TSRMLS_DC)
1866{
1867    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, shutdown_server);
1868    enum_func_status ret = FAIL;
1869    zend_uchar bits[1];
1870    DBG_ENTER("mysqlnd_conn_data::shutdown");
1871    DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
1872
1873    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
1874        int1store(bits, level);
1875
1876        ret = conn->m->simple_command(conn, COM_SHUTDOWN, bits, 1, PROT_OK_PACKET, FALSE, TRUE TSRMLS_CC);
1877
1878        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
1879    }
1880    DBG_RETURN(ret);
1881}
1882/* }}} */
1883
1884
1885/* {{{ mysqlnd_send_close */
1886static enum_func_status
1887MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1888{
1889    enum_func_status ret = PASS;
1890    MYSQLND_NET * net = conn->net;
1891    php_stream * net_stream = net->data->m.get_stream(net TSRMLS_CC);
1892    enum mysqlnd_connection_state state;
1893
1894    DBG_ENTER("mysqlnd_send_close");
1895    DBG_INF_FMT("conn=%llu net->data->stream->abstract=%p", conn->thread_id, net_stream? net_stream->abstract:NULL);
1896
1897    if (CONN_GET_STATE(conn) >= CONN_READY) {
1898        MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
1899        if (conn->persistent) {
1900            MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
1901        }
1902    }
1903    state = CONN_GET_STATE(conn);
1904    DBG_INF_FMT("state=%u", state);
1905    switch (state) {
1906        case CONN_READY:
1907            DBG_INF("Connection clean, sending COM_QUIT");
1908            if (net_stream) {
1909                ret = conn->m->simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, TRUE, TRUE TSRMLS_CC);
1910                net->data->m.close_stream(net, conn->stats, conn->error_info TSRMLS_CC);
1911            }
1912            CONN_SET_STATE(conn, CONN_QUIT_SENT);
1913            break;
1914        case CONN_SENDING_LOAD_DATA:
1915            /*
1916              Don't send COM_QUIT if we are in a middle of a LOAD DATA or we
1917              will crash (assert) a debug server.
1918            */
1919        case CONN_NEXT_RESULT_PENDING:
1920        case CONN_QUERY_SENT:
1921        case CONN_FETCHING_DATA:
1922            MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE);
1923            DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme);
1924            /*
1925              Do nothing, the connection will be brutally closed
1926              and the server will catch it and free close from its side.
1927            */
1928            /* Fall-through */
1929        case CONN_ALLOCED:
1930            /*
1931              Allocated but not connected or there was failure when trying
1932              to connect with pre-allocated connect.
1933
1934              Fall-through
1935            */
1936            CONN_SET_STATE(conn, CONN_QUIT_SENT);
1937            /* Fall-through */
1938        case CONN_QUIT_SENT:
1939            /* The user has killed its own connection */
1940            net->data->m.close_stream(net, conn->stats, conn->error_info TSRMLS_CC);
1941            break;
1942    }
1943
1944    DBG_RETURN(ret);
1945}
1946/* }}} */
1947
1948
1949/* {{{ mysqlnd_conn_data::get_reference */
1950static MYSQLND_CONN_DATA *
1951MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1952{
1953    DBG_ENTER("mysqlnd_conn_data::get_reference");
1954    ++conn->refcount;
1955    DBG_INF_FMT("conn=%llu new_refcount=%u", conn->thread_id, conn->refcount);
1956    DBG_RETURN(conn);
1957}
1958/* }}} */
1959
1960
1961/* {{{ mysqlnd_conn_data::free_reference */
1962static enum_func_status
1963MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1964{
1965    enum_func_status ret = PASS;
1966    DBG_ENTER("mysqlnd_conn_data::free_reference");
1967    DBG_INF_FMT("conn=%llu old_refcount=%u", conn->thread_id, conn->refcount);
1968    if (!(--conn->refcount)) {
1969        /*
1970          No multithreading issues as we don't share the connection :)
1971          This will free the object too, of course because references has
1972          reached zero.
1973        */
1974        ret = conn->m->send_close(conn TSRMLS_CC);
1975        conn->m->dtor(conn TSRMLS_CC);
1976    }
1977    DBG_RETURN(ret);
1978}
1979/* }}} */
1980
1981
1982/* {{{ mysqlnd_conn_data::get_state */
1983static enum mysqlnd_connection_state
1984MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
1985{
1986    DBG_ENTER("mysqlnd_conn_data::get_state");
1987    DBG_RETURN(conn->state);
1988}
1989/* }}} */
1990
1991
1992/* {{{ mysqlnd_conn_data::set_state */
1993static void
1994MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_connection_state new_state TSRMLS_DC)
1995{
1996    DBG_ENTER("mysqlnd_conn_data::set_state");
1997    DBG_INF_FMT("New state=%u", new_state);
1998    conn->state = new_state;
1999    DBG_VOID_RETURN;
2000}
2001/* }}} */
2002
2003
2004/* {{{ mysqlnd_conn_data::field_count */
2005static unsigned int
2006MYSQLND_METHOD(mysqlnd_conn_data, field_count)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2007{
2008    return conn->field_count;
2009}
2010/* }}} */
2011
2012
2013/* {{{ mysqlnd_conn_data::server_status */
2014static unsigned int
2015MYSQLND_METHOD(mysqlnd_conn_data, server_status)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2016{
2017    return conn->upsert_status->server_status;
2018}
2019/* }}} */
2020
2021
2022/* {{{ mysqlnd_conn_data::insert_id */
2023static uint64_t
2024MYSQLND_METHOD(mysqlnd_conn_data, insert_id)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2025{
2026    return conn->upsert_status->last_insert_id;
2027}
2028/* }}} */
2029
2030
2031/* {{{ mysqlnd_conn_data::affected_rows */
2032static uint64_t
2033MYSQLND_METHOD(mysqlnd_conn_data, affected_rows)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2034{
2035    return conn->upsert_status->affected_rows;
2036}
2037/* }}} */
2038
2039
2040/* {{{ mysqlnd_conn_data::warning_count */
2041static unsigned int
2042MYSQLND_METHOD(mysqlnd_conn_data, warning_count)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2043{
2044    return conn->upsert_status->warning_count;
2045}
2046/* }}} */
2047
2048
2049/* {{{ mysqlnd_conn_data::info */
2050static const char *
2051MYSQLND_METHOD(mysqlnd_conn_data, info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2052{
2053    return conn->last_message;
2054}
2055/* }}} */
2056
2057/* {{{ mysqlnd_get_client_info */
2058PHPAPI const char * mysqlnd_get_client_info()
2059{
2060    return MYSQLND_VERSION;
2061}
2062/* }}} */
2063
2064
2065/* {{{ mysqlnd_get_client_version */
2066PHPAPI unsigned int mysqlnd_get_client_version()
2067{
2068    return MYSQLND_VERSION_ID;
2069}
2070/* }}} */
2071
2072
2073/* {{{ mysqlnd_conn_data::get_server_info */
2074static const char *
2075MYSQLND_METHOD(mysqlnd_conn_data, get_server_info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2076{
2077    return conn->server_version;
2078}
2079/* }}} */
2080
2081
2082/* {{{ mysqlnd_conn_data::get_host_info */
2083static const char *
2084MYSQLND_METHOD(mysqlnd_conn_data, get_host_info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2085{
2086    return conn->host_info;
2087}
2088/* }}} */
2089
2090
2091/* {{{ mysqlnd_conn_data::get_proto_info */
2092static unsigned int
2093MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2094{
2095    return conn->protocol_version;
2096}
2097/* }}} */
2098
2099
2100/* {{{ mysqlnd_conn_data::charset_name */
2101static const char *
2102MYSQLND_METHOD(mysqlnd_conn_data, charset_name)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2103{
2104    return conn->charset->name;
2105}
2106/* }}} */
2107
2108
2109/* {{{ mysqlnd_conn_data::thread_id */
2110static uint64_t
2111MYSQLND_METHOD(mysqlnd_conn_data, thread_id)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2112{
2113    return conn->thread_id;
2114}
2115/* }}} */
2116
2117
2118/* {{{ mysqlnd_conn_data::get_server_version */
2119static unsigned long
2120MYSQLND_METHOD(mysqlnd_conn_data, get_server_version)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2121{
2122    long major, minor, patch;
2123    char *p;
2124
2125    if (!(p = conn->server_version)) {
2126        return 0;
2127    }
2128
2129    major = strtol(p, &p, 10);
2130    p += 1; /* consume the dot */
2131    minor = strtol(p, &p, 10);
2132    p += 1; /* consume the dot */
2133    patch = strtol(p, &p, 10);
2134
2135    return (unsigned long)(major * 10000L + (unsigned long)(minor * 100L + patch));
2136}
2137/* }}} */
2138
2139
2140/* {{{ mysqlnd_conn_data::more_results */
2141static zend_bool
2142MYSQLND_METHOD(mysqlnd_conn_data, more_results)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2143{
2144    DBG_ENTER("mysqlnd_conn_data::more_results");
2145    /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
2146    DBG_RETURN(conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
2147}
2148/* }}} */
2149
2150
2151/* {{{ mysqlnd_conn_data::next_result */
2152static enum_func_status
2153MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2154{
2155    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, next_result);
2156    enum_func_status ret = FAIL;
2157
2158    DBG_ENTER("mysqlnd_conn_data::next_result");
2159    DBG_INF_FMT("conn=%llu", conn->thread_id);
2160
2161    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2162        do {
2163            if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING) {
2164                break;
2165            }
2166
2167            SET_EMPTY_ERROR(*conn->error_info);
2168            SET_ERROR_AFF_ROWS(conn);
2169            /*
2170              We are sure that there is a result set, since conn->state is set accordingly
2171              in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered()
2172            */
2173            if (FAIL == (ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC))) {
2174                /*
2175                  There can be an error in the middle of a multi-statement, which will cancel the multi-statement.
2176                  So there are no more results and we should just return FALSE, error_no has been set
2177                */
2178                if (!conn->error_info->error_no) {
2179                    DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__);
2180                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Serious error. PID=%d", getpid());
2181                    CONN_SET_STATE(conn, CONN_QUIT_SENT);
2182                    conn->m->send_close(conn TSRMLS_CC);
2183                } else {
2184                    DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info->error_no, conn->error_info->error);
2185                }
2186                break;
2187            }
2188            if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
2189                MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
2190            }
2191        } while (0);
2192        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
2193    }
2194
2195    DBG_RETURN(ret);
2196}
2197/* }}} */
2198
2199
2200/* {{{ mysqlnd_field_type_name */
2201PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type)
2202{
2203    switch(field_type) {
2204        case FIELD_TYPE_STRING:
2205        case FIELD_TYPE_VAR_STRING:
2206            return "string";
2207        case FIELD_TYPE_TINY:
2208        case FIELD_TYPE_SHORT:
2209        case FIELD_TYPE_LONG:
2210        case FIELD_TYPE_LONGLONG:
2211        case FIELD_TYPE_INT24:
2212            return "int";
2213        case FIELD_TYPE_FLOAT:
2214        case FIELD_TYPE_DOUBLE:
2215        case FIELD_TYPE_DECIMAL:
2216        case FIELD_TYPE_NEWDECIMAL:
2217            return "real";
2218        case FIELD_TYPE_TIMESTAMP:
2219            return "timestamp";
2220        case FIELD_TYPE_YEAR:
2221            return "year";
2222        case FIELD_TYPE_DATE:
2223        case FIELD_TYPE_NEWDATE:
2224            return "date";
2225        case FIELD_TYPE_TIME:
2226            return "time";
2227        case FIELD_TYPE_SET:
2228            return "set";
2229        case FIELD_TYPE_ENUM:
2230            return "enum";
2231        case FIELD_TYPE_GEOMETRY:
2232            return "geometry";
2233        case FIELD_TYPE_DATETIME:
2234            return "datetime";
2235        case FIELD_TYPE_TINY_BLOB:
2236        case FIELD_TYPE_MEDIUM_BLOB:
2237        case FIELD_TYPE_LONG_BLOB:
2238        case FIELD_TYPE_BLOB:
2239            return "blob";
2240        case FIELD_TYPE_NULL:
2241            return "null";
2242        case FIELD_TYPE_BIT:
2243            return "bit";
2244        default:
2245            return "unknown";
2246    }
2247}
2248/* }}} */
2249
2250
2251/* {{{ mysqlnd_conn_data::change_user */
2252static enum_func_status
2253MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
2254                                          const char * user,
2255                                          const char * passwd,
2256                                          const char * db,
2257                                          zend_bool silent,
2258                                          size_t passwd_len
2259                                          TSRMLS_DC)
2260{
2261    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, change_user);
2262    enum_func_status ret = FAIL;
2263
2264    DBG_ENTER("mysqlnd_conn_data::change_user");
2265    DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%u",
2266                conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 );
2267
2268    if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2269        goto end;
2270    }
2271
2272    SET_EMPTY_ERROR(*conn->error_info);
2273    SET_ERROR_AFF_ROWS(conn);
2274
2275    if (!user) {
2276        user = "";
2277    }
2278    if (!passwd) {
2279        passwd = "";
2280    }
2281    if (!db) {
2282        db = "";
2283    }
2284
2285    /* XXX: passwords that have \0 inside work during auth, but in this case won't work with change user */
2286    ret = mysqlnd_run_authentication(conn, user, passwd, strlen(passwd), db, strlen(db),
2287                                    conn->auth_plugin_data, conn->auth_plugin_data_len, conn->options->auth_protocol,
2288                                    0 /*charset not used*/, conn->options, conn->server_capabilities, silent, TRUE/*is_change*/ TSRMLS_CC);
2289
2290    /*
2291      Here we should close all statements. Unbuffered queries should not be a
2292      problem as we won't allow sending COM_CHANGE_USER.
2293    */
2294    conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
2295end:
2296    DBG_INF(ret == PASS? "PASS":"FAIL");
2297    DBG_RETURN(ret);
2298}
2299/* }}} */
2300
2301
2302/* {{{ mysqlnd_conn_data::set_client_option */
2303static enum_func_status
2304MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const conn,
2305                                                enum mysqlnd_option option,
2306                                                const char * const value
2307                                                TSRMLS_DC)
2308{
2309    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option);
2310    enum_func_status ret = PASS;
2311    DBG_ENTER("mysqlnd_conn_data::set_client_option");
2312    DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
2313
2314    if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2315        goto end;
2316    }
2317    switch (option) {
2318        case MYSQL_OPT_COMPRESS:
2319#ifdef WHEN_SUPPORTED_BY_MYSQLI
2320        case MYSQL_OPT_READ_TIMEOUT:
2321        case MYSQL_OPT_WRITE_TIMEOUT:
2322#endif
2323        case MYSQLND_OPT_SSL_KEY:
2324        case MYSQLND_OPT_SSL_CERT:
2325        case MYSQLND_OPT_SSL_CA:
2326        case MYSQLND_OPT_SSL_CAPATH:
2327        case MYSQLND_OPT_SSL_CIPHER:
2328        case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
2329        case MYSQL_OPT_CONNECT_TIMEOUT:
2330        case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
2331        case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
2332        case MYSQL_SERVER_PUBLIC_KEY:
2333            ret = conn->net->data->m.set_client_option(conn->net, option, value TSRMLS_CC);
2334            break;
2335#ifdef MYSQLND_STRING_TO_INT_CONVERSION
2336        case MYSQLND_OPT_INT_AND_FLOAT_NATIVE:
2337            conn->options->int_and_float_native = *(unsigned int*) value;
2338            break;
2339#endif
2340        case MYSQL_OPT_LOCAL_INFILE:
2341            if (value && (*(unsigned int*) value) ? 1 : 0) {
2342                conn->options->flags |= CLIENT_LOCAL_FILES;
2343            } else {
2344                conn->options->flags &= ~CLIENT_LOCAL_FILES;
2345            }
2346            break;
2347        case MYSQL_INIT_COMMAND:
2348        {
2349            char ** new_init_commands;
2350            char * new_command;
2351            /* when num_commands is 0, then realloc will be effectively a malloc call, internally */
2352            /* Don't assign to conn->options->init_commands because in case of OOM we will lose the pointer and leak */
2353            new_init_commands = mnd_perealloc(conn->options->init_commands, sizeof(char *) * (conn->options->num_commands + 1), conn->persistent);
2354            if (!new_init_commands) {
2355                goto oom;
2356            }
2357            conn->options->init_commands = new_init_commands;
2358            new_command = mnd_pestrdup(value, conn->persistent);
2359            if (!new_command) {
2360                goto oom;
2361            }
2362            conn->options->init_commands[conn->options->num_commands] = new_command;
2363            ++conn->options->num_commands;
2364            break;
2365        }
2366        case MYSQL_READ_DEFAULT_FILE:
2367        case MYSQL_READ_DEFAULT_GROUP:
2368#ifdef WHEN_SUPPORTED_BY_MYSQLI
2369        case MYSQL_SET_CLIENT_IP:
2370        case MYSQL_REPORT_DATA_TRUNCATION:
2371#endif
2372            /* currently not supported. Todo!! */
2373            break;
2374        case MYSQL_SET_CHARSET_NAME:
2375        {
2376            char * new_charset_name;
2377            if (!mysqlnd_find_charset_name(value)) {
2378                SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Unknown character set");
2379                ret = FAIL;
2380                break;
2381            }
2382
2383            new_charset_name = mnd_pestrdup(value, conn->persistent);
2384            if (!new_charset_name) {
2385                goto oom;
2386            }
2387            if (conn->options->charset_name) {
2388                mnd_pefree(conn->options->charset_name, conn->persistent);
2389            }
2390            conn->options->charset_name = new_charset_name;
2391            DBG_INF_FMT("charset=%s", conn->options->charset_name);
2392            break;
2393        }
2394        case MYSQL_OPT_NAMED_PIPE:
2395            conn->options->protocol = MYSQL_PROTOCOL_PIPE;
2396            break;
2397        case MYSQL_OPT_PROTOCOL:
2398            if (*(unsigned int*) value < MYSQL_PROTOCOL_LAST) {
2399                conn->options->protocol = *(unsigned int*) value;
2400            }
2401            break;
2402#ifdef WHEN_SUPPORTED_BY_MYSQLI
2403        case MYSQL_SET_CHARSET_DIR:
2404        case MYSQL_OPT_RECONNECT:
2405            /* we don't need external character sets, all character sets are
2406               compiled in. For compatibility we just ignore this setting.
2407               Same for protocol, we don't support old protocol */
2408        case MYSQL_OPT_USE_REMOTE_CONNECTION:
2409        case MYSQL_OPT_USE_EMBEDDED_CONNECTION:
2410        case MYSQL_OPT_GUESS_CONNECTION:
2411            /* todo: throw an error, we don't support embedded */
2412            break;
2413#endif
2414        case MYSQLND_OPT_MAX_ALLOWED_PACKET:
2415            if (*(unsigned int*) value > (1<<16)) {
2416                conn->options->max_allowed_packet = *(unsigned int*) value;
2417            }
2418            break;
2419        case MYSQLND_OPT_AUTH_PROTOCOL:
2420        {
2421            char * new_auth_protocol = value? mnd_pestrdup(value, conn->persistent) : NULL;
2422            if (value && !new_auth_protocol) {
2423                goto oom;
2424            }
2425            if (conn->options->auth_protocol) {
2426                mnd_pefree(conn->options->auth_protocol, conn->persistent);
2427            }
2428            conn->options->auth_protocol = new_auth_protocol;
2429            DBG_INF_FMT("auth_protocol=%s", conn->options->auth_protocol);
2430            break;
2431        }
2432        case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS:
2433            if (value && (*(unsigned int*) value) ? 1 : 0) {
2434                conn->options->flags |= CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
2435            } else {
2436                conn->options->flags &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
2437            }
2438            break;
2439        case MYSQL_OPT_CONNECT_ATTR_RESET:
2440            if (conn->options->connect_attr) {
2441                DBG_INF_FMT("Before reset %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
2442                zend_hash_clean(conn->options->connect_attr);
2443            }
2444            break;
2445        case MYSQL_OPT_CONNECT_ATTR_DELETE:
2446            if (conn->options->connect_attr && value) {
2447                DBG_INF_FMT("Before delete %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
2448                zend_hash_del(conn->options->connect_attr, value, strlen(value));
2449                DBG_INF_FMT("%d left", zend_hash_num_elements(conn->options->connect_attr));
2450            }
2451            break;
2452#ifdef WHEN_SUPPORTED_BY_MYSQLI
2453        case MYSQL_SHARED_MEMORY_BASE_NAME:
2454        case MYSQL_OPT_USE_RESULT:
2455        case MYSQL_SECURE_AUTH:
2456            /* not sure, todo ? */
2457#endif
2458        default:
2459            ret = FAIL;
2460    }
2461    conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
2462    DBG_RETURN(ret);
2463oom:
2464    SET_OOM_ERROR(*conn->error_info);
2465    conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
2466end:
2467    DBG_RETURN(FAIL);
2468}
2469/* }}} */
2470
2471
2472/* {{{ connect_attr_item_edtor */
2473static void
2474connect_attr_item_edtor(void * pDest)
2475{
2476#ifdef ZTS
2477    TSRMLS_FETCH();
2478#endif
2479    DBG_ENTER("connect_attr_item_edtor");
2480    mnd_efree(*(char **) pDest);
2481    DBG_VOID_RETURN;
2482}
2483/* }}} */
2484
2485
2486/* {{{ connect_attr_item_pdtor */
2487static void
2488connect_attr_item_pdtor(void * pDest)
2489{
2490#ifdef ZTS
2491    TSRMLS_FETCH();
2492#endif
2493    DBG_ENTER("connect_attr_item_pdtor");
2494    mnd_pefree(*(char **) pDest, 1);
2495    DBG_VOID_RETURN;
2496}
2497/* }}} */
2498
2499
2500/* {{{ mysqlnd_conn_data::set_client_option_2d */
2501static enum_func_status
2502MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * const conn,
2503                                                        enum mysqlnd_option option,
2504                                                        const char * const key,
2505                                                        const char * const value
2506                                                        TSRMLS_DC)
2507{
2508    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option_2d);
2509    enum_func_status ret = PASS;
2510    DBG_ENTER("mysqlnd_conn_data::set_client_option_2d");
2511    DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
2512
2513    if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2514        goto end;
2515    }
2516    switch (option) {
2517        case MYSQL_OPT_CONNECT_ATTR_ADD:
2518            if (!conn->options->connect_attr) {
2519                DBG_INF("Initializing connect_attr hash");
2520                conn->options->connect_attr = mnd_pemalloc(sizeof(HashTable), conn->persistent);
2521                if (!conn->options->connect_attr) {
2522                    goto oom;
2523                }
2524                zend_hash_init(conn->options->connect_attr, 0, NULL, conn->persistent? connect_attr_item_pdtor:connect_attr_item_edtor, conn->persistent);
2525            }
2526            DBG_INF_FMT("Adding [%s][%s]", key, value);
2527            {
2528                const char * copyv = mnd_pestrdup(value, conn->persistent);
2529                if (!copyv) {
2530                    goto oom;
2531                }
2532                zend_hash_update(conn->options->connect_attr, key, strlen(key), &copyv, sizeof(char *), NULL);
2533            }
2534            break;
2535        default:
2536            ret = FAIL;
2537    }
2538    conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
2539    DBG_RETURN(ret);
2540oom:
2541    SET_OOM_ERROR(*conn->error_info);
2542    conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC);
2543end:
2544    DBG_RETURN(FAIL);
2545}
2546/* }}} */
2547
2548
2549/* {{{ mysqlnd_conn_data::use_result */
2550static MYSQLND_RES *
2551MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC)
2552{
2553    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, use_result);
2554    MYSQLND_RES * result = NULL;
2555
2556    DBG_ENTER("mysqlnd_conn_data::use_result");
2557    DBG_INF_FMT("conn=%llu", conn->thread_id);
2558
2559    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2560        do {
2561            if (!conn->current_result) {
2562                break;
2563            }
2564
2565            /* Nothing to store for UPSERT/LOAD DATA */
2566            if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
2567                SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
2568                DBG_ERR("Command out of sync");
2569                break;
2570            }
2571
2572            MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS);
2573
2574            conn->current_result->conn = conn->m->get_reference(conn TSRMLS_CC);
2575            result = conn->current_result->m.use_result(conn->current_result, FALSE TSRMLS_CC);
2576
2577            if (!result) {
2578                conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
2579            }
2580            conn->current_result = NULL;
2581        } while (0);
2582
2583        conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
2584    }
2585
2586    DBG_RETURN(result);
2587}
2588/* }}} */
2589
2590
2591/* {{{ mysqlnd_conn_data::store_result */
2592static MYSQLND_RES *
2593MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC)
2594{
2595    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, store_result);
2596    MYSQLND_RES * result = NULL;
2597
2598    DBG_ENTER("mysqlnd_conn_data::store_result");
2599    DBG_INF_FMT("conn=%llu conn=%p", conn->thread_id, conn);
2600
2601    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2602        do {
2603            unsigned int f = flags;
2604            if (!conn->current_result) {
2605                break;
2606            }
2607
2608            /* Nothing to store for UPSERT/LOAD DATA*/
2609            if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
2610                SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
2611                DBG_ERR("Command out of sync");
2612                break;
2613            }
2614
2615            MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
2616
2617            /* overwrite */
2618            if ((conn->m->get_client_api_capabilities(conn TSRMLS_CC) & MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA)) {
2619                if (MYSQLND_G(fetch_data_copy)) {
2620                    f &= ~MYSQLND_STORE_NO_COPY;
2621                    f |= MYSQLND_STORE_COPY;
2622                }
2623            } else {
2624                /* if for some reason PDO borks something */
2625                if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
2626                    f |= MYSQLND_STORE_COPY;
2627                }
2628            }
2629            if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
2630                SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Unknown fetch mode");
2631                DBG_ERR("Unknown fetch mode");
2632                break;
2633            }
2634            result = conn->current_result->m.store_result(conn->current_result, conn, f TSRMLS_CC);
2635            if (!result) {
2636                conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC);
2637            }
2638            conn->current_result = NULL;
2639        } while (0);
2640
2641        conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS TSRMLS_CC);
2642    }
2643    DBG_RETURN(result);
2644}
2645/* }}} */
2646
2647
2648/* {{{ mysqlnd_conn_data::get_connection_stats */
2649static void
2650MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats)(const MYSQLND_CONN_DATA * const conn,
2651                                                   zval * return_value TSRMLS_DC ZEND_FILE_LINE_DC)
2652{
2653    DBG_ENTER("mysqlnd_conn_data::get_connection_stats");
2654    mysqlnd_fill_stats_hash(conn->stats, mysqlnd_stats_values_names, return_value TSRMLS_CC ZEND_FILE_LINE_CC);
2655    DBG_VOID_RETURN;
2656}
2657/* }}} */
2658
2659
2660/* {{{ mysqlnd_conn_data::set_autocommit */
2661static enum_func_status
2662MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit)(MYSQLND_CONN_DATA * conn, unsigned int mode TSRMLS_DC)
2663{
2664    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_autocommit);
2665    enum_func_status ret = FAIL;
2666    DBG_ENTER("mysqlnd_conn_data::set_autocommit");
2667
2668    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2669        ret = conn->m->query(conn, (mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", sizeof("SET AUTOCOMMIT=1") - 1 TSRMLS_CC);
2670        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
2671    }
2672
2673    DBG_RETURN(ret);
2674}
2675/* }}} */
2676
2677
2678/* {{{ mysqlnd_conn_data::tx_commit */
2679static enum_func_status
2680MYSQLND_METHOD(mysqlnd_conn_data, tx_commit)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
2681{
2682    return conn->m->tx_commit_or_rollback(conn, TRUE, TRANS_COR_NO_OPT, NULL TSRMLS_CC);
2683}
2684/* }}} */
2685
2686
2687/* {{{ mysqlnd_conn_data::tx_rollback */
2688static enum_func_status
2689MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
2690{
2691    return conn->m->tx_commit_or_rollback(conn, FALSE, TRANS_COR_NO_OPT, NULL TSRMLS_CC);
2692}
2693/* }}} */
2694
2695
2696/* {{{ mysqlnd_tx_cor_options_to_string */
2697static void
2698MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string)(const MYSQLND_CONN_DATA * const conn, smart_str * str, const unsigned int mode TSRMLS_DC)
2699{
2700    if (mode & TRANS_COR_AND_CHAIN && !(mode & TRANS_COR_AND_NO_CHAIN)) {
2701        if (str->len) {
2702            smart_str_appendl(str, " ", sizeof(" ") - 1);
2703        }
2704        smart_str_appendl(str, "AND CHAIN", sizeof("AND CHAIN") - 1);
2705    } else if (mode & TRANS_COR_AND_NO_CHAIN && !(mode & TRANS_COR_AND_CHAIN)) {
2706        if (str->len) {
2707            smart_str_appendl(str, " ", sizeof(" ") - 1);
2708        }
2709        smart_str_appendl(str, "AND NO CHAIN", sizeof("AND NO CHAIN") - 1);
2710    }
2711
2712    if (mode & TRANS_COR_RELEASE && !(mode & TRANS_COR_NO_RELEASE)) {
2713        if (str->len) {
2714            smart_str_appendl(str, " ", sizeof(" ") - 1);
2715        }
2716        smart_str_appendl(str, "RELEASE", sizeof("RELEASE") - 1);
2717    } else if (mode & TRANS_COR_NO_RELEASE && !(mode & TRANS_COR_RELEASE)) {
2718        if (str->len) {
2719            smart_str_appendl(str, " ", sizeof(" ") - 1);
2720        }
2721        smart_str_appendl(str, "NO RELEASE", sizeof("NO RELEASE") - 1);
2722    }
2723    smart_str_0(str);
2724}
2725/* }}} */
2726
2727
2728/* {{{ mysqlnd_escape_string_for_tx_name_in_comment */
2729static char *
2730mysqlnd_escape_string_for_tx_name_in_comment(const char * const name TSRMLS_DC)
2731{
2732    char * ret = NULL;
2733    DBG_ENTER("mysqlnd_escape_string_for_tx_name_in_comment");
2734    if (name) {
2735        zend_bool warned = FALSE;
2736        const char * p_orig = name;
2737        char * p_copy;
2738        p_copy = ret = mnd_emalloc(strlen(name) + 1 + 2 + 2 + 1); /* space, open, close, NullS */
2739        *p_copy++ = ' ';
2740        *p_copy++ = '/';
2741        *p_copy++ = '*';
2742        while (1) {
2743            register char v = *p_orig;
2744            if (v == 0) {
2745                break;
2746            }
2747            if ((v >= '0' && v <= '9') ||
2748                (v >= 'a' && v <= 'z') ||
2749                (v >= 'A' && v <= 'Z') ||
2750                v == '-' ||
2751                v == '_' ||
2752                v == ' ' ||
2753                v == '=')
2754            {
2755                *p_copy++ = v;
2756            } else if (warned == FALSE) {
2757                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Transaction name truncated. Must be only [0-9A-Za-z\\-_=]+");
2758                warned = TRUE;
2759            }
2760            ++p_orig;
2761        }
2762        *p_copy++ = '*';
2763        *p_copy++ = '/';
2764        *p_copy++ = 0;
2765    }
2766    DBG_RETURN(ret);
2767}
2768/* }}} */
2769
2770
2771/* {{{ mysqlnd_conn_data::tx_commit_ex */
2772static enum_func_status
2773MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * conn, const zend_bool commit, const unsigned int flags, const char * const name TSRMLS_DC)
2774{
2775    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_commit_or_rollback);
2776    enum_func_status ret = FAIL;
2777    DBG_ENTER("mysqlnd_conn_data::tx_commit_or_rollback");
2778
2779    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2780        do {
2781            smart_str tmp_str = {0, 0, 0};
2782            conn->m->tx_cor_options_to_string(conn, &tmp_str, flags TSRMLS_CC);
2783            smart_str_0(&tmp_str);
2784
2785
2786            {
2787                char * query;
2788                size_t query_len;
2789                char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name TSRMLS_CC);
2790
2791                query_len = mnd_sprintf(&query, 0, (commit? "COMMIT%s %s":"ROLLBACK%s %s"),
2792                                        name_esc? name_esc:"", tmp_str.c? tmp_str.c:"");
2793                smart_str_free(&tmp_str);
2794                if (name_esc) {
2795                    mnd_efree(name_esc);
2796                    name_esc = NULL;
2797                }
2798                if (!query) {
2799                    SET_OOM_ERROR(*conn->error_info);
2800                    break;
2801                }
2802
2803                ret = conn->m->query(conn, query, query_len TSRMLS_CC);
2804                mnd_sprintf_free(query);
2805            }
2806        } while (0);
2807        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
2808    }
2809
2810    DBG_RETURN(ret);
2811}
2812/* }}} */
2813
2814
2815/* {{{ mysqlnd_conn_data::tx_begin */
2816static enum_func_status
2817MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsigned int mode, const char * const name TSRMLS_DC)
2818{
2819    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_begin);
2820    enum_func_status ret = FAIL;
2821    DBG_ENTER("mysqlnd_conn_data::tx_begin");
2822
2823    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2824        do {
2825            smart_str tmp_str = {0, 0, 0};
2826            if (mode & TRANS_START_WITH_CONSISTENT_SNAPSHOT) {
2827                if (tmp_str.len) {
2828                    smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
2829                }
2830                smart_str_appendl(&tmp_str, "WITH CONSISTENT SNAPSHOT", sizeof("WITH CONSISTENT SNAPSHOT") - 1);
2831            }
2832            if (mode & (TRANS_START_READ_WRITE | TRANS_START_READ_ONLY)) {
2833                unsigned long server_version = conn->m->get_server_version(conn TSRMLS_CC);
2834                if (server_version < 50605L) {
2835                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "This server version doesn't support 'READ WRITE' and 'READ ONLY'. Minimum 5.6.5 is required");
2836                    smart_str_free(&tmp_str);
2837                    break;
2838                } else if (mode & TRANS_START_READ_WRITE) {
2839                    if (tmp_str.len) {
2840                        smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
2841                    }
2842                    smart_str_appendl(&tmp_str, "READ WRITE", sizeof("READ WRITE") - 1);
2843                } else if (mode & TRANS_START_READ_ONLY) {
2844                    if (tmp_str.len) {
2845                        smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
2846                    }
2847                    smart_str_appendl(&tmp_str, "READ ONLY", sizeof("READ ONLY") - 1);
2848                }
2849            }
2850            smart_str_0(&tmp_str);
2851
2852            {
2853                char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name TSRMLS_CC);
2854                char * query;
2855                unsigned int query_len = mnd_sprintf(&query, 0, "START TRANSACTION%s %s", name_esc? name_esc:"", tmp_str.c? tmp_str.c:"");
2856                smart_str_free(&tmp_str);
2857                if (name_esc) {
2858                    mnd_efree(name_esc);
2859                    name_esc = NULL;
2860                }
2861                if (!query) {
2862                    SET_OOM_ERROR(*conn->error_info);
2863                    break;
2864                }
2865                ret = conn->m->query(conn, query, query_len TSRMLS_CC);
2866                mnd_sprintf_free(query);
2867            }
2868        } while (0);
2869        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
2870    }
2871
2872    DBG_RETURN(ret);
2873}
2874/* }}} */
2875
2876
2877/* {{{ mysqlnd_conn_data::tx_savepoint */
2878static enum_func_status
2879MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint)(MYSQLND_CONN_DATA * conn, const char * const name TSRMLS_DC)
2880{
2881    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_savepoint);
2882    enum_func_status ret = FAIL;
2883    DBG_ENTER("mysqlnd_conn_data::tx_savepoint");
2884
2885    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2886        do {
2887            char * query;
2888            unsigned int query_len;
2889            if (!name) {
2890                SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
2891                break;
2892            }
2893            query_len = mnd_sprintf(&query, 0, "SAVEPOINT `%s`", name);
2894            if (!query) {
2895                SET_OOM_ERROR(*conn->error_info);
2896                break;
2897            }
2898            ret = conn->m->query(conn, query, query_len TSRMLS_CC);
2899            mnd_sprintf_free(query);
2900        } while (0);
2901        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
2902    }
2903
2904    DBG_RETURN(ret);
2905}
2906/* }}} */
2907
2908
2909/* {{{ mysqlnd_conn_data::tx_savepoint_release */
2910static enum_func_status
2911MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn, const char * const name TSRMLS_DC)
2912{
2913    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_savepoint_release);
2914    enum_func_status ret = FAIL;
2915    DBG_ENTER("mysqlnd_conn_data::tx_savepoint_release");
2916
2917    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
2918        do {
2919            char * query;
2920            unsigned int query_len;
2921            if (!name) {
2922                SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
2923                break;
2924            }
2925            query_len = mnd_sprintf(&query, 0, "RELEASE SAVEPOINT `%s`", name);
2926            if (!query) {
2927                SET_OOM_ERROR(*conn->error_info);
2928                break;
2929            }
2930            ret = conn->m->query(conn, query, query_len TSRMLS_CC);
2931            mnd_sprintf_free(query);
2932        } while (0);
2933        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
2934    }
2935
2936    DBG_RETURN(ret);
2937}
2938/* }}} */
2939
2940
2941/* {{{ mysqlnd_conn_data::negotiate_client_api_capabilities */
2942static unsigned int
2943MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC)
2944{
2945    unsigned int ret = 0;
2946    DBG_ENTER("mysqlnd_conn_data::negotiate_client_api_capabilities");
2947    if (conn) {
2948        ret = conn->client_api_capabilities;
2949        conn->client_api_capabilities = flags;
2950    }
2951
2952    DBG_RETURN(ret);
2953}
2954/* }}} */
2955
2956
2957/* {{{ mysqlnd_conn_data::get_client_api_capabilities */
2958static unsigned int
2959MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC)
2960{
2961    DBG_ENTER("mysqlnd_conn_data::get_client_api_capabilities");
2962    DBG_RETURN(conn? conn->client_api_capabilities : 0);
2963}
2964/* }}} */
2965
2966
2967/* {{{ mysqlnd_conn_data::local_tx_start */
2968static enum_func_status
2969MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, size_t this_func TSRMLS_DC)
2970{
2971    enum_func_status ret = PASS;
2972    DBG_ENTER("mysqlnd_conn_data::local_tx_start");
2973    DBG_RETURN(ret);
2974}
2975/* }}} */
2976
2977
2978/* {{{ mysqlnd_conn_data::local_tx_end */
2979static enum_func_status
2980MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, size_t this_func, enum_func_status status TSRMLS_DC)
2981{
2982    DBG_ENTER("mysqlnd_conn_data::local_tx_end");
2983    DBG_RETURN(status);
2984}
2985/* }}} */
2986
2987
2988/* {{{ mysqlnd_conn_data::init */
2989static enum_func_status
2990MYSQLND_METHOD(mysqlnd_conn_data, init)(MYSQLND_CONN_DATA * conn TSRMLS_DC)
2991{
2992    DBG_ENTER("mysqlnd_conn_data::init");
2993    mysqlnd_stats_init(&conn->stats, STAT_LAST);
2994    SET_ERROR_AFF_ROWS(conn);
2995
2996    conn->net = mysqlnd_net_init(conn->persistent, conn->stats, conn->error_info TSRMLS_CC);
2997    conn->protocol = mysqlnd_protocol_init(conn->persistent TSRMLS_CC);
2998
2999    DBG_RETURN(conn->stats && conn->net && conn->protocol? PASS:FAIL);
3000}
3001/* }}} */
3002
3003
3004MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND_CONN_DATA * const conn TSRMLS_DC);
3005
3006
3007MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
3008    MYSQLND_METHOD(mysqlnd_conn_data, init),
3009    MYSQLND_METHOD(mysqlnd_conn_data, connect),
3010
3011    MYSQLND_METHOD(mysqlnd_conn_data, escape_string),
3012    MYSQLND_METHOD(mysqlnd_conn_data, set_charset),
3013    MYSQLND_METHOD(mysqlnd_conn_data, query),
3014    MYSQLND_METHOD(mysqlnd_conn_data, send_query),
3015    MYSQLND_METHOD(mysqlnd_conn_data, reap_query),
3016    MYSQLND_METHOD(mysqlnd_conn_data, use_result),
3017    MYSQLND_METHOD(mysqlnd_conn_data, store_result),
3018    MYSQLND_METHOD(mysqlnd_conn_data, next_result),
3019    MYSQLND_METHOD(mysqlnd_conn_data, more_results),
3020
3021    _mysqlnd_stmt_init,
3022
3023    MYSQLND_METHOD(mysqlnd_conn_data, shutdown),
3024    MYSQLND_METHOD(mysqlnd_conn_data, refresh),
3025
3026    MYSQLND_METHOD(mysqlnd_conn_data, ping),
3027    MYSQLND_METHOD(mysqlnd_conn_data, kill),
3028    MYSQLND_METHOD(mysqlnd_conn_data, select_db),
3029    MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info),
3030    MYSQLND_METHOD(mysqlnd_conn_data, change_user),
3031
3032    MYSQLND_METHOD(mysqlnd_conn_data, errno),
3033    MYSQLND_METHOD(mysqlnd_conn_data, error),
3034    MYSQLND_METHOD(mysqlnd_conn_data, sqlstate),
3035    MYSQLND_METHOD(mysqlnd_conn_data, thread_id),
3036
3037    MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats),
3038
3039    MYSQLND_METHOD(mysqlnd_conn_data, get_server_version),
3040    MYSQLND_METHOD(mysqlnd_conn_data, get_server_info),
3041    MYSQLND_METHOD(mysqlnd_conn_data, statistic),
3042    MYSQLND_METHOD(mysqlnd_conn_data, get_host_info),
3043    MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info),
3044    MYSQLND_METHOD(mysqlnd_conn_data, info),
3045    MYSQLND_METHOD(mysqlnd_conn_data, charset_name),
3046    MYSQLND_METHOD(mysqlnd_conn_data, list_fields),
3047    MYSQLND_METHOD(mysqlnd_conn_data, list_method),
3048
3049    MYSQLND_METHOD(mysqlnd_conn_data, insert_id),
3050    MYSQLND_METHOD(mysqlnd_conn_data, affected_rows),
3051    MYSQLND_METHOD(mysqlnd_conn_data, warning_count),
3052    MYSQLND_METHOD(mysqlnd_conn_data, field_count),
3053
3054    MYSQLND_METHOD(mysqlnd_conn_data, server_status),
3055
3056    MYSQLND_METHOD(mysqlnd_conn_data, set_server_option),
3057    MYSQLND_METHOD(mysqlnd_conn_data, set_client_option),
3058    MYSQLND_METHOD(mysqlnd_conn_data, free_contents),
3059    MYSQLND_METHOD(mysqlnd_conn_data, free_options),
3060
3061    MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor),
3062
3063    mysqlnd_query_read_result_set_header,
3064
3065    MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference),
3066    MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference),
3067    MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state),
3068    MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state),
3069
3070    MYSQLND_METHOD(mysqlnd_conn_data, simple_command),
3071    MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response),
3072    MYSQLND_METHOD(mysqlnd_conn_data, restart_psession),
3073    MYSQLND_METHOD(mysqlnd_conn_data, end_psession),
3074    MYSQLND_METHOD(mysqlnd_conn_data, send_close),
3075
3076    MYSQLND_METHOD(mysqlnd_conn_data, ssl_set),
3077    mysqlnd_result_init,
3078    MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit),
3079    MYSQLND_METHOD(mysqlnd_conn_data, tx_commit),
3080    MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback),
3081    MYSQLND_METHOD(mysqlnd_conn_data, tx_begin),
3082    MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback),
3083    MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string),
3084    MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint),
3085    MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release),
3086
3087    MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start),
3088    MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end),
3089    MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands),
3090    MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags),
3091    MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake),
3092    MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request),
3093    MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name),
3094
3095    MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d),
3096
3097    MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities),
3098    MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)
3099MYSQLND_CLASS_METHODS_END;
3100
3101
3102/* {{{ mysqlnd_conn::get_reference */
3103static MYSQLND *
3104MYSQLND_METHOD(mysqlnd_conn, clone_object)(MYSQLND * const conn TSRMLS_DC)
3105{
3106    MYSQLND * ret;
3107    DBG_ENTER("mysqlnd_conn::get_reference");
3108    ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).clone_connection_object(conn TSRMLS_CC);
3109    DBG_RETURN(ret);
3110}
3111/* }}} */
3112
3113
3114/* {{{ mysqlnd_conn_data::dtor */
3115static void
3116MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn TSRMLS_DC)
3117{
3118    DBG_ENTER("mysqlnd_conn::dtor");
3119    DBG_INF_FMT("conn=%llu", conn->data->thread_id);
3120
3121    conn->data->m->free_reference(conn->data TSRMLS_CC);
3122
3123    mnd_pefree(conn, conn->persistent);
3124
3125    DBG_VOID_RETURN;
3126}
3127/* }}} */
3128
3129
3130/* {{{ mysqlnd_conn_data::close */
3131static enum_func_status
3132MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, enum_connection_close_type close_type TSRMLS_DC)
3133{
3134    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_methods, close);
3135    MYSQLND_CONN_DATA * conn = conn_handle->data;
3136    enum_func_status ret = FAIL;
3137
3138    DBG_ENTER("mysqlnd_conn::close");
3139    DBG_INF_FMT("conn=%llu", conn->thread_id);
3140
3141    if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) {
3142        if (CONN_GET_STATE(conn) >= CONN_READY) {
3143            static enum_mysqlnd_collected_stats close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
3144                STAT_CLOSE_EXPLICIT,
3145                STAT_CLOSE_IMPLICIT,
3146                STAT_CLOSE_DISCONNECT
3147            };
3148            MYSQLND_INC_CONN_STATISTIC(conn->stats, close_type_to_stat_map[close_type]);
3149        }
3150
3151        /*
3152          Close now, free_reference will try,
3153          if we are last, but that's not a problem.
3154        */
3155        ret = conn->m->send_close(conn TSRMLS_CC);
3156
3157        /* do it after free_reference/dtor and we might crash */
3158        conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC);
3159
3160        conn_handle->m->dtor(conn_handle TSRMLS_CC);
3161    }
3162    DBG_RETURN(ret);
3163}
3164/* }}} */
3165
3166
3167MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
3168    MYSQLND_METHOD(mysqlnd_conn, connect),
3169    MYSQLND_METHOD(mysqlnd_conn, clone_object),
3170    MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor),
3171    MYSQLND_METHOD(mysqlnd_conn, close)
3172MYSQLND_CLASS_METHODS_END;
3173
3174
3175/* {{{ _mysqlnd_init */
3176PHPAPI MYSQLND *
3177_mysqlnd_init(unsigned int flags, zend_bool persistent TSRMLS_DC)
3178{
3179    MYSQLND * ret;
3180    DBG_ENTER("mysqlnd_init");
3181    ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_connection(persistent TSRMLS_CC);
3182    if (ret && ret->data) {
3183        ret->data->m->negotiate_client_api_capabilities(ret->data, flags TSRMLS_CC);
3184    }
3185    DBG_RETURN(ret);
3186}
3187/* }}} */
3188
3189/*
3190 * Local variables:
3191 * tab-width: 4
3192 * c-basic-offset: 4
3193 * End:
3194 * vim600: noet sw=4 ts=4 fdm=marker
3195 * vim<600: noet sw=4 ts=4
3196 */
3197