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