From 0d3e994e33aa85965616a064bbf635ec8d043c1a Mon Sep 17 00:00:00 2001
From: znone <glyc@sina.com.cn>
Date: Fri, 02 Oct 2020 06:11:48 +0000
Subject: [PATCH] The database can be operated asynchronously through asio.

---
 include/qtl_database_pool.hpp |    2 
 test/test_mariadb.mak         |    2 
 include/qtl_mysql.hpp         |    6 
 test/TestMariaDB.cpp          |  462 +++++++----------------------------
 include/qtl_asio.hpp          |  306 +++++++++++++++++++++++
 README_CN.md                  |    1 
 README.md                     |    1 
 7 files changed, 411 insertions(+), 369 deletions(-)

diff --git a/README.md b/README.md
index 635771a..9b39083 100644
--- a/README.md
+++ b/README.md
@@ -224,6 +224,7 @@
 
 ```
 Database connections are usually not thread-safe. User code should guarantee that a connection can only be used by one thread at a time.
+For use this feature, GCC requires version 5 or higher.
 
 ## About MySQL
 
diff --git a/README_CN.md b/README_CN.md
index 87e108f..f516d4e 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -224,6 +224,7 @@
 
 ```
 数据库连接通常不是线程安全的。用户代码应该保证,一个连接只能同时由一个线程使用。
+使用这项功能,GCC需要 5 或更高版本才行。
 
 ## 有关MySQL的说明
 
