znone
2020-10-02 0d3e994e33aa85965616a064bbf635ec8d043c1a
The database can be operated asynchronously through asio.
6 files modified
1 files added
780 ■■■■ changed files
README.md 1 ●●●● patch | view | raw | blame | history
README_CN.md 1 ●●●● patch | view | raw | blame | history
include/qtl_asio.hpp 306 ●●●●● patch | view | raw | blame | history
include/qtl_database_pool.hpp 2 ●●● patch | view | raw | blame | history
include/qtl_mysql.hpp 6 ●●●● patch | view | raw | blame | history
test/TestMariaDB.cpp 462 ●●●● patch | view | raw | blame | history
test/test_mariadb.mak 2 ●●● patch | view | raw | blame | history
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
README_CN.md
@@ -224,6 +224,7 @@
```
数据库连接通常不是线程安全的。用户代码应该保证,一个连接只能同时由一个线程使用。
使用这项功能,GCC需要 5 或更高版本才行。
## 有关MySQL的说明
include/qtl_asio.hpp
New file
@@ -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_
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())
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)
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;
}
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