source: src/router/php7/ext/mysqlnd/mysqlnd.c @ 30549

Last change on this file since 30549 was 30549, checked in by brainslayer, 12 months ago

php 7

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