diff --git a/include/qtl_asio.hpp b/include/qtl_asio.hpp
new file mode 100644
index 0000000..cbf51c9
--- /dev/null
+++ b/include/qtl_asio.hpp
@@ -0,0 +1,306 @@
+#ifndef _QTL_ASIO_H_
+#define _QTL_ASIO_H_
+
+#include <qtl_async.hpp>
+#include <asio/version.hpp>
+#define ASIO_STANDALONE
+#if ASIO_VERSION < 101200
+#include <asio/io_service.hpp>
+#else
+#include <asio/io_context.hpp>
+#endif // ASIO_VERSION
+#include <asio/strand.hpp>
+#include <asio/ip/tcp.hpp>
+#include <asio/async_result.hpp>
+#include <asio/steady_timer.hpp>
+
+#if ASIO_VERSION < 100000
+#error The asio version required by QTL is at least 10.0 
+#endif
+
+namespace qtl
+{
+
+namespace NS_ASIO = ::asio;
+
+namespace asio
+{
+
+class service
+{
+public:
+#if ASIO_VERSION < 101200
+	typedef NS_ASIO::io_service service_type;
+#else
+	typedef NS_ASIO::io_context service_type;
+#endif // ASIO_VERSION
+
+	service() { }
+	explicit service(int concurrency_hint) : _service(concurrency_hint) { }
+
+	void reset()
+	{
+		_service.reset();
+	}
+
+	void run()
+	{
+		_service.run();
+	}
+
+	void stop()
+	{
+		_service.stop();
+	}
+
+	service_type& context() { return _service; }
+
+private:
+
+	class event_item : public qtl::event
+	{
+	public:
+		event_item(service_type& service, qtl::socket_type fd)
+			: _strand(service), _socket(service, NS_ASIO::ip::tcp::v4(), fd), _timer(service), _busying(false)
+		{
+		}
+
+		NS_ASIO::ip::tcp::socket& next_layer() { return _socket; }
+
+	public: // qtl::event
+		virtual void set_io_handler(int flags, long timeout, std::function<void(int)>&& handler) override
+		{
+			if (flags&qtl::event::ef_read)
+			{
+#if ASIO_VERSION < 101200
+				_socket.async_read_some(NS_ASIO::null_buffers(), _strand.wrap([this, handler](const NS_ASIO::error_code& ec, size_t bytes_transferred) {
+#else
+				_socket.async_wait(NS_ASIO::socket_base::wait_read, _strand.wrap([this, handler](const NS_ASIO::error_code& ec) {
+#endif // ASIO_VERSION
+					if (!ec)
+						handler(qtl::event::ef_read);
+					else if (ec == NS_ASIO::error::make_error_code(NS_ASIO::error::operation_aborted))
+						handler(qtl::event::ef_timeout);
+					else
+						handler(qtl::event::ef_exception);
+					_busying = false;
+				}));
+				_busying = true;
+			}
+			if (flags&qtl::event::ef_write)
+			{
+#if ASIO_VERSION < 101200
+				_socket.async_write_some(NS_ASIO::null_buffers(), _strand.wrap([this, handler](const NS_ASIO::error_code& ec, size_t bytes_transferred) {
+#else
+				_socket.async_wait(NS_ASIO::socket_base::wait_write, _strand.wrap([this, handler](const NS_ASIO::error_code& ec) {
+#endif //ASIO_VERSION
+					if (!ec)
+						handler(qtl::event::ef_write);
+					else if (ec == NS_ASIO::error::make_error_code(NS_ASIO::error::operation_aborted))
+						handler(qtl::event::ef_timeout);
+					else
+						handler(qtl::event::ef_exception);
+					_busying = false;
+				}));
+				_busying = true;
+			}
+			if (timeout > 0)
+			{
+#if ASIO_VERSION < 101200
+				_timer.expires_from_now(std::chrono::seconds(timeout));
+#else
+				_timer.expires_after(NS_ASIO::chrono::seconds(timeout));
+#endif // ASIO_VERSION
+				_timer.async_wait(_strand.wrap([this, handler](NS_ASIO::error_code ec) {
+					if (!ec)
+					{
+						_socket.cancel(ec);
+					}
+				}));
+			}
+		}
+
+		virtual void remove() override
+		{
+#if ASIO_VERSION < 101200 && (!defined(_WIN32) && _WIN32_WINNT >= 0x0603 )
+			_socket.release();
+#endif //Windows 8.1
+		}
+		virtual bool is_busying() override
+		{
+			return _busying;
+		}
+
+	private:
+		service_type::strand _strand;
+		NS_ASIO::ip::tcp::socket _socket;
+		NS_ASIO::steady_timer _timer;
+		bool _busying;
+	};
+
+public:
+
+	template<typename Connection>
+	event_item* add(Connection* connection)
+	{
+		event_item* item = new event_item(_service, connection->socket());
+		_events.push_back(std::unique_ptr<event_item>(item));
+		return item;
+	}
+
+private:
+	service_type _service;
+	std::vector<std::unique_ptr<event_item>> _events;
+};
+
+#if ASIO_VERSION < 101200
+
+template <typename Handler, typename Signature>
+using async_init_type  = NS_ASIO::detail::async_result_init<Handler, Signature>;
+
+template <typename Handler, typename Signature>
+inline typename NS_ASIO::handler_type<Handler, Signature>::type 
+get_async_handler(async_init_type<Handler, Signature>& init)
+{
+	return init.handler;
+}
+
+#else
+
+template <typename Handler, typename Signature>
+using async_init_type = NS_ASIO::async_completion<Handler, Signature>;
+
+template <typename Handler, typename Signature>
+inline typename async_init_type<Handler, Signature>::completion_handler_type 
+get_async_handler(async_init_type<Handler, Signature>& init)
+{
+	return init.completion_handler;
+}
+
+#endif // ASIO_VERSION
+
+template<typename Connection, typename OpenHandler, typename... Args>
+inline ASIO_INITFN_RESULT_TYPE(OpenHandler, void(typename Connection::exception_type)) 
+async_open(service& service, Connection& db, OpenHandler&& handler, Args&&... args)
+{
+	async_init_type<OpenHandler,
+		void(typename Connection::exception_type)> init(std::forward<OpenHandler>(handler));
+	db.open(service, get_async_handler(init), std::forward<Args>(args)...);
+	return init.result.get();
+}
+
+template<typename Connection, typename CloseHandler, typename... Args>
+inline ASIO_INITFN_RESULT_TYPE(CloseHandler, void()) 
+async_close(Connection& db, CloseHandler&& handler, Args&&... args)
+{
+	async_init_type<CloseHandler,
+		void()> init(std::forward<CloseHandler>(handler));
+	db.close(get_async_handler(init), std::forward<Args>(args)...);
+	return init.result.get();
+}
+
+template<typename Connection, typename ExecuteHandler, typename... Args>
+inline ASIO_INITFN_RESULT_TYPE(ExecuteHandler, void(typename Connection::exception_type, uint64_t))  
+async_execute(Connection& db, ExecuteHandler&& handler, Args&&... args)
+{
+	async_init_type<ExecuteHandler,
+		void(typename Connection::exception_type, uint64_t)> init(std::forward<ExecuteHandler>(handler));
+	db.execute(get_async_handler(init), std::forward<Args>(args)...);
+	return init.result.get();
+}
+
+template<typename Connection, typename ExecuteHandler, typename... Args>
+inline ASIO_INITFN_RESULT_TYPE(ExecuteHandler, void(typename Connection::exception_type, uint64_t))  
+async_execute_direct(Connection& db, ExecuteHandler&& handler, Args&&... args)
+{
+	async_init_type<ExecuteHandler,
+		void(typename Connection::exception_type, uint64_t)> init(std::forward<ExecuteHandler>(handler));
+	db.execute_direct(get_async_handler(init), std::forward<Args>(args)...);
+	return init.result.get();
+}
+
+template<typename Connection, typename ExecuteHandler, typename... Args>
+inline ASIO_INITFN_RESULT_TYPE(ExecuteHandler, void(typename Connection::exception_type, uint64_t)) 
+ async_insert(Connection& db, ExecuteHandler&& handler, Args&&... args)
+{
+	async_init_type<ExecuteHandler,
+		void(typename Connection::exception_type, uint64_t)> init(std::forward<ExecuteHandler>(handler));
+	db.insert(get_async_handler(init), std::forward<Args>(args)...);
+	return init.result.get();
+}
+
+template<typename Connection, typename ExecuteHandler, typename... Args>
+inline ASIO_INITFN_RESULT_TYPE(ExecuteHandler, void(typename Connection::exception_type, uint64_t)) 
+ async_insert_direct(Connection& db, ExecuteHandler&& handler, Args&&... args)
+{
+	async_init_type<ExecuteHandler,
+		void(typename Connection::exception_type, uint64_t)> init(std::forward<ExecuteHandler>(handler));
+	db.insert_direct(get_async_handler(init), std::forward<Args>(args)...);
+	return init.result.get();
+}
+
+template<typename Connection, typename FinishHandler, typename... Args>
+inline ASIO_INITFN_RESULT_TYPE(FinishHandler, void(typename Connection::exception_type)) 
+ async_query(Connection& db, FinishHandler&& handler, Args&&... args)
+{
+	async_init_type<FinishHandler,
+		void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
+	db.query(std::forward<Args>(args)..., get_async_handler(init));
+	return init.result.get();
+}
+
+template<typename Connection, typename FinishHandler, typename... Args>
+inline ASIO_INITFN_RESULT_TYPE(FinishHandler, void(typename Connection::exception_type)) 
+async_query_explicit(Connection& db, FinishHandler&& handler, Args&&... args)
+{
+	async_init_type<FinishHandler,
+		void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
+	db.query_explicit(std::forward<Args>(args)..., get_async_handler(init));
+	return init.result.get();
+}
+
+template<typename Connection, typename A1, typename A2, typename FinishHandler, typename... RowHandlers>
+inline ASIO_INITFN_RESULT_TYPE(FinishHandler, void(typename Connection::exception_type))
+async_query_multi_with_params(Connection& db, A1&& a1, A2&& a2, FinishHandler&& handler, RowHandlers&&... row_handlers)
+{
+	async_init_type<FinishHandler,
+		void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
+	db.query_multi_with_params(std::forward<A1>(a1), std::forward<A2>(a2), get_async_handler(init), std::forward<RowHandlers>(row_handlers)...);
+	return init.result.get();
+}
+
+template<typename Connection, typename A1, typename FinishHandler, typename... RowHandlers>
+inline ASIO_INITFN_RESULT_TYPE(FinishHandler, void(typename Connection::exception_type))
+async_query_multi_with_params(Connection& db, A1&& a1, FinishHandler&& handler, RowHandlers&&... row_handlers)
+{
+	async_init_type<FinishHandler,
+		void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
+	db.query_multi_with_params(std::forward<A1>(a1), get_async_handler(init), std::forward<RowHandlers>(row_handlers)...);
+	return init.result.get();
+}
+
+template<typename Connection, typename A1, typename A2, typename FinishHandler, typename... RowHandlers>
+inline ASIO_INITFN_RESULT_TYPE(FinishHandler, void(typename Connection::exception_type))
+async_query_multi(Connection& db, A1&& a1, A2&& a2, FinishHandler&& handler, RowHandlers&&... row_handlers)
+{
+	async_init_type<FinishHandler,
+		void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
+	db.query_multi(std::forward<A1>(a1), std::forward<A2>(a2), get_async_handler(init), std::forward<RowHandlers>(row_handlers)...);
+	return init.result.get();
+}
+
+template<typename Connection, typename A1, typename FinishHandler, typename... RowHandlers>
+inline ASIO_INITFN_RESULT_TYPE(FinishHandler, void(typename Connection::exception_type))
+async_query_multi(Connection& db, A1&& a1, FinishHandler&& handler, RowHandlers&&... row_handlers)
+{
+	async_init_type<FinishHandler,
+		void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
+	db.query_multi(std::forward<A1>(a1), get_async_handler(init), std::forward<RowHandlers>(row_handlers)...);
+	return init.result.get();
+}
+
+}
+
+}
+
+#endif //_QTL_ASIO_H_
diff --git a/include/qtl_database_pool.hpp b/include/qtl_database_pool.hpp
index 81c5f28..4b53dfd 100644
--- a/include/qtl_database_pool.hpp
+++ b/include/qtl_database_pool.hpp
@@ -223,7 +223,7 @@
 	void test_alive()
 	{
 		if (m_connections.empty())
-			return false;
+			return;
 		std::unique_lock<std::mutex> lock(m_pool_mutex);
 		auto it = m_connections.begin();
 		while (it != m_connections.end())
diff --git a/include/qtl_mysql.hpp b/include/qtl_mysql.hpp
index 8bb6e6d..179df88 100644
--- a/include/qtl_mysql.hpp
+++ b/include/qtl_mysql.hpp
@@ -1582,7 +1582,7 @@
 		int status  = mysql_close_start(m_mysql);
 		if (status)
 		{
-			wait_close(status, [this, handler]() {
+			wait_close(status, [this, handler]() mutable {
 				handler();
 				m_mysql = nullptr;
 			});
@@ -1781,7 +1781,7 @@
 	void wait_close(int status, CloseHandler&& handler) NOEXCEPT
 	{
 		m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql),
-			[this, handler](int flags) {
+			[this, handler](int flags) mutable {
 			MYSQL* ret = nullptr;
 			int status = mysql_close_cont(m_mysql, mysql_status(flags));
 			if (status)
@@ -1809,7 +1809,7 @@
 	void wait_query(int status, int field_count, RowHandler&& row_handler, ResultHandler&& result_handler) NOEXCEPT
 	{
 		m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql),
-			[this, field_count, row_handler, result_handler](int flags) {
+			[this, field_count, row_handler, result_handler](int flags) mutable {
 			MYSQL_RES* result = 0;
 			int status = mysql_store_result_cont(&result, m_mysql, mysql_status(flags));
 			if (status)
diff --git a/test/TestMariaDB.cpp b/test/TestMariaDB.cpp
index 8435e6b..c78eda6 100644
--- a/test/TestMariaDB.cpp
+++ b/test/TestMariaDB.cpp
@@ -6,260 +6,11 @@
 #include <time.h>
 #include <limits.h>
 #include "../include/qtl_mysql_pool.hpp"
+#include "../include/qtl_asio.hpp"
 
 #if MARIADB_VERSION_ID < 0100000
 #error "The program need mariadb version > 10.0"
 #endif 
-
-class simple_event_loop
-{
-public:
-	simple_event_loop()
-	{
-		m_expired = 0;
-		m_maxfd = 0;
-		m_stoped = false;
-		FD_ZERO(&m_readset);
-		FD_ZERO(&m_writeset);
-		FD_ZERO(&m_exceptset);
-	}
-
-	void reset()
-	{
-		m_stoped = false;
-	}
-
-	void run()
-	{
-		while (!m_stoped)
-			do_once(nullptr);
-	}
-
-	void run_for(long seconds)
-	{
-		time_t expired, now;
-		time(&now);
-		expired = now + seconds;
-		timeval tv = { seconds };
-		do
-		{
-			do_once(&tv);
-			time(&now);
-			tv.tv_sec = expired - now;
-			tv.tv_usec = 0;
-		} while (!m_stoped && now < expired);
-	}
-
-	void stop()
-	{
-		m_stoped = true;
-	}
-
-private:
-	class event_item : public qtl::event
-	{
-	public:
-		event_item(simple_event_loop* loop, qtl::socket_type fd)
-			: m_loop(loop), m_fd(fd), m_expired(LONG_MAX)
-		{
-		}
-
-		virtual void set_io_handler(int flags, long timeout, std::function<void(int)>&& handler) NOEXCEPT override
-		{
-			if (timeout > 0)
-			{
-				time_t now;
-				time(&now);
-				if (m_expired > now + timeout)
-					m_expired = now + timeout;
-				m_loop->set(this, flags, m_expired);
-			}
-			else
-			{
-				m_expired = LONG_MAX;
-				m_loop->set(this, flags, 0);
-			}
-			m_io_handler = std::forward<std::function<void(int)>>(handler);
-		}
-		virtual void remove() override
-		{
-			m_loop->remove(this);
-		}
-		virtual bool is_busying() override
-		{
-			return m_io_handler!=nullptr;
-		}
-
-		simple_event_loop* m_loop;
-		qtl::socket_type m_fd;
-		time_t m_expired;
-		std::function<void(int)> m_io_handler;
-	};
-	std::vector<std::unique_ptr<event_item>> m_events;
-
-	//implement for QTL
-public:
-	template<typename Connection>
-	event_item* add(Connection* connection)
-	{
-		qtl::socket_type fd = connection->socket();
-		event_item* result = new event_item(this, fd);
-		std::unique_ptr<event_item> item(result);
-		m_events.emplace_back(std::move(item));
-		return result;
-	}
-
-	template<typename Handler>
-	event_item* set_timeout(const timeval& timeout, Handler&& handler)
-	{
-		event_item* result = new event_item(this, 0);
-		std::unique_ptr<event_item> item(result);
-		::time(&result->m_expired);
-		result->m_expired+=timeout.tv_sec;
-		if(m_expired>result->m_expired)
-			m_expired=result->m_expired;
-		m_events.emplace_back(std::move(item));
-		return result;
-	}
-
-public:
-	void set(event_item* item, int flags, time_t expired)
-	{
-		if (item->m_fd + 1 > m_maxfd)
-			m_maxfd = item->m_fd + 1;
-		if (flags&qtl::event::ef_read)
-			FD_SET(item->m_fd, &m_readset);
-		if (flags&qtl::event::ef_write)
-			FD_SET(item->m_fd, &m_writeset);
-		if (flags&qtl::event::ef_exception)
-			FD_SET(item->m_fd, &m_exceptset);
-		if (expired > 0 && m_expired > expired)
-			m_expired = expired;
-	}
-
-	void remove(event_item* item)
-	{
-		auto it = m_events.begin();
-		while (it != m_events.end())
-		{
-			if (it->get() == item)
-			{
-				m_events.erase(it);
-				return;
-			}
-			++it;
-		}
-	}
-
-private:
-	fd_set m_readset, m_writeset, m_exceptset;
-	qtl::socket_type m_maxfd;
-	int do_once(timeval* timeout)
-	{
-		fd_set rs = m_readset, ws = m_writeset, es = m_exceptset;
-		timeval tv = { INT_MAX, 0 };
-		time_t now;
-		time(&now);
-		if (timeout)
-		{
-			tv = *timeout;
-			timeout = &tv;
-		}
-
-		long d = m_expired - now;
-		if (d > 0 && tv.tv_sec > d)
-		{
-			tv.tv_sec = d;
-			timeout = &tv;
-		}
-
-		qtl::socket_type maxfd = m_maxfd;
-		FD_ZERO(&m_readset);
-		FD_ZERO(&m_writeset);
-		FD_ZERO(&m_exceptset);
-		m_maxfd = 0;
-
-		if (maxfd == 0)
-		{
-			if (timeout)
-			{
-				std::this_thread::sleep_for(std::chrono::microseconds(timeout->tv_sec*std::micro::den + timeout->tv_usec));
-				check_timeout();
-			}
-			else
-			{
-				std::this_thread::sleep_for(std::chrono::seconds(1));
-			}
-			return 0;
-		}
-		else
-		{
-			int ret = select(maxfd, &rs, &ws, &es, timeout);
-			if (ret > 0)
-			{
-				for (auto& item : m_events)
-				{
-					int flags = 0;
-					if (FD_ISSET(item->m_fd, &rs))
-						flags |= qtl::event::ef_read;
-					if (FD_ISSET(item->m_fd, &ws))
-						flags |= qtl::event::ef_write;
-					if (FD_ISSET(item->m_fd, &es))
-						flags |= qtl::event::ef_exception;
-					if (flags && item->m_io_handler)
-					{
-						auto handler = std::move(item->m_io_handler);
-						handler(flags);
-					}
-				}
-			}
-			else if (ret == 0)
-			{
-				time(&now);
-				for (auto& item : m_events)
-				{
-					if (item->m_expired > 0 && item->m_io_handler && item->m_expired < now)
-					{
-						item->m_io_handler(qtl::event::ef_timeout);
-						item->m_expired = 0;
-						item->m_io_handler = nullptr;
-					}
-				}
-			}
-			else if (ret < 0)
-			{
-				int errc;
-#ifdef _WIN32
-				errc = WSAGetLastError();
-#else
-				errc = errno;
-#endif
-				throw std::system_error(errc, std::system_category());
-			}
-			return ret;
-		}
-	}
-
-	void check_timeout()
-	{
-		time_t now;
-		time(&now);
-		for (auto& item : m_events)
-		{
-			if (item->m_expired > 0 && item->m_io_handler && item->m_expired < now)
-			{
-				item->m_io_handler(qtl::event::ef_timeout);
-				item->m_expired = 0;
-				item->m_io_handler = nullptr;
-			}
-		}
-	}
-
-	time_t m_expired;
-	bool m_stoped;
-};
-
-simple_event_loop ev;
 
 using namespace qtl::mysql;
 
@@ -270,152 +21,135 @@
 
 const char mysql_server[]="localhost";
 const char mysql_user[]="root";
-const char mysql_password[]="";
+const char mysql_password[]="123456";
 const char mysql_database[]="test";
 
-void SimpleTest()
+class MariaDBTest
 {
-	async_connection connection;
-	ev.reset();
-	connection.open(ev, [&connection](const error& e) {
-		if (e)
-		{
-			LogError(e);
-			ev.stop();
-		}
-		else
-		{
-			printf("Connect to mysql ok.\n");
-			connection.simple_query("select * from test", 0, [](MYSQL_ROW row, int field_count) {
-				for (int i = 0; i != field_count; i++)
-					printf("%s\t", row[i]);
-				printf("\n");
-				return true;
-			}, [&connection](const error& e, size_t row_count) {
-				if (e)
-					LogError(e);
-				else
-					printf("Total %lu rows.\n", row_count);
+public:
+	MariaDBTest()
+	{
+		Open();
+		_service.run();
+	}
 
-				connection.close([]() {
-					printf("Close connection ok.\n");
-					ev.stop();
-				});
-			});
-		}
-	}, mysql_server, mysql_user, mysql_password, mysql_database);
+private:
+	void Open();
+	void Close();
+	void SimpleQuery();
+	void Execute();
+	void Query();
+	void MultiQuery();
 
-	ev.run();
-}
+private:
+	async_connection _connection;
+	qtl::asio::service _service;
 
-void ExecuteTest()
-{
-	async_connection connection;
-	ev.reset();
-	connection.open(ev, [&connection](const error& e) {
-		if (e)
-		{
-			LogError(e);
-			ev.stop();
-		}
-		else
-		{
-			printf("Connect to mysql ok.\n");
-			connection.execute([&connection](const error& e, uint64_t affected) mutable {
-					if (e)
-						LogError(e);
-					else
-						printf("Insert %llu records ok.\n", affected);
-					connection.close([]() {
-						printf("Close connection ok.\n");
-						ev.stop();
-					});
-			}, "insert into test(name, createtime, company) values(?, now(), ?)", 0, std::make_tuple("test name", "test company"));
-		}
-	}, mysql_server, mysql_user, mysql_password, mysql_database);
-
-	ev.run();
 };
 
-void QueryTest()
+void MariaDBTest::Open()
 {
-	async_connection connection;
-	ev.reset();
-	connection.open(ev, [&connection](const error& e) {
+	_connection.open(_service, [this](const error& e) {
 		if (e)
 		{
 			LogError(e);
-			ev.stop();
+			_service.stop();
 		}
 		else
 		{
 			printf("Connect to mysql ok.\n");
-			connection.query("select id, name, CreateTime, Company from test", 0, 
-				[](int64_t id, const std::string& name, const qtl::mysql::time& create_time, const std::string& company) {
-				char szTime[128] = { 0 };
-				if (create_time.year != 0)
-				{
-					struct tm tm;
-					create_time.as_tm(tm);
-					strftime(szTime, 128, "%c", &tm);
-				}
-				printf("%lld\t%s\t%s\t%s\n", id, name.data(), szTime, company.data());
-			}, [&connection](const error& e) {
-				printf("query has completed.\n");
-				if (e)
-					LogError(e);
-
-				connection.close([]() {
-					printf("Close connection ok.\n");
-					ev.stop();
-				});
-			});
+			SimpleQuery();
 		}
 	}, mysql_server, mysql_user, mysql_password, mysql_database);
-
-	ev.run();
 }
 
-void MultiQueryTest()
+void MariaDBTest::Close()
 {
-	async_connection connection;
-	ev.reset();
-	connection.open(ev, [&connection](const error& e) {
+	_connection.close([this]() {
+		printf("Connection is closed.\n");
+		_service.stop();
+	});
+}
+
+void MariaDBTest::SimpleQuery()
+{
+	_connection.simple_query("select * from test", 0, [](MYSQL_ROW row, int field_count) {
+		for (int i = 0; i != field_count; i++)
+			printf("%s\t", row[i]);
+		printf("\n");
+		return true;
+	}, [this](const error& e, size_t row_count) {
 		if (e)
 		{
 			LogError(e);
-			ev.stop();
 		}
 		else
 		{
-			printf("Connect to mysql ok.\n");
-			connection.query_multi("call test_proc",
-				[&connection](const error& e) {
-				if (e)
-					LogError(e);
-
-				connection.close([]() {
-					printf("Close connection ok.\n");
-					ev.stop();
-				});
-			}, [](uint32_t i, const std::string& str) {
-				printf("0=\"%d\", 'hello world'=\"%s\"\n", i, str.data());
-			}, [](const qtl::mysql::time& time) {
-				struct tm tm;
-				time.as_tm(tm);
-				printf("current time is: %s\n", asctime(&tm));
-			});
+			printf("Total %lu rows.\n", row_count);
+			Execute();
 		}
-	}, mysql_server, mysql_user, mysql_password, mysql_database);
+	});
+}
 
-	ev.run();
+void MariaDBTest::Execute()
+{
+	_connection.simple_query("select * from test", 0, [](MYSQL_ROW row, int field_count) {
+		for (int i = 0; i != field_count; i++)
+			printf("%s\t", row[i]);
+		printf("\n");
+		return true;
+	}, [this](const error& e, size_t row_count) {
+		if (e)
+			LogError(e);
+		else
+		{
+			printf("Total %lu rows.\n", row_count);
+			Query();
+		}
+	});
+}
+
+void MariaDBTest::Query()
+{
+	_connection.query("select id, name, CreateTime, Company from test", 0, 
+		[](int64_t id, const std::string& name, const qtl::mysql::time& create_time, const std::string& company) {
+			char szTime[128] = { 0 };
+			if (create_time.year != 0)
+			{
+				struct tm tm;
+				create_time.as_tm(tm);
+				strftime(szTime, 128, "%c", &tm);
+			}
+			printf("%lld\t%s\t%s\t%s\n", id, name.data(), szTime, company.data());
+	}, [this](const error& e) {
+		printf("query has completed.\n");
+		if (e)
+			LogError(e);
+		else
+			MultiQuery();
+	});
+}
+
+void MariaDBTest::MultiQuery()
+{
+	_connection.query_multi("call test_proc",
+		[this](const error& e) {
+			if (e)
+				LogError(e);
+			else
+				Close();
+			
+	}, [](uint32_t i, const std::string& str) {
+		printf("0=\"%d\", 'hello world'=\"%s\"\n", i, str.data());
+	}, [](const qtl::mysql::time& time) {
+		struct tm tm;
+		time.as_tm(tm);
+		printf("current time is: %s\n", asctime(&tm));
+	});
 }
 
 int main(int argc, char* argv[])
 {
-	ExecuteTest();
-	SimpleTest();
-	QueryTest();
-	MultiQueryTest();
+	MariaDBTest test;
 	return 0;
 }
diff --git a/test/test_mariadb.mak b/test/test_mariadb.mak
index a0314d9..ee0f245 100644
--- a/test/test_mariadb.mak
+++ b/test/test_mariadb.mak
@@ -3,7 +3,7 @@
 PCH_HEADER=stdafx.h
 PCH=stdafx.h.gch
 OBJ=TestMariaDB.o
-CFLAGS=-g -D_DEBUG -O2 -I/usr/include/mariadb -I/usr/local/include 
+CFLAGS=-g -D_DEBUG -O2 -I/usr/include -I/usr/include/mariadb -I/usr/local/include -I/usr/local/include/mariadb 
 CXXFLAGS=-I../include -std=c++11
 LDFLAGS= -L/usr/local/lib -L/usr/local/mariadb/lib -lmariadb
 

--
Gitblit v1.9.3