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