1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2018 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
36 extern MYSQLND_CHARSET *mysqlnd_charsets;
37
38 PHPAPI const char * const mysqlnd_server_gone = "MySQL server has gone away";
39 PHPAPI const char * const mysqlnd_out_of_sync = "Commands out of sync; you can't run this command now";
40 PHPAPI const char * const mysqlnd_out_of_memory = "Out of memory";
41
42 PHPAPI MYSQLND_STATS * mysqlnd_global_stats = NULL;
43
44
45 /* {{{ mysqlnd_upsert_status::reset */
46 void
MYSQLND_METHOD(mysqlnd_upsert_status,reset)47 MYSQLND_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 */
58 void
MYSQLND_METHOD(mysqlnd_upsert_status,set_affected_rows_to_error)59 MYSQLND_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
66 MYSQLND_CLASS_METHODS_START(mysqlnd_upsert_status)
67 MYSQLND_METHOD(mysqlnd_upsert_status, reset),
68 MYSQLND_METHOD(mysqlnd_upsert_status, set_affected_rows_to_error),
69 MYSQLND_CLASS_METHODS_END;
70
71
72 /* {{{ mysqlnd_upsert_status_init */
73 void
mysqlnd_upsert_status_init(MYSQLND_UPSERT_STATUS * const upsert_status)74 mysqlnd_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 */
83 static void
mysqlnd_error_list_pdtor(void * pDest)84 mysqlnd_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 */
98 static void
MYSQLND_METHOD(mysqlnd_error_info,reset)99 MYSQLND_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 */
116 static void
MYSQLND_METHOD(mysqlnd_error_info,set_client_error)117 MYSQLND_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
146 MYSQLND_CLASS_METHODS_START(mysqlnd_error_info)
147 MYSQLND_METHOD(mysqlnd_error_info, reset),
148 MYSQLND_METHOD(mysqlnd_error_info, set_client_error),
149 MYSQLND_CLASS_METHODS_END;
150
151
152
153 /* {{{ mysqlnd_error_info_init */
154 PHPAPI enum_func_status
mysqlnd_error_info_init(MYSQLND_ERROR_INFO * const info,const zend_bool persistent)155 mysqlnd_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 */
172 PHPAPI void
mysqlnd_error_info_free_contents(MYSQLND_ERROR_INFO * const info)173 mysqlnd_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 */
190 static enum mysqlnd_connection_state
MYSQLND_METHOD(mysqlnd_connection_state,get)191 MYSQLND_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 */
201 static void
MYSQLND_METHOD(mysqlnd_connection_state,set)202 MYSQLND_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
212 MYSQLND_CLASS_METHODS_START(mysqlnd_connection_state)
213 MYSQLND_METHOD(mysqlnd_connection_state, get),
214 MYSQLND_METHOD(mysqlnd_connection_state, set),
215 MYSQLND_CLASS_METHODS_END;
216
217
218 /* {{{ mysqlnd_connection_state_init */
219 PHPAPI void
mysqlnd_connection_state_init(struct st_mysqlnd_connection_state * const state)220 mysqlnd_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 */
232 static void
MYSQLND_METHOD(mysqlnd_conn_data,free_options)233 MYSQLND_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 */
272 static void
MYSQLND_METHOD(mysqlnd_conn_data,free_contents)273 MYSQLND_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 */
345 static void
MYSQLND_METHOD_PRIVATE(mysqlnd_conn_data,dtor)346 MYSQLND_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 */
386 static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data,set_server_option)387 MYSQLND_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 */
407 static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data,restart_psession)408 MYSQLND_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 */
423 static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data,end_psession)424 MYSQLND_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 */
433 static struct st_mysqlnd_authentication_plugin *
MYSQLND_METHOD(mysqlnd_conn_data,fetch_auth_plugin_by_name)434 MYSQLND_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 */
451 static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data,execute_init_commands)452 MYSQLND_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 */
483 static unsigned int
MYSQLND_METHOD(mysqlnd_conn_data,get_updated_connect_flags)484 MYSQLND_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 */
527 static enum_func_status
MYSQLND_METHOD(mysqlnd_conn_data,connect_handshake)528 MYSQLND_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 */
553 static MYSQLND_STRING
MYSQLND_METHOD(mysqlnd_conn_data,get_scheme)554 MYSQLND_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 */
590 static enum_func_status
591 MYSQLND_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 }
793 err:
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 */
817 static enum_func_status
818 MYSQLND_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 */
849 static enum_func_status
850 MYSQLND_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 */
874 static enum_func_status
875 MYSQLND_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 */
904 static enum_func_status
905 MYSQLND_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 */
932 MYSQLND_RES *
933 MYSQLND_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 */
972 static unsigned int
973 MYSQLND_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 */
981 static const char *
982 MYSQLND_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 */
990 static const char *
991 MYSQLND_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 */
999 PHPAPI zend_ulong
1000 mysqlnd_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 */
1009 static enum_func_status
1010 MYSQLND_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 */
1033 static zend_ulong
1034 MYSQLND_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 */
1056 static enum_func_status
1057 MYSQLND_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 */
1079 static enum_func_status
1080 MYSQLND_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 */
1104 static enum_func_status
1105 MYSQLND_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 */
1128 static enum_func_status
1129 MYSQLND_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 */
1151 static enum_func_status
1152 MYSQLND_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 */
1177 static enum_func_status
1178 MYSQLND_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 */
1216 static enum_func_status
1217 MYSQLND_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 */
1239 static enum_func_status
1240 MYSQLND_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 */
1262 static enum_func_status
1263 MYSQLND_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 */
1329 static MYSQLND_CONN_DATA *
1330 MYSQLND_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 */
1341 static enum_func_status
1342 MYSQLND_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 */
1362 static unsigned int
1363 MYSQLND_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 */
1371 static unsigned int
1372 MYSQLND_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 */
1380 static uint64_t
1381 MYSQLND_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 */
1389 static uint64_t
1390 MYSQLND_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 */
1398 static unsigned int
1399 MYSQLND_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 */
1407 static const char *
1408 MYSQLND_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 */
1416 PHPAPI const char * mysqlnd_get_client_info()
1417 {
1418 return PHP_MYSQLND_VERSION;
1419 }
1420 /* }}} */
1421
1422
1423 /* {{{ mysqlnd_get_client_version */
1424 PHPAPI unsigned int mysqlnd_get_client_version()
1425 {
1426 return MYSQLND_VERSION_ID;
1427 }
1428 /* }}} */
1429
1430
1431 /* {{{ mysqlnd_conn_data::get_server_info */
1432 static const char *
1433 MYSQLND_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 */
1441 static const char *
1442 MYSQLND_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 */
1450 static unsigned int
1451 MYSQLND_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 */
1459 static const char *
1460 MYSQLND_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 */
1468 static uint64_t
1469 MYSQLND_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 */
1477 static zend_ulong
1478 MYSQLND_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 */
1499 static zend_bool
1500 MYSQLND_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 */
1510 static enum_func_status
1511 MYSQLND_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 */
1559 PHPAPI 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 */
1612 static enum_func_status
1613 MYSQLND_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);
1656 end:
1657 DBG_INF(ret == PASS? "PASS":"FAIL");
1658 DBG_RETURN(ret);
1659 }
1660 /* }}} */
1661
1662
1663 /* {{{ mysqlnd_conn_data::set_client_option */
1664 static enum_func_status
1665 MYSQLND_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);
1826 oom:
1827 SET_OOM_ERROR(conn->error_info);
1828 conn->m->local_tx_end(conn, this_func, FAIL);
1829 end:
1830 DBG_RETURN(FAIL);
1831 }
1832 /* }}} */
1833
1834
1835 /* {{{ mysqlnd_conn_data::set_client_option_2d */
1836 static enum_func_status
1837 MYSQLND_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);
1873 oom:
1874 SET_OOM_ERROR(conn->error_info);
1875 conn->m->local_tx_end(conn, this_func, FAIL);
1876 end:
1877 DBG_RETURN(FAIL);
1878 }
1879 /* }}} */
1880
1881
1882 /* {{{ mysqlnd_conn_data::use_result */
1883 static MYSQLND_RES *
1884 MYSQLND_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 */
1925 static MYSQLND_RES *
1926 MYSQLND_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 */
1982 static void
1983 MYSQLND_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 */
1994 static enum_func_status
1995 MYSQLND_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 */
2012 static enum_func_status
2013 MYSQLND_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 */
2021 static enum_func_status
2022 MYSQLND_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 */
2030 static void
2031 MYSQLND_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 */
2062 static char *
2063 mysqlnd_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 */
2105 static enum_func_status
2106 MYSQLND_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 */
2149 static enum_func_status
2150 MYSQLND_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 */
2211 static enum_func_status
2212 MYSQLND_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 */
2243 static enum_func_status
2244 MYSQLND_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 */
2275 static size_t
2276 MYSQLND_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 */
2291 static size_t
2292 MYSQLND_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 */
2301 static enum_func_status
2302 MYSQLND_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 */
2311 static enum_func_status
2312 MYSQLND_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 */
2321 MYSQLND_STMT *
2322 MYSQLND_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
2332 MYSQLND_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)
2419 MYSQLND_CLASS_METHODS_END;
2420
2421
2422 /* {{{ mysqlnd_conn::get_reference */
2423 static MYSQLND *
2424 MYSQLND_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 */
2435 static void
2436 MYSQLND_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 */
2451 static enum_func_status
2452 MYSQLND_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
2487 MYSQLND_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)
2492 MYSQLND_CLASS_METHODS_END;
2493
2494
2495 #include "php_network.h"
2496
2497 /* {{{ mysqlnd_stream_array_to_fd_set */
2498 MYSQLND **
2499 mysqlnd_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 */
2535 static unsigned int
2536 mysqlnd_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 */
2572 static unsigned int
2573 mysqlnd_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 */
2615 PHPAPI enum_func_status
2616 mysqlnd_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 */
2693 PHPAPI 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 */
2741 PHPAPI MYSQLND *
2742 mysqlnd_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 */
2764