1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 7                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 2006-2015 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, MYSQLND_SEND_QUERY_IMPLICIT, NULL, NULL) &&
1187            PASS == conn->m->reap_query(conn, MYSQLND_REAP_RESULT_IMPLICIT))
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                                              enum_mysqlnd_send_query_type type, zval *read_cb, zval *err_cb)
1205{
1206    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, send_query);
1207    enum_func_status ret = FAIL;
1208    DBG_ENTER("mysqlnd_conn_data::send_query");
1209    DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
1210    DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
1211
1212    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1213        ret = conn->m->simple_command(conn, COM_QUERY, (zend_uchar *) query, query_len,
1214                                      PROT_LAST /* we will handle the OK packet*/,
1215                                      FALSE, FALSE);
1216        if (PASS == ret) {
1217            CONN_SET_STATE(conn, CONN_QUERY_SENT);
1218        }
1219        conn->m->local_tx_end(conn, this_func, ret);
1220    }
1221    DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
1222    DBG_RETURN(ret);
1223}
1224/* }}} */
1225
1226
1227/* {{{ mysqlnd_conn_data::reap_query */
1228static enum_func_status
1229MYSQLND_METHOD(mysqlnd_conn_data, reap_query)(MYSQLND_CONN_DATA * conn, enum_mysqlnd_reap_result_type type)
1230{
1231    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, reap_query);
1232    enum_mysqlnd_connection_state state = CONN_GET_STATE(conn);
1233    enum_func_status ret = FAIL;
1234    DBG_ENTER("mysqlnd_conn_data::reap_query");
1235    DBG_INF_FMT("conn=%llu", conn->thread_id);
1236
1237    DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
1238    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1239        if (state <= CONN_READY || state == CONN_QUIT_SENT) {
1240            php_error_docref(NULL, E_WARNING, "Connection not opened, clear or has been closed");
1241            DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state);
1242            DBG_RETURN(ret);
1243        }
1244        ret = conn->m->query_read_result_set_header(conn, NULL);
1245
1246        conn->m->local_tx_end(conn, this_func, ret);
1247    }
1248    DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status);
1249    DBG_RETURN(ret);
1250}
1251/* }}} */
1252
1253
1254#include "php_network.h"
1255
1256/* {{{ mysqlnd_stream_array_to_fd_set */
1257MYSQLND ** mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array)
1258{
1259    int cnt = 0;
1260    MYSQLND **p = conn_array, **p_p;
1261    MYSQLND **ret = NULL;
1262
1263    while (*p) {
1264        if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
1265            cnt++;
1266        }
1267        p++;
1268    }
1269    if (cnt) {
1270        MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
1271        p_p = p = conn_array;
1272        while (*p) {
1273            if (CONN_GET_STATE((*p)->data) <= CONN_READY || CONN_GET_STATE((*p)->data) == CONN_QUIT_SENT) {
1274                *ret_p = *p;
1275                *p = NULL;
1276                ret_p++;
1277            } else {
1278                *p_p = *p;
1279                p_p++;
1280            }
1281            p++;
1282        }
1283        *ret_p = NULL;
1284    }
1285    return ret;
1286}
1287/* }}} */
1288
1289
1290/* {{{ mysqlnd_stream_array_to_fd_set */
1291static int mysqlnd_stream_array_to_fd_set(MYSQLND ** conn_array, fd_set * fds, php_socket_t * max_fd)
1292{
1293    php_socket_t this_fd;
1294    php_stream *stream = NULL;
1295    unsigned int cnt = 0;
1296    MYSQLND **p = conn_array;
1297    DBG_ENTER("mysqlnd_stream_array_to_fd_set");
1298
1299    while (*p) {
1300        /* get the fd.
1301         * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
1302         * when casting.  It is only used here so that the buffered data warning
1303         * is not displayed.
1304         * */
1305        stream = (*p)->data->net->data->m.get_stream((*p)->data->net);
1306        DBG_INF_FMT("conn=%llu stream=%p", (*p)->data->thread_id, stream);
1307        if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
1308                                        (void*)&this_fd, 1) && ZEND_VALID_SOCKET(this_fd)) {
1309
1310            PHP_SAFE_FD_SET(this_fd, fds);
1311
1312            if (this_fd > *max_fd) {
1313                *max_fd = this_fd;
1314            }
1315            cnt++;
1316        }
1317        p++;
1318    }
1319    DBG_RETURN(cnt ? 1 : 0);
1320}
1321/* }}} */
1322
1323
1324/* {{{ mysqlnd_stream_array_from_fd_set */
1325static int mysqlnd_stream_array_from_fd_set(MYSQLND ** conn_array, fd_set * fds)
1326{
1327    php_socket_t this_fd;
1328    php_stream *stream = NULL;
1329    int ret = 0;
1330    zend_bool disproportion = FALSE;
1331    MYSQLND **fwd = conn_array, **bckwd = conn_array;
1332    DBG_ENTER("mysqlnd_stream_array_from_fd_set");
1333
1334    while (*fwd) {
1335        stream = (*fwd)->data->net->data->m.get_stream((*fwd)->data->net);
1336        DBG_INF_FMT("conn=%llu stream=%p", (*fwd)->data->thread_id, stream);
1337        if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
1338                                        (void*)&this_fd, 1) && ZEND_VALID_SOCKET(this_fd)) {
1339            if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
1340                if (disproportion) {
1341                    *bckwd = *fwd;
1342                }
1343                bckwd++;
1344                fwd++;
1345                ret++;
1346                continue;
1347            }
1348        }
1349        disproportion = TRUE;
1350        fwd++;
1351    }
1352    *bckwd = NULL;/* NULL-terminate the list */
1353
1354    DBG_RETURN(ret);
1355}
1356/* }}} */
1357
1358
1359#ifndef PHP_WIN32
1360#define php_select(m, r, w, e, t)   select(m, r, w, e, t)
1361#else
1362#include "win32/select.h"
1363#endif
1364
1365
1366/* {{{ mysqlnd_poll */
1367PHPAPI enum_func_status
1368mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num)
1369{
1370    struct timeval  tv;
1371    struct timeval *tv_p = NULL;
1372    fd_set          rfds, wfds, efds;
1373    php_socket_t    max_fd = 0;
1374    int             retval, sets = 0;
1375    int             set_count, max_set_count = 0;
1376
1377    DBG_ENTER("_mysqlnd_poll");
1378    if (sec < 0 || usec < 0) {
1379        php_error_docref(NULL, E_WARNING, "Negative values passed for sec and/or usec");
1380        DBG_RETURN(FAIL);
1381    }
1382
1383    FD_ZERO(&rfds);
1384    FD_ZERO(&wfds);
1385    FD_ZERO(&efds);
1386
1387    if (r_array != NULL) {
1388        *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array);
1389        set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd);
1390        if (set_count > max_set_count) {
1391            max_set_count = set_count;
1392        }
1393        sets += set_count;
1394    }
1395
1396    if (e_array != NULL) {
1397        set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd);
1398        if (set_count > max_set_count) {
1399            max_set_count = set_count;
1400        }
1401        sets += set_count;
1402    }
1403
1404    if (!sets) {
1405        php_error_docref(NULL, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
1406        DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
1407        DBG_RETURN(FAIL);
1408    }
1409
1410    PHP_SAFE_MAX_FD(max_fd, max_set_count);
1411
1412    /* Solaris + BSD do not like microsecond values which are >= 1 sec */
1413    if (usec > 999999) {
1414        tv.tv_sec = sec + (usec / 1000000);
1415        tv.tv_usec = usec % 1000000;
1416    } else {
1417        tv.tv_sec = sec;
1418        tv.tv_usec = usec;
1419    }
1420
1421    tv_p = &tv;
1422
1423    retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
1424
1425    if (retval == -1) {
1426        php_error_docref(NULL, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
1427                        errno, strerror(errno), max_fd);
1428        DBG_RETURN(FAIL);
1429    }
1430
1431    if (r_array != NULL) {
1432        mysqlnd_stream_array_from_fd_set(r_array, &rfds);
1433    }
1434    if (e_array != NULL) {
1435        mysqlnd_stream_array_from_fd_set(e_array, &efds);
1436    }
1437
1438    *desc_num = retval;
1439    DBG_RETURN(PASS);
1440}
1441/* }}} */
1442
1443
1444/*
1445  COM_FIELD_LIST is special, different from a SHOW FIELDS FROM :
1446  - There is no result set header - status from the command, which
1447    impacts us to allocate big chunk of memory for reading the metadata.
1448  - The EOF packet is consumed by the metadata packet reader.
1449*/
1450
1451/* {{{ mysqlnd_conn_data::list_fields */
1452MYSQLND_RES *
1453MYSQLND_METHOD(mysqlnd_conn_data, list_fields)(MYSQLND_CONN_DATA * conn, const char *table, const char *achtung_wild)
1454{
1455    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_fields);
1456    /* db + \0 + wild + \0 (for wild) */
1457    zend_uchar buff[MYSQLND_MAX_ALLOWED_DB_LEN * 2 + 1 + 1], *p;
1458    size_t table_len, wild_len;
1459    MYSQLND_RES * result = NULL;
1460    DBG_ENTER("mysqlnd_conn_data::list_fields");
1461    DBG_INF_FMT("conn=%llu table=%s wild=%s", conn->thread_id, table? table:"",achtung_wild? achtung_wild:"");
1462
1463    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1464        do {
1465            p = buff;
1466            if (table && (table_len = strlen(table))) {
1467                size_t to_copy = MIN(table_len, MYSQLND_MAX_ALLOWED_DB_LEN);
1468                memcpy(p, table, to_copy);
1469                p += to_copy;
1470                *p++ = '\0';
1471            }
1472
1473            if (achtung_wild && (wild_len = strlen(achtung_wild))) {
1474                size_t to_copy = MIN(wild_len, MYSQLND_MAX_ALLOWED_DB_LEN);
1475                memcpy(p, achtung_wild, to_copy);
1476                p += to_copy;
1477                *p++ = '\0';
1478            }
1479
1480            if (PASS != conn->m->simple_command(conn, COM_FIELD_LIST, buff, p - buff,
1481                                               PROT_LAST /* we will handle the OK packet*/,
1482                                               FALSE, TRUE)) {
1483                conn->m->local_tx_end(conn, 0, FAIL);
1484                break;
1485            }
1486
1487            /*
1488               Prepare for the worst case.
1489               MyISAM goes to 2500 BIT columns, double it for safety.
1490            */
1491            result = conn->m->result_init(5000, conn->persistent);
1492            if (!result) {
1493                break;
1494            }
1495
1496            if (FAIL == result->m.read_result_metadata(result, conn)) {
1497                DBG_ERR("Error occurred while reading metadata");
1498                result->m.free_result(result, TRUE);
1499                result = NULL;
1500                break;
1501            }
1502
1503            result->type = MYSQLND_RES_NORMAL;
1504            result->unbuf = mysqlnd_result_unbuffered_init(result->field_count, FALSE, result->persistent);
1505            if (!result->unbuf) {
1506                /* OOM */
1507                SET_OOM_ERROR(*conn->error_info);
1508                result->m.free_result(result, TRUE);
1509                result = NULL;
1510                break;
1511            }
1512            result->unbuf->eof_reached = TRUE;
1513        } while (0);
1514        conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS);
1515    }
1516
1517    DBG_RETURN(result);
1518}
1519/* }}} */
1520
1521
1522/* {{{ mysqlnd_conn_data::list_method */
1523MYSQLND_RES *
1524MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const char * query, const char *achtung_wild, char *par1)
1525{
1526    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, list_method);
1527    char * show_query = NULL;
1528    size_t show_query_len;
1529    MYSQLND_RES * result = NULL;
1530
1531    DBG_ENTER("mysqlnd_conn_data::list_method");
1532    DBG_INF_FMT("conn=%llu query=%s wild=%u", conn->thread_id, query, achtung_wild);
1533
1534    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1535        if (par1) {
1536            if (achtung_wild) {
1537                show_query_len = mnd_sprintf(&show_query, 0, query, par1, achtung_wild);
1538            } else {
1539                show_query_len = mnd_sprintf(&show_query, 0, query, par1);
1540            }
1541        } else {
1542            if (achtung_wild) {
1543                show_query_len = mnd_sprintf(&show_query, 0, query, achtung_wild);
1544            } else {
1545                show_query_len = strlen(show_query = (char *)query);
1546            }
1547        }
1548
1549        if (PASS == conn->m->query(conn, show_query, show_query_len)) {
1550            result = conn->m->store_result(conn, MYSQLND_STORE_NO_COPY);
1551        }
1552        if (show_query != query) {
1553            mnd_sprintf_free(show_query);
1554        }
1555        conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS);
1556    }
1557    DBG_RETURN(result);
1558}
1559/* }}} */
1560
1561
1562/* {{{ mysqlnd_conn_data::errno */
1563static unsigned int
1564MYSQLND_METHOD(mysqlnd_conn_data, errno)(const MYSQLND_CONN_DATA * const conn)
1565{
1566    return conn->error_info->error_no;
1567}
1568/* }}} */
1569
1570
1571/* {{{ mysqlnd_conn_data::error */
1572static const char *
1573MYSQLND_METHOD(mysqlnd_conn_data, error)(const MYSQLND_CONN_DATA * const conn)
1574{
1575    return conn->error_info->error;
1576}
1577/* }}} */
1578
1579
1580/* {{{ mysqlnd_conn_data::sqlstate */
1581static const char *
1582MYSQLND_METHOD(mysqlnd_conn_data, sqlstate)(const MYSQLND_CONN_DATA * const conn)
1583{
1584    return conn->error_info->sqlstate[0] ? conn->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
1585}
1586/* }}} */
1587
1588
1589/* {{{ mysqlnd_old_escape_string */
1590PHPAPI  zend_ulong
1591mysqlnd_old_escape_string(char * newstr, const char * escapestr, size_t escapestr_len)
1592{
1593    DBG_ENTER("mysqlnd_old_escape_string");
1594    DBG_RETURN(mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), newstr, escapestr, escapestr_len));
1595}
1596/* }}} */
1597
1598
1599/* {{{ mysqlnd_conn_data::ssl_set */
1600static enum_func_status
1601MYSQLND_METHOD(mysqlnd_conn_data, ssl_set)(MYSQLND_CONN_DATA * const conn, const char * key, const char * const cert,
1602                                      const char * const ca, const char * const capath, const char * const cipher)
1603{
1604    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ssl_set);
1605    enum_func_status ret = FAIL;
1606    MYSQLND_NET * net = conn->net;
1607    DBG_ENTER("mysqlnd_conn_data::ssl_set");
1608
1609    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1610        ret = (PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_KEY, key) &&
1611            PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CERT, cert) &&
1612            PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CA, ca) &&
1613            PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CAPATH, capath) &&
1614            PASS == net->data->m.set_client_option(net, MYSQLND_OPT_SSL_CIPHER, cipher)) ? PASS : FAIL;
1615
1616        conn->m->local_tx_end(conn, this_func, ret);
1617    }
1618    DBG_RETURN(ret);
1619}
1620/* }}} */
1621
1622
1623/* {{{ mysqlnd_conn_data::escape_string */
1624static zend_ulong
1625MYSQLND_METHOD(mysqlnd_conn_data, escape_string)(MYSQLND_CONN_DATA * const conn, char * newstr, const char * escapestr, size_t escapestr_len)
1626{
1627    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, escape_string);
1628    zend_ulong ret = FAIL;
1629    DBG_ENTER("mysqlnd_conn_data::escape_string");
1630    DBG_INF_FMT("conn=%llu", conn->thread_id);
1631
1632    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1633        DBG_INF_FMT("server_status=%u", conn->upsert_status->server_status);
1634        if (conn->upsert_status->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
1635            ret = mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len);
1636        } else {
1637            ret = mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len);
1638        }
1639        conn->m->local_tx_end(conn, this_func, PASS);
1640    }
1641    DBG_RETURN(ret);
1642}
1643/* }}} */
1644
1645
1646/* {{{ mysqlnd_conn_data::dump_debug_info */
1647static enum_func_status
1648MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info)(MYSQLND_CONN_DATA * const conn)
1649{
1650    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, server_dump_debug_information);
1651    enum_func_status ret = FAIL;
1652    DBG_ENTER("mysqlnd_conn_data::dump_debug_info");
1653    DBG_INF_FMT("conn=%llu", conn->thread_id);
1654    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1655        ret = conn->m->simple_command(conn, COM_DEBUG, NULL, 0, PROT_EOF_PACKET, FALSE, TRUE);
1656
1657        conn->m->local_tx_end(conn, this_func, ret);
1658    }
1659
1660    DBG_RETURN(ret);
1661}
1662/* }}} */
1663
1664
1665/* {{{ mysqlnd_conn_data::select_db */
1666static enum_func_status
1667MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, unsigned int db_len)
1668{
1669    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, select_db);
1670    enum_func_status ret = FAIL;
1671
1672    DBG_ENTER("mysqlnd_conn_data::select_db");
1673    DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
1674
1675    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1676        ret = conn->m->simple_command(conn, COM_INIT_DB, (zend_uchar*) db, db_len, PROT_OK_PACKET, FALSE, TRUE);
1677        /*
1678          The server sends 0 but libmysql doesn't read it and has established
1679          a protocol of giving back -1. Thus we have to follow it :(
1680        */
1681        SET_ERROR_AFF_ROWS(conn);
1682        if (ret == PASS) {
1683            if (conn->connect_or_select_db) {
1684                mnd_pefree(conn->connect_or_select_db, conn->persistent);
1685            }
1686            conn->connect_or_select_db = mnd_pestrndup(db, db_len, conn->persistent);
1687            conn->connect_or_select_db_len = db_len;
1688            if (!conn->connect_or_select_db) {
1689                /* OOM */
1690                SET_OOM_ERROR(*conn->error_info);
1691                ret = FAIL;
1692            }
1693        }
1694        conn->m->local_tx_end(conn, this_func, ret);
1695    }
1696    DBG_RETURN(ret);
1697}
1698/* }}} */
1699
1700
1701/* {{{ mysqlnd_conn_data::ping */
1702static enum_func_status
1703MYSQLND_METHOD(mysqlnd_conn_data, ping)(MYSQLND_CONN_DATA * const conn)
1704{
1705    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, ping);
1706    enum_func_status ret = FAIL;
1707
1708    DBG_ENTER("mysqlnd_conn_data::ping");
1709    DBG_INF_FMT("conn=%llu", conn->thread_id);
1710
1711    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1712        ret = conn->m->simple_command(conn, COM_PING, NULL, 0, PROT_OK_PACKET, TRUE, TRUE);
1713        /*
1714          The server sends 0 but libmysql doesn't read it and has established
1715          a protocol of giving back -1. Thus we have to follow it :(
1716        */
1717        SET_ERROR_AFF_ROWS(conn);
1718
1719        conn->m->local_tx_end(conn, this_func, ret);
1720    }
1721    DBG_INF_FMT("ret=%u", ret);
1722    DBG_RETURN(ret);
1723}
1724/* }}} */
1725
1726
1727/* {{{ mysqlnd_conn_data::statistic */
1728static enum_func_status
1729MYSQLND_METHOD(mysqlnd_conn_data, statistic)(MYSQLND_CONN_DATA * conn, zend_string **message)
1730{
1731    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, get_server_statistics);
1732    enum_func_status ret = FAIL;
1733    MYSQLND_PACKET_STATS * stats_header;
1734
1735    DBG_ENTER("mysqlnd_conn_data::statistic");
1736    DBG_INF_FMT("conn=%llu", conn->thread_id);
1737
1738    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1739        do {
1740            ret = conn->m->simple_command(conn, COM_STATISTICS, NULL, 0, PROT_LAST, FALSE, TRUE);
1741            if (FAIL == ret) {
1742                break;
1743            }
1744            stats_header = conn->protocol->m.get_stats_packet(conn->protocol, FALSE);
1745            if (!stats_header) {
1746                SET_OOM_ERROR(*conn->error_info);
1747                break;
1748            }
1749
1750            if (PASS == (ret = PACKET_READ(stats_header, conn))) {
1751                /* will be freed by Zend, thus don't use the mnd_ allocator */
1752                *message = zend_string_init(stats_header->message, stats_header->message_len, 0);
1753                DBG_INF((*message)->val);
1754            }
1755            PACKET_FREE(stats_header);
1756        } while (0);
1757
1758        conn->m->local_tx_end(conn, this_func, ret);
1759    }
1760    DBG_RETURN(ret);
1761}
1762/* }}} */
1763
1764
1765/* {{{ mysqlnd_conn_data::kill */
1766static enum_func_status
1767MYSQLND_METHOD(mysqlnd_conn_data, kill)(MYSQLND_CONN_DATA * conn, unsigned int pid)
1768{
1769    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, kill_connection);
1770    enum_func_status ret = FAIL;
1771    zend_uchar buff[4];
1772
1773    DBG_ENTER("mysqlnd_conn_data::kill");
1774    DBG_INF_FMT("conn=%llu pid=%u", conn->thread_id, pid);
1775
1776    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1777        int4store(buff, pid);
1778
1779        /* If we kill ourselves don't expect OK packet, PROT_LAST will skip it */
1780        if (pid != conn->thread_id) {
1781            ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_OK_PACKET, FALSE, TRUE);
1782            /*
1783              The server sends 0 but libmysql doesn't read it and has established
1784              a protocol of giving back -1. Thus we have to follow it :(
1785            */
1786            SET_ERROR_AFF_ROWS(conn);
1787        } else if (PASS == (ret = conn->m->simple_command(conn, COM_PROCESS_KILL, buff, 4, PROT_LAST, FALSE, TRUE))) {
1788            CONN_SET_STATE(conn, CONN_QUIT_SENT);
1789            conn->m->send_close(conn);
1790        }
1791
1792        conn->m->local_tx_end(conn, this_func, ret);
1793    }
1794    DBG_RETURN(ret);
1795}
1796/* }}} */
1797
1798
1799/* {{{ mysqlnd_conn_data::set_charset */
1800static enum_func_status
1801MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, const char * const csname)
1802{
1803    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_charset);
1804    enum_func_status ret = FAIL;
1805    const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
1806
1807    DBG_ENTER("mysqlnd_conn_data::set_charset");
1808    DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname);
1809
1810    if (!charset) {
1811        SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
1812                         "Invalid characterset or character set not supported");
1813        DBG_RETURN(ret);
1814    }
1815
1816    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1817        char * query;
1818        size_t query_len = mnd_sprintf(&query, 0, "SET NAMES %s", csname);
1819
1820        if (FAIL == (ret = conn->m->query(conn, query, query_len))) {
1821            php_error_docref(NULL, E_WARNING, "Error executing query");
1822        } else if (conn->error_info->error_no) {
1823            ret = FAIL;
1824        } else {
1825            conn->charset = charset;
1826        }
1827        mnd_sprintf_free(query);
1828
1829        conn->m->local_tx_end(conn, this_func, ret);
1830    }
1831
1832    DBG_INF(ret == PASS? "PASS":"FAIL");
1833    DBG_RETURN(ret);
1834}
1835/* }}} */
1836
1837
1838/* {{{ mysqlnd_conn_data::refresh */
1839static enum_func_status
1840MYSQLND_METHOD(mysqlnd_conn_data, refresh)(MYSQLND_CONN_DATA * const conn, uint8_t options)
1841{
1842    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, refresh_server);
1843    enum_func_status ret = FAIL;
1844    zend_uchar bits[1];
1845    DBG_ENTER("mysqlnd_conn_data::refresh");
1846    DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
1847
1848    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1849        int1store(bits, options);
1850
1851        ret = conn->m->simple_command(conn, COM_REFRESH, bits, 1, PROT_OK_PACKET, FALSE, TRUE);
1852
1853        conn->m->local_tx_end(conn, this_func, ret);
1854    }
1855    DBG_RETURN(ret);
1856}
1857/* }}} */
1858
1859
1860/* {{{ mysqlnd_conn_data::shutdown */
1861static enum_func_status
1862MYSQLND_METHOD(mysqlnd_conn_data, shutdown)(MYSQLND_CONN_DATA * const conn, uint8_t level)
1863{
1864    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, shutdown_server);
1865    enum_func_status ret = FAIL;
1866    zend_uchar bits[1];
1867    DBG_ENTER("mysqlnd_conn_data::shutdown");
1868    DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
1869
1870    if (PASS == conn->m->local_tx_start(conn, this_func)) {
1871        int1store(bits, level);
1872
1873        ret = conn->m->simple_command(conn, COM_SHUTDOWN, bits, 1, PROT_OK_PACKET, FALSE, TRUE);
1874
1875        conn->m->local_tx_end(conn, this_func, ret);
1876    }
1877    DBG_RETURN(ret);
1878}
1879/* }}} */
1880
1881
1882/* {{{ mysqlnd_send_close */
1883static enum_func_status
1884MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn)
1885{
1886    enum_func_status ret = PASS;
1887    MYSQLND_NET * net = conn->net;
1888    php_stream * net_stream = net->data->m.get_stream(net);
1889    enum mysqlnd_connection_state state;
1890
1891    DBG_ENTER("mysqlnd_send_close");
1892    DBG_INF_FMT("conn=%llu net->data->stream->abstract=%p", conn->thread_id, net_stream? net_stream->abstract:NULL);
1893
1894    if (CONN_GET_STATE(conn) >= CONN_READY) {
1895        MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_CONNECTIONS);
1896        if (conn->persistent) {
1897            MYSQLND_DEC_CONN_STATISTIC(conn->stats, STAT_OPENED_PERSISTENT_CONNECTIONS);
1898        }
1899    }
1900    state = CONN_GET_STATE(conn);
1901    DBG_INF_FMT("state=%u", state);
1902    switch (state) {
1903        case CONN_READY:
1904            DBG_INF("Connection clean, sending COM_QUIT");
1905            if (net_stream) {
1906                ret = conn->m->simple_command(conn, COM_QUIT, NULL, 0, PROT_LAST, TRUE, TRUE);
1907                net->data->m.close_stream(net, conn->stats, conn->error_info);
1908            }
1909            CONN_SET_STATE(conn, CONN_QUIT_SENT);
1910            break;
1911        case CONN_SENDING_LOAD_DATA:
1912            /*
1913              Don't send COM_QUIT if we are in a middle of a LOAD DATA or we
1914              will crash (assert) a debug server.
1915            */
1916        case CONN_NEXT_RESULT_PENDING:
1917        case CONN_QUERY_SENT:
1918        case CONN_FETCHING_DATA:
1919            MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE);
1920            DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme);
1921            /*
1922              Do nothing, the connection will be brutally closed
1923              and the server will catch it and free close from its side.
1924            */
1925            /* Fall-through */
1926        case CONN_ALLOCED:
1927            /*
1928              Allocated but not connected or there was failure when trying
1929              to connect with pre-allocated connect.
1930
1931              Fall-through
1932            */
1933            CONN_SET_STATE(conn, CONN_QUIT_SENT);
1934            /* Fall-through */
1935        case CONN_QUIT_SENT:
1936            /* The user has killed its own connection */
1937            net->data->m.close_stream(net, conn->stats, conn->error_info);
1938            break;
1939    }
1940
1941    DBG_RETURN(ret);
1942}
1943/* }}} */
1944
1945
1946/* {{{ mysqlnd_conn_data::get_reference */
1947static MYSQLND_CONN_DATA *
1948MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference)(MYSQLND_CONN_DATA * const conn)
1949{
1950    DBG_ENTER("mysqlnd_conn_data::get_reference");
1951    ++conn->refcount;
1952    DBG_INF_FMT("conn=%llu new_refcount=%u", conn->thread_id, conn->refcount);
1953    DBG_RETURN(conn);
1954}
1955/* }}} */
1956
1957
1958/* {{{ mysqlnd_conn_data::free_reference */
1959static enum_func_status
1960MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference)(MYSQLND_CONN_DATA * const conn)
1961{
1962    enum_func_status ret = PASS;
1963    DBG_ENTER("mysqlnd_conn_data::free_reference");
1964    DBG_INF_FMT("conn=%llu old_refcount=%u", conn->thread_id, conn->refcount);
1965    if (!(--conn->refcount)) {
1966        /*
1967          No multithreading issues as we don't share the connection :)
1968          This will free the object too, of course because references has
1969          reached zero.
1970        */
1971        ret = conn->m->send_close(conn);
1972        conn->m->dtor(conn);
1973    }
1974    DBG_RETURN(ret);
1975}
1976/* }}} */
1977
1978
1979/* {{{ mysqlnd_conn_data::get_state */
1980static enum mysqlnd_connection_state
1981MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state)(const MYSQLND_CONN_DATA * const conn)
1982{
1983    DBG_ENTER("mysqlnd_conn_data::get_state");
1984    DBG_RETURN(conn->state);
1985}
1986/* }}} */
1987
1988
1989/* {{{ mysqlnd_conn_data::set_state */
1990static void
1991MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_connection_state new_state)
1992{
1993    DBG_ENTER("mysqlnd_conn_data::set_state");
1994    DBG_INF_FMT("New state=%u", new_state);
1995    conn->state = new_state;
1996    DBG_VOID_RETURN;
1997}
1998/* }}} */
1999
2000
2001/* {{{ mysqlnd_conn_data::field_count */
2002static unsigned int
2003MYSQLND_METHOD(mysqlnd_conn_data, field_count)(const MYSQLND_CONN_DATA * const conn)
2004{
2005    return conn->field_count;
2006}
2007/* }}} */
2008
2009
2010/* {{{ mysqlnd_conn_data::server_status */
2011static unsigned int
2012MYSQLND_METHOD(mysqlnd_conn_data, server_status)(const MYSQLND_CONN_DATA * const conn)
2013{
2014    return conn->upsert_status->server_status;
2015}
2016/* }}} */
2017
2018
2019/* {{{ mysqlnd_conn_data::insert_id */
2020static uint64_t
2021MYSQLND_METHOD(mysqlnd_conn_data, insert_id)(const MYSQLND_CONN_DATA * const conn)
2022{
2023    return conn->upsert_status->last_insert_id;
2024}
2025/* }}} */
2026
2027
2028/* {{{ mysqlnd_conn_data::affected_rows */
2029static uint64_t
2030MYSQLND_METHOD(mysqlnd_conn_data, affected_rows)(const MYSQLND_CONN_DATA * const conn)
2031{
2032    return conn->upsert_status->affected_rows;
2033}
2034/* }}} */
2035
2036
2037/* {{{ mysqlnd_conn_data::warning_count */
2038static unsigned int
2039MYSQLND_METHOD(mysqlnd_conn_data, warning_count)(const MYSQLND_CONN_DATA * const conn)
2040{
2041    return conn->upsert_status->warning_count;
2042}
2043/* }}} */
2044
2045
2046/* {{{ mysqlnd_conn_data::info */
2047static const char *
2048MYSQLND_METHOD(mysqlnd_conn_data, info)(const MYSQLND_CONN_DATA * const conn)
2049{
2050    return conn->last_message;
2051}
2052/* }}} */
2053
2054/* {{{ mysqlnd_get_client_info */
2055PHPAPI const char * mysqlnd_get_client_info()
2056{
2057    return PHP_MYSQLND_VERSION;
2058}
2059/* }}} */
2060
2061
2062/* {{{ mysqlnd_get_client_version */
2063PHPAPI unsigned int mysqlnd_get_client_version()
2064{
2065    return MYSQLND_VERSION_ID;
2066}
2067/* }}} */
2068
2069
2070/* {{{ mysqlnd_conn_data::get_server_info */
2071static const char *
2072MYSQLND_METHOD(mysqlnd_conn_data, get_server_info)(const MYSQLND_CONN_DATA * const conn)
2073{
2074    return conn->server_version;
2075}
2076/* }}} */
2077
2078
2079/* {{{ mysqlnd_conn_data::get_host_info */
2080static const char *
2081MYSQLND_METHOD(mysqlnd_conn_data, get_host_info)(const MYSQLND_CONN_DATA * const conn)
2082{
2083    return conn->host_info;
2084}
2085/* }}} */
2086
2087
2088/* {{{ mysqlnd_conn_data::get_proto_info */
2089static unsigned int
2090MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info)(const MYSQLND_CONN_DATA * const conn)
2091{
2092    return conn->protocol_version;
2093}
2094/* }}} */
2095
2096
2097/* {{{ mysqlnd_conn_data::charset_name */
2098static const char *
2099MYSQLND_METHOD(mysqlnd_conn_data, charset_name)(const MYSQLND_CONN_DATA * const conn)
2100{
2101    return conn->charset->name;
2102}
2103/* }}} */
2104
2105
2106/* {{{ mysqlnd_conn_data::thread_id */
2107static uint64_t
2108MYSQLND_METHOD(mysqlnd_conn_data, thread_id)(const MYSQLND_CONN_DATA * const conn)
2109{
2110    return conn->thread_id;
2111}
2112/* }}} */
2113
2114
2115/* {{{ mysqlnd_conn_data::get_server_version */
2116static zend_ulong
2117MYSQLND_METHOD(mysqlnd_conn_data, get_server_version)(const MYSQLND_CONN_DATA * const conn)
2118{
2119    zend_long major, minor, patch;
2120    char *p;
2121
2122    if (!(p = conn->server_version)) {
2123        return 0;
2124    }
2125
2126    major = ZEND_STRTOL(p, &p, 10);
2127    p += 1; /* consume the dot */
2128    minor = ZEND_STRTOL(p, &p, 10);
2129    p += 1; /* consume the dot */
2130    patch = ZEND_STRTOL(p, &p, 10);
2131
2132    return (zend_ulong)(major * Z_L(10000) + (zend_ulong)(minor * Z_L(100) + patch));
2133}
2134/* }}} */
2135
2136
2137/* {{{ mysqlnd_conn_data::more_results */
2138static zend_bool
2139MYSQLND_METHOD(mysqlnd_conn_data, more_results)(const MYSQLND_CONN_DATA * const conn)
2140{
2141    DBG_ENTER("mysqlnd_conn_data::more_results");
2142    /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
2143    DBG_RETURN(conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
2144}
2145/* }}} */
2146
2147
2148/* {{{ mysqlnd_conn_data::next_result */
2149static enum_func_status
2150MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn)
2151{
2152    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, next_result);
2153    enum_func_status ret = FAIL;
2154
2155    DBG_ENTER("mysqlnd_conn_data::next_result");
2156    DBG_INF_FMT("conn=%llu", conn->thread_id);
2157
2158    if (PASS == conn->m->local_tx_start(conn, this_func)) {
2159        do {
2160            if (CONN_GET_STATE(conn) != CONN_NEXT_RESULT_PENDING) {
2161                break;
2162            }
2163
2164            SET_EMPTY_ERROR(*conn->error_info);
2165            SET_ERROR_AFF_ROWS(conn);
2166            /*
2167              We are sure that there is a result set, since conn->state is set accordingly
2168              in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered()
2169            */
2170            if (FAIL == (ret = conn->m->query_read_result_set_header(conn, NULL))) {
2171                /*
2172                  There can be an error in the middle of a multi-statement, which will cancel the multi-statement.
2173                  So there are no more results and we should just return FALSE, error_no has been set
2174                */
2175                if (!conn->error_info->error_no) {
2176                    DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__);
2177                    php_error_docref(NULL, E_WARNING, "Serious error. PID=%d", getpid());
2178                    CONN_SET_STATE(conn, CONN_QUIT_SENT);
2179                    conn->m->send_close(conn);
2180                } else {
2181                    DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info->error_no, conn->error_info->error);
2182                }
2183                break;
2184            }
2185            if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status->affected_rows) {
2186                MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status->affected_rows);
2187            }
2188        } while (0);
2189        conn->m->local_tx_end(conn, this_func, ret);
2190    }
2191
2192    DBG_RETURN(ret);
2193}
2194/* }}} */
2195
2196
2197/* {{{ mysqlnd_field_type_name */
2198PHPAPI const char *mysqlnd_field_type_name(enum mysqlnd_field_types field_type)
2199{
2200    switch(field_type) {
2201        case FIELD_TYPE_STRING:
2202        case FIELD_TYPE_VAR_STRING:
2203            return "string";
2204        case FIELD_TYPE_TINY:
2205        case FIELD_TYPE_SHORT:
2206        case FIELD_TYPE_LONG:
2207        case FIELD_TYPE_LONGLONG:
2208        case FIELD_TYPE_INT24:
2209            return "int";
2210        case FIELD_TYPE_FLOAT:
2211        case FIELD_TYPE_DOUBLE:
2212        case FIELD_TYPE_DECIMAL:
2213        case FIELD_TYPE_NEWDECIMAL:
2214            return "real";
2215        case FIELD_TYPE_TIMESTAMP:
2216            return "timestamp";
2217        case FIELD_TYPE_YEAR:
2218            return "year";
2219        case FIELD_TYPE_DATE:
2220        case FIELD_TYPE_NEWDATE:
2221            return "date";
2222        case FIELD_TYPE_TIME:
2223            return "time";
2224        case FIELD_TYPE_SET:
2225            return "set";
2226        case FIELD_TYPE_ENUM:
2227            return "enum";
2228        case FIELD_TYPE_GEOMETRY:
2229            return "geometry";
2230        case FIELD_TYPE_DATETIME:
2231            return "datetime";
2232        case FIELD_TYPE_TINY_BLOB:
2233        case FIELD_TYPE_MEDIUM_BLOB:
2234        case FIELD_TYPE_LONG_BLOB:
2235        case FIELD_TYPE_BLOB:
2236            return "blob";
2237        case FIELD_TYPE_NULL:
2238            return "null";
2239        case FIELD_TYPE_BIT:
2240            return "bit";
2241        default:
2242            return "unknown";
2243    }
2244}
2245/* }}} */
2246
2247
2248/* {{{ mysqlnd_conn_data::change_user */
2249static enum_func_status
2250MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
2251                                          const char * user,
2252                                          const char * passwd,
2253                                          const char * db,
2254                                          zend_bool silent,
2255                                          size_t passwd_len
2256                                         )
2257{
2258    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, change_user);
2259    enum_func_status ret = FAIL;
2260
2261    DBG_ENTER("mysqlnd_conn_data::change_user");
2262    DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%u",
2263                conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 );
2264
2265    if (PASS != conn->m->local_tx_start(conn, this_func)) {
2266        goto end;
2267    }
2268
2269    SET_EMPTY_ERROR(*conn->error_info);
2270    SET_ERROR_AFF_ROWS(conn);
2271
2272    if (!user) {
2273        user = "";
2274    }
2275    if (!passwd) {
2276        passwd = "";
2277    }
2278    if (!db) {
2279        db = "";
2280    }
2281
2282    /* XXX: passwords that have \0 inside work during auth, but in this case won't work with change user */
2283    ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, strlen(db),
2284                                    conn->auth_plugin_data, conn->auth_plugin_data_len, conn->options->auth_protocol,
2285                                    0 /*charset not used*/, conn->options, conn->server_capabilities, silent, TRUE/*is_change*/);
2286
2287    /*
2288      Here we should close all statements. Unbuffered queries should not be a
2289      problem as we won't allow sending COM_CHANGE_USER.
2290    */
2291    conn->m->local_tx_end(conn, this_func, ret);
2292end:
2293    DBG_INF(ret == PASS? "PASS":"FAIL");
2294    DBG_RETURN(ret);
2295}
2296/* }}} */
2297
2298
2299/* {{{ mysqlnd_conn_data::set_client_option */
2300static enum_func_status
2301MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const conn,
2302                                                enum mysqlnd_option option,
2303                                                const char * const value
2304                                                )
2305{
2306    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option);
2307    enum_func_status ret = PASS;
2308    DBG_ENTER("mysqlnd_conn_data::set_client_option");
2309    DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
2310
2311    if (PASS != conn->m->local_tx_start(conn, this_func)) {
2312        goto end;
2313    }
2314    switch (option) {
2315        case MYSQL_OPT_COMPRESS:
2316#ifdef WHEN_SUPPORTED_BY_MYSQLI
2317        case MYSQL_OPT_READ_TIMEOUT:
2318        case MYSQL_OPT_WRITE_TIMEOUT:
2319#endif
2320        case MYSQLND_OPT_SSL_KEY:
2321        case MYSQLND_OPT_SSL_CERT:
2322        case MYSQLND_OPT_SSL_CA:
2323        case MYSQLND_OPT_SSL_CAPATH:
2324        case MYSQLND_OPT_SSL_CIPHER:
2325        case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
2326        case MYSQL_OPT_CONNECT_TIMEOUT:
2327        case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
2328        case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
2329        case MYSQL_SERVER_PUBLIC_KEY:
2330            ret = conn->net->data->m.set_client_option(conn->net, option, value);
2331            break;
2332#ifdef MYSQLND_STRING_TO_INT_CONVERSION
2333        case MYSQLND_OPT_INT_AND_FLOAT_NATIVE:
2334            conn->options->int_and_float_native = *(unsigned int*) value;
2335            break;
2336#endif
2337        case MYSQL_OPT_LOCAL_INFILE:
2338            if (value && (*(unsigned int*) value) ? 1 : 0) {
2339                conn->options->flags |= CLIENT_LOCAL_FILES;
2340            } else {
2341                conn->options->flags &= ~CLIENT_LOCAL_FILES;
2342            }
2343            break;
2344        case MYSQL_INIT_COMMAND:
2345        {
2346            char ** new_init_commands;
2347            char * new_command;
2348            /* when num_commands is 0, then realloc will be effectively a malloc call, internally */
2349            /* Don't assign to conn->options->init_commands because in case of OOM we will lose the pointer and leak */
2350            new_init_commands = mnd_perealloc(conn->options->init_commands, sizeof(char *) * (conn->options->num_commands + 1), conn->persistent);
2351            if (!new_init_commands) {
2352                goto oom;
2353            }
2354            conn->options->init_commands = new_init_commands;
2355            new_command = mnd_pestrdup(value, conn->persistent);
2356            if (!new_command) {
2357                goto oom;
2358            }
2359            conn->options->init_commands[conn->options->num_commands] = new_command;
2360            ++conn->options->num_commands;
2361            break;
2362        }
2363        case MYSQL_READ_DEFAULT_FILE:
2364        case MYSQL_READ_DEFAULT_GROUP:
2365#ifdef WHEN_SUPPORTED_BY_MYSQLI
2366        case MYSQL_SET_CLIENT_IP:
2367        case MYSQL_REPORT_DATA_TRUNCATION:
2368#endif
2369            /* currently not supported. Todo!! */
2370            break;
2371        case MYSQL_SET_CHARSET_NAME:
2372        {
2373            char * new_charset_name;
2374            if (!mysqlnd_find_charset_name(value)) {
2375                SET_CLIENT_ERROR(*conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Unknown character set");
2376                ret = FAIL;
2377                break;
2378            }
2379
2380            new_charset_name = mnd_pestrdup(value, conn->persistent);
2381            if (!new_charset_name) {
2382                goto oom;
2383            }
2384            if (conn->options->charset_name) {
2385                mnd_pefree(conn->options->charset_name, conn->persistent);
2386            }
2387            conn->options->charset_name = new_charset_name;
2388            DBG_INF_FMT("charset=%s", conn->options->charset_name);
2389            break;
2390        }
2391        case MYSQL_OPT_NAMED_PIPE:
2392            conn->options->protocol = MYSQL_PROTOCOL_PIPE;
2393            break;
2394        case MYSQL_OPT_PROTOCOL:
2395            if (*(unsigned int*) value < MYSQL_PROTOCOL_LAST) {
2396                conn->options->protocol = *(unsigned int*) value;
2397            }
2398            break;
2399#ifdef WHEN_SUPPORTED_BY_MYSQLI
2400        case MYSQL_SET_CHARSET_DIR:
2401        case MYSQL_OPT_RECONNECT:
2402            /* we don't need external character sets, all character sets are
2403               compiled in. For compatibility we just ignore this setting.
2404               Same for protocol, we don't support old protocol */
2405        case MYSQL_OPT_USE_REMOTE_CONNECTION:
2406        case MYSQL_OPT_USE_EMBEDDED_CONNECTION:
2407        case MYSQL_OPT_GUESS_CONNECTION:
2408            /* todo: throw an error, we don't support embedded */
2409            break;
2410#endif
2411        case MYSQLND_OPT_MAX_ALLOWED_PACKET:
2412            if (*(unsigned int*) value > (1<<16)) {
2413                conn->options->max_allowed_packet = *(unsigned int*) value;
2414            }
2415            break;
2416        case MYSQLND_OPT_AUTH_PROTOCOL:
2417        {
2418            char * new_auth_protocol = value? mnd_pestrdup(value, conn->persistent) : NULL;
2419            if (value && !new_auth_protocol) {
2420                goto oom;
2421            }
2422            if (conn->options->auth_protocol) {
2423                mnd_pefree(conn->options->auth_protocol, conn->persistent);
2424            }
2425            conn->options->auth_protocol = new_auth_protocol;
2426            DBG_INF_FMT("auth_protocol=%s", conn->options->auth_protocol);
2427            break;
2428        }
2429        case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS:
2430            if (value && (*(unsigned int*) value) ? 1 : 0) {
2431                conn->options->flags |= CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
2432            } else {
2433                conn->options->flags &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
2434            }
2435            break;
2436        case MYSQL_OPT_CONNECT_ATTR_RESET:
2437            if (conn->options->connect_attr) {
2438                DBG_INF_FMT("Before reset %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
2439                zend_hash_clean(conn->options->connect_attr);
2440            }
2441            break;
2442        case MYSQL_OPT_CONNECT_ATTR_DELETE:
2443            if (conn->options->connect_attr && value) {
2444                DBG_INF_FMT("Before delete %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
2445                zend_hash_str_del(conn->options->connect_attr, value, strlen(value));
2446                DBG_INF_FMT("%d left", zend_hash_num_elements(conn->options->connect_attr));
2447            }
2448            break;
2449#ifdef WHEN_SUPPORTED_BY_MYSQLI
2450        case MYSQL_SHARED_MEMORY_BASE_NAME:
2451        case MYSQL_OPT_USE_RESULT:
2452        case MYSQL_SECURE_AUTH:
2453            /* not sure, todo ? */
2454#endif
2455        default:
2456            ret = FAIL;
2457    }
2458    conn->m->local_tx_end(conn, this_func, ret);
2459    DBG_RETURN(ret);
2460oom:
2461    SET_OOM_ERROR(*conn->error_info);
2462    conn->m->local_tx_end(conn, this_func, FAIL);
2463end:
2464    DBG_RETURN(FAIL);
2465}
2466/* }}} */
2467
2468
2469/* {{{ mysqlnd_conn_data::set_client_option_2d */
2470static enum_func_status
2471MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * const conn,
2472                                                        enum mysqlnd_option option,
2473                                                        const char * const key,
2474                                                        const char * const value
2475                                                        )
2476{
2477    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option_2d);
2478    enum_func_status ret = PASS;
2479    DBG_ENTER("mysqlnd_conn_data::set_client_option_2d");
2480    DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
2481
2482    if (PASS != conn->m->local_tx_start(conn, this_func)) {
2483        goto end;
2484    }
2485    switch (option) {
2486        case MYSQL_OPT_CONNECT_ATTR_ADD:
2487            if (!conn->options->connect_attr) {
2488                DBG_INF("Initializing connect_attr hash");
2489                conn->options->connect_attr = mnd_pemalloc(sizeof(HashTable), conn->persistent);
2490                if (!conn->options->connect_attr) {
2491                    goto oom;
2492                }
2493                zend_hash_init(conn->options->connect_attr, 0, NULL, ZVAL_PTR_DTOR, conn->persistent);
2494            }
2495            DBG_INF_FMT("Adding [%s][%s]", key, value);
2496            {
2497                zval attrz;
2498                ZVAL_NEW_STR(&attrz, zend_string_init(value, strlen(value), 1));
2499                zend_hash_str_update(conn->options->connect_attr, key, strlen(key), &attrz);
2500            }
2501            break;
2502        default:
2503            ret = FAIL;
2504    }
2505    conn->m->local_tx_end(conn, this_func, ret);
2506    DBG_RETURN(ret);
2507oom:
2508    SET_OOM_ERROR(*conn->error_info);
2509    conn->m->local_tx_end(conn, this_func, FAIL);
2510end:
2511    DBG_RETURN(FAIL);
2512}
2513/* }}} */
2514
2515
2516/* {{{ mysqlnd_conn_data::use_result */
2517static MYSQLND_RES *
2518MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
2519{
2520    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, use_result);
2521    MYSQLND_RES * result = NULL;
2522
2523    DBG_ENTER("mysqlnd_conn_data::use_result");
2524    DBG_INF_FMT("conn=%llu", conn->thread_id);
2525
2526    if (PASS == conn->m->local_tx_start(conn, this_func)) {
2527        do {
2528            if (!conn->current_result) {
2529                break;
2530            }
2531
2532            /* Nothing to store for UPSERT/LOAD DATA */
2533            if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
2534                SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
2535                DBG_ERR("Command out of sync");
2536                break;
2537            }
2538
2539            MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS);
2540
2541            conn->current_result->conn = conn->m->get_reference(conn);
2542            result = conn->current_result->m.use_result(conn->current_result, FALSE);
2543
2544            if (!result) {
2545                conn->current_result->m.free_result(conn->current_result, TRUE);
2546            }
2547            conn->current_result = NULL;
2548        } while (0);
2549
2550        conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS);
2551    }
2552
2553    DBG_RETURN(result);
2554}
2555/* }}} */
2556
2557
2558/* {{{ mysqlnd_conn_data::store_result */
2559static MYSQLND_RES *
2560MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
2561{
2562    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, store_result);
2563    MYSQLND_RES * result = NULL;
2564
2565    DBG_ENTER("mysqlnd_conn_data::store_result");
2566    DBG_INF_FMT("conn=%llu conn=%p", conn->thread_id, conn);
2567
2568    if (PASS == conn->m->local_tx_start(conn, this_func)) {
2569        do {
2570            unsigned int f = flags;
2571            if (!conn->current_result) {
2572                break;
2573            }
2574
2575            /* Nothing to store for UPSERT/LOAD DATA*/
2576            if (conn->last_query_type != QUERY_SELECT || CONN_GET_STATE(conn) != CONN_FETCHING_DATA) {
2577                SET_CLIENT_ERROR(*conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
2578                DBG_ERR("Command out of sync");
2579                break;
2580            }
2581
2582            MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
2583
2584            /* overwrite */
2585            if ((conn->m->get_client_api_capabilities(conn) & MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA)) {
2586                if (MYSQLND_G(fetch_data_copy)) {
2587                    f &= ~MYSQLND_STORE_NO_COPY;
2588                    f |= MYSQLND_STORE_COPY;
2589                }
2590            } else {
2591                /* if for some reason PDO borks something */
2592                if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
2593                    f |= MYSQLND_STORE_COPY;
2594                }
2595            }
2596            if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
2597                SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Unknown fetch mode");
2598                DBG_ERR("Unknown fetch mode");
2599                break;
2600            }
2601            result = conn->current_result->m.store_result(conn->current_result, conn, f);
2602            if (!result) {
2603                conn->current_result->m.free_result(conn->current_result, TRUE);
2604            }
2605            conn->current_result = NULL;
2606        } while (0);
2607
2608        conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS);
2609    }
2610    DBG_RETURN(result);
2611}
2612/* }}} */
2613
2614
2615/* {{{ mysqlnd_conn_data::get_connection_stats */
2616static void
2617MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats)(const MYSQLND_CONN_DATA * const conn,
2618                                                   zval * return_value ZEND_FILE_LINE_DC)
2619{
2620    DBG_ENTER("mysqlnd_conn_data::get_connection_stats");
2621    mysqlnd_fill_stats_hash(conn->stats, mysqlnd_stats_values_names, return_value ZEND_FILE_LINE_CC);
2622    DBG_VOID_RETURN;
2623}
2624/* }}} */
2625
2626
2627/* {{{ mysqlnd_conn_data::set_autocommit */
2628static enum_func_status
2629MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit)(MYSQLND_CONN_DATA * conn, unsigned int mode)
2630{
2631    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_autocommit);
2632    enum_func_status ret = FAIL;
2633    DBG_ENTER("mysqlnd_conn_data::set_autocommit");
2634
2635    if (PASS == conn->m->local_tx_start(conn, this_func)) {
2636        ret = conn->m->query(conn, (mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", sizeof("SET AUTOCOMMIT=1") - 1);
2637        conn->m->local_tx_end(conn, this_func, ret);
2638    }
2639
2640    DBG_RETURN(ret);
2641}
2642/* }}} */
2643
2644
2645/* {{{ mysqlnd_conn_data::tx_commit */
2646static enum_func_status
2647MYSQLND_METHOD(mysqlnd_conn_data, tx_commit)(MYSQLND_CONN_DATA * conn)
2648{
2649    return conn->m->tx_commit_or_rollback(conn, TRUE, TRANS_COR_NO_OPT, NULL);
2650}
2651/* }}} */
2652
2653
2654/* {{{ mysqlnd_conn_data::tx_rollback */
2655static enum_func_status
2656MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback)(MYSQLND_CONN_DATA * conn)
2657{
2658    return conn->m->tx_commit_or_rollback(conn, FALSE, TRANS_COR_NO_OPT, NULL);
2659}
2660/* }}} */
2661
2662
2663/* {{{ mysqlnd_tx_cor_options_to_string */
2664static void
2665MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string)(const MYSQLND_CONN_DATA * const conn, smart_str * str, const unsigned int mode)
2666{
2667    if (mode & TRANS_COR_AND_CHAIN && !(mode & TRANS_COR_AND_NO_CHAIN)) {
2668        if (str->s && str->s->len) {
2669            smart_str_appendl(str, " ", sizeof(" ") - 1);
2670        }
2671        smart_str_appendl(str, "AND CHAIN", sizeof("AND CHAIN") - 1);
2672    } else if (mode & TRANS_COR_AND_NO_CHAIN && !(mode & TRANS_COR_AND_CHAIN)) {
2673        if (str->s && str->s->len) {
2674            smart_str_appendl(str, " ", sizeof(" ") - 1);
2675        }
2676        smart_str_appendl(str, "AND NO CHAIN", sizeof("AND NO CHAIN") - 1);
2677    }
2678
2679    if (mode & TRANS_COR_RELEASE && !(mode & TRANS_COR_NO_RELEASE)) {
2680        if (str->s && str->s->len) {
2681            smart_str_appendl(str, " ", sizeof(" ") - 1);
2682        }
2683        smart_str_appendl(str, "RELEASE", sizeof("RELEASE") - 1);
2684    } else if (mode & TRANS_COR_NO_RELEASE && !(mode & TRANS_COR_RELEASE)) {
2685        if (str->s && str->s->len) {
2686            smart_str_appendl(str, " ", sizeof(" ") - 1);
2687        }
2688        smart_str_appendl(str, "NO RELEASE", sizeof("NO RELEASE") - 1);
2689    }
2690    smart_str_0(str);
2691}
2692/* }}} */
2693
2694
2695/* {{{ mysqlnd_escape_string_for_tx_name_in_comment */
2696static char *
2697mysqlnd_escape_string_for_tx_name_in_comment(const char * const name)
2698{
2699    char * ret = NULL;
2700    DBG_ENTER("mysqlnd_escape_string_for_tx_name_in_comment");
2701    if (name) {
2702        zend_bool warned = FALSE;
2703        const char * p_orig = name;
2704        char * p_copy;
2705        p_copy = ret = mnd_emalloc(strlen(name) + 1 + 2 + 2 + 1); /* space, open, close, NullS */
2706        *p_copy++ = ' ';
2707        *p_copy++ = '/';
2708        *p_copy++ = '*';
2709        while (1) {
2710            register char v = *p_orig;
2711            if (v == 0) {
2712                break;
2713            }
2714            if ((v >= '0' && v <= '9') ||
2715                (v >= 'a' && v <= 'z') ||
2716                (v >= 'A' && v <= 'Z') ||
2717                v == '-' ||
2718                v == '_' ||
2719                v == ' ' ||
2720                v == '=')
2721            {
2722                *p_copy++ = v;
2723            } else if (warned == FALSE) {
2724                php_error_docref(NULL, E_WARNING, "Transaction name truncated. Must be only [0-9A-Za-z\\-_=]+");
2725                warned = TRUE;
2726            }
2727            ++p_orig;
2728        }
2729        *p_copy++ = '*';
2730        *p_copy++ = '/';
2731        *p_copy++ = 0;
2732    }
2733    DBG_RETURN(ret);
2734}
2735/* }}} */
2736
2737
2738/* {{{ mysqlnd_conn_data::tx_commit_ex */
2739static enum_func_status
2740MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * conn, const zend_bool commit, const unsigned int flags, const char * const name)
2741{
2742    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_commit_or_rollback);
2743    enum_func_status ret = FAIL;
2744    DBG_ENTER("mysqlnd_conn_data::tx_commit_or_rollback");
2745
2746    if (PASS == conn->m->local_tx_start(conn, this_func)) {
2747        do {
2748            smart_str tmp_str = {0, 0};
2749            conn->m->tx_cor_options_to_string(conn, &tmp_str, flags);
2750            smart_str_0(&tmp_str);
2751
2752
2753            {
2754                char * query;
2755                size_t query_len;
2756                char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name);
2757
2758                query_len = mnd_sprintf(&query, 0, (commit? "COMMIT%s %s":"ROLLBACK%s %s"),
2759                                        name_esc? name_esc:"", tmp_str.s? tmp_str.s->val:"");
2760                smart_str_free(&tmp_str);
2761                if (name_esc) {
2762                    mnd_efree(name_esc);
2763                    name_esc = NULL;
2764                }
2765                if (!query) {
2766                    SET_OOM_ERROR(*conn->error_info);
2767                    break;
2768                }
2769
2770                ret = conn->m->query(conn, query, query_len);
2771                mnd_sprintf_free(query);
2772            }
2773        } while (0);
2774        conn->m->local_tx_end(conn, this_func, ret);
2775    }
2776
2777    DBG_RETURN(ret);
2778}
2779/* }}} */
2780
2781
2782/* {{{ mysqlnd_conn_data::tx_begin */
2783static enum_func_status
2784MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsigned int mode, const char * const name)
2785{
2786    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_begin);
2787    enum_func_status ret = FAIL;
2788    DBG_ENTER("mysqlnd_conn_data::tx_begin");
2789
2790    if (PASS == conn->m->local_tx_start(conn, this_func)) {
2791        do {
2792            smart_str tmp_str = {0, 0};
2793            if (mode & TRANS_START_WITH_CONSISTENT_SNAPSHOT) {
2794                if (tmp_str.s) {
2795                    smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
2796                }
2797                smart_str_appendl(&tmp_str, "WITH CONSISTENT SNAPSHOT", sizeof("WITH CONSISTENT SNAPSHOT") - 1);
2798            }
2799            if (mode & (TRANS_START_READ_WRITE | TRANS_START_READ_ONLY)) {
2800                zend_ulong server_version = conn->m->get_server_version(conn);
2801                if (server_version < 50605L) {
2802                    php_error_docref(NULL, E_WARNING, "This server version doesn't support 'READ WRITE' and 'READ ONLY'. Minimum 5.6.5 is required");
2803                    smart_str_free(&tmp_str);
2804                    break;
2805                } else if (mode & TRANS_START_READ_WRITE) {
2806                    if (tmp_str.s && tmp_str.s->len) {
2807                        smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
2808                    }
2809                    smart_str_appendl(&tmp_str, "READ WRITE", sizeof("READ WRITE") - 1);
2810                } else if (mode & TRANS_START_READ_ONLY) {
2811                    if (tmp_str.s && tmp_str.s->len) {
2812                        smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
2813                    }
2814                    smart_str_appendl(&tmp_str, "READ ONLY", sizeof("READ ONLY") - 1);
2815                }
2816            }
2817            smart_str_0(&tmp_str);
2818
2819            {
2820                char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name);
2821                char * query;
2822                unsigned int query_len = mnd_sprintf(&query, 0, "START TRANSACTION%s %s", name_esc? name_esc:"", tmp_str.s? tmp_str.s->val:"");
2823                smart_str_free(&tmp_str);
2824                if (name_esc) {
2825                    mnd_efree(name_esc);
2826                    name_esc = NULL;
2827                }
2828                if (!query) {
2829                    SET_OOM_ERROR(*conn->error_info);
2830                    break;
2831                }
2832                ret = conn->m->query(conn, query, query_len);
2833                mnd_sprintf_free(query);
2834            }
2835        } while (0);
2836        conn->m->local_tx_end(conn, this_func, ret);
2837    }
2838
2839    DBG_RETURN(ret);
2840}
2841/* }}} */
2842
2843
2844/* {{{ mysqlnd_conn_data::tx_savepoint */
2845static enum_func_status
2846MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint)(MYSQLND_CONN_DATA * conn, const char * const name)
2847{
2848    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_savepoint);
2849    enum_func_status ret = FAIL;
2850    DBG_ENTER("mysqlnd_conn_data::tx_savepoint");
2851
2852    if (PASS == conn->m->local_tx_start(conn, this_func)) {
2853        do {
2854            char * query;
2855            unsigned int query_len;
2856            if (!name) {
2857                SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
2858                break;
2859            }
2860            query_len = mnd_sprintf(&query, 0, "SAVEPOINT `%s`", name);
2861            if (!query) {
2862                SET_OOM_ERROR(*conn->error_info);
2863                break;
2864            }
2865            ret = conn->m->query(conn, query, query_len);
2866            mnd_sprintf_free(query);
2867        } while (0);
2868        conn->m->local_tx_end(conn, this_func, ret);
2869    }
2870
2871    DBG_RETURN(ret);
2872}
2873/* }}} */
2874
2875
2876/* {{{ mysqlnd_conn_data::tx_savepoint_release */
2877static enum_func_status
2878MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn, const char * const name)
2879{
2880    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, tx_savepoint_release);
2881    enum_func_status ret = FAIL;
2882    DBG_ENTER("mysqlnd_conn_data::tx_savepoint_release");
2883
2884    if (PASS == conn->m->local_tx_start(conn, this_func)) {
2885        do {
2886            char * query;
2887            unsigned int query_len;
2888            if (!name) {
2889                SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
2890                break;
2891            }
2892            query_len = mnd_sprintf(&query, 0, "RELEASE SAVEPOINT `%s`", name);
2893            if (!query) {
2894                SET_OOM_ERROR(*conn->error_info);
2895                break;
2896            }
2897            ret = conn->m->query(conn, query, query_len);
2898            mnd_sprintf_free(query);
2899        } while (0);
2900        conn->m->local_tx_end(conn, this_func, ret);
2901    }
2902
2903    DBG_RETURN(ret);
2904}
2905/* }}} */
2906
2907
2908/* {{{ mysqlnd_conn_data::negotiate_client_api_capabilities */
2909static unsigned int
2910MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
2911{
2912    unsigned int ret = 0;
2913    DBG_ENTER("mysqlnd_conn_data::negotiate_client_api_capabilities");
2914    if (conn) {
2915        ret = conn->client_api_capabilities;
2916        conn->client_api_capabilities = flags;
2917    }
2918
2919    DBG_RETURN(ret);
2920}
2921/* }}} */
2922
2923
2924/* {{{ mysqlnd_conn_data::get_client_api_capabilities */
2925static unsigned int
2926MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn)
2927{
2928    DBG_ENTER("mysqlnd_conn_data::get_client_api_capabilities");
2929    DBG_RETURN(conn? conn->client_api_capabilities : 0);
2930}
2931/* }}} */
2932
2933
2934/* {{{ mysqlnd_conn_data::local_tx_start */
2935static enum_func_status
2936MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, size_t this_func)
2937{
2938    enum_func_status ret = PASS;
2939    DBG_ENTER("mysqlnd_conn_data::local_tx_start");
2940    DBG_RETURN(ret);
2941}
2942/* }}} */
2943
2944
2945/* {{{ mysqlnd_conn_data::local_tx_end */
2946static enum_func_status
2947MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, size_t this_func, enum_func_status status)
2948{
2949    DBG_ENTER("mysqlnd_conn_data::local_tx_end");
2950    DBG_RETURN(status);
2951}
2952/* }}} */
2953
2954
2955/* {{{ mysqlnd_conn_data::init */
2956static enum_func_status
2957MYSQLND_METHOD(mysqlnd_conn_data, init)(MYSQLND_CONN_DATA * conn)
2958{
2959    DBG_ENTER("mysqlnd_conn_data::init");
2960    mysqlnd_stats_init(&conn->stats, STAT_LAST);
2961    SET_ERROR_AFF_ROWS(conn);
2962
2963    conn->net = mysqlnd_net_init(conn->persistent, conn->stats, conn->error_info);
2964    conn->protocol = mysqlnd_protocol_init(conn->persistent);
2965
2966    DBG_RETURN(conn->stats && conn->net && conn->protocol? PASS:FAIL);
2967}
2968/* }}} */
2969
2970
2971MYSQLND_STMT * _mysqlnd_stmt_init(MYSQLND_CONN_DATA * const conn);
2972
2973
2974MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
2975    MYSQLND_METHOD(mysqlnd_conn_data, init),
2976    MYSQLND_METHOD(mysqlnd_conn_data, connect),
2977
2978    MYSQLND_METHOD(mysqlnd_conn_data, escape_string),
2979    MYSQLND_METHOD(mysqlnd_conn_data, set_charset),
2980    MYSQLND_METHOD(mysqlnd_conn_data, query),
2981    MYSQLND_METHOD(mysqlnd_conn_data, send_query),
2982    MYSQLND_METHOD(mysqlnd_conn_data, reap_query),
2983    MYSQLND_METHOD(mysqlnd_conn_data, use_result),
2984    MYSQLND_METHOD(mysqlnd_conn_data, store_result),
2985    MYSQLND_METHOD(mysqlnd_conn_data, next_result),
2986    MYSQLND_METHOD(mysqlnd_conn_data, more_results),
2987
2988    _mysqlnd_stmt_init,
2989
2990    MYSQLND_METHOD(mysqlnd_conn_data, shutdown),
2991    MYSQLND_METHOD(mysqlnd_conn_data, refresh),
2992
2993    MYSQLND_METHOD(mysqlnd_conn_data, ping),
2994    MYSQLND_METHOD(mysqlnd_conn_data, kill),
2995    MYSQLND_METHOD(mysqlnd_conn_data, select_db),
2996    MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info),
2997    MYSQLND_METHOD(mysqlnd_conn_data, change_user),
2998
2999    MYSQLND_METHOD(mysqlnd_conn_data, errno),
3000    MYSQLND_METHOD(mysqlnd_conn_data, error),
3001    MYSQLND_METHOD(mysqlnd_conn_data, sqlstate),
3002    MYSQLND_METHOD(mysqlnd_conn_data, thread_id),
3003
3004    MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats),
3005
3006    MYSQLND_METHOD(mysqlnd_conn_data, get_server_version),
3007    MYSQLND_METHOD(mysqlnd_conn_data, get_server_info),
3008    MYSQLND_METHOD(mysqlnd_conn_data, statistic),
3009    MYSQLND_METHOD(mysqlnd_conn_data, get_host_info),
3010    MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info),
3011    MYSQLND_METHOD(mysqlnd_conn_data, info),
3012    MYSQLND_METHOD(mysqlnd_conn_data, charset_name),
3013    MYSQLND_METHOD(mysqlnd_conn_data, list_fields),
3014    MYSQLND_METHOD(mysqlnd_conn_data, list_method),
3015
3016    MYSQLND_METHOD(mysqlnd_conn_data, insert_id),
3017    MYSQLND_METHOD(mysqlnd_conn_data, affected_rows),
3018    MYSQLND_METHOD(mysqlnd_conn_data, warning_count),
3019    MYSQLND_METHOD(mysqlnd_conn_data, field_count),
3020
3021    MYSQLND_METHOD(mysqlnd_conn_data, server_status),
3022
3023    MYSQLND_METHOD(mysqlnd_conn_data, set_server_option),
3024    MYSQLND_METHOD(mysqlnd_conn_data, set_client_option),
3025    MYSQLND_METHOD(mysqlnd_conn_data, free_contents),
3026    MYSQLND_METHOD(mysqlnd_conn_data, free_options),
3027
3028    MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor),
3029
3030    mysqlnd_query_read_result_set_header,
3031
3032    MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference),
3033    MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference),
3034    MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_state),
3035    MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, set_state),
3036
3037    MYSQLND_METHOD(mysqlnd_conn_data, simple_command),
3038    MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response),
3039    MYSQLND_METHOD(mysqlnd_conn_data, restart_psession),
3040    MYSQLND_METHOD(mysqlnd_conn_data, end_psession),
3041    MYSQLND_METHOD(mysqlnd_conn_data, send_close),
3042
3043    MYSQLND_METHOD(mysqlnd_conn_data, ssl_set),
3044    mysqlnd_result_init,
3045    MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit),
3046    MYSQLND_METHOD(mysqlnd_conn_data, tx_commit),
3047    MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback),
3048    MYSQLND_METHOD(mysqlnd_conn_data, tx_begin),
3049    MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback),
3050    MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string),
3051    MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint),
3052    MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release),
3053
3054    MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start),
3055    MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end),
3056    MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands),
3057    MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags),
3058    MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake),
3059    MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request),
3060    MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name),
3061
3062    MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d),
3063
3064    MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities),
3065    MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)
3066MYSQLND_CLASS_METHODS_END;
3067
3068
3069/* {{{ mysqlnd_conn::get_reference */
3070static MYSQLND *
3071MYSQLND_METHOD(mysqlnd_conn, clone_object)(MYSQLND * const conn)
3072{
3073    MYSQLND * ret;
3074    DBG_ENTER("mysqlnd_conn::get_reference");
3075    ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).clone_connection_object(conn);
3076    DBG_RETURN(ret);
3077}
3078/* }}} */
3079
3080
3081/* {{{ mysqlnd_conn_data::dtor */
3082static void
3083MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn)
3084{
3085    DBG_ENTER("mysqlnd_conn::dtor");
3086    DBG_INF_FMT("conn=%llu", conn->data->thread_id);
3087
3088    conn->data->m->free_reference(conn->data);
3089
3090    mnd_pefree(conn, conn->persistent);
3091
3092    DBG_VOID_RETURN;
3093}
3094/* }}} */
3095
3096
3097/* {{{ mysqlnd_conn_data::close */
3098static enum_func_status
3099MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, enum_connection_close_type close_type)
3100{
3101    size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_methods, close);
3102    MYSQLND_CONN_DATA * conn = conn_handle->data;
3103    enum_func_status ret = FAIL;
3104
3105    DBG_ENTER("mysqlnd_conn::close");
3106    DBG_INF_FMT("conn=%llu", conn->thread_id);
3107
3108    if (PASS == conn->m->local_tx_start(conn, this_func)) {
3109        if (CONN_GET_STATE(conn) >= CONN_READY) {
3110            static enum_mysqlnd_collected_stats close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
3111                STAT_CLOSE_EXPLICIT,
3112                STAT_CLOSE_IMPLICIT,
3113                STAT_CLOSE_DISCONNECT
3114            };
3115            MYSQLND_INC_CONN_STATISTIC(conn->stats, close_type_to_stat_map[close_type]);
3116        }
3117
3118        /*
3119          Close now, free_reference will try,
3120          if we are last, but that's not a problem.
3121        */
3122        ret = conn->m->send_close(conn);
3123
3124        /* do it after free_reference/dtor and we might crash */
3125        conn->m->local_tx_end(conn, this_func, ret);
3126
3127        conn_handle->m->dtor(conn_handle);
3128    }
3129    DBG_RETURN(ret);
3130}
3131/* }}} */
3132
3133
3134MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
3135    MYSQLND_METHOD(mysqlnd_conn, connect),
3136    MYSQLND_METHOD(mysqlnd_conn, clone_object),
3137    MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor),
3138    MYSQLND_METHOD(mysqlnd_conn, close)
3139MYSQLND_CLASS_METHODS_END;
3140
3141
3142/* {{{ mysqlnd_init */
3143PHPAPI MYSQLND *
3144mysqlnd_init(unsigned int flags, zend_bool persistent)
3145{
3146    MYSQLND * ret;
3147    DBG_ENTER("mysqlnd_init");
3148    ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_connection(persistent);
3149    if (ret && ret->data) {
3150        ret->data->m->negotiate_client_api_capabilities(ret->data, flags);
3151    }
3152    DBG_RETURN(ret);
3153}
3154/* }}} */
3155
3156/*
3157 * Local variables:
3158 * tab-width: 4
3159 * c-basic-offset: 4
3160 * End:
3161 * vim600: noet sw=4 ts=4 fdm=marker
3162 * vim<600: noet sw=4 ts=4
3163 */
3164