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