source: src/router/php7/ext/mysqlnd/mysqlnd_connection.c @ 31874

Last change on this file since 31874 was 31874, checked in by brainslayer, 6 weeks ago

update php

File size: 81.1 KB
Line 
1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 7                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 2006-2017 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  +----------------------------------------------------------------------+
18*/
19
20#include "php.h"
21#include "mysqlnd.h"
22#include "mysqlnd_connection.h"
23#include "mysqlnd_vio.h"
24#include "mysqlnd_protocol_frame_codec.h"
25#include "mysqlnd_auth.h"
26#include "mysqlnd_wireprotocol.h"
27#include "mysqlnd_priv.h"
28#include "mysqlnd_result.h"
29#include "mysqlnd_statistics.h"
30#include "mysqlnd_charset.h"
31#include "mysqlnd_debug.h"
32#include "mysqlnd_ext_plugin.h"
33#include "zend_smart_str.h"
34
35
36extern MYSQLND_CHARSET *mysqlnd_charsets;
37
38PHPAPI const char * const mysqlnd_server_gone = "MySQL server has gone away";
39PHPAPI const char * const mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now";
40PHPAPI const char * const mysqlnd_out_of_memory = "Out of memory";
41
42PHPAPI MYSQLND_STATS * mysqlnd_global_stats = NULL;
43
44
45/* {{{ mysqlnd_upsert_status::reset */
46void
47MYSQLND_METHOD(mysqlnd_upsert_status, reset)(MYSQLND_UPSERT_STATUS * const upsert_status)
48{
49        upsert_status->warning_count = 0;
50        upsert_status->server_status = 0;
51        upsert_status->affected_rows = 0;
52        upsert_status->last_insert_id = 0;
53}
54/* }}} */
55
56
57/* {{{ mysqlnd_upsert_status::set_affected_rows_to_error */
58void
59MYSQLND_METHOD(mysqlnd_upsert_status, set_affected_rows_to_error)(MYSQLND_UPSERT_STATUS * upsert_status)
60{
61        upsert_status->affected_rows = (uint64_t) ~0;
62}
63/* }}} */
64
65
66MYSQLND_CLASS_METHODS_START(mysqlnd_upsert_status)
67        MYSQLND_METHOD(mysqlnd_upsert_status, reset),
68        MYSQLND_METHOD(mysqlnd_upsert_status, set_affected_rows_to_error),
69MYSQLND_CLASS_METHODS_END;
70
71
72/* {{{ mysqlnd_upsert_status_init */
73void
74mysqlnd_upsert_status_init(MYSQLND_UPSERT_STATUS * const upsert_status)
75{
76        upsert_status->m = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_upsert_status);
77        upsert_status->m->reset(upsert_status);
78}
79/* }}} */
80
81
82/* {{{ mysqlnd_error_list_pdtor */
83static void
84mysqlnd_error_list_pdtor(void * pDest)
85{
86        MYSQLND_ERROR_LIST_ELEMENT * element = (MYSQLND_ERROR_LIST_ELEMENT *) pDest;
87
88        DBG_ENTER("mysqlnd_error_list_pdtor");
89        if (element->error) {
90                mnd_pefree(element->error, TRUE);
91        }
92        DBG_VOID_RETURN;
93}
94/* }}} */
95
96
97/* {{{ mysqlnd_error_info::reset */
98static void
99MYSQLND_METHOD(mysqlnd_error_info, reset)(MYSQLND_ERROR_INFO * const info)
100{
101        DBG_ENTER("mysqlnd_error_info::reset");
102
103        info->error_no = 0;
104        info->error[0] = '\0';
105        memset(info->sqlstate, 0, sizeof(info->sqlstate));
106        if (info->error_list) {
107                zend_llist_clean(info->error_list);
108        }
109
110        DBG_VOID_RETURN;
111}
112/* }}} */
113
114
115/* {{{ mysqlnd_error_info::set_client_error */
116static void
117MYSQLND_METHOD(mysqlnd_error_info, set_client_error)(MYSQLND_ERROR_INFO * const info,
118                                                                                                         const unsigned int err_no,
119                                                                                                         const char * const sqlstate,
120                                                                                                         const char * const error)
121{
122        DBG_ENTER("mysqlnd_error_info::set_client_error");
123        if (err_no) {
124                info->error_no = err_no;
125                strlcpy(info->sqlstate, sqlstate, sizeof(info->sqlstate));
126                strlcpy(info->error, error, sizeof(info->error));
127                if (info->error_list) {
128                        MYSQLND_ERROR_LIST_ELEMENT error_for_the_list = {0};
129
130                        error_for_the_list.error_no = err_no;
131                        strlcpy(error_for_the_list.sqlstate, sqlstate, sizeof(error_for_the_list.sqlstate));
132                        error_for_the_list.error = mnd_pestrdup(error, TRUE);
133                        if (error_for_the_list.error) {
134                                DBG_INF_FMT("adding error [%s] to the list", error_for_the_list.error);
135                                zend_llist_add_element(info->error_list, &error_for_the_list);
136                        }
137                }
138        } else {
139                info->m->reset(info);
140        }
141        DBG_VOID_RETURN;
142}
143/* }}} */
144
145
146MYSQLND_CLASS_METHODS_START(mysqlnd_error_info)
147        MYSQLND_METHOD(mysqlnd_error_info, reset),
148        MYSQLND_METHOD(mysqlnd_error_info, set_client_error),
149MYSQLND_CLASS_METHODS_END;
150
151
152
153/* {{{ mysqlnd_error_info_init */
154PHPAPI enum_func_status
155mysqlnd_error_info_init(MYSQLND_ERROR_INFO * const info, const zend_bool persistent)
156{
157        DBG_ENTER("mysqlnd_error_info_init");
158        info->m = mysqlnd_error_info_get_methods();
159        info->m->reset(info);
160
161        info->error_list = mnd_pecalloc(1, sizeof(zend_llist), persistent);
162        if (info->error_list) {
163                zend_llist_init(info->error_list, sizeof(MYSQLND_ERROR_LIST_ELEMENT), (llist_dtor_func_t) mysqlnd_error_list_pdtor, persistent);
164        }
165        info->persistent = persistent;
166        DBG_RETURN(info->error_list? PASS:FAIL);
167}
168/* }}} */
169
170
171/* {{{ mysqlnd_error_info_free_contents */
172PHPAPI void
173mysqlnd_error_info_free_contents(MYSQLND_ERROR_INFO * const info)
174{
175        DBG_ENTER("mysqlnd_error_info_free_contents");
176        info->m->reset(info);
177        if (info->error_list) {
178                mnd_pefree(info->error_list, info->persistent);
179                info->error_list = NULL;
180        }
181
182        DBG_VOID_RETURN;
183}
184/* }}} */
185
186
187
188
189/* {{{ mysqlnd_connection_state::get */
190static enum mysqlnd_connection_state
191MYSQLND_METHOD(mysqlnd_connection_state, get)(const struct st_mysqlnd_connection_state * const state_struct)
192{
193        DBG_ENTER("mysqlnd_connection_state::get");
194        DBG_INF_FMT("State=%u", state_struct->state);
195        DBG_RETURN(state_struct->state);
196}
197/* }}} */
198
199
200/* {{{ mysqlnd_connection_state::set */
201static void
202MYSQLND_METHOD(mysqlnd_connection_state, set)(struct st_mysqlnd_connection_state * const state_struct, const enum mysqlnd_connection_state state)
203{
204        DBG_ENTER("mysqlnd_connection_state::set");
205        DBG_INF_FMT("New state=%u", state);
206        state_struct->state = state;
207        DBG_VOID_RETURN;
208}
209/* }}} */
210
211
212MYSQLND_CLASS_METHODS_START(mysqlnd_connection_state)
213        MYSQLND_METHOD(mysqlnd_connection_state, get),
214        MYSQLND_METHOD(mysqlnd_connection_state, set),
215MYSQLND_CLASS_METHODS_END;
216
217
218/* {{{ mysqlnd_connection_state_init */
219PHPAPI void
220mysqlnd_connection_state_init(struct st_mysqlnd_connection_state * const state)
221{
222        DBG_ENTER("mysqlnd_connection_state_init");
223        state->m = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_connection_state);
224        state->state = CONN_ALLOCED;
225        DBG_VOID_RETURN;
226}
227/* }}} */
228
229
230
231/* {{{ mysqlnd_conn_data::free_options */
232static void
233MYSQLND_METHOD(mysqlnd_conn_data, free_options)(MYSQLND_CONN_DATA * conn)
234{
235        zend_bool pers = conn->persistent;
236
237        if (conn->options->charset_name) {
238                mnd_pefree(conn->options->charset_name, pers);
239                conn->options->charset_name = NULL;
240        }
241        if (conn->options->auth_protocol) {
242                mnd_pefree(conn->options->auth_protocol, pers);
243                conn->options->auth_protocol = NULL;
244        }
245        if (conn->options->num_commands) {
246                unsigned int i;
247                for (i = 0; i < conn->options->num_commands; i++) {
248                        /* allocated with pestrdup */
249                        mnd_pefree(conn->options->init_commands[i], pers);
250                }
251                mnd_pefree(conn->options->init_commands, pers);
252                conn->options->init_commands = NULL;
253        }
254        if (conn->options->cfg_file) {
255                mnd_pefree(conn->options->cfg_file, pers);
256                conn->options->cfg_file = NULL;
257        }
258        if (conn->options->cfg_section) {
259                mnd_pefree(conn->options->cfg_section, pers);
260                conn->options->cfg_section = NULL;
261        }
262        if (conn->options->connect_attr) {
263                zend_hash_destroy(conn->options->connect_attr);
264                mnd_pefree(conn->options->connect_attr, pers);
265                conn->options->connect_attr = NULL;
266        }
267}
268/* }}} */
269
270
271/* {{{ mysqlnd_conn_data::free_contents */
272static void
273MYSQLND_METHOD(mysqlnd_conn_data, free_contents)(MYSQLND_CONN_DATA * conn)
274{
275        zend_bool pers = conn->persistent;
276
277        DBG_ENTER("mysqlnd_conn_data::free_contents");
278
279        if (conn->current_result) {
280                conn->current_result->m.free_result(conn->current_result, TRUE);
281                conn->current_result = NULL;
282        }
283
284        if (conn->protocol_frame_codec) {
285                conn->protocol_frame_codec->data->m.free_contents(conn->protocol_frame_codec);
286        }
287
288        if (conn->vio) {
289                conn->vio->data->m.free_contents(conn->vio);
290        }
291
292        DBG_INF("Freeing memory of members");
293
294        if (conn->hostname.s) {
295                mnd_pefree(conn->hostname.s, pers);
296                conn->hostname.s = NULL;
297        }
298        if (conn->username.s) {
299                mnd_pefree(conn->username.s, pers);
300                conn->username.s = NULL;
301        }
302        if (conn->password.s) {
303                mnd_pefree(conn->password.s, pers);
304                conn->password.s = NULL;
305        }
306        if (conn->connect_or_select_db.s) {
307                mnd_pefree(conn->connect_or_select_db.s, pers);
308                conn->connect_or_select_db.s = NULL;
309        }
310        if (conn->unix_socket.s) {
311                mnd_pefree(conn->unix_socket.s, pers);
312                conn->unix_socket.s = NULL;
313        }
314        DBG_INF_FMT("scheme=%s", conn->scheme.s);
315        if (conn->scheme.s) {
316                mnd_pefree(conn->scheme.s, pers);
317                conn->scheme.s = NULL;
318        }
319        if (conn->server_version) {
320                mnd_pefree(conn->server_version, pers);
321                conn->server_version = NULL;
322        }
323        if (conn->host_info) {
324                mnd_pefree(conn->host_info, pers);
325                conn->host_info = NULL;
326        }
327        if (conn->authentication_plugin_data.s) {
328                mnd_pefree(conn->authentication_plugin_data.s, pers);
329                conn->authentication_plugin_data.s = NULL;
330        }
331        if (conn->last_message.s) {
332                mnd_pefree(conn->last_message.s, pers);
333                conn->last_message.s = NULL;
334        }
335
336        conn->charset = NULL;
337        conn->greet_charset = NULL;
338
339        DBG_VOID_RETURN;
340}
341/* }}} */
342
343
344/* {{{ mysqlnd_conn_data::dtor */
345static void
346MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor)(MYSQLND_CONN_DATA * conn)
347{
348        DBG_ENTER("mysqlnd_conn_data::dtor");
349        DBG_INF_FMT("conn=%llu", conn->thread_id);
350
351        conn->m->free_contents(conn);
352        conn->m->free_options(conn);
353
354        if (conn->error_info) {
355                mysqlnd_error_info_free_contents(conn->error_info);
356                conn->error_info = NULL;
357        }
358
359        if (conn->protocol_frame_codec) {
360                mysqlnd_pfc_free(conn->protocol_frame_codec, conn->stats, conn->error_info);
361                conn->protocol_frame_codec = NULL;
362        }
363
364        if (conn->vio) {
365                mysqlnd_vio_free(conn->vio, conn->stats, conn->error_info);
366                conn->vio = NULL;
367        }
368
369        if (conn->payload_decoder_factory) {
370                mysqlnd_protocol_payload_decoder_factory_free(conn->payload_decoder_factory);
371                conn->payload_decoder_factory = NULL;
372        }
373
374        if (conn->stats) {
375                mysqlnd_stats_end(conn->stats, conn->persistent);
376        }
377
378        mnd_pefree(conn, conn->persistent);
379
380        DBG_VOID_RETURN;
381}
382/* }}} */
383
384
385/* {{{ mysqlnd_conn_data::set_server_option */
386static enum_func_status
387MYSQLND_METHOD(mysqlnd_conn_data, set_server_option)(MYSQLND_CONN_DATA * const conn, enum_mysqlnd_server_option option)
388{
389        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_server_option);
390        enum_func_status ret = FAIL;
391        DBG_ENTER("mysqlnd_conn_data::set_server_option");
392        if (PASS == conn->m->local_tx_start(conn, this_func)) {
393                struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_SET_OPTION, conn, option);
394                if (command) {
395                        ret = command->run(command);
396                        command->free_command(command);
397                }
398
399                conn->m->local_tx_end(conn, this_func, ret);
400        }
401        DBG_RETURN(ret);
402}
403/* }}} */
404
405
406/* {{{ mysqlnd_conn_data::restart_psession */
407static enum_func_status
408MYSQLND_METHOD(mysqlnd_conn_data, restart_psession)(MYSQLND_CONN_DATA * conn)
409{
410        DBG_ENTER("mysqlnd_conn_data::restart_psession");
411        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_REUSED);
412        /* Free here what should not be seen by the next script */
413        if (conn->last_message.s) {
414                mnd_pefree(conn->last_message.s, conn->persistent);
415                conn->last_message.s = NULL;
416        }
417        DBG_RETURN(PASS);
418}
419/* }}} */
420
421
422/* {{{ mysqlnd_conn_data::end_psession */
423static enum_func_status
424MYSQLND_METHOD(mysqlnd_conn_data, end_psession)(MYSQLND_CONN_DATA * conn)
425{
426        DBG_ENTER("mysqlnd_conn_data::end_psession");
427        DBG_RETURN(PASS);
428}
429/* }}} */
430
431
432/* {{{ mysqlnd_conn_data::fetch_auth_plugin_by_name */
433static struct st_mysqlnd_authentication_plugin *
434MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name)(const char * const requested_protocol)
435{
436        struct st_mysqlnd_authentication_plugin * auth_plugin;
437        char * plugin_name = NULL;
438        DBG_ENTER("mysqlnd_conn_data::fetch_auth_plugin_by_name");
439
440        mnd_sprintf(&plugin_name, 0, "auth_plugin_%s", requested_protocol);
441        DBG_INF_FMT("looking for %s auth plugin", plugin_name);
442        auth_plugin = mysqlnd_plugin_find(plugin_name);
443        mnd_sprintf_free(plugin_name);
444
445        DBG_RETURN(auth_plugin);
446}
447/* }}} */
448
449
450/* {{{ mysqlnd_conn_data::execute_init_commands */
451static enum_func_status
452MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands)(MYSQLND_CONN_DATA * conn)
453{
454        enum_func_status ret = PASS;
455
456        DBG_ENTER("mysqlnd_conn_data::execute_init_commands");
457        if (conn->options->init_commands) {
458                unsigned int current_command = 0;
459                for (; current_command < conn->options->num_commands; ++current_command) {
460                        const char * const command = conn->options->init_commands[current_command];
461                        if (command) {
462                                MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_EXECUTED_COUNT);
463                                if (PASS != conn->m->query(conn, command, strlen(command))) {
464                                        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_INIT_COMMAND_FAILED_COUNT);
465                                        ret = FAIL;
466                                        break;
467                                }
468                                if (conn->last_query_type == QUERY_SELECT) {
469                                        MYSQLND_RES * result = conn->m->use_result(conn, 0);
470                                        if (result) {
471                                                result->m.free_result(result, TRUE);
472                                        }
473                                }
474                        }
475                }
476        }
477        DBG_RETURN(ret);
478}
479/* }}} */
480
481
482/* {{{ mysqlnd_conn_data::get_updated_connect_flags */
483static unsigned int
484MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA * conn, unsigned int mysql_flags)
485{
486#ifdef MYSQLND_COMPRESSION_ENABLED
487        MYSQLND_PFC * pfc = conn->protocol_frame_codec;
488#endif
489        MYSQLND_VIO * vio = conn->vio;
490
491        DBG_ENTER("mysqlnd_conn_data::get_updated_connect_flags");
492        /* we allow load data local infile by default */
493        mysql_flags |= MYSQLND_CAPABILITIES;
494
495        mysql_flags |= conn->options->flags; /* use the flags from set_client_option() */
496
497#ifndef MYSQLND_COMPRESSION_ENABLED
498        if (mysql_flags & CLIENT_COMPRESS) {
499                mysql_flags &= ~CLIENT_COMPRESS;
500        }
501#else
502        if (pfc && pfc->data->flags & MYSQLND_PROTOCOL_FLAG_USE_COMPRESSION) {
503                mysql_flags |= CLIENT_COMPRESS;
504        }
505#endif
506#ifndef MYSQLND_SSL_SUPPORTED
507        if (mysql_flags & CLIENT_SSL) {
508                mysql_flags &= ~CLIENT_SSL;
509        }
510#else
511        if (vio && (vio->data->options.ssl_key ||
512                                vio->data->options.ssl_cert ||
513                                vio->data->options.ssl_ca ||
514                                vio->data->options.ssl_capath ||
515                                vio->data->options.ssl_cipher))
516        {
517                mysql_flags |= CLIENT_SSL;
518        }
519#endif
520
521        DBG_RETURN(mysql_flags);
522}
523/* }}} */
524
525
526/* {{{ mysqlnd_conn_data::connect_handshake */
527static enum_func_status
528MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn,
529                                                const MYSQLND_CSTRING * const scheme,
530                                                const MYSQLND_CSTRING * const username,
531                                                const MYSQLND_CSTRING * const password,
532                                                const MYSQLND_CSTRING * const database,
533                                                const unsigned int mysql_flags)
534{
535        enum_func_status ret = FAIL;
536        DBG_ENTER("mysqlnd_conn_data::connect_handshake");
537
538        if (PASS == conn->vio->data->m.connect(conn->vio, *scheme, conn->persistent, conn->stats, conn->error_info) &&
539                PASS == conn->protocol_frame_codec->data->m.reset(conn->protocol_frame_codec, conn->stats, conn->error_info))
540        {
541                size_t client_flags = mysql_flags;
542                struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_HANDSHAKE, conn, username, password, database, client_flags);
543                if (command) {
544                        ret = command->run(command);
545                        command->free_command(command);
546                }
547        }
548        DBG_RETURN(ret);
549}
550/* }}} */
551
552/* {{{ mysqlnd_conn_data::get_scheme */
553static MYSQLND_STRING
554MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_CSTRING hostname, MYSQLND_CSTRING *socket_or_pipe, unsigned int port, zend_bool * unix_socket, zend_bool * named_pipe)
555{
556        MYSQLND_STRING transport;
557        DBG_ENTER("mysqlnd_conn_data::get_scheme");
558#ifndef PHP_WIN32
559        if (hostname.l == sizeof("localhost") - 1 && !strncasecmp(hostname.s, "localhost", hostname.l)) {
560                DBG_INF_FMT("socket=%s", socket_or_pipe->s? socket_or_pipe->s:"n/a");
561                if (!socket_or_pipe->s) {
562                        socket_or_pipe->s = "/tmp/mysql.sock";
563                        socket_or_pipe->l = strlen(socket_or_pipe->s);
564                }
565                transport.l = mnd_sprintf(&transport.s, 0, "unix://%s", socket_or_pipe->s);
566                *unix_socket = TRUE;
567#else
568        if (hostname.l == sizeof(".") - 1 && hostname.s[0] == '.') {
569                /* named pipe in socket */
570                if (!socket_or_pipe->s) {
571                        socket_or_pipe->s = "\\\\.\\pipe\\MySQL";
572                        socket_or_pipe->l = strlen(socket_or_pipe->s);
573                }
574                transport.l = mnd_sprintf(&transport.s, 0, "pipe://%s", socket_or_pipe->s);
575                *named_pipe = TRUE;
576#endif
577        } else {
578                if (!port) {
579                        port = 3306;
580                }
581                transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port);
582        }
583        DBG_INF_FMT("transport=%s", transport.s? transport.s:"OOM");
584        DBG_RETURN(transport);
585}
586/* }}} */
587
588
589/* {{{ mysqlnd_conn_data::connect */
590static enum_func_status
591MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn,
592                                                MYSQLND_CSTRING hostname,
593                                                MYSQLND_CSTRING username,
594                                                MYSQLND_CSTRING password,
595                                                MYSQLND_CSTRING database,
596                                                unsigned int port,
597                                                MYSQLND_CSTRING socket_or_pipe,
598                                                unsigned int mysql_flags
599                                        )
600{
601        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), connect);
602        zend_bool unix_socket = FALSE;
603        zend_bool named_pipe = FALSE;
604        zend_bool reconnect = FALSE;
605        zend_bool saved_compression = FALSE;
606        zend_bool local_tx_started = FALSE;
607        MYSQLND_PFC * pfc = conn->protocol_frame_codec;
608        MYSQLND_STRING transport = { NULL, 0 };
609
610        DBG_ENTER("mysqlnd_conn_data::connect");
611        DBG_INF_FMT("conn=%p", conn);
612
613        if (PASS != conn->m->local_tx_start(conn, this_func)) {
614                goto err;
615        }
616        local_tx_started = TRUE;
617
618        SET_EMPTY_ERROR(conn->error_info);
619        UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
620
621        DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u persistent=%u state=%u",
622                                hostname.s?hostname.s:"", username.s?username.s:"", database.s?database.s:"", port, mysql_flags,
623                                conn? conn->persistent:0, conn? (int)GET_CONNECTION_STATE(&conn->state):-1);
624
625        if (GET_CONNECTION_STATE(&conn->state) > CONN_ALLOCED) {
626                DBG_INF("Connecting on a connected handle.");
627
628                if (GET_CONNECTION_STATE(&conn->state) < CONN_QUIT_SENT) {
629                        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CLOSE_IMPLICIT);
630                        reconnect = TRUE;
631                        conn->m->send_close(conn);
632                }
633
634                conn->m->free_contents(conn);
635                /* Now reconnect using the same handle */
636                if (pfc->data->compressed) {
637                        /*
638                          we need to save the state. As we will re-connect, pfc->compressed should be off, or
639                          we will look for a compression header as part of the greet message, but there will
640                          be none.
641                        */
642                        saved_compression = TRUE;
643                        pfc->data->compressed = FALSE;
644                }
645                if (pfc->data->ssl) {
646                        pfc->data->ssl = FALSE;
647                }
648        } else {
649                unsigned int max_allowed_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE;
650                conn->m->set_client_option(conn, MYSQLND_OPT_MAX_ALLOWED_PACKET, (char *)&max_allowed_size);
651        }
652
653        if (!hostname.s || !hostname.s[0]) {
654                hostname.s = "localhost";
655                hostname.l = strlen(hostname.s);
656        }
657        if (!username.s) {
658                DBG_INF_FMT("no user given, using empty string");
659                username.s = "";
660                username.l = 0;
661        }
662        if (!password.s) {
663                DBG_INF_FMT("no password given, using empty string");
664                password.s = "";
665                password.l = 0;
666        }
667        if (!database.s) {
668                DBG_INF_FMT("no db given, using empty string");
669                database.s = "";
670                database.l = 0;
671        } else {
672                mysql_flags |= CLIENT_CONNECT_WITH_DB;
673        }
674
675        transport = conn->m->get_scheme(conn, hostname, &socket_or_pipe, port, &unix_socket, &named_pipe);
676
677        mysql_flags = conn->m->get_updated_connect_flags(conn, mysql_flags);
678
679        {
680                const MYSQLND_CSTRING scheme = { transport.s, transport.l };
681                if (FAIL == conn->m->connect_handshake(conn, &scheme, &username, &password, &database, mysql_flags)) {
682                        goto err;
683                }
684        }
685
686        {
687                SET_CONNECTION_STATE(&conn->state, CONN_READY);
688
689                if (saved_compression) {
690                        pfc->data->compressed = TRUE;
691                }
692                /*
693                  If a connect on a existing handle is performed and mysql_flags is
694                  passed which doesn't CLIENT_COMPRESS, then we need to overwrite the value
695                  which we set based on saved_compression.
696                */
697                pfc->data->compressed = mysql_flags & CLIENT_COMPRESS? TRUE:FALSE;
698
699
700                conn->scheme.s = mnd_pestrndup(transport.s, transport.l, conn->persistent);
701                conn->scheme.l = transport.l;
702                if (transport.s) {
703                        mnd_sprintf_free(transport.s);
704                        transport.s = NULL;
705                }
706
707                if (!conn->scheme.s) {
708                        goto err; /* OOM */
709                }
710
711                conn->username.l                = username.l;
712                conn->username.s                = mnd_pestrndup(username.s, conn->username.l, conn->persistent);
713                conn->password.l                = password.l;
714                conn->password.s                = mnd_pestrndup(password.s, conn->password.l, conn->persistent);
715                conn->port                              = port;
716                conn->connect_or_select_db.l = database.l;
717                conn->connect_or_select_db.s = mnd_pestrndup(database.s, conn->connect_or_select_db.l, conn->persistent);
718
719                if (!conn->username.s || !conn->password.s|| !conn->connect_or_select_db.s) {
720                        SET_OOM_ERROR(conn->error_info);
721                        goto err; /* OOM */
722                }
723
724                if (!unix_socket && !named_pipe) {
725                        conn->hostname.s = mnd_pestrndup(hostname.s, hostname.l, conn->persistent);
726                        if (!conn->hostname.s) {
727                                SET_OOM_ERROR(conn->error_info);
728                                goto err; /* OOM */
729                        }
730                        conn->hostname.l = hostname.l;
731                        {
732                                char *p;
733                                mnd_sprintf(&p, 0, "%s via TCP/IP", conn->hostname.s);
734                                if (!p) {
735                                        SET_OOM_ERROR(conn->error_info);
736                                        goto err; /* OOM */
737                                }
738                                conn->host_info = mnd_pestrdup(p, conn->persistent);
739                                mnd_sprintf_free(p);
740                                if (!conn->host_info) {
741                                        SET_OOM_ERROR(conn->error_info);
742                                        goto err; /* OOM */
743                                }
744                        }
745                } else {
746                        conn->unix_socket.s = mnd_pestrdup(socket_or_pipe.s, conn->persistent);
747                        if (unix_socket) {
748                                conn->host_info = mnd_pestrdup("Localhost via UNIX socket", conn->persistent);
749                        } else if (named_pipe) {
750                                char *p;
751                                mnd_sprintf(&p, 0, "%s via named pipe", conn->unix_socket.s);
752                                if (!p) {
753                                        SET_OOM_ERROR(conn->error_info);
754                                        goto err; /* OOM */
755                                }
756                                conn->host_info =  mnd_pestrdup(p, conn->persistent);
757                                mnd_sprintf_free(p);
758                                if (!conn->host_info) {
759                                        SET_OOM_ERROR(conn->error_info);
760                                        goto err; /* OOM */
761                                }
762                        } else {
763                                php_error_docref(NULL, E_WARNING, "Impossible. Should be either socket or a pipe. Report a bug!");
764                        }
765                        if (!conn->unix_socket.s || !conn->host_info) {
766                                SET_OOM_ERROR(conn->error_info);
767                                goto err; /* OOM */
768                        }
769                        conn->unix_socket.l = strlen(conn->unix_socket.s);
770                }
771
772                SET_EMPTY_ERROR(conn->error_info);
773
774                mysqlnd_local_infile_default(conn);
775
776                if (FAIL == conn->m->execute_init_commands(conn)) {
777                        goto err;
778                }
779
780                MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_CONNECT_SUCCESS, 1, STAT_OPENED_CONNECTIONS, 1);
781                if (reconnect) {
782                        MYSQLND_INC_GLOBAL_STATISTIC(STAT_RECONNECT);
783                }
784                if (conn->persistent) {
785                        MYSQLND_INC_CONN_STATISTIC_W_VALUE2(conn->stats, STAT_PCONNECT_SUCCESS, 1, STAT_OPENED_PERSISTENT_CONNECTIONS, 1);
786                }
787
788                DBG_INF_FMT("connection_id=%llu", conn->thread_id);
789
790                conn->m->local_tx_end(conn, this_func, PASS);
791                DBG_RETURN(PASS);
792        }
793err:
794        if (transport.s) {
795                mnd_sprintf_free(transport.s);
796                transport.s = NULL;
797        }
798
799        DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme.s);
800        if (!conn->error_info->error_no) {
801                SET_CLIENT_ERROR(conn->error_info, CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, conn->error_info->error? conn->error_info->error:"Unknown error");
802                php_error_docref(NULL, E_WARNING, "[%u] %.128s (trying to connect via %s)", conn->error_info->error_no, conn->error_info->error, conn->scheme.s);
803        }
804
805        conn->m->free_contents(conn);
806        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE);
807        if (TRUE == local_tx_started) {
808                conn->m->local_tx_end(conn, this_func, FAIL);
809        }
810
811        DBG_RETURN(FAIL);
812}
813/* }}} */
814
815
816/* {{{ mysqlnd_conn::connect */
817static enum_func_status
818MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle,
819                                                const MYSQLND_CSTRING hostname,
820                                                const MYSQLND_CSTRING username,
821                                                const MYSQLND_CSTRING password,
822                                                const MYSQLND_CSTRING database,
823                                                unsigned int port,
824                                                const MYSQLND_CSTRING socket_or_pipe,
825                                                unsigned int mysql_flags)
826{
827        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), connect);
828        enum_func_status ret = FAIL;
829        MYSQLND_CONN_DATA * conn = conn_handle->data;
830
831        DBG_ENTER("mysqlnd_conn::connect");
832
833        if (PASS == conn->m->local_tx_start(conn, this_func)) {
834                mysqlnd_options4(conn_handle, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "mysqlnd");
835                ret = conn->m->connect(conn, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
836
837                conn->m->local_tx_end(conn, this_func, FAIL);
838        }
839        DBG_RETURN(ret);
840}
841/* }}} */
842
843
844/* {{{ mysqlnd_conn_data::query */
845/*
846  If conn->error_info->error_no is not zero, then we had an error.
847  Still the result from the query is PASS
848*/
849static enum_func_status
850MYSQLND_METHOD(mysqlnd_conn_data, query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len)
851{
852        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), query);
853        enum_func_status ret = FAIL;
854        DBG_ENTER("mysqlnd_conn_data::query");
855        DBG_INF_FMT("conn=%p conn=%llu query=%s", conn, conn->thread_id, query);
856
857        if (PASS == conn->m->local_tx_start(conn, this_func)) {
858                if (PASS == conn->m->send_query(conn, query, query_len, MYSQLND_SEND_QUERY_IMPLICIT, NULL, NULL) &&
859                        PASS == conn->m->reap_query(conn, MYSQLND_REAP_RESULT_IMPLICIT))
860                {
861                        ret = PASS;
862                        if (conn->last_query_type == QUERY_UPSERT && UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status)) {
863                                MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
864                        }
865                }
866                conn->m->local_tx_end(conn, this_func, ret);
867        }
868        DBG_RETURN(ret);
869}
870/* }}} */
871
872
873/* {{{ mysqlnd_conn_data::send_query */
874static enum_func_status
875MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const char * const query, const size_t query_len,
876                                                                                          enum_mysqlnd_send_query_type type, zval *read_cb, zval *err_cb)
877{
878        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), send_query);
879        enum_func_status ret = FAIL;
880        DBG_ENTER("mysqlnd_conn_data::send_query");
881        DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
882        DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
883
884        if (type == MYSQLND_SEND_QUERY_IMPLICIT || PASS == conn->m->local_tx_start(conn, this_func))
885        {
886                const MYSQLND_CSTRING query_string = {query, query_len};
887                struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_QUERY, conn, query_string);
888                if (command) {
889                        ret = command->run(command);
890                        command->free_command(command);
891                }
892
893                if (type == MYSQLND_SEND_QUERY_EXPLICIT) {
894                        conn->m->local_tx_end(conn, this_func, ret);
895                }
896        }
897        DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
898        DBG_RETURN(ret);
899}
900/* }}} */
901
902
903/* {{{ mysqlnd_conn_data::reap_query */
904static enum_func_status
905MYSQLND_METHOD(mysqlnd_conn_data, reap_query)(MYSQLND_CONN_DATA * conn, enum_mysqlnd_reap_result_type type)
906{
907        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), reap_query);
908        enum_func_status ret = FAIL;
909        DBG_ENTER("mysqlnd_conn_data::reap_query");
910        DBG_INF_FMT("conn=%llu", conn->thread_id);
911
912        DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
913        if (type == MYSQLND_REAP_RESULT_IMPLICIT || PASS == conn->m->local_tx_start(conn, this_func))
914        {
915                struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_REAP_RESULT, conn);
916                if (command) {
917                        ret = command->run(command);
918                        command->free_command(command);
919                }
920
921                if (type == MYSQLND_REAP_RESULT_EXPLICIT) {
922                        conn->m->local_tx_end(conn, this_func, ret);
923                }
924        }
925        DBG_INF_FMT("conn->server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
926        DBG_RETURN(ret);
927}
928/* }}} */
929
930
931/* {{{ mysqlnd_conn_data::list_method */
932MYSQLND_RES *
933MYSQLND_METHOD(mysqlnd_conn_data, list_method)(MYSQLND_CONN_DATA * conn, const char * const query, const char * const achtung_wild, const char * const par1)
934{
935        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), list_method);
936        char * show_query = NULL;
937        size_t show_query_len;
938        MYSQLND_RES * result = NULL;
939
940        DBG_ENTER("mysqlnd_conn_data::list_method");
941        DBG_INF_FMT("conn=%llu query=%s wild=%u", conn->thread_id, query, achtung_wild);
942
943        if (PASS == conn->m->local_tx_start(conn, this_func)) {
944                if (par1) {
945                        if (achtung_wild) {
946                                show_query_len = mnd_sprintf(&show_query, 0, query, par1, achtung_wild);
947                        } else {
948                                show_query_len = mnd_sprintf(&show_query, 0, query, par1);
949                        }
950                } else {
951                        if (achtung_wild) {
952                                show_query_len = mnd_sprintf(&show_query, 0, query, achtung_wild);
953                        } else {
954                                show_query_len = strlen(show_query = (char *)query);
955                        }
956                }
957
958                if (PASS == conn->m->query(conn, show_query, show_query_len)) {
959                        result = conn->m->store_result(conn, MYSQLND_STORE_NO_COPY);
960                }
961                if (show_query != query) {
962                        mnd_sprintf_free(show_query);
963                }
964                conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS);
965        }
966        DBG_RETURN(result);
967}
968/* }}} */
969
970
971/* {{{ mysqlnd_conn_data::err_no */
972static unsigned int
973MYSQLND_METHOD(mysqlnd_conn_data, err_no)(const MYSQLND_CONN_DATA * const conn)
974{
975        return conn->error_info->error_no;
976}
977/* }}} */
978
979
980/* {{{ mysqlnd_conn_data::error */
981static const char *
982MYSQLND_METHOD(mysqlnd_conn_data, error)(const MYSQLND_CONN_DATA * const conn)
983{
984        return conn->error_info->error;
985}
986/* }}} */
987
988
989/* {{{ mysqlnd_conn_data::sqlstate */
990static const char *
991MYSQLND_METHOD(mysqlnd_conn_data, sqlstate)(const MYSQLND_CONN_DATA * const conn)
992{
993        return conn->error_info->sqlstate[0] ? conn->error_info->sqlstate:MYSQLND_SQLSTATE_NULL;
994}
995/* }}} */
996
997
998/* {{{ mysqlnd_old_escape_string */
999PHPAPI  zend_ulong
1000mysqlnd_old_escape_string(char * newstr, const char * escapestr, size_t escapestr_len)
1001{
1002        DBG_ENTER("mysqlnd_old_escape_string");
1003        DBG_RETURN(mysqlnd_cset_escape_slashes(mysqlnd_find_charset_name("latin1"), newstr, escapestr, escapestr_len));
1004}
1005/* }}} */
1006
1007
1008/* {{{ mysqlnd_conn_data::ssl_set */
1009static enum_func_status
1010MYSQLND_METHOD(mysqlnd_conn_data, ssl_set)(MYSQLND_CONN_DATA * const conn, const char * key, const char * const cert,
1011                                                                          const char * const ca, const char * const capath, const char * const cipher)
1012{
1013        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), ssl_set);
1014        enum_func_status ret = FAIL;
1015        MYSQLND_VIO * vio = conn->vio;
1016        DBG_ENTER("mysqlnd_conn_data::ssl_set");
1017
1018        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1019                ret = (PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_KEY, key) &&
1020                        PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CERT, cert) &&
1021                        PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CA, ca) &&
1022                        PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CAPATH, capath) &&
1023                        PASS == vio->data->m.set_client_option(vio, MYSQLND_OPT_SSL_CIPHER, cipher)) ? PASS : FAIL;
1024
1025                conn->m->local_tx_end(conn, this_func, ret);
1026        }
1027        DBG_RETURN(ret);
1028}
1029/* }}} */
1030
1031
1032/* {{{ mysqlnd_conn_data::escape_string */
1033static zend_ulong
1034MYSQLND_METHOD(mysqlnd_conn_data, escape_string)(MYSQLND_CONN_DATA * const conn, char * newstr, const char * escapestr, size_t escapestr_len)
1035{
1036        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), escape_string);
1037        zend_ulong ret = FAIL;
1038        DBG_ENTER("mysqlnd_conn_data::escape_string");
1039        DBG_INF_FMT("conn=%llu", conn->thread_id);
1040
1041        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1042                DBG_INF_FMT("server_status=%u", UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status));
1043                if (UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_STATUS_NO_BACKSLASH_ESCAPES) {
1044                        ret = mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len);
1045                } else {
1046                        ret = mysqlnd_cset_escape_slashes(conn->charset, newstr, escapestr, escapestr_len);
1047                }
1048                conn->m->local_tx_end(conn, this_func, PASS);
1049        }
1050        DBG_RETURN(ret);
1051}
1052/* }}} */
1053
1054
1055/* {{{ mysqlnd_conn_data::dump_debug_info */
1056static enum_func_status
1057MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info)(MYSQLND_CONN_DATA * const conn)
1058{
1059        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), server_dump_debug_information);
1060        enum_func_status ret = FAIL;
1061        DBG_ENTER("mysqlnd_conn_data::dump_debug_info");
1062        DBG_INF_FMT("conn=%llu", conn->thread_id);
1063        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1064                struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_DEBUG, conn);
1065                if (command) {
1066                        ret = command->run(command);
1067                        command->free_command(command);
1068                }
1069
1070                conn->m->local_tx_end(conn, this_func, ret);
1071        }
1072
1073        DBG_RETURN(ret);
1074}
1075/* }}} */
1076
1077
1078/* {{{ mysqlnd_conn_data::select_db */
1079static enum_func_status
1080MYSQLND_METHOD(mysqlnd_conn_data, select_db)(MYSQLND_CONN_DATA * const conn, const char * const db, const size_t db_len)
1081{
1082        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), select_db);
1083        enum_func_status ret = FAIL;
1084
1085        DBG_ENTER("mysqlnd_conn_data::select_db");
1086        DBG_INF_FMT("conn=%llu db=%s", conn->thread_id, db);
1087
1088        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1089                const MYSQLND_CSTRING database = {db, db_len};
1090                struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_INIT_DB, conn, database);
1091                if (command) {
1092                        ret = command->run(command);
1093                        command->free_command(command);
1094                }
1095
1096                conn->m->local_tx_end(conn, this_func, ret);
1097        }
1098        DBG_RETURN(ret);
1099}
1100/* }}} */
1101
1102
1103/* {{{ mysqlnd_conn_data::ping */
1104static enum_func_status
1105MYSQLND_METHOD(mysqlnd_conn_data, ping)(MYSQLND_CONN_DATA * const conn)
1106{
1107        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), ping);
1108        enum_func_status ret = FAIL;
1109
1110        DBG_ENTER("mysqlnd_conn_data::ping");
1111        DBG_INF_FMT("conn=%llu", conn->thread_id);
1112
1113        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1114                struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_PING, conn);
1115                if (command) {
1116                        ret = command->run(command);
1117                        command->free_command(command);
1118                }
1119                conn->m->local_tx_end(conn, this_func, ret);
1120        }
1121        DBG_INF_FMT("ret=%u", ret);
1122        DBG_RETURN(ret);
1123}
1124/* }}} */
1125
1126
1127/* {{{ mysqlnd_conn_data::statistic */
1128static enum_func_status
1129MYSQLND_METHOD(mysqlnd_conn_data, statistic)(MYSQLND_CONN_DATA * conn, zend_string **message)
1130{
1131        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), get_server_statistics);
1132        enum_func_status ret = FAIL;
1133
1134        DBG_ENTER("mysqlnd_conn_data::statistic");
1135        DBG_INF_FMT("conn=%llu", conn->thread_id);
1136
1137        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1138                struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_STATISTICS, conn, message);
1139                if (command) {
1140                        ret = command->run(command);
1141                        command->free_command(command);
1142                }
1143                conn->m->local_tx_end(conn, this_func, ret);
1144        }
1145        DBG_RETURN(ret);
1146}
1147/* }}} */
1148
1149
1150/* {{{ mysqlnd_conn_data::kill */
1151static enum_func_status
1152MYSQLND_METHOD(mysqlnd_conn_data, kill)(MYSQLND_CONN_DATA * conn, unsigned int pid)
1153{
1154        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), kill_connection);
1155        enum_func_status ret = FAIL;
1156
1157        DBG_ENTER("mysqlnd_conn_data::kill");
1158        DBG_INF_FMT("conn=%llu pid=%u", conn->thread_id, pid);
1159
1160        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1161                unsigned int process_id = pid;
1162                /* 'unsigned char' is promoted to 'int' when passed through '...' */
1163                unsigned int read_response = (pid != conn->thread_id);
1164                struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_PROCESS_KILL, conn, process_id, read_response);
1165                if (command) {
1166                        ret = command->run(command);
1167                        command->free_command(command);
1168                }
1169                conn->m->local_tx_end(conn, this_func, ret);
1170        }
1171        DBG_RETURN(ret);
1172}
1173/* }}} */
1174
1175
1176/* {{{ mysqlnd_conn_data::set_charset */
1177static enum_func_status
1178MYSQLND_METHOD(mysqlnd_conn_data, set_charset)(MYSQLND_CONN_DATA * const conn, const char * const csname)
1179{
1180        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_charset);
1181        enum_func_status ret = FAIL;
1182        const MYSQLND_CHARSET * const charset = mysqlnd_find_charset_name(csname);
1183
1184        DBG_ENTER("mysqlnd_conn_data::set_charset");
1185        DBG_INF_FMT("conn=%llu cs=%s", conn->thread_id, csname);
1186
1187        if (!charset) {
1188                SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE,
1189                                                 "Invalid characterset or character set not supported");
1190                DBG_RETURN(ret);
1191        }
1192
1193        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1194                char * query;
1195                size_t query_len = mnd_sprintf(&query, 0, "SET NAMES %s", csname);
1196
1197                if (FAIL == (ret = conn->m->query(conn, query, query_len))) {
1198                        php_error_docref(NULL, E_WARNING, "Error executing query");
1199                } else if (conn->error_info->error_no) {
1200                        ret = FAIL;
1201                } else {
1202                        conn->charset = charset;
1203                }
1204                mnd_sprintf_free(query);
1205
1206                conn->m->local_tx_end(conn, this_func, ret);
1207        }
1208
1209        DBG_INF(ret == PASS? "PASS":"FAIL");
1210        DBG_RETURN(ret);
1211}
1212/* }}} */
1213
1214
1215/* {{{ mysqlnd_conn_data::refresh */
1216static enum_func_status
1217MYSQLND_METHOD(mysqlnd_conn_data, refresh)(MYSQLND_CONN_DATA * const conn, uint8_t options)
1218{
1219        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), refresh_server);
1220        enum_func_status ret = FAIL;
1221        DBG_ENTER("mysqlnd_conn_data::refresh");
1222        DBG_INF_FMT("conn=%llu options=%lu", conn->thread_id, options);
1223
1224        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1225                unsigned int options_param = (unsigned int) options;
1226                struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_REFRESH, conn, options_param);
1227                if (command) {
1228                        ret = command->run(command);
1229                        command->free_command(command);
1230                }
1231                conn->m->local_tx_end(conn, this_func, ret);
1232        }
1233        DBG_RETURN(ret);
1234}
1235/* }}} */
1236
1237
1238/* {{{ mysqlnd_conn_data::shutdown */
1239static enum_func_status
1240MYSQLND_METHOD(mysqlnd_conn_data, shutdown)(MYSQLND_CONN_DATA * const conn, uint8_t level)
1241{
1242        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), shutdown_server);
1243        enum_func_status ret = FAIL;
1244        DBG_ENTER("mysqlnd_conn_data::shutdown");
1245        DBG_INF_FMT("conn=%llu level=%lu", conn->thread_id, level);
1246
1247        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1248                unsigned int level_param = (unsigned int) level;
1249                struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_SHUTDOWN, conn, level_param);
1250                if (command) {
1251                        ret = command->run(command);
1252                        command->free_command(command);
1253                }
1254                conn->m->local_tx_end(conn, this_func, ret);
1255        }
1256        DBG_RETURN(ret);
1257}
1258/* }}} */
1259
1260
1261/* {{{ mysqlnd_send_close */
1262static enum_func_status
1263MYSQLND_METHOD(mysqlnd_conn_data, send_close)(MYSQLND_CONN_DATA * const conn)
1264{
1265        enum_func_status ret = PASS;
1266        MYSQLND_VIO * vio = conn->vio;
1267        php_stream * net_stream = vio->data->m.get_stream(vio);
1268        enum mysqlnd_connection_state state = GET_CONNECTION_STATE(&conn->state);
1269
1270        DBG_ENTER("mysqlnd_send_close");
1271        DBG_INF_FMT("conn=%llu vio->data->stream->abstract=%p", conn->thread_id, net_stream? net_stream->abstract:NULL);
1272        DBG_INF_FMT("state=%u", state);
1273
1274        if (state >= CONN_READY) {
1275                MYSQLND_DEC_GLOBAL_STATISTIC(STAT_OPENED_CONNECTIONS);
1276                if (conn->persistent) {
1277                        MYSQLND_DEC_GLOBAL_STATISTIC(STAT_OPENED_PERSISTENT_CONNECTIONS);
1278                }
1279        }
1280        switch (state) {
1281                case CONN_READY:
1282                        DBG_INF("Connection clean, sending COM_QUIT");
1283                        if (net_stream) {
1284                                struct st_mysqlnd_protocol_command * command = conn->command_factory(COM_QUIT, conn);
1285                                if (command) {
1286                                        ret = command->run(command);
1287                                        command->free_command(command);
1288                                }
1289                                vio->data->m.close_stream(vio, conn->stats, conn->error_info);
1290                        }
1291                        SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
1292                        break;
1293                case CONN_SENDING_LOAD_DATA:
1294                        /*
1295                          Don't send COM_QUIT if we are in a middle of a LOAD DATA or we
1296                          will crash (assert) a debug server.
1297                        */
1298                case CONN_NEXT_RESULT_PENDING:
1299                case CONN_QUERY_SENT:
1300                case CONN_FETCHING_DATA:
1301                        MYSQLND_INC_GLOBAL_STATISTIC(STAT_CLOSE_IN_MIDDLE);
1302                        DBG_ERR_FMT("Brutally closing connection [%p][%s]", conn, conn->scheme.s);
1303                        /*
1304                          Do nothing, the connection will be brutally closed
1305                          and the server will catch it and free close from its side.
1306                        */
1307                        /* Fall-through */
1308                case CONN_ALLOCED:
1309                        /*
1310                          Allocated but not connected or there was failure when trying
1311                          to connect with pre-allocated connect.
1312
1313                          Fall-through
1314                        */
1315                        SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
1316                        /* Fall-through */
1317                case CONN_QUIT_SENT:
1318                        /* The user has killed its own connection */
1319                        vio->data->m.close_stream(vio, conn->stats, conn->error_info);
1320                        break;
1321        }
1322
1323        DBG_RETURN(ret);
1324}
1325/* }}} */
1326
1327
1328/* {{{ mysqlnd_conn_data::get_reference */
1329static MYSQLND_CONN_DATA *
1330MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference)(MYSQLND_CONN_DATA * const conn)
1331{
1332        DBG_ENTER("mysqlnd_conn_data::get_reference");
1333        ++conn->refcount;
1334        DBG_INF_FMT("conn=%llu new_refcount=%u", conn->thread_id, conn->refcount);
1335        DBG_RETURN(conn);
1336}
1337/* }}} */
1338
1339
1340/* {{{ mysqlnd_conn_data::free_reference */
1341static enum_func_status
1342MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference)(MYSQLND_CONN_DATA * const conn)
1343{
1344        enum_func_status ret = PASS;
1345        DBG_ENTER("mysqlnd_conn_data::free_reference");
1346        DBG_INF_FMT("conn=%llu old_refcount=%u", conn->thread_id, conn->refcount);
1347        if (!(--conn->refcount)) {
1348                /*
1349                  No multithreading issues as we don't share the connection :)
1350                  This will free the object too, of course because references has
1351                  reached zero.
1352                */
1353                ret = conn->m->send_close(conn);
1354                conn->m->dtor(conn);
1355        }
1356        DBG_RETURN(ret);
1357}
1358/* }}} */
1359
1360
1361/* {{{ mysqlnd_conn_data::field_count */
1362static unsigned int
1363MYSQLND_METHOD(mysqlnd_conn_data, field_count)(const MYSQLND_CONN_DATA * const conn)
1364{
1365        return conn->field_count;
1366}
1367/* }}} */
1368
1369
1370/* {{{ mysqlnd_conn_data::server_status */
1371static unsigned int
1372MYSQLND_METHOD(mysqlnd_conn_data, server_status)(const MYSQLND_CONN_DATA * const conn)
1373{
1374        return UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status);
1375}
1376/* }}} */
1377
1378
1379/* {{{ mysqlnd_conn_data::insert_id */
1380static uint64_t
1381MYSQLND_METHOD(mysqlnd_conn_data, insert_id)(const MYSQLND_CONN_DATA * const conn)
1382{
1383        return UPSERT_STATUS_GET_LAST_INSERT_ID(conn->upsert_status);
1384}
1385/* }}} */
1386
1387
1388/* {{{ mysqlnd_conn_data::affected_rows */
1389static uint64_t
1390MYSQLND_METHOD(mysqlnd_conn_data, affected_rows)(const MYSQLND_CONN_DATA * const conn)
1391{
1392        return UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status);
1393}
1394/* }}} */
1395
1396
1397/* {{{ mysqlnd_conn_data::warning_count */
1398static unsigned int
1399MYSQLND_METHOD(mysqlnd_conn_data, warning_count)(const MYSQLND_CONN_DATA * const conn)
1400{
1401        return UPSERT_STATUS_GET_WARNINGS(conn->upsert_status);
1402}
1403/* }}} */
1404
1405
1406/* {{{ mysqlnd_conn_data::info */
1407static const char *
1408MYSQLND_METHOD(mysqlnd_conn_data, info)(const MYSQLND_CONN_DATA * const conn)
1409{
1410        return conn->last_message.s;
1411}
1412/* }}} */
1413
1414
1415/* {{{ mysqlnd_get_client_info */
1416PHPAPI const char * mysqlnd_get_client_info()
1417{
1418        return PHP_MYSQLND_VERSION;
1419}
1420/* }}} */
1421
1422
1423/* {{{ mysqlnd_get_client_version */
1424PHPAPI unsigned int mysqlnd_get_client_version()
1425{
1426        return MYSQLND_VERSION_ID;
1427}
1428/* }}} */
1429
1430
1431/* {{{ mysqlnd_conn_data::get_server_info */
1432static const char *
1433MYSQLND_METHOD(mysqlnd_conn_data, get_server_info)(const MYSQLND_CONN_DATA * const conn)
1434{
1435        return conn->server_version;
1436}
1437/* }}} */
1438
1439
1440/* {{{ mysqlnd_conn_data::get_host_info */
1441static const char *
1442MYSQLND_METHOD(mysqlnd_conn_data, get_host_info)(const MYSQLND_CONN_DATA * const conn)
1443{
1444        return conn->host_info;
1445}
1446/* }}} */
1447
1448
1449/* {{{ mysqlnd_conn_data::get_proto_info */
1450static unsigned int
1451MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info)(const MYSQLND_CONN_DATA * const conn)
1452{
1453        return conn->protocol_version;
1454}
1455/* }}} */
1456
1457
1458/* {{{ mysqlnd_conn_data::charset_name */
1459static const char *
1460MYSQLND_METHOD(mysqlnd_conn_data, charset_name)(const MYSQLND_CONN_DATA * const conn)
1461{
1462        return conn->charset->name;
1463}
1464/* }}} */
1465
1466
1467/* {{{ mysqlnd_conn_data::thread_id */
1468static uint64_t
1469MYSQLND_METHOD(mysqlnd_conn_data, thread_id)(const MYSQLND_CONN_DATA * const conn)
1470{
1471        return conn->thread_id;
1472}
1473/* }}} */
1474
1475
1476/* {{{ mysqlnd_conn_data::get_server_version */
1477static zend_ulong
1478MYSQLND_METHOD(mysqlnd_conn_data, get_server_version)(const MYSQLND_CONN_DATA * const conn)
1479{
1480        zend_long major, minor, patch;
1481        char *p;
1482
1483        if (!(p = conn->server_version)) {
1484                return 0;
1485        }
1486
1487        major = ZEND_STRTOL(p, &p, 10);
1488        p += 1; /* consume the dot */
1489        minor = ZEND_STRTOL(p, &p, 10);
1490        p += 1; /* consume the dot */
1491        patch = ZEND_STRTOL(p, &p, 10);
1492
1493        return (zend_ulong)(major * Z_L(10000) + (zend_ulong)(minor * Z_L(100) + patch));
1494}
1495/* }}} */
1496
1497
1498/* {{{ mysqlnd_conn_data::more_results */
1499static zend_bool
1500MYSQLND_METHOD(mysqlnd_conn_data, more_results)(const MYSQLND_CONN_DATA * const conn)
1501{
1502        DBG_ENTER("mysqlnd_conn_data::more_results");
1503        /* (conn->state == CONN_NEXT_RESULT_PENDING) too */
1504        DBG_RETURN(UPSERT_STATUS_GET_SERVER_STATUS(conn->upsert_status) & SERVER_MORE_RESULTS_EXISTS? TRUE:FALSE);
1505}
1506/* }}} */
1507
1508
1509/* {{{ mysqlnd_conn_data::next_result */
1510static enum_func_status
1511MYSQLND_METHOD(mysqlnd_conn_data, next_result)(MYSQLND_CONN_DATA * const conn)
1512{
1513        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), next_result);
1514        enum_func_status ret = FAIL;
1515
1516        DBG_ENTER("mysqlnd_conn_data::next_result");
1517        DBG_INF_FMT("conn=%llu", conn->thread_id);
1518
1519        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1520                do {
1521                        if (GET_CONNECTION_STATE(&conn->state) != CONN_NEXT_RESULT_PENDING) {
1522                                break;
1523                        }
1524
1525                        SET_EMPTY_ERROR(conn->error_info);
1526                        UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
1527                        /*
1528                          We are sure that there is a result set, since conn->state is set accordingly
1529                          in mysqlnd_store_result() or mysqlnd_fetch_row_unbuffered()
1530                        */
1531                        if (FAIL == (ret = conn->m->query_read_result_set_header(conn, NULL))) {
1532                                /*
1533                                  There can be an error in the middle of a multi-statement, which will cancel the multi-statement.
1534                                  So there are no more results and we should just return FALSE, error_no has been set
1535                                */
1536                                if (!conn->error_info->error_no) {
1537                                        DBG_ERR_FMT("Serious error. %s::%u", __FILE__, __LINE__);
1538                                        php_error_docref(NULL, E_WARNING, "Serious error. PID=%d", getpid());
1539                                        SET_CONNECTION_STATE(&conn->state, CONN_QUIT_SENT);
1540                                        conn->m->send_close(conn);
1541                                } else {
1542                                        DBG_INF_FMT("Error from the server : (%u) %s", conn->error_info->error_no, conn->error_info->error);
1543                                }
1544                                break;
1545                        }
1546                        if (conn->last_query_type == QUERY_UPSERT && UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status)) {
1547                                MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, UPSERT_STATUS_GET_AFFECTED_ROWS(conn->upsert_status));
1548                        }
1549                } while (0);
1550                conn->m->local_tx_end(conn, this_func, ret);
1551        }
1552
1553        DBG_RETURN(ret);
1554}
1555/* }}} */
1556
1557
1558/* {{{ mysqlnd_field_type_name */
1559PHPAPI const char * mysqlnd_field_type_name(const enum mysqlnd_field_types field_type)
1560{
1561        switch(field_type) {
1562                case FIELD_TYPE_JSON:
1563                        return "json";
1564                case FIELD_TYPE_STRING:
1565                case FIELD_TYPE_VAR_STRING:
1566                        return "string";
1567                case FIELD_TYPE_TINY:
1568                case FIELD_TYPE_SHORT:
1569                case FIELD_TYPE_LONG:
1570                case FIELD_TYPE_LONGLONG:
1571                case FIELD_TYPE_INT24:
1572                        return "int";
1573                case FIELD_TYPE_FLOAT:
1574                case FIELD_TYPE_DOUBLE:
1575                case FIELD_TYPE_DECIMAL:
1576                case FIELD_TYPE_NEWDECIMAL:
1577                        return "real";
1578                case FIELD_TYPE_TIMESTAMP:
1579                        return "timestamp";
1580                case FIELD_TYPE_YEAR:
1581                        return "year";
1582                case FIELD_TYPE_DATE:
1583                case FIELD_TYPE_NEWDATE:
1584                        return "date";
1585                case FIELD_TYPE_TIME:
1586                        return "time";
1587                case FIELD_TYPE_SET:
1588                        return "set";
1589                case FIELD_TYPE_ENUM:
1590                        return "enum";
1591                case FIELD_TYPE_GEOMETRY:
1592                        return "geometry";
1593                case FIELD_TYPE_DATETIME:
1594                        return "datetime";
1595                case FIELD_TYPE_TINY_BLOB:
1596                case FIELD_TYPE_MEDIUM_BLOB:
1597                case FIELD_TYPE_LONG_BLOB:
1598                case FIELD_TYPE_BLOB:
1599                        return "blob";
1600                case FIELD_TYPE_NULL:
1601                        return "null";
1602                case FIELD_TYPE_BIT:
1603                        return "bit";
1604                default:
1605                        return "unknown";
1606        }
1607}
1608/* }}} */
1609
1610
1611/* {{{ mysqlnd_conn_data::change_user */
1612static enum_func_status
1613MYSQLND_METHOD(mysqlnd_conn_data, change_user)(MYSQLND_CONN_DATA * const conn,
1614                                                                                           const char * user,
1615                                                                                           const char * passwd,
1616                                                                                           const char * db,
1617                                                                                           zend_bool silent,
1618                                                                                           size_t passwd_len
1619                        )
1620{
1621        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), change_user);
1622        enum_func_status ret = FAIL;
1623
1624        DBG_ENTER("mysqlnd_conn_data::change_user");
1625        DBG_INF_FMT("conn=%llu user=%s passwd=%s db=%s silent=%u",
1626                                conn->thread_id, user?user:"", passwd?"***":"null", db?db:"", (silent == TRUE)?1:0 );
1627
1628        if (PASS != conn->m->local_tx_start(conn, this_func)) {
1629                goto end;
1630        }
1631
1632        SET_EMPTY_ERROR(conn->error_info);
1633        UPSERT_STATUS_SET_AFFECTED_ROWS_TO_ERROR(conn->upsert_status);
1634
1635        if (!user) {
1636                user = "";
1637        }
1638        if (!passwd) {
1639                passwd = "";
1640                passwd_len = 0;
1641        }
1642        if (!db) {
1643                db = "";
1644        }
1645
1646        /* XXX: passwords that have \0 inside work during auth, but in this case won't work with change user */
1647        ret = mysqlnd_run_authentication(conn, user, passwd, passwd_len, db, strlen(db),
1648                                                                         conn->authentication_plugin_data, conn->options->auth_protocol,
1649                                                                        0 /*charset not used*/, conn->options, conn->server_capabilities, silent, TRUE/*is_change*/);
1650
1651        /*
1652          Here we should close all statements. Unbuffered queries should not be a
1653          problem as we won't allow sending COM_CHANGE_USER.
1654        */
1655        conn->m->local_tx_end(conn, this_func, ret);
1656end:
1657        DBG_INF(ret == PASS? "PASS":"FAIL");
1658        DBG_RETURN(ret);
1659}
1660/* }}} */
1661
1662
1663/* {{{ mysqlnd_conn_data::set_client_option */
1664static enum_func_status
1665MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const conn,
1666                                                                                                enum_mysqlnd_client_option option,
1667                                                                                                const char * const value
1668                                                                                                )
1669{
1670        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_client_option);
1671        enum_func_status ret = PASS;
1672        DBG_ENTER("mysqlnd_conn_data::set_client_option");
1673        DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
1674
1675        if (PASS != conn->m->local_tx_start(conn, this_func)) {
1676                goto end;
1677        }
1678        switch (option) {
1679#ifdef WHEN_SUPPORTED_BY_MYSQLI
1680                case MYSQL_OPT_READ_TIMEOUT:
1681                case MYSQL_OPT_WRITE_TIMEOUT:
1682#endif
1683                case MYSQLND_OPT_SSL_KEY:
1684                case MYSQLND_OPT_SSL_CERT:
1685                case MYSQLND_OPT_SSL_CA:
1686                case MYSQLND_OPT_SSL_CAPATH:
1687                case MYSQLND_OPT_SSL_CIPHER:
1688                case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
1689                case MYSQL_OPT_CONNECT_TIMEOUT:
1690                case MYSQLND_OPT_NET_READ_BUFFER_SIZE:
1691                        ret = conn->vio->data->m.set_client_option(conn->vio, option, value);
1692                        break;
1693                case MYSQLND_OPT_NET_CMD_BUFFER_SIZE:
1694                case MYSQL_OPT_COMPRESS:
1695                case MYSQL_SERVER_PUBLIC_KEY:
1696                        ret = conn->protocol_frame_codec->data->m.set_client_option(conn->protocol_frame_codec, option, value);
1697                        break;
1698#ifdef MYSQLND_STRING_TO_INT_CONVERSION
1699                case MYSQLND_OPT_INT_AND_FLOAT_NATIVE:
1700                        conn->options->int_and_float_native = *(unsigned int*) value;
1701                        break;
1702#endif
1703                case MYSQL_OPT_LOCAL_INFILE:
1704                        if (value && (*(unsigned int*) value) ? 1 : 0) {
1705                                conn->options->flags |= CLIENT_LOCAL_FILES;
1706                        } else {
1707                                conn->options->flags &= ~CLIENT_LOCAL_FILES;
1708                        }
1709                        break;
1710                case MYSQL_INIT_COMMAND:
1711                {
1712                        char ** new_init_commands;
1713                        char * new_command;
1714                        /* when num_commands is 0, then realloc will be effectively a malloc call, internally */
1715                        /* Don't assign to conn->options->init_commands because in case of OOM we will lose the pointer and leak */
1716                        new_init_commands = mnd_perealloc(conn->options->init_commands, sizeof(char *) * (conn->options->num_commands + 1), conn->persistent);
1717                        if (!new_init_commands) {
1718                                goto oom;
1719                        }
1720                        conn->options->init_commands = new_init_commands;
1721                        new_command = mnd_pestrdup(value, conn->persistent);
1722                        if (!new_command) {
1723                                goto oom;
1724                        }
1725                        conn->options->init_commands[conn->options->num_commands] = new_command;
1726                        ++conn->options->num_commands;
1727                        break;
1728                }
1729                case MYSQL_READ_DEFAULT_FILE:
1730                case MYSQL_READ_DEFAULT_GROUP:
1731#ifdef WHEN_SUPPORTED_BY_MYSQLI
1732                case MYSQL_SET_CLIENT_IP:
1733                case MYSQL_REPORT_DATA_TRUNCATION:
1734#endif
1735                        /* currently not supported. Todo!! */
1736                        break;
1737                case MYSQL_SET_CHARSET_NAME:
1738                {
1739                        char * new_charset_name;
1740                        if (!mysqlnd_find_charset_name(value)) {
1741                                SET_CLIENT_ERROR(conn->error_info, CR_CANT_FIND_CHARSET, UNKNOWN_SQLSTATE, "Unknown character set");
1742                                ret = FAIL;
1743                                break;
1744                        }
1745
1746                        new_charset_name = mnd_pestrdup(value, conn->persistent);
1747                        if (!new_charset_name) {
1748                                goto oom;
1749                        }
1750                        if (conn->options->charset_name) {
1751                                mnd_pefree(conn->options->charset_name, conn->persistent);
1752                        }
1753                        conn->options->charset_name = new_charset_name;
1754                        DBG_INF_FMT("charset=%s", conn->options->charset_name);
1755                        break;
1756                }
1757                case MYSQL_OPT_NAMED_PIPE:
1758                        conn->options->protocol = MYSQL_PROTOCOL_PIPE;
1759                        break;
1760                case MYSQL_OPT_PROTOCOL:
1761                        if (*(unsigned int*) value < MYSQL_PROTOCOL_LAST) {
1762                                conn->options->protocol = *(unsigned int*) value;
1763                        }
1764                        break;
1765#ifdef WHEN_SUPPORTED_BY_MYSQLI
1766                case MYSQL_SET_CHARSET_DIR:
1767                case MYSQL_OPT_RECONNECT:
1768                        /* we don't need external character sets, all character sets are
1769                           compiled in. For compatibility we just ignore this setting.
1770                           Same for protocol, we don't support old protocol */
1771                case MYSQL_OPT_USE_REMOTE_CONNECTION:
1772                case MYSQL_OPT_USE_EMBEDDED_CONNECTION:
1773                case MYSQL_OPT_GUESS_CONNECTION:
1774                        /* todo: throw an error, we don't support embedded */
1775                        break;
1776#endif
1777                case MYSQLND_OPT_MAX_ALLOWED_PACKET:
1778                        if (*(unsigned int*) value > (1<<16)) {
1779                                conn->options->max_allowed_packet = *(unsigned int*) value;
1780                        }
1781                        break;
1782                case MYSQLND_OPT_AUTH_PROTOCOL:
1783                {
1784                        char * new_auth_protocol = value? mnd_pestrdup(value, conn->persistent) : NULL;
1785                        if (value && !new_auth_protocol) {
1786                                goto oom;
1787                        }
1788                        if (conn->options->auth_protocol) {
1789                                mnd_pefree(conn->options->auth_protocol, conn->persistent);
1790                        }
1791                        conn->options->auth_protocol = new_auth_protocol;
1792                        DBG_INF_FMT("auth_protocol=%s", conn->options->auth_protocol);
1793                        break;
1794                }
1795                case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS:
1796                        if (value && (*(unsigned int*) value) ? 1 : 0) {
1797                                conn->options->flags |= CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
1798                        } else {
1799                                conn->options->flags &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
1800                        }
1801                        break;
1802                case MYSQL_OPT_CONNECT_ATTR_RESET:
1803                        if (conn->options->connect_attr) {
1804                                DBG_INF_FMT("Before reset %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
1805                                zend_hash_clean(conn->options->connect_attr);
1806                        }
1807                        break;
1808                case MYSQL_OPT_CONNECT_ATTR_DELETE:
1809                        if (conn->options->connect_attr && value) {
1810                                DBG_INF_FMT("Before delete %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr));
1811                                zend_hash_str_del(conn->options->connect_attr, value, strlen(value));
1812                                DBG_INF_FMT("%d left", zend_hash_num_elements(conn->options->connect_attr));
1813                        }
1814                        break;
1815#ifdef WHEN_SUPPORTED_BY_MYSQLI
1816                case MYSQL_SHARED_MEMORY_BASE_NAME:
1817                case MYSQL_OPT_USE_RESULT:
1818                case MYSQL_SECURE_AUTH:
1819                        /* not sure, todo ? */
1820#endif
1821                default:
1822                        ret = FAIL;
1823        }
1824        conn->m->local_tx_end(conn, this_func, ret);
1825        DBG_RETURN(ret);
1826oom:
1827        SET_OOM_ERROR(conn->error_info);
1828        conn->m->local_tx_end(conn, this_func, FAIL);
1829end:
1830        DBG_RETURN(FAIL);
1831}
1832/* }}} */
1833
1834
1835/* {{{ mysqlnd_conn_data::set_client_option_2d */
1836static enum_func_status
1837MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * const conn,
1838                                                                                                                const enum_mysqlnd_client_option option,
1839                                                                                                                const char * const key,
1840                                                                                                                const char * const value
1841                                                                                                                )
1842{
1843        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_client_option_2d);
1844        enum_func_status ret = PASS;
1845        DBG_ENTER("mysqlnd_conn_data::set_client_option_2d");
1846        DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option);
1847
1848        if (PASS != conn->m->local_tx_start(conn, this_func)) {
1849                goto end;
1850        }
1851        switch (option) {
1852                case MYSQL_OPT_CONNECT_ATTR_ADD:
1853                        if (!conn->options->connect_attr) {
1854                                DBG_INF("Initializing connect_attr hash");
1855                                conn->options->connect_attr = mnd_pemalloc(sizeof(HashTable), conn->persistent);
1856                                if (!conn->options->connect_attr) {
1857                                        goto oom;
1858                                }
1859                                zend_hash_init(conn->options->connect_attr, 0, NULL, ZVAL_PTR_DTOR, conn->persistent);
1860                        }
1861                        DBG_INF_FMT("Adding [%s][%s]", key, value);
1862                        {
1863                                zval attrz;
1864                                ZVAL_NEW_STR(&attrz, zend_string_init(value, strlen(value), 1));
1865                                zend_hash_str_update(conn->options->connect_attr, key, strlen(key), &attrz);
1866                        }
1867                        break;
1868                default:
1869                        ret = FAIL;
1870        }
1871        conn->m->local_tx_end(conn, this_func, ret);
1872        DBG_RETURN(ret);
1873oom:
1874        SET_OOM_ERROR(conn->error_info);
1875        conn->m->local_tx_end(conn, this_func, FAIL);
1876end:
1877        DBG_RETURN(FAIL);
1878}
1879/* }}} */
1880
1881
1882/* {{{ mysqlnd_conn_data::use_result */
1883static MYSQLND_RES *
1884MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
1885{
1886        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), use_result);
1887        MYSQLND_RES * result = NULL;
1888
1889        DBG_ENTER("mysqlnd_conn_data::use_result");
1890        DBG_INF_FMT("conn=%llu", conn->thread_id);
1891
1892        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1893                do {
1894                        if (!conn->current_result) {
1895                                break;
1896                        }
1897
1898                        /* Nothing to store for UPSERT/LOAD DATA */
1899                        if (conn->last_query_type != QUERY_SELECT || GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
1900                                SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
1901                                DBG_ERR("Command out of sync");
1902                                break;
1903                        }
1904
1905                        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_UNBUFFERED_SETS);
1906
1907                        conn->current_result->conn = conn->m->get_reference(conn);
1908                        result = conn->current_result->m.use_result(conn->current_result, FALSE);
1909
1910                        if (!result) {
1911                                conn->current_result->m.free_result(conn->current_result, TRUE);
1912                        }
1913                        conn->current_result = NULL;
1914                } while (0);
1915
1916                conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS);
1917        }
1918
1919        DBG_RETURN(result);
1920}
1921/* }}} */
1922
1923
1924/* {{{ mysqlnd_conn_data::store_result */
1925static MYSQLND_RES *
1926MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, const unsigned int flags)
1927{
1928        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), store_result);
1929        MYSQLND_RES * result = NULL;
1930
1931        DBG_ENTER("mysqlnd_conn_data::store_result");
1932        DBG_INF_FMT("conn=%llu conn=%p", conn->thread_id, conn);
1933
1934        if (PASS == conn->m->local_tx_start(conn, this_func)) {
1935                do {
1936                        unsigned int f = flags;
1937                        if (!conn->current_result) {
1938                                break;
1939                        }
1940
1941                        /* Nothing to store for UPSERT/LOAD DATA*/
1942                        if (conn->last_query_type != QUERY_SELECT || GET_CONNECTION_STATE(&conn->state) != CONN_FETCHING_DATA) {
1943                                SET_CLIENT_ERROR(conn->error_info, CR_COMMANDS_OUT_OF_SYNC, UNKNOWN_SQLSTATE, mysqlnd_out_of_sync);
1944                                DBG_ERR("Command out of sync");
1945                                break;
1946                        }
1947
1948                        MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS);
1949
1950                        /* overwrite */
1951                        if ((conn->m->get_client_api_capabilities(conn) & MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA)) {
1952                                if (MYSQLND_G(fetch_data_copy)) {
1953                                        f &= ~MYSQLND_STORE_NO_COPY;
1954                                        f |= MYSQLND_STORE_COPY;
1955                                }
1956                        } else {
1957                                /* if for some reason PDO borks something */
1958                                if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
1959                                        f |= MYSQLND_STORE_COPY;
1960                                }
1961                        }
1962                        if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) {
1963                                SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Unknown fetch mode");
1964                                DBG_ERR("Unknown fetch mode");
1965                                break;
1966                        }
1967                        result = conn->current_result->m.store_result(conn->current_result, conn, f);
1968                        if (!result) {
1969                                conn->current_result->m.free_result(conn->current_result, TRUE);
1970                        }
1971                        conn->current_result = NULL;
1972                } while (0);
1973
1974                conn->m->local_tx_end(conn, this_func, result == NULL? FAIL:PASS);
1975        }
1976        DBG_RETURN(result);
1977}
1978/* }}} */
1979
1980
1981/* {{{ mysqlnd_conn_data::get_connection_stats */
1982static void
1983MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats)(const MYSQLND_CONN_DATA * const conn,
1984                                                                                                                zval * return_value ZEND_FILE_LINE_DC)
1985{
1986        DBG_ENTER("mysqlnd_conn_data::get_connection_stats");
1987        mysqlnd_fill_stats_hash(conn->stats, mysqlnd_stats_values_names, return_value ZEND_FILE_LINE_CC);
1988        DBG_VOID_RETURN;
1989}
1990/* }}} */
1991
1992
1993/* {{{ mysqlnd_conn_data::set_autocommit */
1994static enum_func_status
1995MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit)(MYSQLND_CONN_DATA * conn, unsigned int mode)
1996{
1997        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), set_autocommit);
1998        enum_func_status ret = FAIL;
1999        DBG_ENTER("mysqlnd_conn_data::set_autocommit");
2000
2001        if (PASS == conn->m->local_tx_start(conn, this_func)) {
2002                ret = conn->m->query(conn, (mode) ? "SET AUTOCOMMIT=1":"SET AUTOCOMMIT=0", sizeof("SET AUTOCOMMIT=1") - 1);
2003                conn->m->local_tx_end(conn, this_func, ret);
2004        }
2005
2006        DBG_RETURN(ret);
2007}
2008/* }}} */
2009
2010
2011/* {{{ mysqlnd_conn_data::tx_commit */
2012static enum_func_status
2013MYSQLND_METHOD(mysqlnd_conn_data, tx_commit)(MYSQLND_CONN_DATA * conn)
2014{
2015        return conn->m->tx_commit_or_rollback(conn, TRUE, TRANS_COR_NO_OPT, NULL);
2016}
2017/* }}} */
2018
2019
2020/* {{{ mysqlnd_conn_data::tx_rollback */
2021static enum_func_status
2022MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback)(MYSQLND_CONN_DATA * conn)
2023{
2024        return conn->m->tx_commit_or_rollback(conn, FALSE, TRANS_COR_NO_OPT, NULL);
2025}
2026/* }}} */
2027
2028
2029/* {{{ mysqlnd_tx_cor_options_to_string */
2030static void
2031MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string)(const MYSQLND_CONN_DATA * const conn, smart_str * str, const unsigned int mode)
2032{
2033        if (mode & TRANS_COR_AND_CHAIN && !(mode & TRANS_COR_AND_NO_CHAIN)) {
2034                if (str->s && ZSTR_LEN(str->s)) {
2035                        smart_str_appendl(str, " ", sizeof(" ") - 1);
2036                }
2037                smart_str_appendl(str, "AND CHAIN", sizeof("AND CHAIN") - 1);
2038        } else if (mode & TRANS_COR_AND_NO_CHAIN && !(mode & TRANS_COR_AND_CHAIN)) {
2039                if (str->s && ZSTR_LEN(str->s)) {
2040                        smart_str_appendl(str, " ", sizeof(" ") - 1);
2041                }
2042                smart_str_appendl(str, "AND NO CHAIN", sizeof("AND NO CHAIN") - 1);
2043        }
2044
2045        if (mode & TRANS_COR_RELEASE && !(mode & TRANS_COR_NO_RELEASE)) {
2046                if (str->s && ZSTR_LEN(str->s)) {
2047                        smart_str_appendl(str, " ", sizeof(" ") - 1);
2048                }
2049                smart_str_appendl(str, "RELEASE", sizeof("RELEASE") - 1);
2050        } else if (mode & TRANS_COR_NO_RELEASE && !(mode & TRANS_COR_RELEASE)) {
2051                if (str->s && ZSTR_LEN(str->s)) {
2052                        smart_str_appendl(str, " ", sizeof(" ") - 1);
2053                }
2054                smart_str_appendl(str, "NO RELEASE", sizeof("NO RELEASE") - 1);
2055        }
2056        smart_str_0(str);
2057}
2058/* }}} */
2059
2060
2061/* {{{ mysqlnd_escape_string_for_tx_name_in_comment */
2062static char *
2063mysqlnd_escape_string_for_tx_name_in_comment(const char * const name)
2064{
2065        char * ret = NULL;
2066        DBG_ENTER("mysqlnd_escape_string_for_tx_name_in_comment");
2067        if (name) {
2068                zend_bool warned = FALSE;
2069                const char * p_orig = name;
2070                char * p_copy;
2071                p_copy = ret = mnd_emalloc(strlen(name) + 1 + 2 + 2 + 1); /* space, open, close, NullS */
2072                *p_copy++ = ' ';
2073                *p_copy++ = '/';
2074                *p_copy++ = '*';
2075                while (1) {
2076                        register char v = *p_orig;
2077                        if (v == 0) {
2078                                break;
2079                        }
2080                        if ((v >= '0' && v <= '9') ||
2081                                (v >= 'a' && v <= 'z') ||
2082                                (v >= 'A' && v <= 'Z') ||
2083                                v == '-' ||
2084                                v == '_' ||
2085                                v == ' ' ||
2086                                v == '=')
2087                        {
2088                                *p_copy++ = v;
2089                        } else if (warned == FALSE) {
2090                                php_error_docref(NULL, E_WARNING, "Transaction name truncated. Must be only [0-9A-Za-z\\-_=]+");
2091                                warned = TRUE;
2092                        }
2093                        ++p_orig;
2094                }
2095                *p_copy++ = '*';
2096                *p_copy++ = '/';
2097                *p_copy++ = 0;
2098        }
2099        DBG_RETURN(ret);
2100}
2101/* }}} */
2102
2103
2104/* {{{ mysqlnd_conn_data::tx_commit_ex */
2105static enum_func_status
2106MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback)(MYSQLND_CONN_DATA * conn, const zend_bool commit, const unsigned int flags, const char * const name)
2107{
2108        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_commit_or_rollback);
2109        enum_func_status ret = FAIL;
2110        DBG_ENTER("mysqlnd_conn_data::tx_commit_or_rollback");
2111
2112        if (PASS == conn->m->local_tx_start(conn, this_func)) {
2113                do {
2114                        smart_str tmp_str = {0, 0};
2115                        conn->m->tx_cor_options_to_string(conn, &tmp_str, flags);
2116                        smart_str_0(&tmp_str);
2117
2118
2119                        {
2120                                char * query;
2121                                size_t query_len;
2122                                char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name);
2123
2124                                query_len = mnd_sprintf(&query, 0, (commit? "COMMIT%s %s":"ROLLBACK%s %s"),
2125                                                                                name_esc? name_esc:"", tmp_str.s? ZSTR_VAL(tmp_str.s):"");
2126                                smart_str_free(&tmp_str);
2127                                if (name_esc) {
2128                                        mnd_efree(name_esc);
2129                                        name_esc = NULL;
2130                                }
2131                                if (!query) {
2132                                        SET_OOM_ERROR(conn->error_info);
2133                                        break;
2134                                }
2135
2136                                ret = conn->m->query(conn, query, query_len);
2137                                mnd_sprintf_free(query);
2138                        }
2139                } while (0);
2140                conn->m->local_tx_end(conn, this_func, ret);
2141        }
2142
2143        DBG_RETURN(ret);
2144}
2145/* }}} */
2146
2147
2148/* {{{ mysqlnd_conn_data::tx_begin */
2149static enum_func_status
2150MYSQLND_METHOD(mysqlnd_conn_data, tx_begin)(MYSQLND_CONN_DATA * conn, const unsigned int mode, const char * const name)
2151{
2152        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_begin);
2153        enum_func_status ret = FAIL;
2154        DBG_ENTER("mysqlnd_conn_data::tx_begin");
2155
2156        if (PASS == conn->m->local_tx_start(conn, this_func)) {
2157                do {
2158                        smart_str tmp_str = {0, 0};
2159                        if (mode & TRANS_START_WITH_CONSISTENT_SNAPSHOT) {
2160                                if (tmp_str.s) {
2161                                        smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
2162                                }
2163                                smart_str_appendl(&tmp_str, "WITH CONSISTENT SNAPSHOT", sizeof("WITH CONSISTENT SNAPSHOT") - 1);
2164                        }
2165                        if (mode & (TRANS_START_READ_WRITE | TRANS_START_READ_ONLY)) {
2166                                zend_ulong server_version = conn->m->get_server_version(conn);
2167                                if (server_version < 50605L) {
2168                                        php_error_docref(NULL, E_WARNING, "This server version doesn't support 'READ WRITE' and 'READ ONLY'. Minimum 5.6.5 is required");
2169                                        smart_str_free(&tmp_str);
2170                                        break;
2171                                } else if (mode & TRANS_START_READ_WRITE) {
2172                                        if (tmp_str.s && ZSTR_LEN(tmp_str.s)) {
2173                                                smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
2174                                        }
2175                                        smart_str_appendl(&tmp_str, "READ WRITE", sizeof("READ WRITE") - 1);
2176                                } else if (mode & TRANS_START_READ_ONLY) {
2177                                        if (tmp_str.s && ZSTR_LEN(tmp_str.s)) {
2178                                                smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
2179                                        }
2180                                        smart_str_appendl(&tmp_str, "READ ONLY", sizeof("READ ONLY") - 1);
2181                                }
2182                        }
2183                        smart_str_0(&tmp_str);
2184
2185                        {
2186                                char * name_esc = mysqlnd_escape_string_for_tx_name_in_comment(name);
2187                                char * query;
2188                                unsigned int query_len = mnd_sprintf(&query, 0, "START TRANSACTION%s %s", name_esc? name_esc:"", tmp_str.s? ZSTR_VAL(tmp_str.s):"");
2189                                smart_str_free(&tmp_str);
2190                                if (name_esc) {
2191                                        mnd_efree(name_esc);
2192                                        name_esc = NULL;
2193                                }
2194                                if (!query) {
2195                                        SET_OOM_ERROR(conn->error_info);
2196                                        break;
2197                                }
2198                                ret = conn->m->query(conn, query, query_len);
2199                                mnd_sprintf_free(query);
2200                        }
2201                } while (0);
2202                conn->m->local_tx_end(conn, this_func, ret);
2203        }
2204
2205        DBG_RETURN(ret);
2206}
2207/* }}} */
2208
2209
2210/* {{{ mysqlnd_conn_data::tx_savepoint */
2211static enum_func_status
2212MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint)(MYSQLND_CONN_DATA * conn, const char * const name)
2213{
2214        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_savepoint);
2215        enum_func_status ret = FAIL;
2216        DBG_ENTER("mysqlnd_conn_data::tx_savepoint");
2217
2218        if (PASS == conn->m->local_tx_start(conn, this_func)) {
2219                do {
2220                        char * query;
2221                        unsigned int query_len;
2222                        if (!name) {
2223                                SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
2224                                break;
2225                        }
2226                        query_len = mnd_sprintf(&query, 0, "SAVEPOINT `%s`", name);
2227                        if (!query) {
2228                                SET_OOM_ERROR(conn->error_info);
2229                                break;
2230                        }
2231                        ret = conn->m->query(conn, query, query_len);
2232                        mnd_sprintf_free(query);
2233                } while (0);
2234                conn->m->local_tx_end(conn, this_func, ret);
2235        }
2236
2237        DBG_RETURN(ret);
2238}
2239/* }}} */
2240
2241
2242/* {{{ mysqlnd_conn_data::tx_savepoint_release */
2243static enum_func_status
2244MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn, const char * const name)
2245{
2246        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn_data), tx_savepoint_release);
2247        enum_func_status ret = FAIL;
2248        DBG_ENTER("mysqlnd_conn_data::tx_savepoint_release");
2249
2250        if (PASS == conn->m->local_tx_start(conn, this_func)) {
2251                do {
2252                        char * query;
2253                        unsigned int query_len;
2254                        if (!name) {
2255                                SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Savepoint name not provided");
2256                                break;
2257                        }
2258                        query_len = mnd_sprintf(&query, 0, "RELEASE SAVEPOINT `%s`", name);
2259                        if (!query) {
2260                                SET_OOM_ERROR(conn->error_info);
2261                                break;
2262                        }
2263                        ret = conn->m->query(conn, query, query_len);
2264                        mnd_sprintf_free(query);
2265                } while (0);
2266                conn->m->local_tx_end(conn, this_func, ret);
2267        }
2268
2269        DBG_RETURN(ret);
2270}
2271/* }}} */
2272
2273
2274/* {{{ mysqlnd_conn_data::negotiate_client_api_capabilities */
2275static size_t
2276MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const size_t flags)
2277{
2278        unsigned int ret = 0;
2279        DBG_ENTER("mysqlnd_conn_data::negotiate_client_api_capabilities");
2280        if (conn) {
2281                ret = conn->client_api_capabilities;
2282                conn->client_api_capabilities = flags;
2283        }
2284
2285        DBG_RETURN(ret);
2286}
2287/* }}} */
2288
2289
2290/* {{{ mysqlnd_conn_data::get_client_api_capabilities */
2291static size_t
2292MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn)
2293{
2294        DBG_ENTER("mysqlnd_conn_data::get_client_api_capabilities");
2295        DBG_RETURN(conn? conn->client_api_capabilities : 0);
2296}
2297/* }}} */
2298
2299
2300/* {{{ mysqlnd_conn_data::local_tx_start */
2301static enum_func_status
2302MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, const size_t this_func)
2303{
2304        DBG_ENTER("mysqlnd_conn_data::local_tx_start");
2305        DBG_RETURN(PASS);
2306}
2307/* }}} */
2308
2309
2310/* {{{ mysqlnd_conn_data::local_tx_end */
2311static enum_func_status
2312MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end)(MYSQLND_CONN_DATA * conn, const size_t this_func, const enum_func_status status)
2313{
2314        DBG_ENTER("mysqlnd_conn_data::local_tx_end");
2315        DBG_RETURN(status);
2316}
2317/* }}} */
2318
2319
2320/* {{{ _mysqlnd_stmt_init */
2321MYSQLND_STMT *
2322MYSQLND_METHOD(mysqlnd_conn_data, stmt_init)(MYSQLND_CONN_DATA * const conn)
2323{
2324        MYSQLND_STMT * ret;
2325        DBG_ENTER("mysqlnd_conn_data::stmt_init");
2326        ret = conn->object_factory.get_prepared_statement(conn, conn->persistent);
2327        DBG_RETURN(ret);
2328}
2329/* }}} */
2330
2331
2332MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data)
2333        MYSQLND_METHOD(mysqlnd_conn_data, connect),
2334
2335        MYSQLND_METHOD(mysqlnd_conn_data, escape_string),
2336        MYSQLND_METHOD(mysqlnd_conn_data, set_charset),
2337        MYSQLND_METHOD(mysqlnd_conn_data, query),
2338        MYSQLND_METHOD(mysqlnd_conn_data, send_query),
2339        MYSQLND_METHOD(mysqlnd_conn_data, reap_query),
2340        MYSQLND_METHOD(mysqlnd_conn_data, use_result),
2341        MYSQLND_METHOD(mysqlnd_conn_data, store_result),
2342        MYSQLND_METHOD(mysqlnd_conn_data, next_result),
2343        MYSQLND_METHOD(mysqlnd_conn_data, more_results),
2344
2345        MYSQLND_METHOD(mysqlnd_conn_data, stmt_init),
2346
2347        MYSQLND_METHOD(mysqlnd_conn_data, shutdown),
2348        MYSQLND_METHOD(mysqlnd_conn_data, refresh),
2349
2350        MYSQLND_METHOD(mysqlnd_conn_data, ping),
2351        MYSQLND_METHOD(mysqlnd_conn_data, kill),
2352        MYSQLND_METHOD(mysqlnd_conn_data, select_db),
2353        MYSQLND_METHOD(mysqlnd_conn_data, dump_debug_info),
2354        MYSQLND_METHOD(mysqlnd_conn_data, change_user),
2355
2356        MYSQLND_METHOD(mysqlnd_conn_data, err_no),
2357        MYSQLND_METHOD(mysqlnd_conn_data, error),
2358        MYSQLND_METHOD(mysqlnd_conn_data, sqlstate),
2359        MYSQLND_METHOD(mysqlnd_conn_data, thread_id),
2360
2361        MYSQLND_METHOD(mysqlnd_conn_data, get_connection_stats),
2362
2363        MYSQLND_METHOD(mysqlnd_conn_data, get_server_version),
2364        MYSQLND_METHOD(mysqlnd_conn_data, get_server_info),
2365        MYSQLND_METHOD(mysqlnd_conn_data, statistic),
2366        MYSQLND_METHOD(mysqlnd_conn_data, get_host_info),
2367        MYSQLND_METHOD(mysqlnd_conn_data, get_proto_info),
2368        MYSQLND_METHOD(mysqlnd_conn_data, info),
2369        MYSQLND_METHOD(mysqlnd_conn_data, charset_name),
2370        MYSQLND_METHOD(mysqlnd_conn_data, list_method),
2371
2372        MYSQLND_METHOD(mysqlnd_conn_data, insert_id),
2373        MYSQLND_METHOD(mysqlnd_conn_data, affected_rows),
2374        MYSQLND_METHOD(mysqlnd_conn_data, warning_count),
2375        MYSQLND_METHOD(mysqlnd_conn_data, field_count),
2376
2377        MYSQLND_METHOD(mysqlnd_conn_data, server_status),
2378
2379        MYSQLND_METHOD(mysqlnd_conn_data, set_server_option),
2380        MYSQLND_METHOD(mysqlnd_conn_data, set_client_option),
2381        MYSQLND_METHOD(mysqlnd_conn_data, free_contents),
2382        MYSQLND_METHOD(mysqlnd_conn_data, free_options),
2383
2384        MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, dtor),
2385
2386        mysqlnd_query_read_result_set_header,
2387
2388        MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, get_reference),
2389        MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data, free_reference),
2390
2391        MYSQLND_METHOD(mysqlnd_conn_data, restart_psession),
2392        MYSQLND_METHOD(mysqlnd_conn_data, end_psession),
2393        MYSQLND_METHOD(mysqlnd_conn_data, send_close),
2394
2395        MYSQLND_METHOD(mysqlnd_conn_data, ssl_set),
2396        mysqlnd_result_init,
2397        MYSQLND_METHOD(mysqlnd_conn_data, set_autocommit),
2398        MYSQLND_METHOD(mysqlnd_conn_data, tx_commit),
2399        MYSQLND_METHOD(mysqlnd_conn_data, tx_rollback),
2400        MYSQLND_METHOD(mysqlnd_conn_data, tx_begin),
2401        MYSQLND_METHOD(mysqlnd_conn_data, tx_commit_or_rollback),
2402        MYSQLND_METHOD(mysqlnd_conn_data, tx_cor_options_to_string),
2403        MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint),
2404        MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release),
2405
2406        MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start),
2407        MYSQLND_METHOD(mysqlnd_conn_data, local_tx_end),
2408        MYSQLND_METHOD(mysqlnd_conn_data, execute_init_commands),
2409        MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags),
2410        MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake),
2411        MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name),
2412
2413        MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d),
2414
2415        MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities),
2416        MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities),
2417
2418        MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)
2419MYSQLND_CLASS_METHODS_END;
2420
2421
2422/* {{{ mysqlnd_conn::get_reference */
2423static MYSQLND *
2424MYSQLND_METHOD(mysqlnd_conn, clone_object)(MYSQLND * const conn)
2425{
2426        MYSQLND * ret;
2427        DBG_ENTER("mysqlnd_conn::get_reference");
2428        ret = conn->data->object_factory.clone_connection_object(conn);
2429        DBG_RETURN(ret);
2430}
2431/* }}} */
2432
2433
2434/* {{{ mysqlnd_conn_data::dtor */
2435static void
2436MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor)(MYSQLND * conn)
2437{
2438        DBG_ENTER("mysqlnd_conn::dtor");
2439        DBG_INF_FMT("conn=%llu", conn->data->thread_id);
2440
2441        conn->data->m->free_reference(conn->data);
2442
2443        mnd_pefree(conn, conn->persistent);
2444
2445        DBG_VOID_RETURN;
2446}
2447/* }}} */
2448
2449
2450/* {{{ mysqlnd_conn_data::close */
2451static enum_func_status
2452MYSQLND_METHOD(mysqlnd_conn, close)(MYSQLND * conn_handle, const enum_connection_close_type close_type)
2453{
2454        const size_t this_func = STRUCT_OFFSET(MYSQLND_CLASS_METHODS_TYPE(mysqlnd_conn), close);
2455        MYSQLND_CONN_DATA * conn = conn_handle->data;
2456        enum_func_status ret = FAIL;
2457
2458        DBG_ENTER("mysqlnd_conn::close");
2459        DBG_INF_FMT("conn=%llu", conn->thread_id);
2460
2461        if (PASS == conn->m->local_tx_start(conn, this_func)) {
2462                if (GET_CONNECTION_STATE(&conn->state) >= CONN_READY) {
2463                        static enum_mysqlnd_collected_stats close_type_to_stat_map[MYSQLND_CLOSE_LAST] = {
2464                                STAT_CLOSE_EXPLICIT,
2465                                STAT_CLOSE_IMPLICIT,
2466                                STAT_CLOSE_DISCONNECT
2467                        };
2468                        MYSQLND_INC_CONN_STATISTIC(conn->stats, close_type_to_stat_map[close_type]);
2469                }
2470
2471                /*
2472                  Close now, free_reference will try,
2473                  if we are last, but that's not a problem.
2474                */
2475                ret = conn->m->send_close(conn);
2476
2477                /* If we do it after free_reference/dtor then we might crash */
2478                conn->m->local_tx_end(conn, this_func, ret);
2479
2480                conn_handle->m->dtor(conn_handle);
2481        }
2482        DBG_RETURN(ret);
2483}
2484/* }}} */
2485
2486
2487MYSQLND_CLASS_METHODS_START(mysqlnd_conn)
2488        MYSQLND_METHOD(mysqlnd_conn, connect),
2489        MYSQLND_METHOD(mysqlnd_conn, clone_object),
2490        MYSQLND_METHOD_PRIVATE(mysqlnd_conn, dtor),
2491        MYSQLND_METHOD(mysqlnd_conn, close)
2492MYSQLND_CLASS_METHODS_END;
2493
2494
2495#include "php_network.h"
2496
2497/* {{{ mysqlnd_stream_array_to_fd_set */
2498MYSQLND **
2499mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array)
2500{
2501        unsigned int cnt = 0;
2502        MYSQLND **p = conn_array, **p_p;
2503        MYSQLND **ret = NULL;
2504
2505        while (*p) {
2506                const enum mysqlnd_connection_state conn_state = GET_CONNECTION_STATE(&((*p)->data->state));
2507                if (conn_state <= CONN_READY || conn_state == CONN_QUIT_SENT) {
2508                        cnt++;
2509                }
2510                p++;
2511        }
2512        if (cnt) {
2513                MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
2514                p_p = p = conn_array;
2515                while (*p) {
2516                        const enum mysqlnd_connection_state conn_state = GET_CONNECTION_STATE(&((*p)->data->state));
2517                        if (conn_state <= CONN_READY || conn_state == CONN_QUIT_SENT) {
2518                                *ret_p = *p;
2519                                *p = NULL;
2520                                ret_p++;
2521                        } else {
2522                                *p_p = *p;
2523                                p_p++;
2524                        }
2525                        p++;
2526                }
2527                *ret_p = NULL;
2528        }
2529        return ret;
2530}
2531/* }}} */
2532
2533
2534/* {{{ mysqlnd_stream_array_to_fd_set */
2535static unsigned int
2536mysqlnd_stream_array_to_fd_set(MYSQLND ** conn_array, fd_set * fds, php_socket_t * max_fd)
2537{
2538        php_socket_t this_fd;
2539        php_stream *stream = NULL;
2540        unsigned int cnt = 0;
2541        MYSQLND **p = conn_array;
2542        DBG_ENTER("mysqlnd_stream_array_to_fd_set");
2543
2544        while (*p) {
2545                /* get the fd.
2546                 * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
2547                 * when casting.  It is only used here so that the buffered data warning
2548                 * is not displayed.
2549                 * */
2550                stream = (*p)->data->vio->data->m.get_stream((*p)->data->vio);
2551                DBG_INF_FMT("conn=%llu stream=%p", (*p)->data->thread_id, stream);
2552                if (stream != NULL &&
2553                        SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&this_fd, 1) &&
2554                        ZEND_VALID_SOCKET(this_fd))
2555                {
2556
2557                        PHP_SAFE_FD_SET(this_fd, fds);
2558
2559                        if (this_fd > *max_fd) {
2560                                *max_fd = this_fd;
2561                        }
2562                        ++cnt;
2563                }
2564                ++p;
2565        }
2566        DBG_RETURN(cnt ? 1 : 0);
2567}
2568/* }}} */
2569
2570
2571/* {{{ mysqlnd_stream_array_from_fd_set */
2572static unsigned int
2573mysqlnd_stream_array_from_fd_set(MYSQLND ** conn_array, fd_set * fds)
2574{
2575        php_socket_t this_fd;
2576        php_stream *stream = NULL;
2577        unsigned int ret = 0;
2578        zend_bool disproportion = FALSE;
2579        MYSQLND **fwd = conn_array, **bckwd = conn_array;
2580        DBG_ENTER("mysqlnd_stream_array_from_fd_set");
2581
2582        while (*fwd) {
2583                stream = (*fwd)->data->vio->data->m.get_stream((*fwd)->data->vio);
2584                DBG_INF_FMT("conn=%llu stream=%p", (*fwd)->data->thread_id, stream);
2585                if (stream != NULL && SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
2586                                                                                (void*)&this_fd, 1) && ZEND_VALID_SOCKET(this_fd)) {
2587                        if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
2588                                if (disproportion) {
2589                                        *bckwd = *fwd;
2590                                }
2591                                ++bckwd;
2592                                ++fwd;
2593                                ++ret;
2594                                continue;
2595                        }
2596                }
2597                disproportion = TRUE;
2598                ++fwd;
2599        }
2600        *bckwd = NULL;/* NULL-terminate the list */
2601
2602        DBG_RETURN(ret);
2603}
2604/* }}} */
2605
2606
2607#ifndef PHP_WIN32
2608#define php_select(m, r, w, e, t)       select(m, r, w, e, t)
2609#else
2610#include "win32/select.h"
2611#endif
2612
2613
2614/* {{{ mysqlnd_poll */
2615PHPAPI enum_func_status
2616mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num)
2617{
2618        struct timeval  tv;
2619        struct timeval *tv_p = NULL;
2620        fd_set                  rfds, wfds, efds;
2621        php_socket_t    max_fd = 0;
2622        int                             retval, sets = 0;
2623        int                             set_count, max_set_count = 0;
2624
2625        DBG_ENTER("_mysqlnd_poll");
2626        if (sec < 0 || usec < 0) {
2627                php_error_docref(NULL, E_WARNING, "Negative values passed for sec and/or usec");
2628                DBG_RETURN(FAIL);
2629        }
2630
2631        FD_ZERO(&rfds);
2632        FD_ZERO(&wfds);
2633        FD_ZERO(&efds);
2634
2635        if (r_array != NULL) {
2636                *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array);
2637                set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd);
2638                if (set_count > max_set_count) {
2639                        max_set_count = set_count;
2640                }
2641                sets += set_count;
2642        }
2643
2644        if (e_array != NULL) {
2645                set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd);
2646                if (set_count > max_set_count) {
2647                        max_set_count = set_count;
2648                }
2649                sets += set_count;
2650        }
2651
2652        if (!sets) {
2653                php_error_docref(NULL, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
2654                DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
2655                DBG_RETURN(FAIL);
2656        }
2657
2658        PHP_SAFE_MAX_FD(max_fd, max_set_count);
2659
2660        /* Solaris + BSD do not like microsecond values which are >= 1 sec */
2661        if (usec > 999999) {
2662                tv.tv_sec = sec + (usec / 1000000);
2663                tv.tv_usec = usec % 1000000;
2664        } else {
2665                tv.tv_sec = sec;
2666                tv.tv_usec = usec;
2667        }
2668
2669        tv_p = &tv;
2670
2671        retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
2672
2673        if (retval == -1) {
2674                php_error_docref(NULL, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
2675                                                errno, strerror(errno), max_fd);
2676                DBG_RETURN(FAIL);
2677        }
2678
2679        if (r_array != NULL) {
2680                mysqlnd_stream_array_from_fd_set(r_array, &rfds);
2681        }
2682        if (e_array != NULL) {
2683                mysqlnd_stream_array_from_fd_set(e_array, &efds);
2684        }
2685
2686        *desc_num = retval;
2687        DBG_RETURN(PASS);
2688}
2689/* }}} */
2690
2691
2692/* {{{ mysqlnd_connect */
2693PHPAPI MYSQLND * mysqlnd_connection_connect(MYSQLND * conn_handle,
2694                                                                                        const char * const host,
2695                                                                                        const char * const user,
2696                                                                                        const char * const passwd, unsigned int passwd_len,
2697                                                                                        const char * const db, unsigned int db_len,
2698                                                                                        unsigned int port,
2699                                                                                        const char * const sock_or_pipe,
2700                                                                                        unsigned int mysql_flags,
2701                                                                                        unsigned int client_api_flags
2702                                                )
2703{
2704        enum_func_status ret = FAIL;
2705        zend_bool self_alloced = FALSE;
2706        MYSQLND_CSTRING hostname = { host, host? strlen(host) : 0 };
2707        MYSQLND_CSTRING username = { user, user? strlen(user) : 0 };
2708        MYSQLND_CSTRING password = { passwd, passwd_len };
2709        MYSQLND_CSTRING database = { db, db_len };
2710        MYSQLND_CSTRING socket_or_pipe = { sock_or_pipe, sock_or_pipe? strlen(sock_or_pipe) : 0 };
2711
2712        DBG_ENTER("mysqlnd_connect");
2713        DBG_INF_FMT("host=%s user=%s db=%s port=%u flags=%u", host? host:"", user? user:"", db? db:"", port, mysql_flags);
2714
2715        if (!conn_handle) {
2716                self_alloced = TRUE;
2717                if (!(conn_handle = mysqlnd_connection_init(client_api_flags, FALSE, NULL))) {
2718                        /* OOM */
2719                        DBG_RETURN(NULL);
2720                }
2721        }
2722
2723        ret = conn_handle->m->connect(conn_handle, hostname, username, password, database, port, socket_or_pipe, mysql_flags);
2724
2725        if (ret == FAIL) {
2726                if (self_alloced) {
2727                        /*
2728                          We have alloced, thus there are no references to this
2729                          object - we are free to kill it!
2730                        */
2731                        conn_handle->m->dtor(conn_handle);
2732                }
2733                DBG_RETURN(NULL);
2734        }
2735        DBG_RETURN(conn_handle);
2736}
2737/* }}} */
2738
2739
2740/* {{{ mysqlnd_connection_init */
2741PHPAPI MYSQLND *
2742mysqlnd_connection_init(const size_t client_flags, const zend_bool persistent, MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *object_factory)
2743{
2744        MYSQLND_CLASS_METHODS_TYPE(mysqlnd_object_factory) *factory = object_factory? object_factory : &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory);
2745        MYSQLND * ret;
2746        DBG_ENTER("mysqlnd_connection_init");
2747        ret = factory->get_connection(factory, persistent);
2748        if (ret && ret->data) {
2749                ret->data->m->negotiate_client_api_capabilities(ret->data, client_flags);
2750        }
2751        DBG_RETURN(ret);
2752}
2753/* }}} */
2754
2755
2756/*
2757 * Local variables:
2758 * tab-width: 4
2759 * c-basic-offset: 4
2760 * End:
2761 * vim600: noet sw=4 ts=4 fdm=marker
2762 * vim<600: noet sw=4 ts=4
2763 */
Note: See TracBrowser for help on using the repository browser.