From 9f970aa80b444af7eba5eb0f0abd362c27cc7729 Mon Sep 17 00:00:00 2001 From: twosee Date: Sat, 2 Jun 2018 19:15:10 +0800 Subject: [PATCH 01/26] Remove useless code. --- swoole_mysql.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/swoole_mysql.h b/swoole_mysql.h index 5a076e53d36..5dadb26c955 100644 --- a/swoole_mysql.h +++ b/swoole_mysql.h @@ -19,7 +19,6 @@ #ifndef SWOOLE_MYSQL_H_ #define SWOOLE_MYSQL_H_ -//#define SW_MYSQL_STRICT_TYPE //#define SW_MYSQL_DEBUG enum mysql_command @@ -339,7 +338,6 @@ typedef struct _mysql_client int fd; uint32_t transaction :1; uint32_t connected :1; - uint32_t strict; mysql_connector connector; mysql_statement *statement; @@ -353,7 +351,6 @@ typedef struct _mysql_client #endif mysql_response_t response; /* single response */ - swLinkedList *response_list; /* multi responses (in fetch mode) */ } mysql_client; From 353fb3a9c12a5973b9ab95505b62702934a471d5 Mon Sep 17 00:00:00 2001 From: twosee Date: Sat, 2 Jun 2018 19:26:22 +0800 Subject: [PATCH 02/26] Add fetch_mode option and format. --- swoole_mysql.c | 20 ++++++++++++++++++++ swoole_mysql.h | 1 + swoole_mysql_coro.c | 30 +++++++++++++++++++++++++++--- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/swoole_mysql.c b/swoole_mysql.c index 8dde47ef834..f0284ac7b89 100644 --- a/swoole_mysql.c +++ b/swoole_mysql.c @@ -1884,6 +1884,26 @@ static PHP_METHOD(swoole_mysql, connect) connector->strict_type = 0; } + if (php_swoole_array_get_value(_ht, "fetch_mode", value)) + { +#if PHP_MAJOR_VERSION < 7 + if(Z_TYPE_P(value) == IS_BOOL && Z_BVAL_P(value) == 1) +#else + if (Z_TYPE_P(value) == IS_TRUE) +#endif + { + connector->fetch_mode = 1; + } + else + { + connector->fetch_mode = 0; + } + } + else + { + connector->fetch_mode = 0; + } + swClient *cli = emalloc(sizeof(swClient)); int type = SW_SOCK_TCP; diff --git a/swoole_mysql.h b/swoole_mysql.h index 5dadb26c955..eaec4607682 100644 --- a/swoole_mysql.h +++ b/swoole_mysql.h @@ -224,6 +224,7 @@ typedef struct char *password; char *database; zend_bool strict_type; + zend_bool fetch_mode; zend_size_t host_len; zend_size_t user_len; diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index cf782d74c03..9ec0a2686db 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -551,17 +551,41 @@ static PHP_METHOD(swoole_mysql_coro, connect) #if PHP_MAJOR_VERSION < 7 if(Z_TYPE_P(value) == IS_BOOL && Z_BVAL_P(value) == 1) #else - if(Z_TYPE_P(value) == IS_TRUE) + if (Z_TYPE_P(value) == IS_TRUE) #endif { connector->strict_type = 1; - }else{ + } + else + { connector->strict_type = 0; } - } else{ + } + else + { connector->strict_type = 0; } + if (php_swoole_array_get_value(_ht, "fetch_mode", value)) + { +#if PHP_MAJOR_VERSION < 7 + if(Z_TYPE_P(value) == IS_BOOL && Z_BVAL_P(value) == 1) +#else + if (Z_TYPE_P(value) == IS_TRUE) +#endif + { + connector->fetch_mode = 1; + } + else + { + connector->fetch_mode = 0; + } + } + else + { + connector->fetch_mode = 0; + } + swClient *cli = emalloc(sizeof(swClient)); int type = SW_SOCK_TCP; From def0d15d59ca2ffd5699baf4eb5798997d333266 Mon Sep 17 00:00:00 2001 From: twosee Date: Sun, 3 Jun 2018 01:26:58 +0800 Subject: [PATCH 03/26] Add fetchAll method and swoole_mysql_coro_statement_free. --- swoole_mysql.h | 2 + swoole_mysql_coro.c | 180 +++++++++++++++++++++++++++----------------- 2 files changed, 112 insertions(+), 70 deletions(-) diff --git a/swoole_mysql.h b/swoole_mysql.h index eaec4607682..9ba940724e0 100644 --- a/swoole_mysql.h +++ b/swoole_mysql.h @@ -296,6 +296,8 @@ typedef struct uint16_t unreaded_param_count; struct _mysql_client *client; zval *object; + swString *buffer; /* save the mysql multi responses data */ + zval *result; /* save the zval array result */ } mysql_statement; typedef struct diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 9ec0a2686db..746afbc81d8 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -45,6 +45,7 @@ static PHP_METHOD(swoole_mysql_coro, close); static PHP_METHOD(swoole_mysql_coro_statement, __destruct); static PHP_METHOD(swoole_mysql_coro_statement, execute); +static PHP_METHOD(swoole_mysql_coro_statement, fetchAll); ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) ZEND_END_ARG_INFO() @@ -87,6 +88,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_statement_execute, 0, 0, 0) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_statement_fetchAll, 0, 0, 0) +ZEND_END_ARG_INFO() + static zend_class_entry swoole_mysql_coro_ce; static zend_class_entry *swoole_mysql_coro_class_entry_ptr; @@ -121,6 +125,7 @@ static const zend_function_entry swoole_mysql_coro_methods[] = static const zend_function_entry swoole_mysql_coro_statement_methods[] = { PHP_ME(swoole_mysql_coro_statement, execute, arginfo_swoole_mysql_coro_statement_execute, ZEND_ACC_PUBLIC) + PHP_ME(swoole_mysql_coro_statement, fetchAll, arginfo_swoole_mysql_coro_statement_fetchAll, ZEND_ACC_PUBLIC) PHP_ME(swoole_mysql_coro_statement, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_DTOR) PHP_FALIAS(__sleep, swoole_unsupport_serialize, NULL) PHP_FALIAS(__wakeup, swoole_unsupport_serialize, NULL) @@ -174,72 +179,6 @@ void swoole_mysql_coro_init(int module_number TSRMLS_DC) int mysql_query(zval *zobject, mysql_client *client, swString *sql, zval *callback TSRMLS_DC); -static int swoole_mysql_coro_close(zval *this) -{ - SWOOLE_GET_TSRMLS; - mysql_client *client = swoole_get_object(this); - if (!client) - { - swoole_php_fatal_error(E_WARNING, "object is not instanceof swoole_mysql_coro."); - return FAILURE; - } - - if (!client->cli) - { - return FAILURE; - } - - //send quit command - swString_clear(mysql_request_buffer); - client->cmd = SW_MYSQL_COM_QUIT; - bzero(mysql_request_buffer->str, 5); - mysql_request_buffer->str[4] = SW_MYSQL_COM_QUIT;//command - mysql_request_buffer->length = 5; - mysql_pack_length(mysql_request_buffer->length - 4, mysql_request_buffer->str); - SwooleG.main_reactor->write(SwooleG.main_reactor, client->fd, mysql_request_buffer->str, mysql_request_buffer->length); - - zend_update_property_bool(swoole_mysql_coro_class_entry_ptr, this, ZEND_STRL("connected"), 0 TSRMLS_CC); - SwooleG.main_reactor->del(SwooleG.main_reactor, client->fd); - - swConnection *_socket = swReactor_get(SwooleG.main_reactor, client->fd); - _socket->object = NULL; - _socket->active = 0; - - if (client->timer) - { - swTimer_del(&SwooleG.timer, client->timer); - client->timer = NULL; - } - - if (client->statement_list) - { - swLinkedList_node *node = client->statement_list->head; - while (node) - { - mysql_statement *stmt = node->data; - if (stmt->object) - { - // after connection closed, mysql stmt cache closed too - // so we needn't send stmt close command here like pdo. - swoole_set_object(stmt->object, NULL); - efree(stmt->object); - } - efree(stmt); - node = node->next; - } - swLinkedList_free(client->statement_list); - } - - client->cli->close(client->cli); - swClient_free(client->cli); - efree(client->cli); - client->cli = NULL; - client->state = SW_MYSQL_STATE_CLOSED; - client->iowait = SW_MYSQL_CORO_STATUS_CLOSED; - - return SUCCESS; -} - static int swoole_mysql_coro_execute(zval *zobject, mysql_client *client, zval *params TSRMLS_DC) { if (!client->cli) @@ -389,6 +328,22 @@ static int swoole_mysql_coro_execute(zval *zobject, mysql_client *client, zval * return SW_OK; } +static int swoole_mysql_coro_statement_free(mysql_statement *stmt TSRMLS_DC) +{ + if (stmt->object) + { + swoole_set_object(stmt->object, NULL); + efree(stmt->object); + } + + if (stmt->result) + { + sw_zval_free(stmt->result); + } + + return SW_OK; +} + static int swoole_mysql_coro_statement_close(mysql_statement *stmt TSRMLS_DC) { // call mysql-server to destruct this statement @@ -410,13 +365,69 @@ static int swoole_mysql_coro_statement_close(mysql_statement *stmt TSRMLS_DC) //send data, mysql-server would not reply SwooleG.main_reactor->write(SwooleG.main_reactor, stmt->client->fd, mysql_request_buffer->str, mysql_request_buffer->length); - if (stmt->object) + return SW_OK; +} + +static int swoole_mysql_coro_close(zval *this) +{ + SWOOLE_GET_TSRMLS; + mysql_client *client = swoole_get_object(this); + if (!client) { - swoole_set_object(stmt->object, NULL); - efree(stmt->object); + swoole_php_fatal_error(E_WARNING, "object is not instanceof swoole_mysql_coro."); + return FAILURE; } - return SW_OK; + if (!client->cli) + { + return FAILURE; + } + + //send quit command + swString_clear(mysql_request_buffer); + client->cmd = SW_MYSQL_COM_QUIT; + bzero(mysql_request_buffer->str, 5); + mysql_request_buffer->str[4] = SW_MYSQL_COM_QUIT;//command + mysql_request_buffer->length = 5; + mysql_pack_length(mysql_request_buffer->length - 4, mysql_request_buffer->str); + SwooleG.main_reactor->write(SwooleG.main_reactor, client->fd, mysql_request_buffer->str, mysql_request_buffer->length); + + zend_update_property_bool(swoole_mysql_coro_class_entry_ptr, this, ZEND_STRL("connected"), 0 TSRMLS_CC); + SwooleG.main_reactor->del(SwooleG.main_reactor, client->fd); + + swConnection *_socket = swReactor_get(SwooleG.main_reactor, client->fd); + _socket->object = NULL; + _socket->active = 0; + + if (client->timer) + { + swTimer_del(&SwooleG.timer, client->timer); + client->timer = NULL; + } + + if (client->statement_list) + { + swLinkedList_node *node = client->statement_list->head; + while (node) + { + mysql_statement *stmt = node->data; + // after connection closed, mysql stmt cache closed too + // so we needn't send stmt close command here like pdo. + swoole_mysql_coro_statement_free(stmt); + efree(stmt); + node = node->next; + } + swLinkedList_free(client->statement_list); + } + + client->cli->close(client->cli); + swClient_free(client->cli); + efree(client->cli); + client->cli = NULL; + client->state = SW_MYSQL_STATE_CLOSED; + client->iowait = SW_MYSQL_CORO_STATUS_CLOSED; + + return SUCCESS; } static PHP_METHOD(swoole_mysql_coro, __construct) @@ -1085,6 +1096,16 @@ static PHP_METHOD(swoole_mysql_coro_statement, execute) coro_yield(); } +static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) +{ + mysql_statement *stmt = swoole_get_object(getThis()); + if (!stmt) + { + RETURN_FALSE; + } + RETURN_ZVAL(stmt->result, 0, 1); +} + static PHP_METHOD(swoole_mysql_coro_statement, __destruct) { mysql_statement *stmt = swoole_get_object(getThis()); @@ -1093,6 +1114,7 @@ static PHP_METHOD(swoole_mysql_coro_statement, __destruct) return; } swoole_mysql_coro_statement_close(stmt TSRMLS_CC); + swoole_mysql_coro_statement_free(stmt); swLinkedList_remove(stmt->client->statement_list, stmt); efree(stmt); } @@ -1580,6 +1602,11 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) object_init_ex(result, swoole_mysql_coro_statement_class_entry_ptr); swoole_set_object(result, client->statement); client->statement->object = sw_zval_dup(result); + // if (client->connector.fetch_mode) + // { + // in fetch mode, statement save the response data itself + // client->statement->buffer = swString_new(SW_BUFFER_SIZE_BIG); + // } } else { @@ -1611,6 +1638,19 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) result = client->response.result_array; } + if (client->connector.fetch_mode) + { + if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE && Z_TYPE_P(result) == IS_ARRAY) + { + // save result on statement and wait for fetch + client->statement->result = result; + // return true (success) + result = NULL; + SW_ALLOC_INIT_ZVAL(result); + ZVAL_BOOL(result, 1); + } + } + swString_clear(client->buffer); bzero(&client->response, sizeof(client->response)); if (client->defer && !client->suspending) From 1676c6065a587f0b5babdd8b1186339822dfeb2f Mon Sep 17 00:00:00 2001 From: twosee Date: Sun, 3 Jun 2018 01:27:19 +0800 Subject: [PATCH 04/26] Add MySQL fetch mode unit test. --- tests/swoole_mysql_coro/fetch_mode.phpt | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/swoole_mysql_coro/fetch_mode.phpt diff --git a/tests/swoole_mysql_coro/fetch_mode.phpt b/tests/swoole_mysql_coro/fetch_mode.phpt new file mode 100644 index 00000000000..5930eebbcf4 --- /dev/null +++ b/tests/swoole_mysql_coro/fetch_mode.phpt @@ -0,0 +1,29 @@ +--TEST-- +fetch_mode: use fetch to get data +--SKIPIF-- + +--FILE-- + '127.0.0.1', + 'user' => 'root', + 'password' => 'root', + 'database' => 'test', + 'fetch_mode' => true + ]; + + $db->connect($server); + + // now we can make the responses independent + $stmt1 = $db->prepare('SELECT * FROM ckl LIMIT 1'); + assert($stmt1->execute() === true); + $stmt2 = $db->prepare('SELECT * FROM ckl LIMIT 2'); + assert($stmt2->execute() === true); + assert(count($stmt1->fetchAll()) === 1); + assert(count($stmt2->fetchAll()) === 2); +}); +?> +--EXPECT-- \ No newline at end of file From 316f5eeb029fad1c5df2b4d5808ad2b16786ae25 Mon Sep 17 00:00:00 2001 From: twosee Date: Sun, 3 Jun 2018 01:37:35 +0800 Subject: [PATCH 05/26] Fix. --- swoole_mysql_coro.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 746afbc81d8..33ebfb24161 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -1602,6 +1602,7 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) object_init_ex(result, swoole_mysql_coro_statement_class_entry_ptr); swoole_set_object(result, client->statement); client->statement->object = sw_zval_dup(result); + client->statement->result = NULL; // if (client->connector.fetch_mode) // { // in fetch mode, statement save the response data itself @@ -1643,6 +1644,11 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE && Z_TYPE_P(result) == IS_ARRAY) { // save result on statement and wait for fetch + if (client->statement->result) + { + // free the last one + sw_zval_free(client->statement->result); + } client->statement->result = result; // return true (success) result = NULL; From 6ce4c37441b18ea19016d84f7716985728f72ffb Mon Sep 17 00:00:00 2001 From: twosee Date: Sun, 3 Jun 2018 10:25:49 +0800 Subject: [PATCH 06/26] Fix zval free. --- swoole_mysql_coro.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 33ebfb24161..9ffd2347816 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -1103,7 +1103,19 @@ static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) { RETURN_FALSE; } - RETURN_ZVAL(stmt->result, 0, 1); + + if (stmt->result) + { + zval *result; + ZVAL_ZVAL(result, stmt->result, 0, 1); + efree(stmt->result); + stmt->result = NULL; + RETURN_ZVAL(result, 0, 1); + } + else + { + RETURN_NULL(); + } } static PHP_METHOD(swoole_mysql_coro_statement, __destruct) From b5d502c3b3f4f0e8b1d95e6a230b53f2af522000 Mon Sep 17 00:00:00 2001 From: twosee Date: Sun, 3 Jun 2018 10:26:36 +0800 Subject: [PATCH 07/26] Add twice tests and use config constant. --- tests/swoole_mysql_coro/fetch_mode.phpt | 9 +++--- tests/swoole_mysql_coro/fetch_mode_twice.phpt | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 tests/swoole_mysql_coro/fetch_mode_twice.phpt diff --git a/tests/swoole_mysql_coro/fetch_mode.phpt b/tests/swoole_mysql_coro/fetch_mode.phpt index 5930eebbcf4..3a138c2b6fa 100644 --- a/tests/swoole_mysql_coro/fetch_mode.phpt +++ b/tests/swoole_mysql_coro/fetch_mode.phpt @@ -5,13 +5,14 @@ fetch_mode: use fetch to get data --FILE-- '127.0.0.1', - 'user' => 'root', - 'password' => 'root', - 'database' => 'test', + 'host' => MYSQL_SERVER_HOST, + 'user' => MYSQL_SERVER_USER1, + 'password' => MYSQL_SERVER_PWD, + 'database' => MYSQL_SERVER_DB1, 'fetch_mode' => true ]; diff --git a/tests/swoole_mysql_coro/fetch_mode_twice.phpt b/tests/swoole_mysql_coro/fetch_mode_twice.phpt new file mode 100644 index 00000000000..648568b4295 --- /dev/null +++ b/tests/swoole_mysql_coro/fetch_mode_twice.phpt @@ -0,0 +1,29 @@ +--TEST-- +fetch_mode_twice: call fetch twice +--SKIPIF-- + +--FILE-- + MYSQL_SERVER_HOST, + 'user' => MYSQL_SERVER_USER1, + 'password' => MYSQL_SERVER_PWD, + 'database' => MYSQL_SERVER_DB1, + 'fetch_mode' => true + ]; + + $db->connect($server); + + assert($db->query("INSERT INTO ckl (`domain`,`path`,`name`) VALUES ('www.baidu.com', '/search', 'baidu')") === true); + // now we can make the responses independent + $stmt = $db->prepare('SELECT * FROM ckl LIMIT 1'); + assert($stmt->execute() === true); + assert(($ret = $stmt->fetchAll()) && is_array($ret) && count($ret) === 1); + assert($stmt->fetchAll() === null); +}); +?> +--EXPECT-- \ No newline at end of file From 5771403dba644f5bb1f92aaa180bf85c5e643658 Mon Sep 17 00:00:00 2001 From: twosee Date: Sun, 3 Jun 2018 21:53:41 +0800 Subject: [PATCH 08/26] Add fetch method. --- swoole_mysql_coro.c | 56 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 9ffd2347816..ea0fe9e5f18 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -45,6 +45,7 @@ static PHP_METHOD(swoole_mysql_coro, close); static PHP_METHOD(swoole_mysql_coro_statement, __destruct); static PHP_METHOD(swoole_mysql_coro_statement, execute); +static PHP_METHOD(swoole_mysql_coro_statement, fetch); static PHP_METHOD(swoole_mysql_coro_statement, fetchAll); ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) @@ -88,6 +89,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_statement_execute, 0, 0, 0) ZEND_ARG_INFO(0, timeout) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_statement_fetch, 0, 0, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_statement_fetchAll, 0, 0, 0) ZEND_END_ARG_INFO() @@ -125,6 +129,7 @@ static const zend_function_entry swoole_mysql_coro_methods[] = static const zend_function_entry swoole_mysql_coro_statement_methods[] = { PHP_ME(swoole_mysql_coro_statement, execute, arginfo_swoole_mysql_coro_statement_execute, ZEND_ACC_PUBLIC) + PHP_ME(swoole_mysql_coro_statement, fetch, arginfo_swoole_mysql_coro_statement_fetch, ZEND_ACC_PUBLIC) PHP_ME(swoole_mysql_coro_statement, fetchAll, arginfo_swoole_mysql_coro_statement_fetchAll, ZEND_ACC_PUBLIC) PHP_ME(swoole_mysql_coro_statement, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_DTOR) PHP_FALIAS(__sleep, swoole_unsupport_serialize, NULL) @@ -1096,6 +1101,55 @@ static PHP_METHOD(swoole_mysql_coro_statement, execute) coro_yield(); } +static PHP_METHOD(swoole_mysql_coro_statement, fetch) +{ + mysql_statement *stmt = swoole_get_object(getThis()); + if (!stmt) + { + RETURN_FALSE; + } + + if (stmt->result) + { + zval **args[1]; + // the function argument is a reference + ZVAL_NEW_REF(stmt->result, stmt->result); + args[0] = &stmt->result; + + zval *fcn; + SW_MAKE_STD_ZVAL(fcn); + ZVAL_STRING(fcn, "array_shift"); + int ret; + zval *retval = NULL; + ret = sw_call_user_function_ex(EG(function_table), NULL, fcn, &retval, 1, args, 0, NULL TSRMLS_CC); + sw_zval_ptr_dtor(&fcn); + ZVAL_UNREF(stmt->result); + + if (ret == FAILURE || retval == NULL || ZVAL_IS_NULL(retval)) + { + if (stmt->result) + { + sw_zval_free(stmt->result); + stmt->result = NULL; + } + RETURN_NULL(); + } + else + { + if (php_swoole_array_length(stmt->result) == 0) + { + sw_zval_free(stmt->result); + stmt->result = NULL; + } + RETURN_ZVAL(retval, 0, 1); + } + } + else + { + RETURN_NULL(); + } +} + static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) { mysql_statement *stmt = swoole_get_object(getThis()); @@ -1108,7 +1162,7 @@ static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) { zval *result; ZVAL_ZVAL(result, stmt->result, 0, 1); - efree(stmt->result); + sw_zval_free(stmt->result); stmt->result = NULL; RETURN_ZVAL(result, 0, 1); } From 01f7c0f2b5599db4cc3478ae5978f51bdf1049b6 Mon Sep 17 00:00:00 2001 From: twosee Date: Sun, 3 Jun 2018 21:54:50 +0800 Subject: [PATCH 09/26] Add single fetch test. --- tests/swoole_mysql_coro/fetch.phpt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/swoole_mysql_coro/fetch.phpt diff --git a/tests/swoole_mysql_coro/fetch.phpt b/tests/swoole_mysql_coro/fetch.phpt new file mode 100644 index 00000000000..e7d830221de --- /dev/null +++ b/tests/swoole_mysql_coro/fetch.phpt @@ -0,0 +1,30 @@ +--TEST-- +fetch: use fetch to get data +--SKIPIF-- + +--FILE-- + MYSQL_SERVER_HOST, + 'user' => MYSQL_SERVER_USER1, + 'password' => MYSQL_SERVER_PWD, + 'database' => MYSQL_SERVER_DB1, + 'fetch_mode' => true + ]; + + $db->connect($server); + + // now we can make the responses independent + $stmt = $db->prepare('SELECT `id` FROM `userinfo` LIMIT 2'); + assert($stmt->execute() === true); + assert(is_array($stmt->fetch())); + assert(is_array($stmt->fetch())); + assert($stmt->fetch() === null); + assert($stmt->fetchAll() === null); +}); +?> +--EXPECT-- \ No newline at end of file From b67a5c6334c22be93e3cb873d23551df9c1dbc98 Mon Sep 17 00:00:00 2001 From: twosee Date: Sun, 3 Jun 2018 22:24:19 +0800 Subject: [PATCH 10/26] Add without fetch test. --- tests/swoole_mysql_coro/without_fetch.phpt | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/swoole_mysql_coro/without_fetch.phpt diff --git a/tests/swoole_mysql_coro/without_fetch.phpt b/tests/swoole_mysql_coro/without_fetch.phpt new file mode 100644 index 00000000000..55b5fb227b2 --- /dev/null +++ b/tests/swoole_mysql_coro/without_fetch.phpt @@ -0,0 +1,27 @@ +--TEST-- +without_fetch: just execute (test memory leak) +--SKIPIF-- + +--FILE-- + MYSQL_SERVER_HOST, + 'user' => MYSQL_SERVER_USER1, + 'password' => MYSQL_SERVER_PWD, + 'database' => MYSQL_SERVER_DB1, + 'fetch_mode' => true + ]; + + $db->connect($server); + $stmt = $db->prepare('SELECT * FROM `userinfo` LIMIT 1'); + assert($stmt->execute() === true); + assert($stmt->execute() === true); + assert($stmt->execute() === true); + assert(is_array($stmt->fetchAll())); +}); +?> +--EXPECT-- \ No newline at end of file From a5b9ba01e99cf59ea49756d7f4836b202b53af21 Mon Sep 17 00:00:00 2001 From: twosee Date: Mon, 4 Jun 2018 16:24:31 +0800 Subject: [PATCH 11/26] Add mysql_is_over, fix the fetchAll return value. --- swoole_mysql.c | 92 ++++++++++++++++++++++++++++++++++++++++++--- swoole_mysql_coro.c | 10 ++++- 2 files changed, 95 insertions(+), 7 deletions(-) diff --git a/swoole_mysql.c b/swoole_mysql.c index f0284ac7b89..4e3f13d80ad 100644 --- a/swoole_mysql.c +++ b/swoole_mysql.c @@ -1086,6 +1086,7 @@ static sw_inline int mysql_read_eof(mysql_client *client, char *buffer, int n_bu client->response.warnings = mysql_uint2korr(buffer + 5); client->response.status_code = mysql_uint2korr(buffer + 7); + client->buffer->offset += client->response.packet_length + 4; return SW_OK; } @@ -1158,13 +1159,23 @@ static sw_inline int mysql_read_rows(mysql_client *client) return SW_ERR; } //RecordSet end - else if (mysql_read_eof(client, buffer, n_buf) == 0 && (n_buf == 9 || (n_buf > 9 && (client->response.status_code & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS)))) + else if (mysql_read_eof(client, buffer, n_buf) == SW_OK) { - if (n_buf > 9) + n_buf -= 9; + if (client->response.status_code & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) { - // buffer may has multi responses - // we can't solve it in execute function so we return - swTraceLog(SW_TRACE_MYSQL_CLIENT, "remaining %d, more results exists", n_buf - 9); + // buffer has multi responses + if (mysql_is_over(client) == SW_OK && n_buf > 0) + { + // why mysql_is_ok: maybe more responses has received in buffer, we check it now. + swTraceLog(SW_TRACE_MYSQL_CLIENT, "remaining %d, more results exists", n_buf); + } + else + { + swTraceLog(SW_TRACE_MYSQL_CLIENT, "need more"); + // flag shows that more results exist but we hasn't received. +// return SW_AGAIN; + } } if (client->response.columns) { @@ -1475,6 +1486,77 @@ static int mysql_read_columns(mysql_client *client) return SW_OK; } +// this function is used to check if multi responses has received over. +int mysql_is_over(mysql_client *client) +{ + swString *buffer = client->buffer; + char *p; + int n_buf = buffer->length - buffer->offset; // remaining buffer size + int check_offset = buffer->offset; + uint32_t temp; + + while (1) + { + p = buffer->str + check_offset; // where to start checking now + if (buffer->length - buffer->offset < 5) + { + break; + } + temp = mysql_uint3korr(p); //package length + p += 4; + check_offset += 4; + n_buf -= 4; + + if (n_buf < temp) + { + break; + } + + swDebug("type=%d, plength=%d, nbuf=%d.", p[0], temp, n_buf); + + if (p[0] == 0xff) // response type = error + { + goto over; + } + // response type = ok? + if (p[0] == 0 && temp >= 7) + { + int t_nbuf = n_buf; + p++; + t_nbuf--; + + ulong_t val = 0; + char nul; + int retcode; + + retcode = mysql_lcb_ll(p, &val, &nul, t_nbuf); //affecr rows + t_nbuf -= retcode; + p += retcode; + + retcode = mysql_lcb_ll(p, &val, &nul, t_nbuf); //insert id + t_nbuf -= retcode; + p += retcode; + + if ((mysql_uint2korr(p) & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) == 0) + { + over: + client->response.wait_recv = 0; + return SW_OK; + } + } + + n_buf -= temp; + if (n_buf <= 0) + { + break; + } + check_offset += temp; + } + + client->response.wait_recv = 2; + return SW_AGAIN; +} + int mysql_response(mysql_client *client) { diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index ea0fe9e5f18..088d510feaa 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -1161,8 +1161,8 @@ static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) if (stmt->result) { zval *result; - ZVAL_ZVAL(result, stmt->result, 0, 1); - sw_zval_free(stmt->result); + ZVAL_ZVAL(result, stmt->result, 0, 0); + efree(stmt->result); stmt->result = NULL; RETURN_ZVAL(result, 0, 1); } @@ -1559,6 +1559,7 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) while(1) { ret = recv(sock, buffer->str + buffer->length, buffer->size - buffer->length, 0); + swTraceLog(SW_TRACE_MYSQL_CLIENT, "recv-ret=%d, buffer-length=%d.", ret, buffer->length); if (ret < 0) { if (errno == EINTR) @@ -1633,6 +1634,10 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) parse_response: if (mysql_response(client) < 0) { + if (client->response.wait_recv > 0) // not over + { + continue; + } return SW_OK; } @@ -1658,6 +1663,7 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) if (client->response.response_type == 0) { SW_ALLOC_INIT_ZVAL(result); + // prepare finished and create statement if (client->cmd == SW_MYSQL_COM_STMT_PREPARE) { if (client->statement_list == NULL) From 87a493820c67458679fc37979a81d70170db0eb7 Mon Sep 17 00:00:00 2001 From: twosee Date: Mon, 4 Jun 2018 19:36:41 +0800 Subject: [PATCH 12/26] Now we can recv all the responses data. --- swoole_mysql.c | 42 ++++++++++++++---------------------------- swoole_mysql_coro.c | 35 +++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/swoole_mysql.c b/swoole_mysql.c index 4e3f13d80ad..0a35aeee1eb 100644 --- a/swoole_mysql.c +++ b/swoole_mysql.c @@ -1161,22 +1161,6 @@ static sw_inline int mysql_read_rows(mysql_client *client) //RecordSet end else if (mysql_read_eof(client, buffer, n_buf) == SW_OK) { - n_buf -= 9; - if (client->response.status_code & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) - { - // buffer has multi responses - if (mysql_is_over(client) == SW_OK && n_buf > 0) - { - // why mysql_is_ok: maybe more responses has received in buffer, we check it now. - swTraceLog(SW_TRACE_MYSQL_CLIENT, "remaining %d, more results exists", n_buf); - } - else - { - swTraceLog(SW_TRACE_MYSQL_CLIENT, "need more"); - // flag shows that more results exist but we hasn't received. -// return SW_AGAIN; - } - } if (client->response.columns) { mysql_columns_free(client); @@ -1487,39 +1471,42 @@ static int mysql_read_columns(mysql_client *client) } // this function is used to check if multi responses has received over. -int mysql_is_over(mysql_client *client) +int mysql_is_over(mysql_client *client, off_t *check_offset) { swString *buffer = client->buffer; char *p; - int n_buf = buffer->length - buffer->offset; // remaining buffer size - int check_offset = buffer->offset; + if (*check_offset < buffer->offset) + { + *check_offset = buffer->offset; // not check the first again. + } + size_t n_buf = buffer->length - *check_offset; // remaining buffer size uint32_t temp; while (1) { - p = buffer->str + check_offset; // where to start checking now + p = buffer->str + *check_offset; // where to start checking now if (buffer->length - buffer->offset < 5) { break; } temp = mysql_uint3korr(p); //package length p += 4; - check_offset += 4; + *check_offset += 4; n_buf -= 4; - if (n_buf < temp) + *check_offset += temp; + + if (n_buf < temp) //package is incomplete { break; } - swDebug("type=%d, plength=%d, nbuf=%d.", p[0], temp, n_buf); - - if (p[0] == 0xff) // response type = error + if ((uint16_t) p[0] == 0xff) // response type = error { goto over; } // response type = ok? - if (p[0] == 0 && temp >= 7) + if ((uint16_t) p[0] == 0 && temp >= 7) { int t_nbuf = n_buf; p++; @@ -1550,7 +1537,6 @@ int mysql_is_over(mysql_client *client) { break; } - check_offset += temp; } client->response.wait_recv = 2; @@ -1565,7 +1551,7 @@ int mysql_response(mysql_client *client) char *p = buffer->str + buffer->offset; int ret; char nul; - int n_buf = buffer->length - buffer->offset; + size_t n_buf = buffer->length - buffer->offset; while (n_buf > 0) { diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 088d510feaa..0f97924ccea 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -1556,6 +1556,7 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) zval *retval = NULL; zval *result = NULL; + off_t check_offset = 0; while(1) { ret = recv(sock, buffer->str + buffer->length, buffer->size - buffer->length, 0); @@ -1632,13 +1633,40 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) } parse_response: + + // have ever checked, don't parse more responses immediately + if (check_offset > 0) + { + goto more; + } + if (mysql_response(client) < 0) { if (client->response.wait_recv > 0) // not over { continue; } - return SW_OK; + else + { + return SW_OK; + } + } + + if (client->response.status_code & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) + { + more: + // buffer has multi responses + if (mysql_is_over(client, &check_offset) == SW_OK) + { + // why mysql_is_ok: maybe more responses has already received in buffer, we check it now. + swTraceLog(SW_TRACE_MYSQL_CLIENT, "remaining %d, more results exists", buffer->length - buffer->offset); + } + else + { + swTraceLog(SW_TRACE_MYSQL_CLIENT, "need more"); + // flag shows that more results exist but we hasn't received. + continue; + } } //remove from eventloop @@ -1675,11 +1703,6 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) swoole_set_object(result, client->statement); client->statement->object = sw_zval_dup(result); client->statement->result = NULL; - // if (client->connector.fetch_mode) - // { - // in fetch mode, statement save the response data itself - // client->statement->buffer = swString_new(SW_BUFFER_SIZE_BIG); - // } } else { From c560525324b55a9bbe35c18237078e816b7a3561 Mon Sep 17 00:00:00 2001 From: twosee Date: Mon, 4 Jun 2018 20:47:33 +0800 Subject: [PATCH 13/26] Add MYSQL_RESPONSE_BUFFER to separate client and statement buffer. --- swoole_mysql.c | 69 ++++++++++++++++++++++++--------------------- swoole_mysql.h | 2 ++ swoole_mysql_coro.c | 31 ++++++++++++++------ 3 files changed, 62 insertions(+), 40 deletions(-) diff --git a/swoole_mysql.c b/swoole_mysql.c index 0a35aeee1eb..8be11b62dda 100644 --- a/swoole_mysql.c +++ b/swoole_mysql.c @@ -666,6 +666,8 @@ static int mysql_parse_prepare_result(mysql_client *client, char *buf, size_t n_ //skip 1 byte buf += 1; stmt->warning_count = mysql_uint2korr(buf); + stmt->result = NULL; + stmt->buffer = NULL; client->statement = stmt; stmt->client = client; @@ -1086,7 +1088,7 @@ static sw_inline int mysql_read_eof(mysql_client *client, char *buffer, int n_bu client->response.warnings = mysql_uint2korr(buffer + 5); client->response.status_code = mysql_uint2korr(buffer + 7); - client->buffer->offset += client->response.packet_length + 4; + MYSQL_RESPONSE_BUFFER->offset += client->response.packet_length + 4; return SW_OK; } @@ -1095,8 +1097,9 @@ static sw_inline int mysql_read_params(mysql_client *client) { while (1) { - char *buffer = client->buffer->str + client->buffer->offset; - uint32_t n_buf = client->buffer->length - client->buffer->offset; + swString *buffer = MYSQL_RESPONSE_BUFFER; + char *t_buffer = buffer->str + buffer->offset; + uint32_t n_buf = buffer->length - buffer->offset; swTraceLog(SW_TRACE_MYSQL_CLIENT, "n_buf=%d, length=%d.", n_buf, client->response.packet_length); @@ -1116,9 +1119,9 @@ static sw_inline int mysql_read_params(mysql_client *client) } // Read and ignore parameter field. Sentence from MySQL source: // skip parameters data: we don't support it yet - client->response.packet_length = mysql_uint3korr(buffer); - client->response.packet_number = buffer[3]; - client->buffer->offset += (client->response.packet_length + 4); + client->response.packet_length = mysql_uint3korr(t_buffer); + client->response.packet_number = t_buffer[3]; + buffer->offset += (client->response.packet_length + 4); client->statement->unreaded_param_count--; swTraceLog(SW_TRACE_MYSQL_CLIENT, "read param, count=%d.", client->statement->unreaded_param_count); @@ -1129,9 +1132,9 @@ static sw_inline int mysql_read_params(mysql_client *client) { swTraceLog(SW_TRACE_MYSQL_CLIENT, "read eof [2]"); - if (mysql_read_eof(client, buffer, n_buf) == 0) + if (mysql_read_eof(client, t_buffer, n_buf) == 0) { - client->buffer->offset += 9; + buffer->offset += 9; return SW_OK; } else @@ -1144,8 +1147,9 @@ static sw_inline int mysql_read_params(mysql_client *client) static sw_inline int mysql_read_rows(mysql_client *client) { - char *buffer = client->buffer->str + client->buffer->offset; - uint32_t n_buf = client->buffer->length - client->buffer->offset; + swString *buffer = MYSQL_RESPONSE_BUFFER; + char *t_buffer = buffer->str + buffer->offset; + uint32_t n_buf = buffer->length - buffer->offset; int ret; swTraceLog(SW_TRACE_MYSQL_CLIENT, "n_buf=%d", n_buf); @@ -1159,7 +1163,7 @@ static sw_inline int mysql_read_rows(mysql_client *client) return SW_ERR; } //RecordSet end - else if (mysql_read_eof(client, buffer, n_buf) == SW_OK) + else if (mysql_read_eof(client, t_buffer, n_buf) == SW_OK) { if (client->response.columns) { @@ -1168,9 +1172,9 @@ static sw_inline int mysql_read_rows(mysql_client *client) return SW_OK; } - client->response.packet_length = mysql_uint3korr(buffer); - client->response.packet_number = buffer[3]; - buffer += 4; + client->response.packet_length = mysql_uint3korr(t_buffer); + client->response.packet_number = t_buffer[3]; + t_buffer += 4; n_buf -= 4; swTraceLog(SW_TRACE_MYSQL_CLIENT, "record size=%d", client->response.packet_length); @@ -1184,12 +1188,12 @@ static sw_inline int mysql_read_rows(mysql_client *client) if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE) { - ret = mysql_decode_row_prepare(client, buffer, client->response.packet_length); + ret = mysql_decode_row_prepare(client, t_buffer, client->response.packet_length); } else { //decode - ret = mysql_decode_row(client, buffer, client->response.packet_length); + ret = mysql_decode_row(client, t_buffer, client->response.packet_length); } if (ret < 0) @@ -1199,9 +1203,9 @@ static sw_inline int mysql_read_rows(mysql_client *client) //next row client->response.num_row++; - buffer += client->response.packet_length; + t_buffer += client->response.packet_length; n_buf -= client->response.packet_length; - client->buffer->offset += client->response.packet_length + 4; + buffer->offset += client->response.packet_length + 4; } return SW_ERR; @@ -1407,8 +1411,9 @@ static int mysql_decode_field(char *buf, int len, mysql_field *col) static int mysql_read_columns(mysql_client *client) { - char *buffer = client->buffer->str + client->buffer->offset; - uint32_t n_buf = client->buffer->length - client->buffer->offset; + swString *buffer = MYSQL_RESPONSE_BUFFER; + char *t_buffer = buffer->str + buffer->offset; + uint32_t n_buf = buffer->length - buffer->offset; int ret; for (; client->response.index_column < client->response.num_column; client->response.index_column++) @@ -1420,7 +1425,7 @@ static int mysql_read_columns(mysql_client *client) return SW_ERR; } - client->response.packet_length = mysql_uint3korr(buffer); + client->response.packet_length = mysql_uint3korr(t_buffer); //no enough data if (n_buf - 4 < client->response.packet_length) @@ -1428,16 +1433,16 @@ static int mysql_read_columns(mysql_client *client) return SW_ERR; } - client->response.packet_number = buffer[3]; - buffer += 4; + client->response.packet_number = t_buffer[3]; + t_buffer += 4; n_buf -= 4; - ret = mysql_decode_field(buffer, client->response.packet_length, &client->response.columns[client->response.index_column]); + ret = mysql_decode_field(t_buffer, client->response.packet_length, &client->response.columns[client->response.index_column]); if (ret > 0) { - buffer += client->response.packet_length; + t_buffer += client->response.packet_length; n_buf -= client->response.packet_length; - client->buffer->offset += (client->response.packet_length + 4); + buffer->offset += (client->response.packet_length + 4); } else { @@ -1446,12 +1451,12 @@ static int mysql_read_columns(mysql_client *client) } } - if (mysql_read_eof(client, buffer, n_buf) < 0) + if (mysql_read_eof(client, t_buffer, n_buf) < 0) { return SW_ERR; } - buffer += 9; + t_buffer += 9; n_buf -= 9; if (client->cmd != SW_MYSQL_COM_STMT_PREPARE) @@ -1465,7 +1470,7 @@ static int mysql_read_columns(mysql_client *client) } } - client->buffer->offset += buffer - (client->buffer->str + client->buffer->offset); + buffer->offset += t_buffer - (buffer->str + buffer->offset); return SW_OK; } @@ -1473,7 +1478,7 @@ static int mysql_read_columns(mysql_client *client) // this function is used to check if multi responses has received over. int mysql_is_over(mysql_client *client, off_t *check_offset) { - swString *buffer = client->buffer; + swString *buffer = MYSQL_RESPONSE_BUFFER; char *p; if (*check_offset < buffer->offset) { @@ -1546,7 +1551,7 @@ int mysql_is_over(mysql_client *client, off_t *check_offset) int mysql_response(mysql_client *client) { - swString *buffer = client->buffer; + swString *buffer = MYSQL_RESPONSE_BUFFER; char *p = buffer->str + buffer->offset; int ret; @@ -1665,7 +1670,7 @@ int mysql_response(mysql_client *client) { return SW_ERR; } - client->buffer->offset += (4 + ret); + buffer->offset += (4 + ret); client->response.columns = ecalloc(client->response.num_column, sizeof(mysql_field)); client->state = SW_MYSQL_STATE_READ_FIELD; break; diff --git a/swoole_mysql.h b/swoole_mysql.h index 9ba940724e0..6cc2f8a3fd6 100644 --- a/swoole_mysql.h +++ b/swoole_mysql.h @@ -409,6 +409,8 @@ typedef struct _mysql_client mysql_int4store((T),def_temp); \ mysql_int4store((T+4),def_temp2); } while (0) +#define MYSQL_RESPONSE_BUFFER (client->cmd == SW_MYSQL_COM_STMT_EXECUTE ? client->statement->buffer : client->buffer) + int mysql_get_result(mysql_connector *connector, char *buf, int len); int mysql_get_charset(char *name); int mysql_handshake(mysql_connector *connector, char *buf, int len); diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 0f97924ccea..58883d6c81a 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -341,6 +341,11 @@ static int swoole_mysql_coro_statement_free(mysql_statement *stmt TSRMLS_DC) efree(stmt->object); } + if (stmt->buffer) + { + swString_free(stmt->buffer); + } + if (stmt->result) { sw_zval_free(stmt->result); @@ -1551,7 +1556,21 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) int ret; zval *zobject = client->object; - swString *buffer = client->buffer; + + swString *buffer; + if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE) + { + if (client->statement->buffer == NULL) + { + // statement save the response data itself + client->statement->buffer = swString_new(SW_BUFFER_SIZE_BIG); + } + buffer = client->statement->buffer; + } + else + { + buffer = client->buffer; + } zval *retval = NULL; zval *result = NULL; @@ -1656,17 +1675,14 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) { more: // buffer has multi responses - if (mysql_is_over(client, &check_offset) == SW_OK) + if (mysql_is_over(client, &check_offset) != SW_OK) { // why mysql_is_ok: maybe more responses has already received in buffer, we check it now. - swTraceLog(SW_TRACE_MYSQL_CLIENT, "remaining %d, more results exists", buffer->length - buffer->offset); - } - else - { swTraceLog(SW_TRACE_MYSQL_CLIENT, "need more"); // flag shows that more results exist but we hasn't received. continue; } + swTraceLog(SW_TRACE_MYSQL_CLIENT, "remaining %d, more results exists", buffer->length - buffer->offset); } //remove from eventloop @@ -1702,7 +1718,6 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) object_init_ex(result, swoole_mysql_coro_statement_class_entry_ptr); swoole_set_object(result, client->statement); client->statement->object = sw_zval_dup(result); - client->statement->result = NULL; } else { @@ -1752,7 +1767,7 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) } } - swString_clear(client->buffer); + swString_clear(MYSQL_RESPONSE_BUFFER); bzero(&client->response, sizeof(client->response)); if (client->defer && !client->suspending) { From 297c2f282258d01b070e571f2a1411627e453047 Mon Sep 17 00:00:00 2001 From: twosee Date: Mon, 4 Jun 2018 22:23:25 +0800 Subject: [PATCH 14/26] separate parse_response to swoole_mysql_coro_parse_response. --- swoole_mysql_coro.c | 206 ++++++++++++++++++++++++-------------------- 1 file changed, 114 insertions(+), 92 deletions(-) diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 58883d6c81a..65538da4e28 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -1289,6 +1289,111 @@ static PHP_METHOD(swoole_mysql_coro, close) RETURN_TRUE; } +static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result) +{ + zval *zobject = client->object; + + if (mysql_response(client) < 0) + { + if (client->response.wait_recv > 0) // not over + { + return SW_AGAIN; + } + else + { + return SW_ERR; + } + } + + //remove from eventloop + //reactor->del(reactor, event->fd); + + zend_update_property_long(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("affected_rows"), + client->response.affected_rows TSRMLS_CC); + zend_update_property_long(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("insert_id"), + client->response.insert_id TSRMLS_CC); + + if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE) + { + zend_update_property_long(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, + ZEND_STRL("affected_rows"), client->response.affected_rows TSRMLS_CC); + zend_update_property_long(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, + ZEND_STRL("insert_id"), client->response.insert_id TSRMLS_CC); + } + + client->state = SW_MYSQL_STATE_QUERY; + + //OK + if (client->response.response_type == 0) + { + SW_ALLOC_INIT_ZVAL(*result); + // prepare finished and create statement + if (client->cmd == SW_MYSQL_COM_STMT_PREPARE) + { + if (client->statement_list == NULL) + { + client->statement_list = swLinkedList_new(0, NULL); + } + swLinkedList_append(client->statement_list, client->statement); + object_init_ex(*result, swoole_mysql_coro_statement_class_entry_ptr); + swoole_set_object(*result, client->statement); + client->statement->object = sw_zval_dup(*result); + } + else + { + ZVAL_BOOL(*result, 1); + } + } + //ERROR + else if (client->response.response_type == 255) + { + SW_ALLOC_INIT_ZVAL(*result); + ZVAL_BOOL(*result, 0); + + zend_update_property_stringl(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("error"), + client->response.server_msg, client->response.l_server_msg TSRMLS_CC); + zend_update_property_long(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("errno"), + client->response.error_code TSRMLS_CC); + + if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE) + { + zend_update_property_stringl(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, + ZEND_STRL("error"), client->response.server_msg, client->response.l_server_msg TSRMLS_CC); + zend_update_property_long(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, + ZEND_STRL("errno"), client->response.error_code TSRMLS_CC); + } + } + //ResultSet + else + { + *result = client->response.result_array; + } + + if (client->connector.fetch_mode) + { + if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE && Z_TYPE_P(*result) == IS_ARRAY) + { + // save result on statement and wait for fetch + if (client->statement->result) + { + // free the last one + sw_zval_free(client->statement->result); + } + client->statement->result = *result; + // return true (success) + *result = NULL; + SW_ALLOC_INIT_ZVAL(*result); + ZVAL_BOOL(*result, 1); + } + } + + // clean up + swString_clear(MYSQL_RESPONSE_BUFFER); + bzero(&client->response, sizeof(client->response)); + + return SW_OK; +} + static int swoole_mysql_coro_onError(swReactor *reactor, swEvent *event) { #if PHP_MAJOR_VERSION < 7 @@ -1659,16 +1764,16 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) goto more; } - if (mysql_response(client) < 0) + switch (swoole_mysql_coro_parse_response(client, &result)) { - if (client->response.wait_recv > 0) // not over - { - continue; - } - else - { - return SW_OK; - } + case SW_OK: + break; + case SW_ERR: //parse error + return SW_OK; + case SW_AGAIN: + continue; + default: + return SW_ERR; //unknown } if (client->response.status_code & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) @@ -1685,90 +1790,7 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) swTraceLog(SW_TRACE_MYSQL_CLIENT, "remaining %d, more results exists", buffer->length - buffer->offset); } - //remove from eventloop - //reactor->del(reactor, event->fd); - - zend_update_property_long(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("affected_rows"), - client->response.affected_rows TSRMLS_CC); - zend_update_property_long(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("insert_id"), - client->response.insert_id TSRMLS_CC); - - if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE) - { - zend_update_property_long(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, - ZEND_STRL("affected_rows"), client->response.affected_rows TSRMLS_CC); - zend_update_property_long(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, - ZEND_STRL("insert_id"), client->response.insert_id TSRMLS_CC); - } - - client->state = SW_MYSQL_STATE_QUERY; - - //OK - if (client->response.response_type == 0) - { - SW_ALLOC_INIT_ZVAL(result); - // prepare finished and create statement - if (client->cmd == SW_MYSQL_COM_STMT_PREPARE) - { - if (client->statement_list == NULL) - { - client->statement_list = swLinkedList_new(0, NULL); - } - swLinkedList_append(client->statement_list, client->statement); - object_init_ex(result, swoole_mysql_coro_statement_class_entry_ptr); - swoole_set_object(result, client->statement); - client->statement->object = sw_zval_dup(result); - } - else - { - ZVAL_BOOL(result, 1); - } - } - //ERROR - else if (client->response.response_type == 255) - { - SW_ALLOC_INIT_ZVAL(result); - ZVAL_BOOL(result, 0); - - zend_update_property_stringl(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("error"), - client->response.server_msg, client->response.l_server_msg TSRMLS_CC); - zend_update_property_long(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("errno"), - client->response.error_code TSRMLS_CC); - - if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE) - { - zend_update_property_stringl(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, - ZEND_STRL("error"), client->response.server_msg, client->response.l_server_msg TSRMLS_CC); - zend_update_property_long(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, - ZEND_STRL("errno"), client->response.error_code TSRMLS_CC); - } - } - //ResultSet - else - { - result = client->response.result_array; - } - - if (client->connector.fetch_mode) - { - if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE && Z_TYPE_P(result) == IS_ARRAY) - { - // save result on statement and wait for fetch - if (client->statement->result) - { - // free the last one - sw_zval_free(client->statement->result); - } - client->statement->result = result; - // return true (success) - result = NULL; - SW_ALLOC_INIT_ZVAL(result); - ZVAL_BOOL(result, 1); - } - } - swString_clear(MYSQL_RESPONSE_BUFFER); - bzero(&client->response, sizeof(client->response)); if (client->defer && !client->suspending) { client->iowait = SW_MYSQL_CORO_STATUS_DONE; From 7f5e6fffd278b0c9df789f07113ea0ac6234dfc6 Mon Sep 17 00:00:00 2001 From: twosee Date: Mon, 4 Jun 2018 22:23:38 +0800 Subject: [PATCH 15/26] Fix the offset error. --- swoole_mysql.c | 1 - 1 file changed, 1 deletion(-) diff --git a/swoole_mysql.c b/swoole_mysql.c index 8be11b62dda..2bda45f8b1b 100644 --- a/swoole_mysql.c +++ b/swoole_mysql.c @@ -1134,7 +1134,6 @@ static sw_inline int mysql_read_params(mysql_client *client) if (mysql_read_eof(client, t_buffer, n_buf) == 0) { - buffer->offset += 9; return SW_OK; } else From 6034d064f86c3d8bea7da415d8c7a6448cbb7bbd Mon Sep 17 00:00:00 2001 From: twosee Date: Tue, 5 Jun 2018 01:57:48 +0800 Subject: [PATCH 16/26] Add nextResult and it works successful! --- swoole_mysql.c | 8 ++++- swoole_mysql.h | 1 + swoole_mysql_coro.c | 74 ++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/swoole_mysql.c b/swoole_mysql.c index 2bda45f8b1b..c4e3982f01a 100644 --- a/swoole_mysql.c +++ b/swoole_mysql.c @@ -1481,7 +1481,12 @@ int mysql_is_over(mysql_client *client, off_t *check_offset) char *p; if (*check_offset < buffer->offset) { - *check_offset = buffer->offset; // not check the first again. + *check_offset = buffer->offset; // not check the first response again. + } + else if (*check_offset == buffer->length) + { + // have already check all of the data + goto again; } size_t n_buf = buffer->length - *check_offset; // remaining buffer size uint32_t temp; @@ -1543,6 +1548,7 @@ int mysql_is_over(mysql_client *client, off_t *check_offset) } } + again: client->response.wait_recv = 2; return SW_AGAIN; } diff --git a/swoole_mysql.h b/swoole_mysql.h index 6cc2f8a3fd6..1c9dd4baf0f 100644 --- a/swoole_mysql.h +++ b/swoole_mysql.h @@ -417,6 +417,7 @@ int mysql_handshake(mysql_connector *connector, char *buf, int len); int mysql_request(swString *sql, swString *buffer); int mysql_prepare(swString *sql, swString *buffer); int mysql_response(mysql_client *client); +int mysql_is_over(mysql_client *client, off_t *check_offset); #ifdef SW_MYSQL_DEBUG void mysql_client_info(mysql_client *client); diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 65538da4e28..c895253eb98 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -47,6 +47,7 @@ static PHP_METHOD(swoole_mysql_coro_statement, __destruct); static PHP_METHOD(swoole_mysql_coro_statement, execute); static PHP_METHOD(swoole_mysql_coro_statement, fetch); static PHP_METHOD(swoole_mysql_coro_statement, fetchAll); +static PHP_METHOD(swoole_mysql_coro_statement, nextResult); ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) ZEND_END_ARG_INFO() @@ -95,6 +96,9 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_statement_fetchAll, 0, 0, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_statement_nextResult, 0, 0, 0) +ZEND_END_ARG_INFO() + static zend_class_entry swoole_mysql_coro_ce; static zend_class_entry *swoole_mysql_coro_class_entry_ptr; @@ -131,12 +135,14 @@ static const zend_function_entry swoole_mysql_coro_statement_methods[] = PHP_ME(swoole_mysql_coro_statement, execute, arginfo_swoole_mysql_coro_statement_execute, ZEND_ACC_PUBLIC) PHP_ME(swoole_mysql_coro_statement, fetch, arginfo_swoole_mysql_coro_statement_fetch, ZEND_ACC_PUBLIC) PHP_ME(swoole_mysql_coro_statement, fetchAll, arginfo_swoole_mysql_coro_statement_fetchAll, ZEND_ACC_PUBLIC) + PHP_ME(swoole_mysql_coro_statement, nextResult, arginfo_swoole_mysql_coro_statement_nextResult, ZEND_ACC_PUBLIC) PHP_ME(swoole_mysql_coro_statement, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_DTOR) PHP_FALIAS(__sleep, swoole_unsupport_serialize, NULL) PHP_FALIAS(__wakeup, swoole_unsupport_serialize, NULL) PHP_FE_END }; +static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result); static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event); static int swoole_mysql_coro_onWrite(swReactor *reactor, swEvent *event); static int swoole_mysql_coro_onError(swReactor *reactor, swEvent *event); @@ -1165,9 +1171,9 @@ static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) if (stmt->result) { - zval *result; - ZVAL_ZVAL(result, stmt->result, 0, 0); + zval _result = *stmt->result; efree(stmt->result); + zval *result = &_result; stmt->result = NULL; RETURN_ZVAL(result, 0, 1); } @@ -1177,6 +1183,48 @@ static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) } } +static PHP_METHOD(swoole_mysql_coro_statement, nextResult) +{ + mysql_statement *stmt = swoole_get_object(getThis()); + if (!stmt) + { + RETURN_FALSE; + } + + mysql_client *client = stmt->client; + if (!client->cli) + { + swoole_php_fatal_error(E_WARNING, "mysql connection#%d is closed.", client->fd); + RETURN_FALSE; + } + + if (stmt->buffer && stmt->buffer->offset < stmt->buffer->length) + { + client->state = SW_MYSQL_STATE_READ_START; + zval *result = NULL; + if (swoole_mysql_coro_parse_response(stmt->client, &result) == SW_OK) + { + // clean up + if ((client->response.status_code & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) == 0) + { + swString_clear(stmt->buffer); + } + bzero(&client->response, sizeof(client->response)); + + efree(result); + RETURN_ZVAL(result, 0, 1); + } + else + { + RETURN_FALSE; + } + } + else + { + RETURN_NULL() + } +} + static PHP_METHOD(swoole_mysql_coro_statement, __destruct) { mysql_statement *stmt = swoole_get_object(getThis()); @@ -1379,7 +1427,7 @@ static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result) // free the last one sw_zval_free(client->statement->result); } - client->statement->result = *result; + client->statement->result = (zval *)(*result); // return true (success) *result = NULL; SW_ALLOC_INIT_ZVAL(*result); @@ -1387,10 +1435,6 @@ static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result) } } - // clean up - swString_clear(MYSQL_RESPONSE_BUFFER); - bzero(&client->response, sizeof(client->response)); - return SW_OK; } @@ -1701,7 +1745,15 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) case SW_CLOSE: goto close_fd; case SW_WAIT: - goto parse_response; + if (check_offset == buffer->length) + { + continue; + } + else + { + // have already check all of the data + goto parse_response; + } default: return SW_ERR; } @@ -1789,6 +1841,12 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) } swTraceLog(SW_TRACE_MYSQL_CLIENT, "remaining %d, more results exists", buffer->length - buffer->offset); } + else + { + swString_clear(buffer); + } + // clean up + bzero(&client->response, sizeof(client->response)); if (client->defer && !client->suspending) From 939a5f2a52956f3e889a53e3d5739c5e7b75f911 Mon Sep 17 00:00:00 2001 From: twosee Date: Tue, 5 Jun 2018 13:46:01 +0800 Subject: [PATCH 17/26] Fix the is over check bug due to the MySQL protocol problem. --- swoole_mysql.c | 60 ++++++++++++++++++++++++--------------------- swoole_mysql_coro.c | 26 +++++++++++++------- 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/swoole_mysql.c b/swoole_mysql.c index c4e3982f01a..bff6161cc0f 100644 --- a/swoole_mysql.c +++ b/swoole_mysql.c @@ -1499,45 +1499,49 @@ int mysql_is_over(mysql_client *client, off_t *check_offset) break; } temp = mysql_uint3korr(p); //package length + // add header p += 4; *check_offset += 4; n_buf -= 4; - *check_offset += temp; + *check_offset += temp; // add package length - if (n_buf < temp) //package is incomplete + if (*check_offset >= buffer->length) // if false: more packages exist, skip the current one { - break; - } + if (n_buf < temp) //package is incomplete + { + break; + } - if ((uint16_t) p[0] == 0xff) // response type = error - { - goto over; - } - // response type = ok? - if ((uint16_t) p[0] == 0 && temp >= 7) - { - int t_nbuf = n_buf; - p++; - t_nbuf--; + if ((uint16_t) p[0] == 0xff) // response type = error + { + goto over; + } + // response type = ok? + if ((uint16_t) p[0] == 0 && temp >= 7) + { + int t_nbuf = n_buf; + p++; + t_nbuf--; - ulong_t val = 0; - char nul; - int retcode; + ulong_t val = 0; + char nul; + int retcode; - retcode = mysql_lcb_ll(p, &val, &nul, t_nbuf); //affecr rows - t_nbuf -= retcode; - p += retcode; + retcode = mysql_lcb_ll(p, &val, &nul, t_nbuf); //affecr rows + t_nbuf -= retcode; + p += retcode; - retcode = mysql_lcb_ll(p, &val, &nul, t_nbuf); //insert id - t_nbuf -= retcode; - p += retcode; + retcode = mysql_lcb_ll(p, &val, &nul, t_nbuf); //insert id + t_nbuf -= retcode; + p += retcode; - if ((mysql_uint2korr(p) & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) == 0) - { - over: - client->response.wait_recv = 0; - return SW_OK; + if ((mysql_uint2korr(p) & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) == 0) + { + over: + client->response.wait_recv = 0; + return SW_OK; + } } } diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index c895253eb98..60ad0ddb642 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -1200,9 +1200,11 @@ static PHP_METHOD(swoole_mysql_coro_statement, nextResult) if (stmt->buffer && stmt->buffer->offset < stmt->buffer->length) { + client->cmd = SW_MYSQL_COM_STMT_EXECUTE; client->state = SW_MYSQL_STATE_READ_START; + client->statement = stmt; zval *result = NULL; - if (swoole_mysql_coro_parse_response(stmt->client, &result) == SW_OK) + if (swoole_mysql_coro_parse_response(client, &result) == SW_OK) { // clean up if ((client->response.status_code & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) == 0) @@ -1417,22 +1419,28 @@ static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result) *result = client->response.result_array; } - if (client->connector.fetch_mode) + if (client->connector.fetch_mode && client->cmd == SW_MYSQL_COM_STMT_EXECUTE) { - if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE && Z_TYPE_P(*result) == IS_ARRAY) + if (client->statement->result) + { + // free the last one + sw_zval_free(client->statement->result); + client->statement->result = NULL; + } + if (Z_TYPE_P(*result) != IS_TRUE) { // save result on statement and wait for fetch - if (client->statement->result) - { - // free the last one - sw_zval_free(client->statement->result); - } - client->statement->result = (zval *)(*result); + client->statement->result = (zval *) (*result); // return true (success) *result = NULL; SW_ALLOC_INIT_ZVAL(*result); ZVAL_BOOL(*result, 1); } + else + { + // pass the ok response + ZVAL_NULL(*result); + } } return SW_OK; From a66bdb01ac5b4f782011a4db04ae0d703e68e8e5 Mon Sep 17 00:00:00 2001 From: twosee Date: Tue, 5 Jun 2018 15:25:30 +0800 Subject: [PATCH 18/26] Some patches to fine down the mysql coro: Always check the package completeness, add client check_offset attr, add swoole_mysql_coro_parse_end to tidy up, use event loop but instead loop recv. --- swoole_mysql.c | 30 ++++++++++-------- swoole_mysql.h | 3 +- swoole_mysql_coro.c | 75 ++++++++++++++++----------------------------- 3 files changed, 45 insertions(+), 63 deletions(-) diff --git a/swoole_mysql.c b/swoole_mysql.c index bff6161cc0f..a34fe9c0c26 100644 --- a/swoole_mysql.c +++ b/swoole_mysql.c @@ -1475,25 +1475,25 @@ static int mysql_read_columns(mysql_client *client) } // this function is used to check if multi responses has received over. -int mysql_is_over(mysql_client *client, off_t *check_offset) +int mysql_is_over(mysql_client *client) { swString *buffer = MYSQL_RESPONSE_BUFFER; char *p; - if (*check_offset < buffer->offset) + if (client->check_offset < buffer->offset) { - *check_offset = buffer->offset; // not check the first response again. + client->check_offset = buffer->offset; // not check the first response again. } - else if (*check_offset == buffer->length) + else if (client->check_offset == buffer->length) { // have already check all of the data goto again; } - size_t n_buf = buffer->length - *check_offset; // remaining buffer size + size_t n_buf = buffer->length - client->check_offset; // remaining buffer size uint32_t temp; while (1) { - p = buffer->str + *check_offset; // where to start checking now + p = buffer->str + client->check_offset; // where to start checking now if (buffer->length - buffer->offset < 5) { break; @@ -1501,17 +1501,21 @@ int mysql_is_over(mysql_client *client, off_t *check_offset) temp = mysql_uint3korr(p); //package length // add header p += 4; - *check_offset += 4; n_buf -= 4; - *check_offset += temp; // add package length + if (n_buf < temp) //package is incomplete + { + break; + } + else + { + client->check_offset += 4; + } + + client->check_offset += temp; // add package length - if (*check_offset >= buffer->length) // if false: more packages exist, skip the current one + if (client->check_offset >= buffer->length) // if false: more packages exist, skip the current one { - if (n_buf < temp) //package is incomplete - { - break; - } if ((uint16_t) p[0] == 0xff) // response type = error { diff --git a/swoole_mysql.h b/swoole_mysql.h index 1c9dd4baf0f..15519235d2c 100644 --- a/swoole_mysql.h +++ b/swoole_mysql.h @@ -353,6 +353,7 @@ typedef struct _mysql_client zval _onClose; #endif + off_t check_offset; mysql_response_t response; /* single response */ } mysql_client; @@ -417,7 +418,7 @@ int mysql_handshake(mysql_connector *connector, char *buf, int len); int mysql_request(swString *sql, swString *buffer); int mysql_prepare(swString *sql, swString *buffer); int mysql_response(mysql_client *client); -int mysql_is_over(mysql_client *client, off_t *check_offset); +int mysql_is_over(mysql_client *client); #ifdef SW_MYSQL_DEBUG void mysql_client_info(mysql_client *client); diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 60ad0ddb642..3bb02b9da95 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -1206,13 +1206,7 @@ static PHP_METHOD(swoole_mysql_coro_statement, nextResult) zval *result = NULL; if (swoole_mysql_coro_parse_response(client, &result) == SW_OK) { - // clean up - if ((client->response.status_code & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) == 0) - { - swString_clear(stmt->buffer); - } - bzero(&client->response, sizeof(client->response)); - + swoole_mysql_coro_parse_end(client, stmt->buffer); // ending tidy up efree(result); RETURN_ZVAL(result, 0, 1); } @@ -1345,14 +1339,7 @@ static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result) if (mysql_response(client) < 0) { - if (client->response.wait_recv > 0) // not over - { - return SW_AGAIN; - } - else - { - return SW_ERR; - } + return SW_ERR; } //remove from eventloop @@ -1446,6 +1433,20 @@ static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result) return SW_OK; } +static void swoole_mysql_coro_parse_end(mysql_client *client, swString *buffer) +{ + if (client->response.status_code & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) + { + swTraceLog(SW_TRACE_MYSQL_CLIENT, "remaining %d, more results exists", buffer->length - buffer->offset); + } + else + { + // no more, clean up + swString_clear(buffer); + } + bzero(&client->response, sizeof(client->response)); +} + static int swoole_mysql_coro_onError(swReactor *reactor, swEvent *event) { #if PHP_MAJOR_VERSION < 7 @@ -1732,7 +1733,6 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) zval *retval = NULL; zval *result = NULL; - off_t check_offset = 0; while(1) { ret = recv(sock, buffer->str + buffer->length, buffer->size - buffer->length, 0); @@ -1753,9 +1753,9 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) case SW_CLOSE: goto close_fd; case SW_WAIT: - if (check_offset == buffer->length) + if (client->check_offset == buffer->length) { - continue; + return SW_OK; } else { @@ -1818,43 +1818,20 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) parse_response: - // have ever checked, don't parse more responses immediately - if (check_offset > 0) - { - goto more; - } - - switch (swoole_mysql_coro_parse_response(client, &result)) + // always check that is package complete + // and maybe more responses has already received in buffer, we check it now. + if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE && mysql_is_over(client) != SW_OK) { - case SW_OK: - break; - case SW_ERR: //parse error - return SW_OK; - case SW_AGAIN: + // the **last** sever status flag shows that more results exist but we hasn't received. + swTraceLog(SW_TRACE_MYSQL_CLIENT, "need more"); continue; - default: - return SW_ERR; //unknown } - if (client->response.status_code & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) + if (swoole_mysql_coro_parse_response(client, &result) != SW_OK) { - more: - // buffer has multi responses - if (mysql_is_over(client, &check_offset) != SW_OK) - { - // why mysql_is_ok: maybe more responses has already received in buffer, we check it now. - swTraceLog(SW_TRACE_MYSQL_CLIENT, "need more"); - // flag shows that more results exist but we hasn't received. - continue; - } - swTraceLog(SW_TRACE_MYSQL_CLIENT, "remaining %d, more results exists", buffer->length - buffer->offset); - } - else - { - swString_clear(buffer); + return SW_OK;//parse error } - // clean up - bzero(&client->response, sizeof(client->response)); + swoole_mysql_coro_parse_end(client, buffer); // ending tidy up if (client->defer && !client->suspending) From 3dbb5babac51e6f51c945e90c2ea20b2b0658e66 Mon Sep 17 00:00:00 2001 From: twosee Date: Tue, 5 Jun 2018 15:35:18 +0800 Subject: [PATCH 19/26] Tidy up the code. --- swoole_mysql_coro.c | 229 ++++++++++++++++++++++---------------------- 1 file changed, 114 insertions(+), 115 deletions(-) diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 3bb02b9da95..66794a0898f 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -142,7 +142,6 @@ static const zend_function_entry swoole_mysql_coro_statement_methods[] = PHP_FE_END }; -static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result); static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event); static int swoole_mysql_coro_onWrite(swReactor *reactor, swEvent *event); static int swoole_mysql_coro_onError(swReactor *reactor, swEvent *event); @@ -339,6 +338,120 @@ static int swoole_mysql_coro_execute(zval *zobject, mysql_client *client, zval * return SW_OK; } +static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result) +{ + zval *zobject = client->object; + + if (mysql_response(client) < 0) + { + return SW_ERR; + } + + //remove from eventloop + //reactor->del(reactor, event->fd); + + zend_update_property_long(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("affected_rows"), + client->response.affected_rows TSRMLS_CC); + zend_update_property_long(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("insert_id"), + client->response.insert_id TSRMLS_CC); + + if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE) + { + zend_update_property_long(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, + ZEND_STRL("affected_rows"), client->response.affected_rows TSRMLS_CC); + zend_update_property_long(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, + ZEND_STRL("insert_id"), client->response.insert_id TSRMLS_CC); + } + + client->state = SW_MYSQL_STATE_QUERY; + + //OK + if (client->response.response_type == 0) + { + SW_ALLOC_INIT_ZVAL(*result); + // prepare finished and create statement + if (client->cmd == SW_MYSQL_COM_STMT_PREPARE) + { + if (client->statement_list == NULL) + { + client->statement_list = swLinkedList_new(0, NULL); + } + swLinkedList_append(client->statement_list, client->statement); + object_init_ex(*result, swoole_mysql_coro_statement_class_entry_ptr); + swoole_set_object(*result, client->statement); + client->statement->object = sw_zval_dup(*result); + } + else + { + ZVAL_BOOL(*result, 1); + } + } + //ERROR + else if (client->response.response_type == 255) + { + SW_ALLOC_INIT_ZVAL(*result); + ZVAL_BOOL(*result, 0); + + zend_update_property_stringl(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("error"), + client->response.server_msg, client->response.l_server_msg TSRMLS_CC); + zend_update_property_long(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("errno"), + client->response.error_code TSRMLS_CC); + + if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE) + { + zend_update_property_stringl(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, + ZEND_STRL("error"), client->response.server_msg, client->response.l_server_msg TSRMLS_CC); + zend_update_property_long(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, + ZEND_STRL("errno"), client->response.error_code TSRMLS_CC); + } + } + //ResultSet + else + { + *result = client->response.result_array; + } + + if (client->connector.fetch_mode && client->cmd == SW_MYSQL_COM_STMT_EXECUTE) + { + if (client->statement->result) + { + // free the last one + sw_zval_free(client->statement->result); + client->statement->result = NULL; + } + if (Z_TYPE_P(*result) != IS_TRUE) + { + // save result on statement and wait for fetch + client->statement->result = (zval *) (*result); + // return true (success) + *result = NULL; + SW_ALLOC_INIT_ZVAL(*result); + ZVAL_BOOL(*result, 1); + } + else + { + // pass the ok response + ZVAL_NULL(*result); + } + } + + return SW_OK; +} + +static void swoole_mysql_coro_parse_end(mysql_client *client, swString *buffer) +{ + if (client->response.status_code & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) + { + swTraceLog(SW_TRACE_MYSQL_CLIENT, "remaining %d, more results exists", buffer->length - buffer->offset); + } + else + { + // no more, clean up + swString_clear(buffer); + } + bzero(&client->response, sizeof(client->response)); +} + static int swoole_mysql_coro_statement_free(mysql_statement *stmt TSRMLS_DC) { if (stmt->object) @@ -1333,120 +1446,6 @@ static PHP_METHOD(swoole_mysql_coro, close) RETURN_TRUE; } -static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result) -{ - zval *zobject = client->object; - - if (mysql_response(client) < 0) - { - return SW_ERR; - } - - //remove from eventloop - //reactor->del(reactor, event->fd); - - zend_update_property_long(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("affected_rows"), - client->response.affected_rows TSRMLS_CC); - zend_update_property_long(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("insert_id"), - client->response.insert_id TSRMLS_CC); - - if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE) - { - zend_update_property_long(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, - ZEND_STRL("affected_rows"), client->response.affected_rows TSRMLS_CC); - zend_update_property_long(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, - ZEND_STRL("insert_id"), client->response.insert_id TSRMLS_CC); - } - - client->state = SW_MYSQL_STATE_QUERY; - - //OK - if (client->response.response_type == 0) - { - SW_ALLOC_INIT_ZVAL(*result); - // prepare finished and create statement - if (client->cmd == SW_MYSQL_COM_STMT_PREPARE) - { - if (client->statement_list == NULL) - { - client->statement_list = swLinkedList_new(0, NULL); - } - swLinkedList_append(client->statement_list, client->statement); - object_init_ex(*result, swoole_mysql_coro_statement_class_entry_ptr); - swoole_set_object(*result, client->statement); - client->statement->object = sw_zval_dup(*result); - } - else - { - ZVAL_BOOL(*result, 1); - } - } - //ERROR - else if (client->response.response_type == 255) - { - SW_ALLOC_INIT_ZVAL(*result); - ZVAL_BOOL(*result, 0); - - zend_update_property_stringl(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("error"), - client->response.server_msg, client->response.l_server_msg TSRMLS_CC); - zend_update_property_long(swoole_mysql_coro_class_entry_ptr, zobject, ZEND_STRL("errno"), - client->response.error_code TSRMLS_CC); - - if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE) - { - zend_update_property_stringl(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, - ZEND_STRL("error"), client->response.server_msg, client->response.l_server_msg TSRMLS_CC); - zend_update_property_long(swoole_mysql_coro_statement_class_entry_ptr, client->statement->object, - ZEND_STRL("errno"), client->response.error_code TSRMLS_CC); - } - } - //ResultSet - else - { - *result = client->response.result_array; - } - - if (client->connector.fetch_mode && client->cmd == SW_MYSQL_COM_STMT_EXECUTE) - { - if (client->statement->result) - { - // free the last one - sw_zval_free(client->statement->result); - client->statement->result = NULL; - } - if (Z_TYPE_P(*result) != IS_TRUE) - { - // save result on statement and wait for fetch - client->statement->result = (zval *) (*result); - // return true (success) - *result = NULL; - SW_ALLOC_INIT_ZVAL(*result); - ZVAL_BOOL(*result, 1); - } - else - { - // pass the ok response - ZVAL_NULL(*result); - } - } - - return SW_OK; -} - -static void swoole_mysql_coro_parse_end(mysql_client *client, swString *buffer) -{ - if (client->response.status_code & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) - { - swTraceLog(SW_TRACE_MYSQL_CLIENT, "remaining %d, more results exists", buffer->length - buffer->offset); - } - else - { - // no more, clean up - swString_clear(buffer); - } - bzero(&client->response, sizeof(client->response)); -} - static int swoole_mysql_coro_onError(swReactor *reactor, swEvent *event) { #if PHP_MAJOR_VERSION < 7 From b8db6e46c0e66233887eac39673373c4f74d0a95 Mon Sep 17 00:00:00 2001 From: twosee Date: Tue, 5 Jun 2018 16:50:51 +0800 Subject: [PATCH 20/26] Fix the response type check, clear cache attribute totally. --- swoole_mysql.c | 44 ++++++++++++++++++++++++++++---------------- swoole_mysql_coro.c | 1 + 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/swoole_mysql.c b/swoole_mysql.c index a34fe9c0c26..8ce7d5c1eca 100644 --- a/swoole_mysql.c +++ b/swoole_mysql.c @@ -1479,11 +1479,7 @@ int mysql_is_over(mysql_client *client) { swString *buffer = MYSQL_RESPONSE_BUFFER; char *p; - if (client->check_offset < buffer->offset) - { - client->check_offset = buffer->offset; // not check the first response again. - } - else if (client->check_offset == buffer->length) + if (client->check_offset == buffer->length) { // have already check all of the data goto again; @@ -1494,7 +1490,7 @@ int mysql_is_over(mysql_client *client) while (1) { p = buffer->str + client->check_offset; // where to start checking now - if (buffer->length - buffer->offset < 5) + if (unlikely(buffer->length - buffer->offset < 5)) { break; } @@ -1502,8 +1498,7 @@ int mysql_is_over(mysql_client *client) // add header p += 4; n_buf -= 4; - - if (n_buf < temp) //package is incomplete + if (unlikely(n_buf < temp)) //package is incomplete { break; } @@ -1516,21 +1511,30 @@ int mysql_is_over(mysql_client *client) if (client->check_offset >= buffer->length) // if false: more packages exist, skip the current one { - - if ((uint16_t) p[0] == 0xff) // response type = error + switch ((uint8_t) p[0]) { - goto over; + case 0xfe: // eof + { + // +type +warning + p += 3; + swDebug("meet eof and flag=%d", mysql_uint2korr(p)); + goto check_flag; } - // response type = ok? - if ((uint16_t) p[0] == 0 && temp >= 7) + case 0x00: // ok { - int t_nbuf = n_buf; - p++; - t_nbuf--; +// if (temp < 7) +// { +// break; +// } ulong_t val = 0; char nul; int retcode; + int t_nbuf = n_buf; + + //+type + p++; + t_nbuf--; retcode = mysql_lcb_ll(p, &val, &nul, t_nbuf); //affecr rows t_nbuf -= retcode; @@ -1540,12 +1544,20 @@ int mysql_is_over(mysql_client *client) t_nbuf -= retcode; p += retcode; + check_flag: if ((mysql_uint2korr(p) & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) == 0) { over: client->response.wait_recv = 0; + client->check_offset = 0; return SW_OK; } + break; + } + case 0xff: // response type = error + { + goto over; + } } } diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 66794a0898f..00d03521a48 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -450,6 +450,7 @@ static void swoole_mysql_coro_parse_end(mysql_client *client, swString *buffer) swString_clear(buffer); } bzero(&client->response, sizeof(client->response)); + client->statement = NULL; } static int swoole_mysql_coro_statement_free(mysql_statement *stmt TSRMLS_DC) From 38a139d18a128b8d4c573e3f195bd479e1724975 Mon Sep 17 00:00:00 2001 From: twosee Date: Tue, 5 Jun 2018 16:51:43 +0800 Subject: [PATCH 21/26] Add unit test: procedure in fetch mode --- .../swoole_mysql_coro/procedure_in_fetch.phpt | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tests/swoole_mysql_coro/procedure_in_fetch.phpt diff --git a/tests/swoole_mysql_coro/procedure_in_fetch.phpt b/tests/swoole_mysql_coro/procedure_in_fetch.phpt new file mode 100644 index 00000000000..69ebf9e9b4f --- /dev/null +++ b/tests/swoole_mysql_coro/procedure_in_fetch.phpt @@ -0,0 +1,74 @@ +--TEST-- +procedure: procedure in fetch mode +--SKIPIF-- + +--FILE-- + MYSQL_SERVER_HOST, + 'user' => MYSQL_SERVER_USER, + 'password' => MYSQL_SERVER_PWD, + 'database' => MYSQL_SERVER_DB, + 'fetch_mode' => true + ]; + + $clear = <<connect($server); + if ($db->query($clear) && $db->query($procedure)) { + + //SWOOLE + $_map = $map; + $stmt = $db->prepare('CALL reply(?)'); + assert($stmt->execute(['hello mysql!']) === true); + do { + $res = $stmt->fetchAll(); + assert(current($res[0]) === array_shift($_map)); + } while ($stmt->nextResult()); + assert($stmt->affected_rows === 1, 'get the affected rows failed!'); + assert(empty($_map), 'there are some results lost!'); + + //PDO + !extension_loaded('PDO') && exit; + $_map = $map; + $pdo = new PDO( + "mysql:host=" . MYSQL_SERVER_HOST . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", + MYSQL_SERVER_USER, MYSQL_SERVER_PWD + ); + $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + $stmt = $pdo->prepare("CALL reply(?)"); + assert($stmt->execute(['hello mysql!']) === true); + do { + $res = $stmt->fetchAll(); + assert(current($res[0]) === array_shift($_map)); + } while ($ret = $stmt->nextRowset()); + assert($stmt->rowCount() === 1, 'get the affected rows failed!'); + assert(empty($_map), 'there are some results lost!'); + } +}); +?> +--EXPECT-- \ No newline at end of file From 9031ced14e588b0d3149317890a81cdf991169dd Mon Sep 17 00:00:00 2001 From: twosee Date: Tue, 5 Jun 2018 16:58:59 +0800 Subject: [PATCH 22/26] Return false when we are not in fetch mode. --- swoole_mysql_coro.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 00d03521a48..6377a34bd7b 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -1234,6 +1234,11 @@ static PHP_METHOD(swoole_mysql_coro_statement, fetch) RETURN_FALSE; } + if (!stmt->client->connector.fetch_mode) + { + RETURN_FALSE; + } + if (stmt->result) { zval **args[1]; @@ -1283,6 +1288,11 @@ static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) RETURN_FALSE; } + if (!stmt->client->connector.fetch_mode) + { + RETURN_FALSE; + } + if (stmt->result) { zval _result = *stmt->result; @@ -1305,6 +1315,11 @@ static PHP_METHOD(swoole_mysql_coro_statement, nextResult) RETURN_FALSE; } + if (!stmt->client->connector.fetch_mode) + { + RETURN_FALSE; + } + mysql_client *client = stmt->client; if (!client->cli) { From c2f356c5ff0c74b2651841937e94ab0fb2665289 Mon Sep 17 00:00:00 2001 From: twosee Date: Tue, 5 Jun 2018 17:03:49 +0800 Subject: [PATCH 23/26] Without MySQL client connection, we can also parse the new response in buffer. --- swoole_mysql_coro.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 6377a34bd7b..509076f0510 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -1321,11 +1321,6 @@ static PHP_METHOD(swoole_mysql_coro_statement, nextResult) } mysql_client *client = stmt->client; - if (!client->cli) - { - swoole_php_fatal_error(E_WARNING, "mysql connection#%d is closed.", client->fd); - RETURN_FALSE; - } if (stmt->buffer && stmt->buffer->offset < stmt->buffer->length) { From 58a4d1df04d996638fb3d95bb4fc424eb67c4191 Mon Sep 17 00:00:00 2001 From: twosee Date: Tue, 5 Jun 2018 18:33:56 +0800 Subject: [PATCH 24/26] Support procedure without fetch mode. --- swoole_mysql_coro.c | 57 ++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/swoole_mysql_coro.c b/swoole_mysql_coro.c index 509076f0510..735eca9b649 100644 --- a/swoole_mysql_coro.c +++ b/swoole_mysql_coro.c @@ -338,7 +338,7 @@ static int swoole_mysql_coro_execute(zval *zobject, mysql_client *client, zval * return SW_OK; } -static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result) +static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result, int from_next_result) { zval *zobject = client->object; @@ -383,7 +383,15 @@ static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result) } else { - ZVAL_BOOL(*result, 1); + if (from_next_result) + { + // pass the ok response ret val + ZVAL_NULL(*result); + } + else + { + ZVAL_TRUE(*result); + } } } //ERROR @@ -408,30 +416,24 @@ static int swoole_mysql_coro_parse_response(mysql_client *client, zval **result) //ResultSet else { - *result = client->response.result_array; - } - - if (client->connector.fetch_mode && client->cmd == SW_MYSQL_COM_STMT_EXECUTE) - { - if (client->statement->result) - { - // free the last one - sw_zval_free(client->statement->result); - client->statement->result = NULL; - } - if (Z_TYPE_P(*result) != IS_TRUE) + if (client->connector.fetch_mode && client->cmd == SW_MYSQL_COM_STMT_EXECUTE) { + if (client->statement->result) + { + // free the last one + sw_zval_free(client->statement->result); + client->statement->result = NULL; + } // save result on statement and wait for fetch - client->statement->result = (zval *) (*result); - // return true (success) - *result = NULL; + client->statement->result = client->response.result_array; + client->response.result_array = NULL; + // return true (success)] SW_ALLOC_INIT_ZVAL(*result); - ZVAL_BOOL(*result, 1); + ZVAL_TRUE(*result); } else { - // pass the ok response - ZVAL_NULL(*result); + *result = client->response.result_array; } } @@ -1315,10 +1317,10 @@ static PHP_METHOD(swoole_mysql_coro_statement, nextResult) RETURN_FALSE; } - if (!stmt->client->connector.fetch_mode) - { - RETURN_FALSE; - } +// if (!stmt->client->connector.fetch_mode) +// { +// RETURN_FALSE; +// } mysql_client *client = stmt->client; @@ -1328,10 +1330,13 @@ static PHP_METHOD(swoole_mysql_coro_statement, nextResult) client->state = SW_MYSQL_STATE_READ_START; client->statement = stmt; zval *result = NULL; - if (swoole_mysql_coro_parse_response(client, &result) == SW_OK) + if (swoole_mysql_coro_parse_response(client, &result, 1) == SW_OK) { swoole_mysql_coro_parse_end(client, stmt->buffer); // ending tidy up + + zval _result = *result; efree(result); + result = &_result; RETURN_ZVAL(result, 0, 1); } else @@ -1837,7 +1842,7 @@ static int swoole_mysql_coro_onRead(swReactor *reactor, swEvent *event) continue; } - if (swoole_mysql_coro_parse_response(client, &result) != SW_OK) + if (swoole_mysql_coro_parse_response(client, &result, 0) != SW_OK) { return SW_OK;//parse error } From 13ff4ff8ac2723649f05b69f337f49557cf74546 Mon Sep 17 00:00:00 2001 From: twosee Date: Tue, 5 Jun 2018 18:34:43 +0800 Subject: [PATCH 25/26] Add procedure unit test (without fetch mode). --- tests/swoole_mysql_coro/procedure.phpt | 54 ++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 tests/swoole_mysql_coro/procedure.phpt diff --git a/tests/swoole_mysql_coro/procedure.phpt b/tests/swoole_mysql_coro/procedure.phpt new file mode 100644 index 00000000000..9b772b92c5e --- /dev/null +++ b/tests/swoole_mysql_coro/procedure.phpt @@ -0,0 +1,54 @@ +--TEST-- +procedure: procedure without fetch mode +--SKIPIF-- + +--FILE-- + MYSQL_SERVER_HOST, + 'user' => MYSQL_SERVER_USER, + 'password' => MYSQL_SERVER_PWD, + 'database' => MYSQL_SERVER_DB + ]; + + $clear = <<connect($server); + if ($db->query($clear) && $db->query($procedure)) { + //SWOOLE + $_map = $map; + $stmt = $db->prepare('CALL reply(?)'); + $res = $stmt->execute(['hello mysql!']); + do { + assert(current($res[0]) === array_shift($_map)); + } while ($res = $stmt->nextResult()); + assert($stmt->affected_rows === 1, 'get the affected rows failed!'); + assert(empty($_map), 'there are some results lost!'); + } +}); +?> +--EXPECT-- \ No newline at end of file From cf6f43922ffc853a0aaddd0efade5da79fc8a33f Mon Sep 17 00:00:00 2001 From: twosee Date: Tue, 5 Jun 2018 19:25:45 +0800 Subject: [PATCH 26/26] It's a unsigned int value. --- swoole_mysql.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swoole_mysql.c b/swoole_mysql.c index 8ce7d5c1eca..7ba55921b8c 100644 --- a/swoole_mysql.c +++ b/swoole_mysql.c @@ -1562,7 +1562,7 @@ int mysql_is_over(mysql_client *client) } n_buf -= temp; - if (n_buf <= 0) + if (n_buf == 0) { break; }