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