Update tutorial-12-mysql_cli.md

This commit is contained in:
1412
2020-07-29 22:49:03 +08:00
committed by GitHub
parent ef55abe82b
commit 594cabdd19

View File

@@ -5,9 +5,9 @@
# 关于mysql_cli
教程中的mysql_cli使用方式与官方客户端相似是一个命令行交互式的异步MySQL客户端
教程中的mysql_cli使用方式与官方客户端相似是一个命令行交互式的异步MySQL客户端
./mysql_cli \<URL\>
程序运行方式:./mysql_cli \<URL\>
启动之后可以直接在终端输入mysql命令与db进行交互输入quit或Ctrl-C退出。
@@ -49,12 +49,12 @@ void set_query(const std::string& query);
~~~cpp
int main(int argc, char *argv[])
{
...
WFMySQLTask *task = WFTaskFactory::create_mysql_task(url, RETRY_MAX, mysql_callback);
task->get_req()->set_query("SHOW TABLES;");
...
task->start();
...
...
WFMySQLTask *task = WFTaskFactory::create_mysql_task(url, RETRY_MAX, mysql_callback);
task->get_req()->set_query("SHOW TABLES;");
...
task->start();
...
}
~~~
@@ -62,8 +62,6 @@ int main(int argc, char *argv[])
目前支持的命令为**COM_QUERY**,已经能涵盖用户基本的增删改查、建库删库、建表删表、预处理、使用存储过程和使用事务的需求。
暂时不支持statement相关的命令。
因为我们的交互命令中不支持选库(**USE**命令所以如果SQL语句中有涉及到**跨库**的操作,则可以通过**db_name.table_name**的方式指定具体哪个库的哪张表。
**多条命令**可以拼接到一起通过set_query()传给WFMySQLTask一般来说多条语句是可以一次把结果全部拿回来的但由于MySQL协议中回包的方式与我们一问一答的通信有某些特例下不能兼容因此set_query()中的SQL语句有以下注意事项
@@ -72,7 +70,7 @@ int main(int argc, char *argv[])
- 也可以是一条多结果集语句比如CALL存储过程
- 其他情况建议把SQL语句分开多次请求
- 其他情况建议把SQL语句分开多次请求
举个例子:
~~~cpp
@@ -95,9 +93,9 @@ req->set_query("CALL procedure1(); SELECT * FROM table1;");
1. 判断任务状态(代表通信层面状态):用户通过判断 **task->get_state()** 等于WFT_STATE_SUCCESS来查看任务执行是否成功
2. 判断回复包类型(代表返回包解析状态):调用 **resp->get_packet_type()** 查看MySQL返回包类型常见的几个类型为
- MYSQL_PACKET_OK返回非结果集的请求: 解析成功
- MYSQL_PACKET_EOF返回结果集的请求: 解析成功
- MYSQL_PACKET_ERROR请求:失败
- MYSQL_PACKET_OK返回非结果集的请求: 解析成功
- MYSQL_PACKET_EOF返回结果集的请求: 解析成功
- MYSQL_PACKET_ERROR请求:失败
3. 判断结果集状态代表结果集读取状态用户可以使用cursor读取结果集中的内容因为MySQL server返回的数据是多结果集的因此一开始cursor会**自动指向第一个结果集**的读取位置。通过 **get_cursor_status()** 可以拿到的几种状态:
- MYSQL_STATUS_GET_RESULT有数据可读
@@ -137,87 +135,91 @@ req->set_query("CALL procedure1(); SELECT * FROM table1;");
~~~cpp
void task_callback(WFMySQLTask *task)
{
// step-1. 判断任务状态
if (task->get_state() != WFT_STATE_SUCCESS)
{
fprintf(stderr, "task error = %d\n", task->get_error());
return;
}
// step-1. 判断任务状态
if (task->get_state() != WFT_STATE_SUCCESS)
{
fprintf(stderr, "task error = %d\n", task->get_error());
return;
}
MySQLResultCursor cursor(task->get_resp());
bool test_first_result_set_flag = false;
bool test_rewind_flag = false;
MySQLResultCursor cursor(task->get_resp());
bool test_first_result_set_flag = false;
bool test_rewind_flag = false;
begin:
// step-2. 判断回复包状态
switch (resp->get_packet_type())
{
case MYSQL_PACKET_OK:
fprintf(stderr, "OK. %llu rows affected. %d warnings. insert_id=%llu.\n",
task->get_resp()->get_affected_rows(),
task->get_resp()->get_warnings(),
task->get_resp()->get_last_insert_id());
break;
// step-2. 判断回复包状态
switch (resp->get_packet_type())
{
case MYSQL_PACKET_OK:
fprintf(stderr, "OK. %llu rows affected. %d warnings. insert_id=%llu.\n",
task->get_resp()->get_affected_rows(),
task->get_resp()->get_warnings(),
task->get_resp()->get_last_insert_id());
break;
case MYSQL_PACKET_EOF:
do {
fprintf(stderr, "cursor_status=%d field_count=%u rows_count=%u ",
cursor.get_cursor_status(), cursor.get_field_count(), cursor.get_rows_count());
// step-3. 判断结果集状态
if (cursor.get_cursor_status() != MYSQL_STATUS_GET_RESULT)
break;
case MYSQL_PACKET_EOF:
do {
fprintf(stderr, "cursor_status=%d field_count=%u rows_count=%u ",
cursor.get_cursor_status(), cursor.get_field_count(), cursor.get_rows_count());
// step-3. 判断结果集状态
if (cursor.get_cursor_status() != MYSQL_STATUS_GET_RESULT)
break;
// step-4. 读取每个fields。这是个nocopy api
const MySQLField *const *fields = cursor.fetch_fields();
for (int i = 0; i < cursor.get_field_count(); i++)
{
fprintf(stderr, "db=%s table=%s name[%s] type[%s]\n",
fields[i]->get_db().c_str(), fields[i]->get_table().c_str(),
fields[i]->get_name().c_str(), datatype2str(fields[i]->get_data_type()));
}
// step-4. 读取每个fields。这是个nocopy api
const MySQLField *const *fields = cursor.fetch_fields();
for (int i = 0; i < cursor.get_field_count(); i++)
{
fprintf(stderr, "db=%s table=%s name[%s] type[%s]\n",
fields[i]->get_db().c_str(), fields[i]->get_table().c_str(),
fields[i]->get_name().c_str(), datatype2str(fields[i]->get_data_type()));
}
// step-6. 把所有行读出也可以while (cursor.fetch_row(map/vector)) 按step-5拿每一行
std::vector<std::vector<MySQLCell>> rows;
cursor.fetch_all(rows);
for (unsigned int j = 0; j < rows.size(); j++)
{
// step-10. 具体每个cell的读取
for (unsigned int i = 0; i < rows[j].size(); i++)
{
fprintf(stderr, "[%s][%s]", fields[i]->get_name().c_str(),
datatype2str(rows[j][i].get_data_type()));
if (rows[j][i].is_string())
{
std::string res = rows[j][i].as_string();
fprintf(stderr, "[%s]\n", res.c_str());
} else if (rows[j][i].is_int()) {
fprintf(stderr, "[%d]\n", rows[j][i].as_int());
} // else if ...
}
}
// step-8. 拿下一个结果集
} while (cursor.next_result_set());
// step-6. 把所有行读出也可以while (cursor.fetch_row(map/vector)) 按step-5拿每一行
std::vector<std::vector<MySQLCell>> rows;
if (test_first_result_set_flag == false)
{
test_first_result_set_flag = true;
// step-9. 返回第一个结果集
cursor.first_result_set();
goto begin;
}
cursor.fetch_all(rows);
for (unsigned int j = 0; j < rows.size(); j++)
{
// step-10. 具体每个cell的读取
for (unsigned int i = 0; i < rows[j].size(); i++)
{
fprintf(stderr, "[%s][%s]", fields[i]->get_name().c_str(),
datatype2str(rows[j][i].get_data_type()));
// step-10. 判断具体类型is_string()和转换具体类型as_string()
if (rows[j][i].is_string())
{
std::string res = rows[j][i].as_string();
fprintf(stderr, "[%s]\n", res.c_str());
} else if (rows[j][i].is_int()) {
fprintf(stderr, "[%d]\n", rows[j][i].as_int());
} // else if ...
}
}
// step-8. 拿下一个结果集
} while (cursor.next_result_set());
if (test_rewind_flag == false)
{
test_rewind_flag = true;
// step-7. 返回当前结果集头部
cursor.rewind();
goto begin;
}
break;
default:
fprintf(stderr, "Abnormal packet_type=%d\n", resp->get_packet_type());
break;
}
return;
if (test_first_result_set_flag == false)
{
test_first_result_set_flag = true;
// step-9. 返回第一个结果集
cursor.first_result_set();
goto begin;
}
if (test_rewind_flag == false)
{
test_rewind_flag = true;
// step-7. 返回当前结果集头部
cursor.rewind();
goto begin;
}
break;
default:
fprintf(stderr, "Abnormal packet_type=%d\n", resp->get_packet_type());
break;
}
return;
}
~~~
@@ -235,9 +237,9 @@ begin:
class WFMySQLConnection
{
public:
WFMySQLConnection(int id);
int init(const std::string& url);
...
WFMySQLConnection(int id);
int init(const std::string& url);
...
};
~~~
@@ -252,10 +254,10 @@ public:
class WFMySQLConnection
{
public:
...
WFMySQLTask *create_query_task(const std::string& query,
mysql_callback_t callback);
WFMySQLTask *create_disconnect_task(mysql_callback_t callback);
...
WFMySQLTask *create_query_task(const std::string& query,
mysql_callback_t callback);
WFMySQLTask *create_disconnect_task(mysql_callback_t callback);
}
~~~
@@ -270,19 +272,19 @@ public:
### 5. 完整示例
~~~cpp
WFMySQLConnection conn(1);
conn.init("mysql://root@127.0.0.1/test");
WFMySQLConnection conn(1);
conn.init("mysql://root@127.0.0.1/test");
// test transaction
Const char *query = "begin;";
WFMySQLTask *t1 = conn.create_query_task(query, task_callback);
query = "select * from check_tiny for update;";
WFMySQLTask *t2 = conn.create_query_task(query, task_callback);
query = "insert into check_tiny values (8);";
WFMySQLTask *t3 = conn.create_query_task(query, task_callback);
query = "commit;";
WFMySQLTask *t4 = conn.create_query_task(query, task_callback);
WFMySQLTask *t5 = conn.create_disconnect_task(task_callback);
((*t1) > t2 > t3 > t4 > t5).start();
// test transaction
const char *query = "BEGIN;";
WFMySQLTask *t1 = conn.create_query_task(query, task_callback);
query = "SELECT * FROM check_tiny FOR UPDATE;";
WFMySQLTask *t2 = conn.create_query_task(query, task_callback);
query = "INSERT INTO check_tiny VALUES (8);";
WFMySQLTask *t3 = conn.create_query_task(query, task_callback);
query = "COMMIT;";
WFMySQLTask *t4 = conn.create_query_task(query, task_callback);
WFMySQLTask *t5 = conn.create_disconnect_task(task_callback);
((*t1) > t2 > t3 > t4 > t5).start();
~~~