1 /*
2 +----------------------------------------------------------------------+
3 | Swoole |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2012-2018 The Swoole Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.0 of the Apache 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.apache.org/licenses/LICENSE-2.0.html |
11 | If you did not receive a copy of the Apache2.0 license and are unable|
12 | to obtain it through the world-wide-web, please send a note to |
13 | license@swoole.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Twosee <twose@qq.com> |
16 | Author: Tianfeng Han <mikan.tenny@gmail.com> |
17 +----------------------------------------------------------------------+
18 */
19
20 #include "php_swoole_cxx.h"
21 #include "php_swoole_mysql_proto.h"
22
23 #include "swoole_string.h"
24
25 // see mysqlnd 'L64' macro redefined
26 #undef L64
27
28 SW_EXTERN_C_BEGIN
29 #include "ext/hash/php_hash.h"
30 #include "ext/hash/php_hash_sha.h"
31 #include "ext/standard/php_math.h"
32 #ifdef SW_USE_MYSQLND
33 #include "ext/mysqlnd/mysqlnd.h"
34 #include "ext/mysqlnd/mysqlnd_charset.h"
35 #endif
36 SW_EXTERN_C_END
37
38 #include <unordered_map>
39
40 /* keep same with pdo and mysqli */
41 #define MYSQLND_UNKNOWN_SQLSTATE "HY000"
42 #define MYSQLND_SERVER_GONE "MySQL server has gone away"
43 #define MYSQLND_CR_UNKNOWN_ERROR 2000
44 #define MYSQLND_CR_CONNECTION_ERROR 2002
45 #define MYSQLND_CR_SERVER_GONE_ERROR 2006
46 #define MYSQLND_CR_OUT_OF_MEMORY 2008
47 #define MYSQLND_CR_SERVER_LOST 2013
48 #define MYSQLND_CR_COMMANDS_OUT_OF_SYNC 2014
49 #define MYSQLND_CR_CANT_FIND_CHARSET 2019
50 #define MYSQLND_CR_MALFORMED_PACKET 2027
51 #define MYSQLND_CR_NOT_IMPLEMENTED 2054
52 #define MYSQLND_CR_NO_PREPARE_STMT 2030
53 #define MYSQLND_CR_PARAMS_NOT_BOUND 2031
54 #define MYSQLND_CR_INVALID_PARAMETER_NO 2034
55 #define MYSQLND_CR_INVALID_BUFFER_USE 2035
56
57 using swoole::coroutine::Socket;
58 using namespace swoole;
59
60 namespace swoole {
61 class mysql_statement;
62 class mysql_client {
63 public:
64 /* session related {{{ */
65 Socket *socket = nullptr;
66 Socket::timeout_controller *tc = nullptr;
67
68 enum sw_mysql_state state = SW_MYSQL_STATE_CLOSED;
69 bool quit = false;
70 mysql::result_info result;
71
72 std::unordered_map<uint32_t, mysql_statement *> statements;
73 mysql_statement *statement = nullptr;
74 /* }}} */
75
76 std::string host = SW_MYSQL_DEFAULT_HOST;
77 uint16_t port = SW_MYSQL_DEFAULT_PORT;
78 bool ssl = false;
79
80 std::string user = "root";
81 std::string password = "root";
82 std::string database = "test";
83 char charset = SW_MYSQL_DEFAULT_CHARSET;
84
85 double connect_timeout = network::Socket::default_connect_timeout;
86 bool strict_type = false;
87
get_error_code()88 inline int get_error_code() {
89 return error_code;
90 }
91
get_error_msg()92 inline const char *get_error_msg() {
93 return error_msg.c_str();
94 }
95
non_sql_error(int code,const char * msg)96 inline void non_sql_error(int code, const char *msg) {
97 error_code = code;
98 error_msg = std_string::format("SQLSTATE[" MYSQLND_UNKNOWN_SQLSTATE "] [%d] %s", code, msg);
99 }
100
101 template <typename... Args>
non_sql_error(int code,const char * format,Args...args)102 inline void non_sql_error(int code, const char *format, Args... args) {
103 error_code = code;
104 error_msg = std_string::format(
105 "SQLSTATE[" MYSQLND_UNKNOWN_SQLSTATE "] [%d] %s", code, std_string::format(format, args...).c_str());
106 }
107
io_error()108 void io_error() {
109 if (state == SW_MYSQL_STATE_CLOSED) {
110 non_sql_error(MYSQLND_CR_CONNECTION_ERROR, socket->errMsg);
111 } else {
112 non_sql_error(MYSQLND_CR_SERVER_GONE_ERROR,
113 MYSQLND_SERVER_GONE "%s%s",
114 socket->errCode ? " due to " : "",
115 socket->errCode ? socket->errMsg : "");
116 }
117 /* don't send QUIT after IO error */
118 quit = true;
119 close();
120 }
121
proto_error(const char * data,const enum sw_mysql_packet_types expected_type)122 void proto_error(const char *data, const enum sw_mysql_packet_types expected_type) {
123 mysql::server_packet packet(data);
124 non_sql_error(MYSQLND_CR_MALFORMED_PACKET,
125 "Unexpected mysql packet length=%u, number=%u, type=%u, expected_type=%u",
126 packet.header.length,
127 packet.header.number,
128 (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE],
129 expected_type);
130 close();
131 }
132
server_error(const char * data)133 void server_error(const char *data) {
134 mysql::err_packet err_packet(data);
135 error_code = err_packet.code;
136 error_msg =
137 std_string::format("SQLSTATE[%s] [%d] %s", err_packet.sql_state, err_packet.code, err_packet.msg.c_str());
138 state = SW_MYSQL_STATE_IDLE;
139 }
140
get_fetch_mode()141 inline bool get_fetch_mode() {
142 return fetch_mode;
143 }
144
set_fetch_mode(bool v)145 inline bool set_fetch_mode(bool v) {
146 if (sw_unlikely(socket && v)) {
147 non_sql_error(ENOTSUP, "Can not use fetch mode after the connection is established");
148 return false;
149 }
150 fetch_mode = v;
151 return true;
152 }
153
get_defer()154 inline bool get_defer() {
155 return defer;
156 }
157
set_defer(bool v)158 inline bool set_defer(bool v) {
159 // if (sw_unlikely(fetch_mode && v))
160 // {
161 // non_sql_error(ENOTSUP, "Can not use defer mode when fetch mode is on");
162 // return false;
163 // }
164 defer = v;
165 return true;
166 }
167
add_timeout_controller(double timeout,const enum Socket::TimeoutType type)168 void add_timeout_controller(double timeout, const enum Socket::TimeoutType type) {
169 if (sw_unlikely(!socket)) {
170 return;
171 }
172 // Notice: `timeout > 0` is wrong, maybe -1
173 if (timeout != 0) {
174 SW_ASSERT(!tc);
175 tc = new Socket::timeout_controller(socket, timeout, type);
176 }
177 }
178
has_timedout(enum Socket::TimeoutType type)179 inline bool has_timedout(enum Socket::TimeoutType type) {
180 return tc && tc->has_timedout(type);
181 }
182
del_timeout_controller()183 void del_timeout_controller() {
184 if (tc) {
185 delete tc;
186 tc = nullptr;
187 }
188 }
189
190 bool connect(std::string host, uint16_t port, bool ssl);
191
connect()192 inline bool connect() {
193 return connect(host, port, ssl);
194 }
195
is_connected()196 inline bool is_connected() {
197 return socket && socket->is_connected();
198 }
199
get_fd()200 inline int get_fd() {
201 return socket ? socket->get_fd() : -1;
202 }
203
check_connection()204 inline bool check_connection() {
205 if (sw_unlikely(!is_connected())) {
206 non_sql_error(MYSQLND_CR_CONNECTION_ERROR, "%s or %s", strerror(ECONNRESET), strerror(ENOTCONN));
207 return false;
208 }
209 return true;
210 }
211
check_liveness()212 inline bool check_liveness() {
213 if (sw_unlikely(!check_connection())) {
214 return false;
215 }
216 if (sw_unlikely(!socket->check_liveness())) {
217 non_sql_error(MYSQLND_CR_SERVER_GONE_ERROR, MYSQLND_SERVER_GONE);
218 close();
219 return false;
220 }
221 return true;
222 }
223
is_writable()224 inline bool is_writable() {
225 return is_connected() && !socket->has_bound(SW_EVENT_WRITE);
226 }
227
is_available_for_new_request()228 bool is_available_for_new_request() {
229 if (sw_unlikely(state != SW_MYSQL_STATE_IDLE && state != SW_MYSQL_STATE_CLOSED)) {
230 if (socket) {
231 socket->check_bound_co(SW_EVENT_RDWR);
232 }
233 non_sql_error(EINPROGRESS,
234 "MySQL client is busy now on state#%d, "
235 "please use recv/fetchAll/nextResult to get all unread data "
236 "and wait for response then try again",
237 state);
238 return false;
239 }
240 if (sw_unlikely(!check_liveness())) {
241 return false;
242 } else {
243 /* without unread data */
244 String *buffer = socket->get_read_buffer();
245 SW_ASSERT(buffer->length == (size_t) buffer->offset);
246 buffer->clear();
247 return true;
248 }
249 }
250
251 const char *recv_packet();
252
recv_none_error_packet()253 inline const char *recv_none_error_packet() {
254 const char *data = recv_packet();
255 if (sw_unlikely(data && mysql::server_packet::is_err(data))) {
256 server_error(data);
257 return nullptr;
258 }
259 return data;
260 }
261
recv_eof_packet()262 inline const char *recv_eof_packet() {
263 const char *data = recv_packet();
264 if (sw_unlikely(data && !mysql::server_packet::is_eof(data))) {
265 proto_error(data, SW_MYSQL_PACKET_EOF);
266 return nullptr;
267 }
268 #ifdef SW_LOG_TRACE_OPEN
269 mysql::eof_packet eof_packet(data);
270 #endif
271 return data;
272 }
273
send_raw(const char * data,size_t length)274 inline bool send_raw(const char *data, size_t length) {
275 if (sw_unlikely(!check_connection())) {
276 return false;
277 } else {
278 if (sw_unlikely(has_timedout(Socket::TIMEOUT_WRITE))) {
279 io_error();
280 return false;
281 }
282 if (sw_unlikely(socket->send_all(data, length) != (ssize_t) length)) {
283 io_error();
284 return false;
285 }
286 return true;
287 }
288 }
289
290 bool send_packet(mysql::client_packet *packet);
291 bool send_command(enum sw_mysql_command command, const char *sql = nullptr, size_t length = 0);
292 // just for internal
293 void send_command_without_check(enum sw_mysql_command command, const char *sql = nullptr, size_t length = 0);
294
295 void query(zval *return_value, const char *statement, size_t statement_length);
296 void send_query_request(zval *return_value, const char *statement, size_t statement_length);
297 void recv_query_response(zval *return_value);
298 const char *handle_row_data_size(mysql::row_data *row_data, uint8_t size);
299 bool handle_row_data_lcb(mysql::row_data *row_data);
300 void handle_row_data_text(zval *return_value, mysql::row_data *row_data, mysql::field_packet *field);
301 void handle_strict_type(zval *ztext, mysql::field_packet *field);
302 void fetch(zval *return_value);
303 void fetch_all(zval *return_value);
304 void next_result(zval *return_value);
305 bool recv();
306
307 bool send_prepare_request(const char *statement, size_t statement_length);
308 mysql_statement *recv_prepare_response();
309
310 void close();
311
~mysql_client()312 ~mysql_client() {
313 SW_ASSERT(statements.empty());
314 close();
315 }
316
317 private:
318 int error_code = 0;
319 std::string error_msg = "";
320
321 /* unable to support both features at the same time, so we have to set them by method {{{ */
322 bool fetch_mode = false;
323 bool defer = false;
324 /* }}} */
325
326 // recv data of specified length
327 const char *recv_length(size_t need_length, const bool try_to_recycle = false);
328 // usually mysql->connect = connect(TCP) + handshake
329 bool handshake();
330 };
331
332 class mysql_statement {
333 public:
334 std::string statement;
335 mysql::statement info;
336 mysql::result_info result;
337
mysql_statement(mysql_client * client,const char * statement,size_t statement_length)338 mysql_statement(mysql_client *client, const char *statement, size_t statement_length) : client(client) {
339 this->statement = std::string(statement, statement_length);
340 }
341
get_client()342 inline mysql_client *get_client() {
343 return client;
344 }
345
get_error_code()346 inline int get_error_code() {
347 return sw_likely(client) ? client->get_error_code() : error_code;
348 }
349
get_error_msg()350 inline const char *get_error_msg() {
351 return sw_likely(client) ? client->get_error_msg() : error_msg.c_str();
352 }
353
is_available()354 inline bool is_available() {
355 if (sw_unlikely(!client)) {
356 error_code = ECONNRESET;
357 error_msg = "statement must to be recompiled after the connection is broken";
358 return false;
359 }
360 return true;
361 }
362
is_available_for_new_request()363 inline bool is_available_for_new_request() {
364 if (sw_unlikely(!is_available())) {
365 return false;
366 }
367 if (sw_unlikely(!client->is_available_for_new_request())) {
368 return false;
369 }
370 return true;
371 }
372
add_timeout_controller(double timeout,const enum Socket::TimeoutType type)373 inline void add_timeout_controller(double timeout, const enum Socket::TimeoutType type) {
374 if (sw_likely(client)) {
375 client->add_timeout_controller(timeout, type);
376 }
377 }
378
del_timeout_controller()379 inline void del_timeout_controller() {
380 if (sw_likely(client)) {
381 client->del_timeout_controller();
382 }
383 }
384
385 // [notify = false]: mysql_client actively close
close(const bool notify=true)386 inline void close(const bool notify = true) {
387 if (client) {
388 // if client point exists, socket is always available
389 if (notify) {
390 if (sw_likely(client->is_writable())) {
391 char id[4];
392 sw_mysql_int4store(id, info.id);
393 client->send_command_without_check(SW_MYSQL_COM_STMT_CLOSE, id, sizeof(id));
394 }
395 client->statements.erase(info.id);
396 } else {
397 error_code = client->get_error_code();
398 error_msg = client->get_error_msg();
399 }
400 client = nullptr;
401 }
402 }
403
~mysql_statement()404 ~mysql_statement() {
405 close();
406 }
407
408 bool send_prepare_request();
409 bool recv_prepare_response();
410
411 void execute(zval *return_value, zval *params);
412 void send_execute_request(zval *return_value, zval *params);
413 void recv_execute_response(zval *return_value);
414
415 void fetch(zval *return_value);
416 void fetch_all(zval *return_value);
417 void next_result(zval *return_value);
418
419 private:
420 mysql_client *client = nullptr;
421 int error_code = 0;
422 std::string error_msg;
423 };
424 } // namespace swoole
425
426 using swoole::mysql_client;
427 using swoole::mysql_statement;
428
429 static zend_class_entry *swoole_mysql_coro_ce;
430 static zend_object_handlers swoole_mysql_coro_handlers;
431
432 static zend_class_entry *swoole_mysql_coro_exception_ce;
433 static zend_object_handlers swoole_mysql_coro_exception_handlers;
434
435 static zend_class_entry *swoole_mysql_coro_statement_ce;
436 static zend_object_handlers swoole_mysql_coro_statement_handlers;
437
438 struct mysql_coro_t {
439 mysql_client *client;
440 zend_object std;
441 };
442
443 struct mysql_coro_statement_t {
444 mysql_statement *statement;
445 zend_object *zclient;
446 zend_object std;
447 };
448
449 SW_EXTERN_C_BEGIN
450 static PHP_METHOD(swoole_mysql_coro, __construct);
451 static PHP_METHOD(swoole_mysql_coro, __destruct);
452 static PHP_METHOD(swoole_mysql_coro, connect);
453 static PHP_METHOD(swoole_mysql_coro, getDefer);
454 static PHP_METHOD(swoole_mysql_coro, setDefer);
455 static PHP_METHOD(swoole_mysql_coro, query);
456 static PHP_METHOD(swoole_mysql_coro, fetch);
457 static PHP_METHOD(swoole_mysql_coro, fetchAll);
458 static PHP_METHOD(swoole_mysql_coro, nextResult);
459 static PHP_METHOD(swoole_mysql_coro, prepare);
460 static PHP_METHOD(swoole_mysql_coro, recv);
461 static PHP_METHOD(swoole_mysql_coro, begin);
462 static PHP_METHOD(swoole_mysql_coro, commit);
463 static PHP_METHOD(swoole_mysql_coro, rollback);
464 #ifdef SW_USE_MYSQLND
465 static PHP_METHOD(swoole_mysql_coro, escape);
466 #endif
467 static PHP_METHOD(swoole_mysql_coro, close);
468
469 static PHP_METHOD(swoole_mysql_coro_statement, execute);
470 static PHP_METHOD(swoole_mysql_coro_statement, fetch);
471 static PHP_METHOD(swoole_mysql_coro_statement, fetchAll);
472 static PHP_METHOD(swoole_mysql_coro_statement, nextResult);
473 static PHP_METHOD(swoole_mysql_coro_statement, recv);
474 static PHP_METHOD(swoole_mysql_coro_statement, close);
475 SW_EXTERN_C_END
476
477 // clang-format off
478 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0)
479 ZEND_END_ARG_INFO()
480
481 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_optional_timeout, 0, 0, 0)
482 ZEND_ARG_INFO(0, timeout)
483 ZEND_END_ARG_INFO()
484
485 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_connect, 0, 0, 0)
486 ZEND_ARG_ARRAY_INFO(0, server_config, 0)
487 ZEND_END_ARG_INFO()
488
489 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_query, 0, 0, 1)
490 ZEND_ARG_INFO(0, sql)
491 ZEND_ARG_INFO(0, timeout)
492 ZEND_END_ARG_INFO()
493
494 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_prepare, 0, 0, 1)
495 ZEND_ARG_INFO(0, query)
496 ZEND_ARG_INFO(0, timeout)
497 ZEND_END_ARG_INFO()
498
499 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_setDefer, 0, 0, 0)
500 ZEND_ARG_INFO(0, defer)
501 ZEND_END_ARG_INFO()
502
503 #ifdef SW_USE_MYSQLND
504 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_escape, 0, 0, 1)
505 ZEND_ARG_INFO(0, string)
506 ZEND_ARG_INFO(0, flags)
507 ZEND_END_ARG_INFO()
508 #endif
509
510 ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_statement_execute, 0, 0, 0)
511 ZEND_ARG_INFO(0, params)
512 ZEND_ARG_INFO(0, timeout)
513 ZEND_END_ARG_INFO()
514
515 static const zend_function_entry swoole_mysql_coro_methods[] =
516 {
517 PHP_ME(swoole_mysql_coro, __construct, arginfo_swoole_void, ZEND_ACC_PUBLIC)
518 PHP_ME(swoole_mysql_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC)
519 PHP_ME(swoole_mysql_coro, getDefer, arginfo_swoole_void, ZEND_ACC_PUBLIC)
520 PHP_ME(swoole_mysql_coro, setDefer, arginfo_swoole_mysql_coro_setDefer, ZEND_ACC_PUBLIC)
521 PHP_ME(swoole_mysql_coro, connect, arginfo_swoole_mysql_coro_connect, ZEND_ACC_PUBLIC)
522 PHP_ME(swoole_mysql_coro, query, arginfo_swoole_mysql_coro_query, ZEND_ACC_PUBLIC)
523 PHP_ME(swoole_mysql_coro, fetch, arginfo_swoole_void, ZEND_ACC_PUBLIC)
524 PHP_ME(swoole_mysql_coro, fetchAll, arginfo_swoole_void, ZEND_ACC_PUBLIC)
525 PHP_ME(swoole_mysql_coro, nextResult, arginfo_swoole_void, ZEND_ACC_PUBLIC)
526 PHP_ME(swoole_mysql_coro, prepare, arginfo_swoole_mysql_coro_prepare, ZEND_ACC_PUBLIC)
527 PHP_ME(swoole_mysql_coro, recv, arginfo_swoole_void, ZEND_ACC_PUBLIC)
528 PHP_ME(swoole_mysql_coro, begin, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC)
529 PHP_ME(swoole_mysql_coro, commit, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC)
530 PHP_ME(swoole_mysql_coro, rollback, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC)
531 #ifdef SW_USE_MYSQLND
532 PHP_ME(swoole_mysql_coro, escape, arginfo_swoole_mysql_coro_escape, ZEND_ACC_PUBLIC)
533 #endif
534 PHP_ME(swoole_mysql_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC)
535 PHP_FE_END
536 };
537
538 static const zend_function_entry swoole_mysql_coro_statement_methods[] =
539 {
540 PHP_ME(swoole_mysql_coro_statement, execute, arginfo_swoole_mysql_coro_statement_execute, ZEND_ACC_PUBLIC)
541 PHP_ME(swoole_mysql_coro_statement, fetch, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC)
542 PHP_ME(swoole_mysql_coro_statement, fetchAll, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC)
543 PHP_ME(swoole_mysql_coro_statement, nextResult, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC)
544 PHP_ME(swoole_mysql_coro_statement, recv, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC)
545 PHP_ME(swoole_mysql_coro_statement, close, arginfo_swoole_void, ZEND_ACC_PUBLIC)
546 PHP_FE_END
547 };
548 // clang-format on
549
php_swoole_sha256(const char * str,int len,unsigned char * digest)550 void php_swoole_sha256(const char *str, int len, unsigned char *digest) {
551 PHP_SHA256_CTX context;
552 PHP_SHA256Init(&context);
553 PHP_SHA256Update(&context, (unsigned char *) str, len);
554 PHP_SHA256Final(digest, &context);
555 }
556
connect(std::string host,uint16_t port,bool ssl)557 bool mysql_client::connect(std::string host, uint16_t port, bool ssl) {
558 if (socket && (host != this->host || port != this->port || ssl != this->ssl)) {
559 close();
560 }
561 if (!socket) {
562 if (host.compare(0, 6, "unix:/", 0, 6) == 0) {
563 host = host.substr(sizeof("unix:") - 1);
564 host.erase(0, host.find_first_not_of('/') - 1);
565 socket = new Socket(SW_SOCK_UNIX_STREAM);
566 } else if (host.find(':') != std::string::npos) {
567 socket = new Socket(SW_SOCK_TCP6);
568 } else {
569 socket = new Socket(SW_SOCK_TCP);
570 }
571 if (sw_unlikely(socket->get_fd() < 0)) {
572 php_swoole_fatal_error(E_WARNING, "new Socket() failed. Error: %s [%d]", strerror(errno), errno);
573 non_sql_error(MYSQLND_CR_CONNECTION_ERROR, strerror(errno));
574 delete socket;
575 socket = nullptr;
576 return false;
577 }
578 socket->set_zero_copy(true);
579 #ifdef SW_USE_OPENSSL
580 if (ssl) {
581 socket->enable_ssl_encrypt();
582 }
583 #endif
584 socket->set_timeout(connect_timeout, Socket::TIMEOUT_CONNECT);
585 add_timeout_controller(connect_timeout, Socket::TIMEOUT_ALL);
586 if (!socket->connect(host, port)) {
587 io_error();
588 return false;
589 }
590 this->host = host;
591 this->port = port;
592 #ifdef SW_USE_OPENSSL
593 this->ssl = ssl;
594 #endif
595 if (!handshake()) {
596 close();
597 return false;
598 }
599 state = SW_MYSQL_STATE_IDLE;
600 quit = false;
601 del_timeout_controller();
602 }
603 return true;
604 }
605
recv_length(size_t need_length,const bool try_to_recycle)606 const char *mysql_client::recv_length(size_t need_length, const bool try_to_recycle) {
607 if (sw_likely(check_connection())) {
608 ssize_t retval;
609 String *buffer = socket->get_read_buffer();
610 off_t offset = buffer->offset; // save offset instead of buffer point (due to realloc)
611 size_t read_n = buffer->length - buffer->offset; // readable bytes
612 if (try_to_recycle && read_n == 0) {
613 swoole_trace_log(SW_TRACE_MYSQL_CLIENT,
614 "mysql buffer will be recycled, length=%zu, offset=%jd",
615 buffer->length,
616 (intmax_t) offset);
617 buffer->clear();
618 offset = 0;
619 }
620 while (read_n < need_length) {
621 if (sw_unlikely(has_timedout(Socket::TIMEOUT_READ))) {
622 io_error();
623 return nullptr;
624 }
625 if (sw_unlikely(buffer->length == buffer->size)) {
626 /* offset + need_length = new size (min) */
627 if (!buffer->extend(SW_MEM_ALIGNED_SIZE_EX(offset + need_length, SwooleG.pagesize))) {
628 non_sql_error(MYSQLND_CR_OUT_OF_MEMORY, strerror(ENOMEM));
629 return nullptr;
630 } else {
631 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "mysql buffer extend to %zu", buffer->size);
632 }
633 }
634 retval = socket->recv(buffer->str + buffer->length, buffer->size - buffer->length);
635 if (sw_unlikely(retval <= 0)) {
636 io_error();
637 return nullptr;
638 }
639 read_n += retval;
640 buffer->length += retval;
641 }
642 buffer->offset += need_length;
643 return buffer->str + offset;
644 }
645 return nullptr;
646 }
647
recv_packet()648 const char *mysql_client::recv_packet() {
649 const char *p;
650 uint32_t length;
651 p = recv_length(SW_MYSQL_PACKET_HEADER_SIZE, true);
652 if (sw_unlikely(!p)) {
653 return nullptr;
654 }
655 length = mysql::packet::get_length(p);
656 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "recv packet length=%u, number=%u", length, mysql::packet::get_number(p));
657 p = recv_length(length);
658 if (sw_unlikely(!p)) {
659 return nullptr;
660 }
661 /* Notice: why we do this? because buffer maybe reallocated when recv data */
662 return p - SW_MYSQL_PACKET_HEADER_SIZE;
663 }
664
send_packet(mysql::client_packet * packet)665 bool mysql_client::send_packet(mysql::client_packet *packet) {
666 const char *data = packet->get_data();
667 uint32_t length = SW_MYSQL_PACKET_HEADER_SIZE + packet->get_length();
668 if (sw_likely(send_raw(data, length))) {
669 return true;
670 }
671 return false;
672 }
673
send_command(enum sw_mysql_command command,const char * sql,size_t length)674 bool mysql_client::send_command(enum sw_mysql_command command, const char *sql, size_t length) {
675 if (sw_likely(SW_MYSQL_PACKET_HEADER_SIZE + 1 + length <= SwooleG.pagesize)) {
676 mysql::command_packet command_packet(command, sql, length);
677 return send_raw(command_packet.get_data(), command_packet.get_data_length());
678 } else {
679 /* if the data is larger than page_size, copy memory to the kernel buffer multiple times is much faster */
680 size_t send_s = SW_MIN(length, SW_MYSQL_MAX_PACKET_BODY_SIZE - 1), send_n = send_s, number = 0;
681 mysql::command_packet command_packet(command);
682 command_packet.set_header(1 + send_s, number++);
683
684 if (sw_unlikely(!send_raw(command_packet.get_data(), SW_MYSQL_PACKET_HEADER_SIZE + 1)) ||
685 !send_raw(sql, send_s)) {
686 return false;
687 }
688 /* MySQL single packet size is 16M, we must subpackage */
689 while (send_n < length) {
690 send_s = length - send_n;
691 send_s = SW_MIN(send_s, SW_MYSQL_MAX_PACKET_BODY_SIZE);
692 command_packet.set_header(send_s, number++);
693 if (sw_unlikely(!send_raw(command_packet.get_data(), SW_MYSQL_PACKET_HEADER_SIZE)) ||
694 !send_raw(sql + send_n, send_s)) {
695 return false;
696 }
697 send_n += send_s;
698 }
699 return true;
700 }
701 }
702
send_command_without_check(enum sw_mysql_command command,const char * sql,size_t length)703 void mysql_client::send_command_without_check(enum sw_mysql_command command, const char *sql, size_t length) {
704 mysql::command_packet command_packet(command, sql, length);
705 (void) (socket && socket->send(command_packet.get_data(), command_packet.get_data_length()));
706 }
707
handshake()708 bool mysql_client::handshake() {
709 const char *data;
710 // recv greeting pakcet
711 if (sw_unlikely(!(data = recv_none_error_packet()))) {
712 return false;
713 }
714 mysql::greeting_packet greeting_packet(data);
715 // generate login packet
716 do {
717 mysql::login_packet login_packet(&greeting_packet, user, password, database, charset);
718 if (sw_unlikely(!send_packet(&login_packet))) {
719 return false;
720 }
721 } while (0);
722 // recv auth switch request packet, 4 possible packet types
723 switch (mysql::server_packet::parse_type(data = recv_packet())) {
724 case SW_MYSQL_PACKET_AUTH_SWITCH_REQUEST: {
725 mysql::auth_switch_request_packet request(data);
726 mysql::auth_switch_response_packet response(&request, password);
727 if (sw_unlikely(!send_packet(&response))) {
728 return false;
729 }
730 break;
731 }
732 case SW_MYSQL_PACKET_AUTH_SIGNATURE_REQUEST: {
733 mysql::auth_signature_request_packet request(data);
734 if (sw_unlikely(!request.is_vaild())) {
735 goto _proto_error;
736 }
737 if (sw_likely(!request.is_full_auth_required())) {
738 break;
739 }
740 // no cache, need full auth with rsa key (openssl required)
741 #ifdef SW_MYSQL_RSA_SUPPORT
742 // tell the server we are prepared
743 do {
744 mysql::auth_signature_prepared_packet prepared(request.header.number + 1);
745 if (sw_unlikely(!send_packet(&prepared))) {
746 return false;
747 }
748 } while (0);
749 // recv rsa key and encode the password
750 do {
751 if (sw_unlikely(!(data = recv_none_error_packet()))) {
752 return false;
753 }
754 mysql::raw_data_packet raw_data_packet(data);
755 mysql::auth_signature_response_packet response(
756 &raw_data_packet, password, greeting_packet.auth_plugin_data);
757 if (sw_unlikely(!send_packet(&response))) {
758 return false;
759 }
760 } while (0);
761 break;
762 #else
763 error_code = EPROTONOSUPPORT;
764 error_msg = SW_MYSQL_NO_RSA_ERROR;
765 return false;
766 #endif
767 }
768 case SW_MYSQL_PACKET_OK: {
769 #ifdef SW_LOG_TRACE_OPEN
770 mysql::ok_packet ok_packet(data);
771 #endif
772 return true;
773 }
774 case SW_MYSQL_PACKET_ERR:
775 server_error(data);
776 return false;
777 case SW_MYSQL_PACKET_NULL:
778 // io_error
779 return false;
780 default:
781 _proto_error:
782 proto_error(data, SW_MYSQL_PACKET_AUTH_SWITCH_REQUEST);
783 return false;
784 }
785 // maybe ok packet or err packet
786 if (sw_unlikely(!(data = recv_none_error_packet()))) {
787 return false;
788 }
789 #ifdef SW_LOG_TRACE_OPEN
790 mysql::ok_packet ok_packet(data);
791 #endif
792 return true;
793 }
794
query(zval * return_value,const char * statement,size_t statement_length)795 void mysql_client::query(zval *return_value, const char *statement, size_t statement_length) {
796 send_query_request(return_value, statement, statement_length);
797 if (EXPECTED(!defer && Z_TYPE_P(return_value) == IS_TRUE)) {
798 recv_query_response(return_value);
799 }
800 }
801
send_query_request(zval * return_value,const char * statement,size_t statement_length)802 void mysql_client::send_query_request(zval *return_value, const char *statement, size_t statement_length) {
803 if (sw_unlikely(!is_available_for_new_request())) {
804 RETURN_FALSE;
805 }
806 if (sw_unlikely(!send_command(SW_MYSQL_COM_QUERY, statement, statement_length))) {
807 RETURN_FALSE;
808 }
809 state = SW_MYSQL_STATE_QUERY;
810 RETURN_TRUE;
811 };
812
recv_query_response(zval * return_value)813 void mysql_client::recv_query_response(zval *return_value) {
814 const char *data;
815 if (sw_unlikely(!(data = recv_none_error_packet()))) {
816 RETURN_FALSE;
817 }
818 if (mysql::server_packet::is_ok(data)) {
819 mysql::ok_packet ok_packet(data);
820 result.ok = ok_packet;
821 state = ok_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_QUERY_MORE_RESULTS : SW_MYSQL_STATE_IDLE;
822 RETURN_TRUE;
823 }
824 do {
825 mysql::lcb_packet lcb_packet(data);
826 if (sw_unlikely(lcb_packet.length == 0)) {
827 // is it possible?
828 proto_error(data, SW_MYSQL_PACKET_FIELD);
829 RETURN_FALSE;
830 }
831 result.alloc_fields(lcb_packet.length);
832 for (uint32_t i = 0; i < lcb_packet.length; i++) {
833 if (sw_unlikely(!(data = recv_packet()))) {
834 RETURN_FALSE;
835 }
836 result.set_field(i, data);
837 }
838 } while (0);
839 // expect eof
840 if (sw_unlikely(!(data = recv_eof_packet()))) {
841 RETURN_FALSE;
842 }
843 state = SW_MYSQL_STATE_QUERY_FETCH;
844 if (get_fetch_mode()) {
845 RETURN_TRUE;
846 }
847 fetch_all(return_value);
848 }
849
handle_row_data_size(mysql::row_data * row_data,uint8_t size)850 const char *mysql_client::handle_row_data_size(mysql::row_data *row_data, uint8_t size) {
851 const char *p, *data;
852 SW_ASSERT(size < sizeof(row_data->stack_buffer));
853 if (sw_unlikely(!(p = row_data->read(size)))) {
854 uint8_t received = row_data->recv(row_data->stack_buffer, size);
855 if (sw_unlikely(!(data = recv_packet()))) {
856 return nullptr;
857 }
858 row_data->next_packet(data);
859 received += row_data->recv(row_data->stack_buffer + received, size - received);
860 if (sw_unlikely(received != size)) {
861 proto_error(data, SW_MYSQL_PACKET_ROW_DATA);
862 return nullptr;
863 }
864 p = row_data->stack_buffer;
865 }
866 return p;
867 }
868
handle_row_data_lcb(mysql::row_data * row_data)869 bool mysql_client::handle_row_data_lcb(mysql::row_data *row_data) {
870 const char *p, *data;
871 // recv 1 byte to get binary code size
872 if (sw_unlikely(row_data->eof())) {
873 if (sw_unlikely(!(data = recv_packet()))) {
874 return false;
875 }
876 row_data->next_packet(data);
877 if (sw_unlikely(row_data->eof())) {
878 proto_error(data, SW_MYSQL_PACKET_ROW_DATA);
879 return false;
880 }
881 }
882 // decode lcb (use 0 to prevent read_ptr from moving)
883 // recv "size" bytes to get binary code length
884 p = handle_row_data_size(row_data, mysql::read_lcb_size(row_data->read(0)));
885 if (sw_unlikely(!p)) {
886 return false;
887 }
888 mysql::read_lcb(p, &row_data->text.length, &row_data->text.nul);
889 return true;
890 }
891
handle_row_data_text(zval * return_value,mysql::row_data * row_data,mysql::field_packet * field)892 void mysql_client::handle_row_data_text(zval *return_value, mysql::row_data *row_data, mysql::field_packet *field) {
893 const char *p, *data;
894 if (sw_unlikely(!handle_row_data_lcb(row_data))) {
895 RETURN_FALSE;
896 }
897 if (sw_unlikely(!(p = row_data->read(row_data->text.length)))) {
898 size_t received = 0, required = row_data->text.length;
899 if (required < sizeof(row_data->stack_buffer)) {
900 p = handle_row_data_size(row_data, required);
901 if (sw_unlikely(!p)) {
902 RETURN_FALSE;
903 }
904 } else {
905 zend_string *zstring = zend_string_alloc(required, 0);
906 do {
907 received += row_data->recv(ZSTR_VAL(zstring) + received, required - received);
908 if (received == required) {
909 break;
910 }
911 if (row_data->eof()) {
912 if (sw_unlikely(!(data = recv_packet()))) {
913 RETURN_FALSE;
914 }
915 row_data->next_packet(data);
916 }
917 } while (true);
918 ZSTR_VAL(zstring)[ZSTR_LEN(zstring)] = '\0';
919 RETVAL_STR(zstring);
920 goto _return;
921 }
922 }
923 if (row_data->text.nul || field->type == SW_MYSQL_TYPE_NULL) {
924 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s is null", field->name_length, field->name);
925 RETURN_NULL();
926 } else {
927 RETVAL_STRINGL(p, row_data->text.length);
928 _return:
929 swoole_trace_log(SW_TRACE_MYSQL_CLIENT,
930 "%.*s=[%lu]%.*s%s",
931 field->name_length,
932 field->name,
933 Z_STRLEN_P(return_value),
934 (int) SW_MIN(32, Z_STRLEN_P(return_value)),
935 Z_STRVAL_P(return_value),
936 (Z_STRLEN_P(return_value) > 32 ? "..." : ""));
937 }
938 }
939
handle_strict_type(zval * ztext,mysql::field_packet * field)940 void mysql_client::handle_strict_type(zval *ztext, mysql::field_packet *field) {
941 if (sw_likely(Z_TYPE_P(ztext) == IS_STRING)) {
942 char *error;
943 switch (field->type) {
944 /* String */
945 case SW_MYSQL_TYPE_TINY_BLOB:
946 case SW_MYSQL_TYPE_MEDIUM_BLOB:
947 case SW_MYSQL_TYPE_LONG_BLOB:
948 case SW_MYSQL_TYPE_BLOB:
949 case SW_MYSQL_TYPE_DECIMAL:
950 case SW_MYSQL_TYPE_NEWDECIMAL:
951 case SW_MYSQL_TYPE_BIT:
952 case SW_MYSQL_TYPE_STRING:
953 case SW_MYSQL_TYPE_VAR_STRING:
954 case SW_MYSQL_TYPE_VARCHAR:
955 case SW_MYSQL_TYPE_NEWDATE:
956 /* Date Time */
957 case SW_MYSQL_TYPE_TIME:
958 case SW_MYSQL_TYPE_YEAR:
959 case SW_MYSQL_TYPE_TIMESTAMP:
960 case SW_MYSQL_TYPE_DATETIME:
961 case SW_MYSQL_TYPE_DATE:
962 case SW_MYSQL_TYPE_JSON:
963 return;
964 /* Integer */
965 case SW_MYSQL_TYPE_TINY:
966 case SW_MYSQL_TYPE_SHORT:
967 case SW_MYSQL_TYPE_INT24:
968 case SW_MYSQL_TYPE_LONG:
969 if (field->flags & SW_MYSQL_UNSIGNED_FLAG) {
970 ulong_t uint = strtoul(Z_STRVAL_P(ztext), &error, 10);
971 if (sw_likely(*error == '\0')) {
972 zend_string_release(Z_STR_P(ztext));
973 ZVAL_LONG(ztext, uint);
974 }
975 } else {
976 long sint = strtol(Z_STRVAL_P(ztext), &error, 10);
977 if (sw_likely(*error == '\0')) {
978 zend_string_release(Z_STR_P(ztext));
979 ZVAL_LONG(ztext, sint);
980 }
981 }
982 break;
983 case SW_MYSQL_TYPE_LONGLONG:
984 if (field->flags & SW_MYSQL_UNSIGNED_FLAG) {
985 unsigned long long ubigint = strtoull(Z_STRVAL_P(ztext), &error, 10);
986 if (sw_likely(*error == '\0' && ubigint <= ZEND_LONG_MAX)) {
987 zend_string_release(Z_STR_P(ztext));
988 ZVAL_LONG(ztext, ubigint);
989 }
990 } else {
991 long long sbigint = strtoll(Z_STRVAL_P(ztext), &error, 10);
992 if (sw_likely(*error == '\0')) {
993 zend_string_release(Z_STR_P(ztext));
994 ZVAL_LONG(ztext, sbigint);
995 }
996 }
997 break;
998 case SW_MYSQL_TYPE_FLOAT:
999 case SW_MYSQL_TYPE_DOUBLE: {
1000 double mdouble = strtod(Z_STRVAL_P(ztext), &error);
1001 if (sw_likely(*error == '\0')) {
1002 zend_string_release(Z_STR_P(ztext));
1003 ZVAL_DOUBLE(ztext, mdouble);
1004 }
1005 break;
1006 }
1007 default: {
1008 swoole_warning("unknown type[%d] for field [%.*s].", field->type, field->name_length, field->name);
1009 break;
1010 }
1011 }
1012 }
1013 }
1014
fetch(zval * return_value)1015 void mysql_client::fetch(zval *return_value) {
1016 if (sw_unlikely(!is_connected())) {
1017 RETURN_FALSE;
1018 }
1019 if (sw_unlikely(state != SW_MYSQL_STATE_QUERY_FETCH)) {
1020 RETURN_NULL();
1021 }
1022 const char *data;
1023 if (sw_unlikely(!(data = recv_packet()))) {
1024 RETURN_FALSE;
1025 }
1026 if (mysql::server_packet::is_eof(data)) {
1027 mysql::eof_packet eof_packet(data);
1028 state =
1029 eof_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_QUERY_MORE_RESULTS : SW_MYSQL_STATE_IDLE;
1030 RETURN_NULL();
1031 }
1032 do {
1033 mysql::row_data row_data(data);
1034 array_init_size(return_value, result.get_fields_length());
1035 for (uint32_t i = 0; i < result.get_fields_length(); i++) {
1036 mysql::field_packet *field = result.get_field(i);
1037 zval ztext;
1038 handle_row_data_text(&ztext, &row_data, field);
1039 if (sw_unlikely(Z_TYPE_P(&ztext) == IS_FALSE)) {
1040 zval_ptr_dtor(return_value);
1041 RETURN_FALSE;
1042 }
1043 if (strict_type) {
1044 handle_strict_type(&ztext, field);
1045 }
1046 add_assoc_zval_ex(return_value, field->name, field->name_length, &ztext);
1047 }
1048 } while (0);
1049 }
1050
fetch_all(zval * return_value)1051 void mysql_client::fetch_all(zval *return_value) {
1052 array_init(return_value);
1053 while (true) {
1054 zval zrow;
1055 fetch(&zrow);
1056 if (sw_unlikely(ZVAL_IS_NULL(&zrow))) {
1057 // eof
1058 return;
1059 }
1060 if (sw_unlikely(Z_TYPE_P(&zrow) == IS_FALSE)) {
1061 // error
1062 zval_ptr_dtor(return_value);
1063 RETURN_FALSE;
1064 }
1065 (void) add_next_index_zval(return_value, &zrow);
1066 }
1067 }
1068
next_result(zval * return_value)1069 void mysql_client::next_result(zval *return_value) {
1070 if (sw_unlikely(state == SW_MYSQL_STATE_QUERY_FETCH)) {
1071 // skip unread data
1072 fetch_all(return_value);
1073 zval_ptr_dtor(return_value);
1074 next_result(return_value);
1075 } else if (sw_likely(state == SW_MYSQL_STATE_QUERY_MORE_RESULTS)) {
1076 recv_query_response(return_value);
1077 } else if (state == SW_MYSQL_STATE_IDLE) {
1078 RETURN_NULL();
1079 } else {
1080 RETURN_FALSE;
1081 }
1082 }
1083
send_prepare_request(const char * statement,size_t statement_length)1084 bool mysql_client::send_prepare_request(const char *statement, size_t statement_length) {
1085 this->statement = new mysql_statement(this, statement, statement_length);
1086 if (sw_unlikely(!this->statement->send_prepare_request())) {
1087 delete this->statement;
1088 this->statement = nullptr;
1089 return false;
1090 }
1091 return true;
1092 }
1093
recv_prepare_response()1094 mysql_statement *mysql_client::recv_prepare_response() {
1095 if (sw_likely(state == SW_MYSQL_STATE_PREPARE)) {
1096 mysql_statement *statement = this->statement;
1097 SW_ASSERT(statement != nullptr);
1098 this->statement = nullptr;
1099 if (sw_unlikely(!statement->recv_prepare_response())) {
1100 delete statement;
1101 return nullptr;
1102 }
1103 statements[statement->info.id] = statement;
1104 return statement;
1105 }
1106 return nullptr;
1107 }
1108
close()1109 void mysql_client::close() {
1110 state = SW_MYSQL_STATE_CLOSED;
1111 Socket *socket = this->socket;
1112 if (socket) {
1113 del_timeout_controller();
1114 if (!quit && is_writable()) {
1115 send_command_without_check(SW_MYSQL_COM_QUIT);
1116 quit = true;
1117 }
1118 // make statements non-available
1119 while (!statements.empty()) {
1120 auto i = statements.begin();
1121 i->second->close(false);
1122 statements.erase(i);
1123 }
1124 if (sw_likely(!socket->has_bound())) {
1125 this->socket = nullptr;
1126 }
1127 if (sw_likely(socket->close())) {
1128 delete socket;
1129 }
1130 }
1131 }
1132
send_prepare_request()1133 bool mysql_statement::send_prepare_request() {
1134 if (sw_unlikely(!is_available_for_new_request())) {
1135 return false;
1136 }
1137 if (sw_unlikely(!client->send_command(SW_MYSQL_COM_STMT_PREPARE, statement.c_str(), statement.length()))) {
1138 return false;
1139 }
1140 client->state = SW_MYSQL_STATE_PREPARE;
1141 return true;
1142 }
1143
recv_prepare_response()1144 bool mysql_statement::recv_prepare_response() {
1145 if (sw_unlikely(!is_available())) {
1146 return false;
1147 } else {
1148 client->state = SW_MYSQL_STATE_IDLE;
1149 }
1150 const char *data;
1151 if (sw_unlikely(!(data = client->recv_none_error_packet()))) {
1152 return false;
1153 }
1154 info = mysql::statement(data);
1155 if (sw_likely(info.param_count != 0)) {
1156 for (uint16_t i = info.param_count; i--;) {
1157 if (sw_unlikely(!(data = client->recv_packet()))) {
1158 return false;
1159 }
1160 #ifdef SW_LOG_TRACE_OPEN
1161 mysql::param_packet param_packet(data);
1162 #endif
1163 }
1164 if (sw_unlikely(!(data = client->recv_eof_packet()))) {
1165 return false;
1166 }
1167 }
1168 if (info.field_count != 0) {
1169 result.alloc_fields(info.field_count);
1170 for (uint16_t i = 0; i < info.field_count; i++) {
1171 if (sw_unlikely(!(data = client->recv_packet()))) {
1172 return false;
1173 }
1174 result.set_field(i, data);
1175 }
1176 if (sw_unlikely(!(data = client->recv_eof_packet()))) {
1177 return false;
1178 }
1179 }
1180 return true;
1181 }
1182
execute(zval * return_value,zval * params)1183 void mysql_statement::execute(zval *return_value, zval *params) {
1184 send_execute_request(return_value, params);
1185 /* Notice: must check return_value first */
1186 if (EXPECTED(Z_TYPE_P(return_value) == IS_TRUE && !client->get_defer())) {
1187 recv_execute_response(return_value);
1188 }
1189 }
1190
send_execute_request(zval * return_value,zval * params)1191 void mysql_statement::send_execute_request(zval *return_value, zval *params) {
1192 if (sw_unlikely(!is_available_for_new_request())) {
1193 RETURN_FALSE;
1194 }
1195
1196 uint32_t param_count = params ? php_swoole_array_length(params) : 0;
1197
1198 if (sw_unlikely(param_count != info.param_count)) {
1199 client->non_sql_error(MYSQLND_CR_INVALID_PARAMETER_NO,
1200 "Statement#%u expects %u parameter, %u given.",
1201 info.id,
1202 info.param_count,
1203 param_count);
1204 RETURN_FALSE;
1205 }
1206
1207 String *buffer = client->socket->get_write_buffer();
1208 char *p = buffer->str;
1209
1210 memset(p, 0, 5);
1211 // command
1212 buffer->str[4] = SW_MYSQL_COM_STMT_EXECUTE;
1213 buffer->length = 5;
1214 p += 5;
1215
1216 // stmt.id
1217 sw_mysql_int4store(p, info.id);
1218 p += 4;
1219 // flags = CURSOR_TYPE_NO_CURSOR
1220 sw_mysql_int1store(p, 0);
1221 p += 1;
1222 // iteration_count
1223 sw_mysql_int4store(p, 1);
1224 p += 4;
1225 buffer->length += 9;
1226
1227 // TODO: support more types
1228 if (param_count != 0) {
1229 // null bitmap
1230 size_t null_start_offset = p - buffer->str;
1231 unsigned int map_size = (param_count + 7) / 8;
1232 memset(p, 0, map_size);
1233 p += map_size;
1234 buffer->length += map_size;
1235
1236 // rebind
1237 sw_mysql_int1store(p, 1);
1238 p += 1;
1239 buffer->length += 1;
1240
1241 size_t type_start_offset = p - buffer->str;
1242 p += param_count * 2;
1243 buffer->length += param_count * 2;
1244
1245 char stack_buffer[10];
1246 zend_ulong index = 0;
1247 zval *value;
1248 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(params), value) {
1249 switch (client->strict_type ? Z_TYPE_P(value) : (IS_NULL == Z_TYPE_P(value) ? IS_NULL : IS_STRING)) {
1250 case IS_NULL:
1251 *((buffer->str + null_start_offset) + (index / 8)) |= (1UL << (index % 8));
1252 sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_NULL);
1253 break;
1254 case IS_TRUE:
1255 case IS_FALSE:
1256 case IS_LONG:
1257 sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_LONGLONG);
1258 sw_mysql_int8store(stack_buffer, zval_get_long(value));
1259 if (buffer->append(stack_buffer, mysql::get_static_type_size(SW_MYSQL_TYPE_LONGLONG)) < 0) {
1260 RETURN_FALSE;
1261 }
1262 break;
1263 case IS_DOUBLE:
1264 sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_DOUBLE);
1265 sw_mysql_doublestore(stack_buffer, zval_get_double(value));
1266 if (buffer->append(stack_buffer, mysql::get_static_type_size(SW_MYSQL_TYPE_DOUBLE)) < 0) {
1267 RETURN_FALSE;
1268 }
1269 break;
1270 default:
1271 zend::String str_value(value);
1272 uint8_t lcb_size = mysql::write_lcb(stack_buffer, str_value.len());
1273 sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_VAR_STRING);
1274 if (buffer->append(stack_buffer, lcb_size) < 0) {
1275 RETURN_FALSE;
1276 }
1277 if (buffer->append(str_value.val(), str_value.len()) < 0) {
1278 RETURN_FALSE;
1279 }
1280 }
1281 index++;
1282 }
1283 ZEND_HASH_FOREACH_END();
1284 }
1285 do {
1286 size_t length = buffer->length - SW_MYSQL_PACKET_HEADER_SIZE;
1287 size_t send_s = SW_MIN(length, SW_MYSQL_MAX_PACKET_BODY_SIZE);
1288 mysql::packet::set_header(buffer->str, send_s, 0);
1289 if (sw_unlikely(!client->send_raw(buffer->str, SW_MYSQL_PACKET_HEADER_SIZE + send_s))) {
1290 RETURN_FALSE;
1291 }
1292 if (sw_unlikely(length > SW_MYSQL_MAX_PACKET_BODY_SIZE)) {
1293 size_t send_n = SW_MYSQL_MAX_PACKET_BODY_SIZE, number = 1;
1294 /* MySQL single packet size is 16M, we must subpackage */
1295 while (send_n < length) {
1296 send_s = length - send_n;
1297 send_s = SW_MIN(send_s, SW_MYSQL_MAX_PACKET_BODY_SIZE);
1298 mysql::packet::set_header(buffer->str, send_s, number++);
1299 if (sw_unlikely(!client->send_raw(buffer->str, SW_MYSQL_PACKET_HEADER_SIZE)) ||
1300 !client->send_raw(buffer->str + SW_MYSQL_PACKET_HEADER_SIZE + send_n, send_s)) {
1301 RETURN_FALSE;
1302 }
1303 send_n += send_s;
1304 }
1305 }
1306 } while (0);
1307 client->state = SW_MYSQL_STATE_EXECUTE;
1308 RETURN_TRUE;
1309 }
1310
recv_execute_response(zval * return_value)1311 void mysql_statement::recv_execute_response(zval *return_value) {
1312 if (sw_unlikely(!is_available())) {
1313 RETURN_FALSE;
1314 }
1315 const char *data;
1316 if (sw_unlikely(!(data = client->recv_none_error_packet()))) {
1317 RETURN_FALSE;
1318 }
1319 if (mysql::server_packet::is_ok(data)) {
1320 mysql::ok_packet ok_packet(data);
1321 result.ok = ok_packet;
1322 client->state =
1323 ok_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_EXECUTE_MORE_RESULTS : SW_MYSQL_STATE_IDLE;
1324 RETURN_TRUE;
1325 }
1326 do {
1327 mysql::lcb_packet lcb_packet(data);
1328 if (sw_unlikely(lcb_packet.length == 0)) {
1329 // is it possible?
1330 client->proto_error(data, SW_MYSQL_PACKET_FIELD);
1331 RETURN_FALSE;
1332 }
1333 // although we have already known the field data when we prepared the statement,
1334 // we don't know if the data is always reliable, such as when we using stored procedure...
1335 // so we should not optimize here for the time being for stability
1336 result.alloc_fields(lcb_packet.length);
1337 for (size_t i = 0; i < result.get_fields_length(); i++) {
1338 if (sw_unlikely(!(data = client->recv_packet()))) {
1339 RETURN_FALSE;
1340 }
1341 result.set_field(i, data);
1342 }
1343 } while (0);
1344 // expect eof
1345 if (sw_unlikely(!(data = client->recv_eof_packet()))) {
1346 RETURN_FALSE;
1347 }
1348 client->state = SW_MYSQL_STATE_EXECUTE_FETCH;
1349 if (client->get_fetch_mode()) {
1350 RETURN_TRUE;
1351 }
1352 fetch_all(return_value);
1353 }
1354
fetch(zval * return_value)1355 void mysql_statement::fetch(zval *return_value) {
1356 if (sw_unlikely(!is_available())) {
1357 RETURN_FALSE;
1358 }
1359 if (sw_unlikely(client->state != SW_MYSQL_STATE_EXECUTE_FETCH)) {
1360 RETURN_NULL();
1361 }
1362 const char *data;
1363 if (sw_unlikely(!(data = client->recv_packet()))) {
1364 RETURN_FALSE;
1365 }
1366 if (mysql::server_packet::is_eof(data)) {
1367 mysql::eof_packet eof_packet(data);
1368 client->state =
1369 eof_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_EXECUTE_MORE_RESULTS : SW_MYSQL_STATE_IDLE;
1370 RETURN_NULL();
1371 }
1372 do {
1373 mysql::row_data row_data(data);
1374 uint32_t null_bitmap_size = mysql::null_bitmap::get_size(result.get_fields_length());
1375 mysql::null_bitmap null_bitmap(row_data.read(null_bitmap_size), null_bitmap_size);
1376
1377 array_init_size(return_value, result.get_fields_length());
1378 for (uint32_t i = 0; i < result.get_fields_length(); i++) {
1379 mysql::field_packet *field = result.get_field(i);
1380
1381 /* to check Null-Bitmap @see https://dev.mysql.com/doc/internals/en/null-bitmap.html */
1382 if (null_bitmap.is_null(i) || field->type == SW_MYSQL_TYPE_NULL) {
1383 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s is null", field->name_length, field->name);
1384 add_assoc_null_ex(return_value, field->name, field->name_length);
1385 continue;
1386 }
1387
1388 switch (field->type) {
1389 /* String */
1390 case SW_MYSQL_TYPE_TINY_BLOB:
1391 case SW_MYSQL_TYPE_MEDIUM_BLOB:
1392 case SW_MYSQL_TYPE_LONG_BLOB:
1393 case SW_MYSQL_TYPE_BLOB:
1394 case SW_MYSQL_TYPE_DECIMAL:
1395 case SW_MYSQL_TYPE_NEWDECIMAL:
1396 case SW_MYSQL_TYPE_BIT:
1397 case SW_MYSQL_TYPE_JSON:
1398 case SW_MYSQL_TYPE_STRING:
1399 case SW_MYSQL_TYPE_VAR_STRING:
1400 case SW_MYSQL_TYPE_VARCHAR:
1401 case SW_MYSQL_TYPE_NEWDATE: {
1402 _add_string:
1403 zval ztext;
1404 client->handle_row_data_text(&ztext, &row_data, field);
1405 if (sw_unlikely(Z_TYPE_P(&ztext) == IS_FALSE)) {
1406 zval_ptr_dtor(return_value);
1407 RETURN_FALSE;
1408 }
1409 add_assoc_zval_ex(return_value, field->name, field->name_length, &ztext);
1410 break;
1411 }
1412 default: {
1413 const char *p = nullptr;
1414 uint8_t lcb = mysql::get_static_type_size(field->type);
1415 if (lcb == 0) {
1416 client->handle_row_data_lcb(&row_data);
1417 lcb = row_data.text.length;
1418 }
1419 p = client->handle_row_data_size(&row_data, lcb);
1420 if (sw_unlikely(!p)) {
1421 zval_ptr_dtor(return_value);
1422 RETURN_FALSE;
1423 }
1424 /* Date Time */
1425 switch (field->type) {
1426 case SW_MYSQL_TYPE_TIMESTAMP:
1427 case SW_MYSQL_TYPE_DATETIME: {
1428 std::string datetime = mysql::datetime(p, row_data.text.length, field->decimals);
1429 add_assoc_stringl_ex(
1430 return_value, field->name, field->name_length, (char *) datetime.c_str(), datetime.length());
1431 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, datetime.c_str());
1432 break;
1433 }
1434 case SW_MYSQL_TYPE_TIME: {
1435 std::string time = mysql::time(p, row_data.text.length, field->decimals);
1436 add_assoc_stringl_ex(
1437 return_value, field->name, field->name_length, (char *) time.c_str(), time.length());
1438 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, time.c_str());
1439 break;
1440 }
1441 case SW_MYSQL_TYPE_DATE: {
1442 std::string date = mysql::date(p, row_data.text.length);
1443 add_assoc_stringl_ex(
1444 return_value, field->name, field->name_length, (char *) date.c_str(), date.length());
1445 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, date.c_str());
1446 break;
1447 }
1448 case SW_MYSQL_TYPE_YEAR: {
1449 add_assoc_long_ex(return_value, field->name, field->name_length, sw_mysql_uint2korr2korr(p));
1450 swoole_trace_log(
1451 SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, sw_mysql_uint2korr2korr(p));
1452 break;
1453 }
1454 /* Number */
1455 case SW_MYSQL_TYPE_TINY:
1456 if (field->flags & SW_MYSQL_UNSIGNED_FLAG) {
1457 add_assoc_long_ex(return_value, field->name, field->name_length, *(uint8_t *) p);
1458 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint8_t *) p);
1459 } else {
1460 add_assoc_long_ex(return_value, field->name, field->name_length, *(int8_t *) p);
1461 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int8_t *) p);
1462 }
1463 break;
1464 case SW_MYSQL_TYPE_SHORT:
1465 if (field->flags & SW_MYSQL_UNSIGNED_FLAG) {
1466 add_assoc_long_ex(return_value, field->name, field->name_length, *(uint16_t *) p);
1467 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint16_t *) p);
1468 } else {
1469 add_assoc_long_ex(return_value, field->name, field->name_length, *(int16_t *) p);
1470 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int16_t *) p);
1471 }
1472 break;
1473 case SW_MYSQL_TYPE_INT24:
1474 case SW_MYSQL_TYPE_LONG:
1475 if (field->flags & SW_MYSQL_UNSIGNED_FLAG) {
1476 add_assoc_long_ex(return_value, field->name, field->name_length, *(uint32_t *) p);
1477 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint32_t *) p);
1478 } else {
1479 add_assoc_long_ex(return_value, field->name, field->name_length, *(int32_t *) p);
1480 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int32_t *) p);
1481 }
1482 break;
1483 case SW_MYSQL_TYPE_LONGLONG:
1484 if (field->flags & SW_MYSQL_UNSIGNED_FLAG) {
1485 add_assoc_ulong_safe_ex(return_value, field->name, field->name_length, *(uint64_t *) p);
1486 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%lu", field->name_length, field->name, *(uint64_t *) p);
1487 } else {
1488 add_assoc_long_ex(return_value, field->name, field->name_length, *(int64_t *) p);
1489 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%ld", field->name_length, field->name, *(int64_t *) p);
1490 }
1491 break;
1492 case SW_MYSQL_TYPE_FLOAT: {
1493 double dv = sw_php_math_round(*(float *) p, 5, PHP_ROUND_HALF_DOWN);
1494 add_assoc_double_ex(return_value, field->name, field->name_length, dv);
1495 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%.7f", field->name_length, field->name, dv);
1496 } break;
1497 case SW_MYSQL_TYPE_DOUBLE: {
1498 add_assoc_double_ex(return_value, field->name, field->name_length, *(double *) p);
1499 swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%.16f", field->name_length, field->name, *(double *) p);
1500 } break;
1501 default:
1502 swoole_warning("unknown type[%d] for field [%.*s].", field->type, field->name_length, field->name);
1503 goto _add_string;
1504 }
1505 }
1506 }
1507 }
1508 } while (0);
1509 }
1510
fetch_all(zval * return_value)1511 void mysql_statement::fetch_all(zval *return_value) {
1512 if (sw_unlikely(!is_available())) {
1513 RETURN_FALSE;
1514 }
1515
1516 zval zrow;
1517 array_init(return_value);
1518 while (true) {
1519 fetch(&zrow);
1520 if (sw_unlikely(ZVAL_IS_NULL(&zrow))) {
1521 // eof
1522 return;
1523 }
1524 if (sw_unlikely(Z_TYPE_P(&zrow) == IS_FALSE)) {
1525 // error
1526 zval_ptr_dtor(return_value);
1527 RETURN_FALSE;
1528 }
1529 (void) add_next_index_zval(return_value, &zrow);
1530 }
1531 }
1532
next_result(zval * return_value)1533 void mysql_statement::next_result(zval *return_value) {
1534 if (sw_unlikely(!is_available())) {
1535 RETURN_FALSE;
1536 }
1537 if (sw_unlikely(client->state == SW_MYSQL_STATE_EXECUTE_FETCH)) {
1538 // skip unread data
1539 fetch_all(return_value);
1540 zval_ptr_dtor(return_value);
1541 next_result(return_value);
1542 } else if (sw_likely(client->state == SW_MYSQL_STATE_EXECUTE_MORE_RESULTS)) {
1543 recv_execute_response(return_value);
1544 } else if (client->state == SW_MYSQL_STATE_IDLE) {
1545 RETURN_NULL();
1546 } else {
1547 RETURN_FALSE;
1548 }
1549 }
1550
php_swoole_mysql_coro_fetch_object(zend_object * obj)1551 static sw_inline mysql_coro_t *php_swoole_mysql_coro_fetch_object(zend_object *obj) {
1552 return (mysql_coro_t *) ((char *) obj - swoole_mysql_coro_handlers.offset);
1553 }
1554
php_swoole_get_mysql_client(zval * zobject)1555 static sw_inline mysql_client *php_swoole_get_mysql_client(zval *zobject) {
1556 return php_swoole_mysql_coro_fetch_object(Z_OBJ_P(zobject))->client;
1557 }
1558
php_swoole_mysql_coro_free_object(zend_object * object)1559 static void php_swoole_mysql_coro_free_object(zend_object *object) {
1560 mysql_coro_t *zmc = php_swoole_mysql_coro_fetch_object(object);
1561 delete zmc->client;
1562 zend_object_std_dtor(&zmc->std);
1563 }
1564
php_swoole_mysql_coro_create_object(zend_class_entry * ce)1565 static zend_object *php_swoole_mysql_coro_create_object(zend_class_entry *ce) {
1566 mysql_coro_t *zmc = (mysql_coro_t *) zend_object_alloc(sizeof(mysql_coro_t), ce);
1567 zend_object_std_init(&zmc->std, ce);
1568 object_properties_init(&zmc->std, ce);
1569 zmc->std.handlers = &swoole_mysql_coro_handlers;
1570 zmc->client = new mysql_client;
1571 return &zmc->std;
1572 }
1573
php_swoole_mysql_coro_statement_fetch_object(zend_object * obj)1574 static sw_inline mysql_coro_statement_t *php_swoole_mysql_coro_statement_fetch_object(zend_object *obj) {
1575 return (mysql_coro_statement_t *) ((char *) obj - swoole_mysql_coro_statement_handlers.offset);
1576 }
1577
php_swoole_get_mysql_statement(zval * zobject)1578 static sw_inline mysql_statement *php_swoole_get_mysql_statement(zval *zobject) {
1579 return php_swoole_mysql_coro_statement_fetch_object(Z_OBJ_P(zobject))->statement;
1580 }
1581
php_swoole_mysql_coro_statement_free_object(zend_object * object)1582 static void php_swoole_mysql_coro_statement_free_object(zend_object *object) {
1583 mysql_coro_statement_t *zms = php_swoole_mysql_coro_statement_fetch_object(object);
1584 delete zms->statement;
1585 OBJ_RELEASE(zms->zclient);
1586 zend_object_std_dtor(&zms->std);
1587 }
1588
php_swoole_mysql_coro_statement_create_object(zend_class_entry * ce,mysql_statement * statement,zend_object * client)1589 static sw_inline zend_object *php_swoole_mysql_coro_statement_create_object(zend_class_entry *ce,
1590 mysql_statement *statement,
1591 zend_object *client) {
1592 zval zobject;
1593 mysql_coro_statement_t *zms = (mysql_coro_statement_t *) zend_object_alloc(sizeof(mysql_coro_statement_t), ce);
1594 zend_object_std_init(&zms->std, ce);
1595 object_properties_init(&zms->std, ce);
1596 zms->std.handlers = &swoole_mysql_coro_statement_handlers;
1597 ZVAL_OBJ(&zobject, &zms->std);
1598 zend_update_property_long(ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("id"), statement->info.id);
1599 zms->statement = statement;
1600 zms->zclient = client;
1601 GC_ADDREF(client);
1602 return &zms->std;
1603 }
1604
php_swoole_mysql_coro_statement_create_object(mysql_statement * statement,zend_object * client)1605 static sw_inline zend_object *php_swoole_mysql_coro_statement_create_object(mysql_statement *statement,
1606 zend_object *client) {
1607 return php_swoole_mysql_coro_statement_create_object(swoole_mysql_coro_statement_ce, statement, client);
1608 }
1609
php_swoole_mysql_coro_statement_create_object(zend_class_entry * ce)1610 static zend_object *php_swoole_mysql_coro_statement_create_object(zend_class_entry *ce) {
1611 php_swoole_fatal_error(E_ERROR, "you must create mysql statement object by prepare method");
1612 return nullptr;
1613 }
1614
swoole_mysql_coro_sync_error_properties(zval * zobject,int error_code,const char * error_msg,const bool connected=true)1615 static sw_inline void swoole_mysql_coro_sync_error_properties(zval *zobject,
1616 int error_code,
1617 const char *error_msg,
1618 const bool connected = true) {
1619 SW_ASSERT(instanceof_function(Z_OBJCE_P(zobject), swoole_mysql_coro_ce) ||
1620 instanceof_function(Z_OBJCE_P(zobject), swoole_mysql_coro_statement_ce));
1621 zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errno"), error_code);
1622 zend_update_property_string(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("error"), error_msg);
1623 if (!connected) {
1624 zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), connected);
1625 }
1626 }
1627
swoole_mysql_coro_sync_query_result_properties(zval * zobject,mysql_client * mc,zval * return_value)1628 static sw_inline void swoole_mysql_coro_sync_query_result_properties(zval *zobject,
1629 mysql_client *mc,
1630 zval *return_value) {
1631 switch (Z_TYPE_P(return_value)) {
1632 case IS_TRUE: {
1633 mysql::ok_packet *ok_packet = &mc->result.ok;
1634 zend_update_property_long(
1635 Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("affected_rows"), ok_packet->affected_rows);
1636 zend_update_property_long(
1637 Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("insert_id"), ok_packet->last_insert_id);
1638 break;
1639 }
1640 case IS_FALSE: {
1641 swoole_mysql_coro_sync_error_properties(zobject, mc->get_error_code(), mc->get_error_msg());
1642 break;
1643 }
1644 default:
1645 break;
1646 }
1647 }
1648
swoole_mysql_coro_sync_execute_error_properties(zval * zobject,int error_code,const char * error_msg,const bool connected=true)1649 static sw_inline void swoole_mysql_coro_sync_execute_error_properties(zval *zobject,
1650 int error_code,
1651 const char *error_msg,
1652 const bool connected = true) {
1653 swoole_mysql_coro_sync_error_properties(zobject, error_code, error_msg, connected);
1654
1655 /* backward compatibility (sync error info to client) */
1656 zval zclient;
1657 ZVAL_OBJ(&zclient, php_swoole_mysql_coro_statement_fetch_object(Z_OBJ_P(zobject))->zclient);
1658 swoole_mysql_coro_sync_error_properties(&zclient, error_code, error_msg, connected);
1659 }
1660
swoole_mysql_coro_sync_execute_result_properties(zval * zobject,zval * return_value)1661 static sw_inline void swoole_mysql_coro_sync_execute_result_properties(zval *zobject, zval *return_value) {
1662 mysql_coro_statement_t *zms = php_swoole_mysql_coro_statement_fetch_object(Z_OBJ_P(zobject));
1663 mysql_statement *ms = zms->statement;
1664
1665 switch (Z_TYPE_P(return_value)) {
1666 case IS_TRUE: {
1667 mysql::ok_packet *ok_packet = &ms->result.ok;
1668 zend_update_property_long(
1669 Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("affected_rows"), ok_packet->affected_rows);
1670 zend_update_property_long(
1671 Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("insert_id"), ok_packet->last_insert_id);
1672
1673 /* backward compatibility (sync result info to client) */
1674 zval zclient;
1675 ZVAL_OBJ(&zclient, zms->zclient);
1676 zend_update_property_long(
1677 Z_OBJCE_P(&zclient), SW_Z8_OBJ_P(&zclient), ZEND_STRL("affected_rows"), ok_packet->affected_rows);
1678 zend_update_property_long(
1679 Z_OBJCE_P(&zclient), SW_Z8_OBJ_P(&zclient), ZEND_STRL("insert_id"), ok_packet->last_insert_id);
1680 break;
1681 }
1682 case IS_FALSE: {
1683 swoole_mysql_coro_sync_execute_error_properties(zobject, ms->get_error_code(), ms->get_error_msg());
1684 break;
1685 }
1686 default:
1687 break;
1688 }
1689 }
1690
php_swoole_mysql_coro_minit(int module_number)1691 void php_swoole_mysql_coro_minit(int module_number) {
1692 SW_INIT_CLASS_ENTRY(swoole_mysql_coro, "Swoole\\Coroutine\\MySQL", nullptr, "Co\\MySQL", swoole_mysql_coro_methods);
1693 SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro);
1694 SW_SET_CLASS_CLONEABLE(swoole_mysql_coro, sw_zend_class_clone_deny);
1695 SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro, sw_zend_class_unset_property_deny);
1696 SW_SET_CLASS_CUSTOM_OBJECT(
1697 swoole_mysql_coro, php_swoole_mysql_coro_create_object, php_swoole_mysql_coro_free_object, mysql_coro_t, std);
1698
1699 SW_INIT_CLASS_ENTRY(swoole_mysql_coro_statement,
1700 "Swoole\\Coroutine\\MySQL\\Statement",
1701 nullptr,
1702 "Co\\MySQL\\Statement",
1703 swoole_mysql_coro_statement_methods);
1704 SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro_statement);
1705 SW_SET_CLASS_CLONEABLE(swoole_mysql_coro_statement, sw_zend_class_clone_deny);
1706 SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro_statement, sw_zend_class_unset_property_deny);
1707 SW_SET_CLASS_CUSTOM_OBJECT(swoole_mysql_coro_statement,
1708 php_swoole_mysql_coro_statement_create_object,
1709 php_swoole_mysql_coro_statement_free_object,
1710 mysql_coro_statement_t,
1711 std);
1712
1713 SW_INIT_CLASS_ENTRY_EX(swoole_mysql_coro_exception,
1714 "Swoole\\Coroutine\\MySQL\\Exception",
1715 nullptr,
1716 "Co\\MySQL\\Exception",
1717 nullptr,
1718 swoole_exception);
1719 SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro_exception);
1720 SW_SET_CLASS_CLONEABLE(swoole_mysql_coro_exception, sw_zend_class_clone_deny);
1721 SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro_exception, sw_zend_class_unset_property_deny);
1722 SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_mysql_coro_exception);
1723
1724 zend_declare_property_null(swoole_mysql_coro_ce, ZEND_STRL("serverInfo"), ZEND_ACC_PUBLIC);
1725 zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC);
1726 zend_declare_property_bool(swoole_mysql_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC);
1727 zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("connect_errno"), 0, ZEND_ACC_PUBLIC);
1728 zend_declare_property_string(swoole_mysql_coro_ce, ZEND_STRL("connect_error"), "", ZEND_ACC_PUBLIC);
1729 zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("affected_rows"), 0, ZEND_ACC_PUBLIC);
1730 zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("insert_id"), 0, ZEND_ACC_PUBLIC);
1731 zend_declare_property_string(swoole_mysql_coro_ce, ZEND_STRL("error"), "", ZEND_ACC_PUBLIC);
1732 zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("errno"), 0, ZEND_ACC_PUBLIC);
1733
1734 zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC);
1735 zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("affected_rows"), 0, ZEND_ACC_PUBLIC);
1736 zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("insert_id"), 0, ZEND_ACC_PUBLIC);
1737 zend_declare_property_string(swoole_mysql_coro_statement_ce, ZEND_STRL("error"), "", ZEND_ACC_PUBLIC);
1738 zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("errno"), 0, ZEND_ACC_PUBLIC);
1739
1740 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_UNKNOWN_ERROR", MYSQLND_CR_UNKNOWN_ERROR);
1741 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_CONNECTION_ERROR", MYSQLND_CR_CONNECTION_ERROR);
1742 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR", MYSQLND_CR_SERVER_GONE_ERROR);
1743 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_OUT_OF_MEMORY", MYSQLND_CR_OUT_OF_MEMORY);
1744 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_SERVER_LOST", MYSQLND_CR_SERVER_LOST);
1745 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_COMMANDS_OUT_OF_SYNC", MYSQLND_CR_COMMANDS_OUT_OF_SYNC);
1746 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_CANT_FIND_CHARSET", MYSQLND_CR_CANT_FIND_CHARSET);
1747 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_MALFORMED_PACKET", MYSQLND_CR_MALFORMED_PACKET);
1748 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_NOT_IMPLEMENTED", MYSQLND_CR_NOT_IMPLEMENTED);
1749 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_NO_PREPARE_STMT", MYSQLND_CR_NO_PREPARE_STMT);
1750 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_PARAMS_NOT_BOUND", MYSQLND_CR_PARAMS_NOT_BOUND);
1751 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_INVALID_PARAMETER_NO", MYSQLND_CR_INVALID_PARAMETER_NO);
1752 SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_INVALID_BUFFER_USE", MYSQLND_CR_INVALID_BUFFER_USE);
1753 }
1754
PHP_METHOD(swoole_mysql_coro,__construct)1755 static PHP_METHOD(swoole_mysql_coro, __construct) {}
PHP_METHOD(swoole_mysql_coro,__destruct)1756 static PHP_METHOD(swoole_mysql_coro, __destruct) {}
1757
PHP_METHOD(swoole_mysql_coro,connect)1758 static PHP_METHOD(swoole_mysql_coro, connect) {
1759 mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS);
1760 zval *zserver_info = nullptr;
1761
1762 ZEND_PARSE_PARAMETERS_START(0, 1)
1763 Z_PARAM_OPTIONAL
1764 Z_PARAM_ARRAY_EX(zserver_info, 1, 0)
1765 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1766
1767 if (zserver_info) {
1768 HashTable *ht = Z_ARRVAL_P(zserver_info);
1769 zval *ztmp;
1770
1771 if (php_swoole_array_get_value(ht, "host", ztmp)) {
1772 mc->host = std::string(zend::String(ztmp).val());
1773 } else {
1774 zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [host] is required", EINVAL);
1775 RETURN_FALSE;
1776 }
1777 if (php_swoole_array_get_value(ht, "port", ztmp)) {
1778 mc->port = zval_get_long(ztmp);
1779 }
1780 if (php_swoole_array_get_value(ht, "ssl", ztmp)) {
1781 mc->ssl = zval_is_true(ztmp);
1782 #ifndef SW_USE_OPENSSL
1783 if (sw_unlikely(mc->ssl)) {
1784 zend_throw_exception_ex(
1785 swoole_mysql_coro_exception_ce,
1786 EPROTONOSUPPORT,
1787 "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole");
1788 RETURN_FALSE;
1789 }
1790 #endif
1791 }
1792 if (php_swoole_array_get_value(ht, "user", ztmp)) {
1793 mc->user = std::string(zend::String(ztmp).val());
1794 } else {
1795 zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [user] is required", EINVAL);
1796 RETURN_FALSE;
1797 }
1798 if (php_swoole_array_get_value(ht, "password", ztmp)) {
1799 mc->password = std::string(zend::String(ztmp).val());
1800 } else {
1801 zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [password] is required", EINVAL);
1802 RETURN_FALSE;
1803 }
1804 if (php_swoole_array_get_value(ht, "database", ztmp)) {
1805 mc->database = std::string(zend::String(ztmp).val());
1806 } else {
1807 zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [database] is required", EINVAL);
1808 RETURN_FALSE;
1809 }
1810 if (php_swoole_array_get_value(ht, "timeout", ztmp)) {
1811 mc->connect_timeout = zval_get_double(ztmp);
1812 }
1813 if (php_swoole_array_get_value(ht, "charset", ztmp)) {
1814 zend::String zstr_charset(ztmp);
1815 char charset = mysql::get_charset(zstr_charset.val());
1816 if (UNEXPECTED(charset < 0)) {
1817 zend_throw_exception_ex(
1818 swoole_mysql_coro_exception_ce, EINVAL, "Unknown charset [%s]", zstr_charset.val());
1819 RETURN_FALSE;
1820 }
1821 mc->charset = charset;
1822 }
1823 if (php_swoole_array_get_value(ht, "strict_type", ztmp)) {
1824 mc->strict_type = zval_is_true(ztmp);
1825 }
1826 if (php_swoole_array_get_value(ht, "fetch_mode", ztmp)) {
1827 if (UNEXPECTED(!mc->set_fetch_mode(zval_is_true(ztmp)))) {
1828 zend_throw_exception_ex(
1829 swoole_mysql_coro_exception_ce, mc->get_error_code(), "%s", mc->get_error_msg());
1830 RETURN_FALSE;
1831 }
1832 }
1833 }
1834 if (!mc->connect()) {
1835 zend_update_property_long(
1836 swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connect_errno"), mc->get_error_code());
1837 zend_update_property_string(
1838 swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connect_error"), mc->get_error_msg());
1839 RETURN_FALSE;
1840 }
1841 if (zserver_info && php_swoole_array_length(zserver_info) > 0) {
1842 php_array_merge(Z_ARRVAL_P(sw_zend_read_and_convert_property_array(
1843 swoole_mysql_coro_ce, ZEND_THIS, ZEND_STRL("serverInfo"), 0)),
1844 Z_ARRVAL_P(zserver_info));
1845 }
1846 zend_update_property_long(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("sock"), mc->get_fd());
1847 zend_update_property_bool(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 1);
1848 RETURN_TRUE;
1849 }
1850
PHP_METHOD(swoole_mysql_coro,getDefer)1851 static PHP_METHOD(swoole_mysql_coro, getDefer) {
1852 mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS);
1853 RETURN_BOOL(mc->get_defer());
1854 }
1855
PHP_METHOD(swoole_mysql_coro,setDefer)1856 static PHP_METHOD(swoole_mysql_coro, setDefer) {
1857 mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS);
1858 zend_bool defer = 1;
1859
1860 ZEND_PARSE_PARAMETERS_START(0, 1)
1861 Z_PARAM_OPTIONAL
1862 Z_PARAM_BOOL(defer)
1863 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1864
1865 bool ret = mc->set_defer(defer);
1866 if (UNEXPECTED(!ret)) {
1867 zend_throw_exception_ex(swoole_mysql_coro_exception_ce, mc->get_error_code(), "%s", mc->get_error_msg());
1868 }
1869 RETURN_BOOL(ret);
1870 }
1871
PHP_METHOD(swoole_mysql_coro,query)1872 static PHP_METHOD(swoole_mysql_coro, query) {
1873 mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS);
1874 char *sql;
1875 size_t sql_length;
1876 double timeout = 0;
1877
1878 ZEND_PARSE_PARAMETERS_START(1, 2)
1879 Z_PARAM_STRING(sql, sql_length)
1880 Z_PARAM_OPTIONAL
1881 Z_PARAM_DOUBLE(timeout)
1882 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1883
1884 mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR);
1885 mc->query(return_value, sql, sql_length);
1886 mc->del_timeout_controller();
1887 swoole_mysql_coro_sync_query_result_properties(ZEND_THIS, mc, return_value);
1888 }
1889
PHP_METHOD(swoole_mysql_coro,fetch)1890 static PHP_METHOD(swoole_mysql_coro, fetch) {
1891 mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS);
1892 double timeout = 0;
1893
1894 ZEND_PARSE_PARAMETERS_START(0, 1)
1895 Z_PARAM_OPTIONAL
1896 Z_PARAM_DOUBLE(timeout)
1897 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1898
1899 mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR);
1900 mc->fetch(return_value);
1901 mc->del_timeout_controller();
1902 if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) {
1903 swoole_mysql_coro_sync_error_properties(
1904 ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected());
1905 }
1906 }
1907
PHP_METHOD(swoole_mysql_coro,fetchAll)1908 static PHP_METHOD(swoole_mysql_coro, fetchAll) {
1909 mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS);
1910 double timeout = 0;
1911
1912 ZEND_PARSE_PARAMETERS_START(0, 1)
1913 Z_PARAM_OPTIONAL
1914 Z_PARAM_DOUBLE(timeout)
1915 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1916
1917 mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR);
1918 mc->fetch_all(return_value);
1919 mc->del_timeout_controller();
1920 if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) {
1921 swoole_mysql_coro_sync_error_properties(
1922 ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected());
1923 }
1924 }
1925
PHP_METHOD(swoole_mysql_coro,nextResult)1926 static PHP_METHOD(swoole_mysql_coro, nextResult) {
1927 mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS);
1928 double timeout = 0;
1929
1930 ZEND_PARSE_PARAMETERS_START(0, 1)
1931 Z_PARAM_OPTIONAL
1932 Z_PARAM_DOUBLE(timeout)
1933 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1934
1935 mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR);
1936 mc->next_result(return_value);
1937 mc->del_timeout_controller();
1938 swoole_mysql_coro_sync_query_result_properties(ZEND_THIS, mc, return_value);
1939 if (Z_TYPE_P(return_value) == IS_TRUE) {
1940 if (mc->state == SW_MYSQL_STATE_IDLE) {
1941 // the end of procedure
1942 Z_TYPE_INFO_P(return_value) = mc->get_fetch_mode() ? IS_FALSE : IS_NULL;
1943 }
1944 }
1945 }
1946
PHP_METHOD(swoole_mysql_coro,prepare)1947 static PHP_METHOD(swoole_mysql_coro, prepare) {
1948 mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS);
1949 char *statement;
1950 size_t statement_length;
1951 double timeout = 0;
1952
1953 ZEND_PARSE_PARAMETERS_START(1, 2)
1954 Z_PARAM_STRING(statement, statement_length)
1955 Z_PARAM_OPTIONAL
1956 Z_PARAM_DOUBLE(timeout)
1957 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1958
1959 mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR);
1960 if (UNEXPECTED(!mc->send_prepare_request(statement, statement_length))) {
1961 _failed:
1962 swoole_mysql_coro_sync_error_properties(
1963 ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected());
1964 RETVAL_FALSE;
1965 } else if (UNEXPECTED(mc->get_defer())) {
1966 RETVAL_TRUE;
1967 } else {
1968 mysql_statement *statement = mc->recv_prepare_response();
1969 if (UNEXPECTED(!statement)) {
1970 goto _failed;
1971 }
1972 RETVAL_OBJ(php_swoole_mysql_coro_statement_create_object(statement, Z_OBJ_P(ZEND_THIS)));
1973 }
1974 mc->del_timeout_controller();
1975 }
1976
PHP_METHOD(swoole_mysql_coro,recv)1977 static PHP_METHOD(swoole_mysql_coro, recv) {
1978 mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS);
1979 double timeout = 0;
1980
1981 ZEND_PARSE_PARAMETERS_START(0, 1)
1982 Z_PARAM_OPTIONAL
1983 Z_PARAM_DOUBLE(timeout)
1984 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1985
1986 if (UNEXPECTED(!mc->check_connection())) {
1987 swoole_mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), false);
1988 RETURN_FALSE;
1989 }
1990 mc->add_timeout_controller(timeout, Socket::TIMEOUT_READ);
1991 switch (mc->state) {
1992 case SW_MYSQL_STATE_IDLE:
1993 swoole_mysql_coro_sync_error_properties(ZEND_THIS, ENOMSG, "no message to receive");
1994 RETVAL_FALSE;
1995 break;
1996 case SW_MYSQL_STATE_QUERY:
1997 mc->recv_query_response(return_value);
1998 break;
1999 case SW_MYSQL_STATE_PREPARE: {
2000 mysql_statement *statement = mc->recv_prepare_response();
2001 if (UNEXPECTED(!statement)) {
2002 RETVAL_FALSE;
2003 } else {
2004 RETVAL_OBJ(php_swoole_mysql_coro_statement_create_object(statement, Z_OBJ_P(ZEND_THIS)));
2005 }
2006 break;
2007 }
2008 default:
2009 if (UNEXPECTED(mc->state & SW_MYSQL_COMMAND_FLAG_EXECUTE)) {
2010 swoole_mysql_coro_sync_error_properties(ZEND_THIS, EPERM, "please use statement to receive data");
2011 } else {
2012 swoole_mysql_coro_sync_error_properties(
2013 ZEND_THIS, EPERM, "please use fetch/fetchAll/nextResult to get result");
2014 }
2015 RETVAL_FALSE;
2016 }
2017 mc->del_timeout_controller();
2018 }
2019
swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAMETERS,const char * command,size_t command_length)2020 static void swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAMETERS,
2021 const char *command,
2022 size_t command_length) {
2023 mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS);
2024 double timeout = 0;
2025
2026 ZEND_PARSE_PARAMETERS_START(0, 1)
2027 Z_PARAM_OPTIONAL
2028 Z_PARAM_DOUBLE(timeout)
2029 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
2030
2031 if (UNEXPECTED(mc->get_defer())) {
2032 zend_throw_exception_ex(
2033 swoole_mysql_coro_exception_ce,
2034 EPERM,
2035 "you should not query transaction when defer mode is on, if you want, please use `query('%s')` instead",
2036 command);
2037 RETURN_FALSE;
2038 }
2039
2040 mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR);
2041 mc->query(return_value, command, command_length);
2042 mc->del_timeout_controller();
2043 swoole_mysql_coro_sync_query_result_properties(ZEND_THIS, mc, return_value);
2044 }
2045
PHP_METHOD(swoole_mysql_coro,begin)2046 static PHP_METHOD(swoole_mysql_coro, begin) {
2047 swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("BEGIN"));
2048 }
2049
PHP_METHOD(swoole_mysql_coro,commit)2050 static PHP_METHOD(swoole_mysql_coro, commit) {
2051 swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("COMMIT"));
2052 }
2053
PHP_METHOD(swoole_mysql_coro,rollback)2054 static PHP_METHOD(swoole_mysql_coro, rollback) {
2055 swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ROLLBACK"));
2056 }
2057
2058 #ifdef SW_USE_MYSQLND
PHP_METHOD(swoole_mysql_coro,escape)2059 static PHP_METHOD(swoole_mysql_coro, escape) {
2060 mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS);
2061 char *str;
2062 size_t str_length;
2063 zend_long flags = 0;
2064
2065 ZEND_PARSE_PARAMETERS_START(1, 2)
2066 Z_PARAM_STRING(str, str_length)
2067 Z_PARAM_OPTIONAL
2068 Z_PARAM_LONG(flags)
2069 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
2070
2071 char *newstr = (char *) safe_emalloc(2, str_length + 1, 1);
2072 const MYSQLND_CHARSET *cset = mysqlnd_find_charset_nr(mc->charset);
2073 if (!cset) {
2074 php_swoole_fatal_error(E_ERROR, "unknown mysql charset[%d]", mc->charset);
2075 RETURN_FALSE;
2076 }
2077 zend_ulong newstr_len = mysqlnd_cset_escape_slashes(cset, newstr, str, str_length);
2078 if (newstr_len == (zend_ulong) ~0) {
2079 php_swoole_fatal_error(E_ERROR, "mysqlnd_cset_escape_slashes() failed");
2080 RETURN_FALSE;
2081 }
2082 RETVAL_STRINGL(newstr, newstr_len);
2083 efree(newstr);
2084 return;
2085 }
2086 #endif
2087
PHP_METHOD(swoole_mysql_coro,close)2088 static PHP_METHOD(swoole_mysql_coro, close) {
2089 mysql_client *mc = php_swoole_get_mysql_client(ZEND_THIS);
2090 mc->close();
2091 zend_update_property_bool(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 0);
2092 RETURN_TRUE;
2093 }
2094
PHP_METHOD(swoole_mysql_coro_statement,execute)2095 static PHP_METHOD(swoole_mysql_coro_statement, execute) {
2096 mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS);
2097 zval *params = nullptr;
2098 double timeout = 0;
2099
2100 ZEND_PARSE_PARAMETERS_START(0, 2)
2101 Z_PARAM_OPTIONAL
2102 Z_PARAM_ARRAY_EX(params, 1, 0)
2103 Z_PARAM_DOUBLE(timeout)
2104 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
2105
2106 ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR);
2107 ms->execute(return_value, params);
2108 ms->del_timeout_controller();
2109 swoole_mysql_coro_sync_execute_result_properties(ZEND_THIS, return_value);
2110 }
2111
PHP_METHOD(swoole_mysql_coro_statement,fetch)2112 static PHP_METHOD(swoole_mysql_coro_statement, fetch) {
2113 mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS);
2114 double timeout = 0;
2115
2116 ZEND_PARSE_PARAMETERS_START(0, 1)
2117 Z_PARAM_OPTIONAL
2118 Z_PARAM_DOUBLE(timeout)
2119 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
2120
2121 ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR);
2122 ms->fetch(return_value);
2123 ms->del_timeout_controller();
2124 if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) {
2125 swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg());
2126 }
2127 }
2128
PHP_METHOD(swoole_mysql_coro_statement,fetchAll)2129 static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) {
2130 mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS);
2131 double timeout = 0;
2132
2133 ZEND_PARSE_PARAMETERS_START(0, 1)
2134 Z_PARAM_OPTIONAL
2135 Z_PARAM_DOUBLE(timeout)
2136 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
2137
2138 ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR);
2139 ms->fetch_all(return_value);
2140 ms->del_timeout_controller();
2141 if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) {
2142 swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg());
2143 }
2144 }
2145
PHP_METHOD(swoole_mysql_coro_statement,nextResult)2146 static PHP_METHOD(swoole_mysql_coro_statement, nextResult) {
2147 mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS);
2148 double timeout = 0;
2149
2150 ZEND_PARSE_PARAMETERS_START(0, 1)
2151 Z_PARAM_OPTIONAL
2152 Z_PARAM_DOUBLE(timeout)
2153 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
2154
2155 ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR);
2156 ms->next_result(return_value);
2157 ms->del_timeout_controller();
2158 swoole_mysql_coro_sync_execute_result_properties(ZEND_THIS, return_value);
2159 if (Z_TYPE_P(return_value) == IS_TRUE) {
2160 mysql_client *mc = ms->get_client();
2161 if (mc->state == SW_MYSQL_STATE_IDLE) {
2162 // the end of procedure
2163 Z_TYPE_INFO_P(return_value) = mc->get_fetch_mode() ? IS_FALSE : IS_NULL;
2164 }
2165 }
2166 }
2167
PHP_METHOD(swoole_mysql_coro_statement,recv)2168 static PHP_METHOD(swoole_mysql_coro_statement, recv) {
2169 mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS);
2170 double timeout = 0;
2171 enum sw_mysql_state state;
2172
2173 ZEND_PARSE_PARAMETERS_START(0, 1)
2174 Z_PARAM_OPTIONAL
2175 Z_PARAM_DOUBLE(timeout)
2176 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
2177
2178 if (UNEXPECTED(!ms->is_available())) {
2179 swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false);
2180 RETURN_FALSE;
2181 }
2182 ms->add_timeout_controller(timeout, Socket::TIMEOUT_READ);
2183 switch ((state = ms->get_client()->state)) {
2184 case SW_MYSQL_STATE_IDLE:
2185 swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ENOMSG, "no message to receive");
2186 RETVAL_FALSE;
2187 break;
2188 case SW_MYSQL_STATE_EXECUTE:
2189 ms->recv_execute_response(return_value);
2190 break;
2191 default:
2192 if (UNEXPECTED(state & SW_MYSQL_COMMAND_FLAG_QUERY)) {
2193 swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, EPERM, "please use client to receive data");
2194 } else {
2195 swoole_mysql_coro_sync_execute_error_properties(
2196 ZEND_THIS, EPERM, "please use fetch/fetchAll/nextResult to get result");
2197 }
2198 RETVAL_FALSE;
2199 }
2200 ms->del_timeout_controller();
2201 }
2202
PHP_METHOD(swoole_mysql_coro_statement,close)2203 static PHP_METHOD(swoole_mysql_coro_statement, close) {
2204 mysql_statement *ms = php_swoole_get_mysql_statement(ZEND_THIS);
2205 ms->close();
2206 RETURN_TRUE;
2207 }
2208