Ferenc Szontágh
2024-07-01 abf49b44cc47f39d6cceb83866f915bc03b7d900
reformat files
13 files modified
19198 ■■■■ changed files
include/apply_tuple.h 82 ●●●● patch | view | raw | blame | history
include/qtl_asio.hpp 549 ●●●● patch | view | raw | blame | history
include/qtl_async.hpp 560 ●●●● patch | view | raw | blame | history
include/qtl_common.hpp 2655 ●●●● patch | view | raw | blame | history
include/qtl_database_pool.hpp 494 ●●●● patch | view | raw | blame | history
include/qtl_mysql.hpp 3559 ●●●● patch | view | raw | blame | history
include/qtl_mysql_pool.hpp 109 ●●●● patch | view | raw | blame | history
include/qtl_odbc.hpp 3479 ●●●● patch | view | raw | blame | history
include/qtl_odbc_pool.hpp 105 ●●●● patch | view | raw | blame | history
include/qtl_postgres.hpp 5347 ●●●● patch | view | raw | blame | history
include/qtl_postgres_pool.hpp 95 ●●●● patch | view | raw | blame | history
include/qtl_sqlite.hpp 2111 ●●●● patch | view | raw | blame | history
include/qtl_sqlite_pool.hpp 53 ●●●● patch | view | raw | blame | history
include/apply_tuple.h
@@ -9,62 +9,58 @@
namespace qtl
{
namespace detail
{
    namespace detail
    {
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
template <class F, class Tuple>
inline constexpr decltype(auto) apply_tuple(F&& f, Tuple&& t)
{
    return std::apply(std::forward<F>(f), std::forward<Tuple>(t));
}
        template <class F, class Tuple>
        inline constexpr decltype(auto) apply_tuple(F &&f, Tuple &&t)
        {
            return std::apply(std::forward<F>(f), std::forward<Tuple>(t));
        }
#else
namespace detail
{
    template<size_t N>
    struct apply
    {
        template<typename F, typename T, typename... A>
        static inline auto apply_tuple(F&& f, T&& t, A&&... a)
            -> decltype(apply<N-1>::apply_tuple(
            std::forward<F>(f), std::forward<T>(t),
            std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...
            ))
        namespace detail
        {
            return apply<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t),
                std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...
                );
        }
    };
            template <size_t N>
            struct apply
            {
                template <typename F, typename T, typename... A>
                static inline auto apply_tuple(F &&f, T &&t, A &&...a)
                    -> decltype(apply<N - 1>::apply_tuple(
                        std::forward<F>(f), std::forward<T>(t),
                        std::get<N - 1>(std::forward<T>(t)), std::forward<A>(a)...))
                {
                    return apply<N - 1>::apply_tuple(std::forward<F>(f), std::forward<T>(t),
                                                     std::get<N - 1>(std::forward<T>(t)), std::forward<A>(a)...);
                }
            };
    template<>
    struct apply<0>
    {
        template<typename F, typename T, typename... A>
        static inline typename std::result_of<F(A...)>::type apply_tuple(F&& f, T&&, A&&... a)
            template <>
            struct apply<0>
            {
                template <typename F, typename T, typename... A>
                static inline typename std::result_of<F(A...)>::type apply_tuple(F &&f, T &&, A &&...a)
                {
                    return std::forward<F>(f)(std::forward<A>(a)...);
                }
            };
        }
        template <typename F, typename T>
        inline auto apply_tuple(F &&f, T &&t)
            -> decltype(detail::apply<std::tuple_size<
                            typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t)))
        {
            return std::forward<F>(f)(std::forward<A>(a)...);
            return detail::apply<std::tuple_size<
                typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t));
        }
    };
}
template<typename F, typename T>
inline auto apply_tuple(F&& f, T&& t)
    -> decltype(detail::apply< std::tuple_size<
        typename std::decay<T>::type
    >::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t)))
{
    return detail::apply< std::tuple_size<
        typename std::decay<T>::type
    >::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t));
}
#endif // C++17
}
    }
}
include/qtl_asio.hpp
@@ -4,8 +4,8 @@
#include "qtl_async.hpp"
#include <asio/version.hpp>
#define ASIO_STANDALONE
#if defined(_MSC_VER) && _WIN32_WINNT<0x0600
#define ASIO_ENABLE_CANCELIO 1
#if defined(_MSC_VER) && _WIN32_WINNT < 0x0600
#define ASIO_ENABLE_CANCELIO 1
#endif
#if ASIO_VERSION < 101200
#include <asio/io_service.hpp>
@@ -18,67 +18,68 @@
#include <asio/steady_timer.hpp>
#if ASIO_VERSION < 100000
#error The asio version required by QTL is at least 10.0
#error The asio version required by QTL is at least 10.0
#endif
namespace qtl
{
namespace NS_ASIO = ::asio;
    namespace NS_ASIO = ::asio;
namespace asio
{
    namespace asio
    {
class service
{
public:
        class service
        {
        public:
#if ASIO_VERSION < 101200
    typedef NS_ASIO::io_service service_type;
            typedef NS_ASIO::io_service service_type;
#else
    typedef NS_ASIO::io_context service_type;
            typedef NS_ASIO::io_context service_type;
#endif // ASIO_VERSION
    service() NOEXCEPT { }
    explicit service(int concurrency_hint) : _service(concurrency_hint) { }
            service() NOEXCEPT {}
            explicit service(int concurrency_hint) : _service(concurrency_hint) {}
    void reset()
    {
        _service.reset();
    }
    void run()
    {
        _service.run();
    }
    void stop()
    {
        _service.stop();
    }
    service_type& context() NOEXCEPT { return _service; }
private:
    class event_item : public qtl::event
    {
    public:
        event_item(service& service, qtl::socket_type fd)
            : _service(service), _strand(service.context()), _socket(service.context(), NS_ASIO::ip::tcp::v4(), fd), _timer(service.context()), _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)
            void reset()
            {
                _service.reset();
            }
            void run()
            {
                _service.run();
            }
            void stop()
            {
                _service.stop();
            }
            service_type &context() NOEXCEPT { return _service; }
        private:
            class event_item : public qtl::event
            {
            public:
                event_item(service &service, qtl::socket_type fd)
                    : _service(service), _strand(service.context()), _socket(service.context(), NS_ASIO::ip::tcp::v4(), fd), _timer(service.context()), _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) {
                        _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) {
                        _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);
@@ -87,17 +88,18 @@
                    else
                        handler(qtl::event::ef_exception);
                    _busying = false;
                    _timer.cancel();
                }));
                _busying = true;
            }
            if (flags&qtl::event::ef_write)
            {
                    _timer.cancel(); }));
                        _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) {
                        _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
                        _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))
@@ -105,292 +107,315 @@
                    else
                        handler(qtl::event::ef_exception);
                    _timer.cancel();
                    _busying = false;
                }));
                _busying = true;
            }
            if (timeout > 0)
            {
                    _busying = false; }));
                        _busying = true;
                    }
                    if (timeout > 0)
                    {
#if ASIO_VERSION < 101200
                _timer.expires_from_now(std::chrono::seconds(timeout));
                        _timer.expires_from_now(std::chrono::seconds(timeout));
#else
                _timer.expires_after(NS_ASIO::chrono::seconds(timeout));
                        _timer.expires_after(NS_ASIO::chrono::seconds(timeout));
#endif // ASIO_VERSION
                _timer.async_wait(_strand.wrap([this, handler](NS_ASIO::error_code ec) {
                        _timer.async_wait(_strand.wrap([this, handler](NS_ASIO::error_code ec)
                                                       {
                    if (!ec)
                    {
                        _socket.cancel(ec);
                    } }));
                    }
                }));
                }
                virtual void remove() override
                {
                    if (_busying)
                        return;
#if ASIO_VERSION >= 101200 && (!defined(_WIN32) || _WIN32_WINNT >= 0x0603)
                    _socket.release();
#endif // Windows 8.1
                    _service.remove(this);
                }
                virtual bool is_busying() override
                {
                    return _busying;
                }
            private:
                service &_service;
                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(*this, connection->socket());
                std::lock_guard<std::mutex> lock(_mutex);
                _events.push_back(std::unique_ptr<event_item>(item));
                return item;
            }
        }
        virtual void remove() override
        {
            if (_busying) return;
#if ASIO_VERSION >= 101200 && (!defined(_WIN32) || _WIN32_WINNT >= 0x0603 )
            _socket.release();
#endif //Windows 8.1
            _service.remove(this);
        }
        virtual bool is_busying() override
        {
            return _busying;
        }
        private:
            service_type _service;
            std::mutex _mutex;
            std::vector<std::unique_ptr<event_item>> _events;
    private:
        service& _service;
        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(*this, connection->socket());
        std::lock_guard<std::mutex> lock(_mutex);
        _events.push_back(std::unique_ptr<event_item>(item));
        return item;
    }
private:
    service_type _service;
    std::mutex _mutex;
    std::vector<std::unique_ptr<event_item>> _events;
    void remove(event_item* item)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        auto it = std::find_if(_events.begin(), _events.end(), [item](std::unique_ptr<event_item>& v) {
            return item==v.get();
        });
        if (it != _events.end()) _events.erase(it);
    }
};
            void remove(event_item *item)
            {
                std::lock_guard<std::mutex> lock(_mutex);
                auto it = std::find_if(_events.begin(), _events.end(), [item](std::unique_ptr<event_item> &v)
                                       { return item == v.get(); });
                if (it != _events.end())
                    _events.erase(it);
            }
        };
#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>
        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;
}
        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>
        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;
}
        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)
{
        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)
        {
#if ASIO_VERSION < 101200
    async_init_type<OpenHandler,
        void(typename Connection::exception_type)> init(std::forward<OpenHandler>(handler));
            async_init_type<OpenHandler,
                            void(typename Connection::exception_type)>
                init(std::forward<OpenHandler>(handler));
#else
    async_init_type<OpenHandler,
        void(typename Connection::exception_type)> init(handler);
            async_init_type<OpenHandler,
                            void(typename Connection::exception_type)>
                init(handler);
#endif
    db.open(service, get_async_handler(init), std::forward<Args>(args)...);
    return init.result.get();
}
            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)
{
        template <typename Connection, typename CloseHandler, typename... Args>
        inline ASIO_INITFN_RESULT_TYPE(CloseHandler, void())
            async_close(Connection &db, CloseHandler &&handler, Args &&...args)
        {
#if ASIO_VERSION < 101200
    async_init_type<CloseHandler,
        void()> init(std::forward<CloseHandler>(std::forward<CloseHandler>(handler)));
            async_init_type<CloseHandler,
                            void()>
                init(std::forward<CloseHandler>(std::forward<CloseHandler>(handler)));
#else
    async_init_type<CloseHandler,
        void()> init(std::forward<CloseHandler>(handler));
            async_init_type<CloseHandler,
                            void()>
                init(std::forward<CloseHandler>(handler));
#endif
    db.close(get_async_handler(init), std::forward<Args>(args)...);
    return init.result.get();
}
            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)
{
        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)
        {
#if ASIO_VERSION < 101200
    async_init_type<ExecuteHandler,
        void(typename Connection::exception_type, uint64_t)> init(std::forward<ExecuteHandler>(handler));
            async_init_type<ExecuteHandler,
                            void(typename Connection::exception_type, uint64_t)>
                init(std::forward<ExecuteHandler>(handler));
#else
    async_init_type<ExecuteHandler,
        void(typename Connection::exception_type, uint64_t)> init(handler);
            async_init_type<ExecuteHandler,
                            void(typename Connection::exception_type, uint64_t)>
                init(handler);
#endif
    db.execute(get_async_handler(init), std::forward<Args>(args)...);
    return init.result.get();
}
            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)
{
        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)
        {
#if ASIO_VERSION < 101200
    async_init_type<ExecuteHandler,
        void(typename Connection::exception_type, uint64_t)> init(std::forward<ExecuteHandler>(handler));
            async_init_type<ExecuteHandler,
                            void(typename Connection::exception_type, uint64_t)>
                init(std::forward<ExecuteHandler>(handler));
#else
    async_init_type<ExecuteHandler,
        void(typename Connection::exception_type, uint64_t)> init(handler);
            async_init_type<ExecuteHandler,
                            void(typename Connection::exception_type, uint64_t)>
                init(handler);
#endif
    db.execute_direct(get_async_handler(init), std::forward<Args>(args)...);
    return init.result.get();
}
            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)
{
        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)
        {
#if ASIO_VERSION < 101200
    async_init_type<ExecuteHandler,
        void(typename Connection::exception_type, uint64_t)> init(std::forward<ExecuteHandler>(handler));
            async_init_type<ExecuteHandler,
                            void(typename Connection::exception_type, uint64_t)>
                init(std::forward<ExecuteHandler>(handler));
#else
    async_init_type<ExecuteHandler,
        void(typename Connection::exception_type, uint64_t)> init(handler);
            async_init_type<ExecuteHandler,
                            void(typename Connection::exception_type, uint64_t)>
                init(handler);
#endif
    db.insert(get_async_handler(init), std::forward<Args>(args)...);
    return init.result.get();
}
            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)
{
        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)
        {
#if ASIO_VERSION < 101200
    async_init_type<ExecuteHandler,
        void(typename Connection::exception_type, uint64_t)> init(std::forward<ExecuteHandler>(handler));
            async_init_type<ExecuteHandler,
                            void(typename Connection::exception_type, uint64_t)>
                init(std::forward<ExecuteHandler>(handler));
#else
    async_init_type<ExecuteHandler,
        void(typename Connection::exception_type, uint64_t)> init(handler);
            async_init_type<ExecuteHandler,
                            void(typename Connection::exception_type, uint64_t)>
                init(handler);
#endif
    db.insert_direct(get_async_handler(init), std::forward<Args>(args)...);
    return init.result.get();
}
            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)
{
        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)
        {
#if ASIO_VERSION < 101200
    async_init_type<FinishHandler,
        void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
            async_init_type<FinishHandler,
                            void(typename Connection::exception_type)>
                init(std::forward<FinishHandler>(handler));
#else
    async_init_type<FinishHandler,
        void(typename Connection::exception_type)> init(handler);
            async_init_type<FinishHandler,
                            void(typename Connection::exception_type)>
                init(handler);
#endif
    db.query(std::forward<Args>(args)..., get_async_handler(init));
    return init.result.get();
}
            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)
{
        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)
        {
#if ASIO_VERSION < 101200
    async_init_type<FinishHandler,
        void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
            async_init_type<FinishHandler,
                            void(typename Connection::exception_type)>
                init(std::forward<FinishHandler>(handler));
#else
    async_init_type<FinishHandler,
        void(typename Connection::exception_type)> init(handler);
            async_init_type<FinishHandler,
                            void(typename Connection::exception_type)>
                init(handler);
#endif
    db.query_explicit(std::forward<Args>(args)..., get_async_handler(init));
    return init.result.get();
}
            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)
{
        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)
        {
#if ASIO_VERSION < 101200
    async_init_type<FinishHandler,
        void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
            async_init_type<FinishHandler,
                            void(typename Connection::exception_type)>
                init(std::forward<FinishHandler>(handler));
#else
    async_init_type<FinishHandler,
        void(typename Connection::exception_type)> init(handler);
            async_init_type<FinishHandler,
                            void(typename Connection::exception_type)>
                init(handler);
#endif
    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();
}
            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)
{
        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)
        {
#if ASIO_VERSION < 101200
    async_init_type<FinishHandler,
        void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
            async_init_type<FinishHandler,
                            void(typename Connection::exception_type)>
                init(std::forward<FinishHandler>(handler));
#else
    async_init_type<FinishHandler,
        void(typename Connection::exception_type)> init(handler);
            async_init_type<FinishHandler,
                            void(typename Connection::exception_type)>
                init(handler);
#endif
    db.query_multi_with_params(std::forward<A1>(a1), get_async_handler(init), std::forward<RowHandlers>(row_handlers)...);
    return init.result.get();
}
            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)
{
        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)
        {
#if ASIO_VERSION < 101200
    async_init_type<FinishHandler,
        void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
            async_init_type<FinishHandler,
                            void(typename Connection::exception_type)>
                init(std::forward<FinishHandler>(handler));
#else
    async_init_type<FinishHandler,
        void(typename Connection::exception_type)> init(handler);
            async_init_type<FinishHandler,
                            void(typename Connection::exception_type)>
                init(handler);
#endif
    db.query_multi(std::forward<A1>(a1), std::forward<A2>(a2), get_async_handler(init), std::forward<RowHandlers>(row_handlers)...);
    return init.result.get();
}
            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)
{
        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)
        {
#if ASIO_VERSION < 101200
    async_init_type<FinishHandler,
        void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
            async_init_type<FinishHandler,
                            void(typename Connection::exception_type)>
                init(std::forward<FinishHandler>(handler));
#else
    async_init_type<FinishHandler,
        void(typename Connection::exception_type)> init(handler);
            async_init_type<FinishHandler,
                            void(typename Connection::exception_type)>
                init(handler);
#endif
    db.query_multi(std::forward<A1>(a1), get_async_handler(init), std::forward<RowHandlers>(row_handlers)...);
    return init.result.get();
}
            db.query_multi(std::forward<A1>(a1), get_async_handler(init), std::forward<RowHandlers>(row_handlers)...);
            return init.result.get();
        }
}
    }
}
include/qtl_async.hpp
@@ -13,26 +13,26 @@
    typedef SOCKET socket_type;
#else
    typedef int socket_type;
#endif
#endif
namespace detail
{
template<typename Values, typename RowHandler, typename FinishHandler>
struct async_fetch_helper : public std::enable_shared_from_this<async_fetch_helper<Values, RowHandler, FinishHandler>>
{
    async_fetch_helper(const Values& values, const RowHandler& row_handler, const FinishHandler& finish_handler)
        : m_values(values), m_row_handler(row_handler), m_finish_handler(finish_handler), m_auto_close_command(true)
    namespace detail
    {
    }
    template<typename Command, typename Exception>
    void start(const std::shared_ptr<Command>& command)
    {
        auto self = this->shared_from_this();
        command->fetch(std::forward<Values>(m_values), [this, command]() {
            return qtl::detail::apply<RowHandler, Values>(std::forward<RowHandler>(m_row_handler), std::forward<Values>(m_values));
        }, [self, command](const Exception& e) {
        template <typename Values, typename RowHandler, typename FinishHandler>
        struct async_fetch_helper : public std::enable_shared_from_this<async_fetch_helper<Values, RowHandler, FinishHandler>>
        {
            async_fetch_helper(const Values &values, const RowHandler &row_handler, const FinishHandler &finish_handler)
                : m_values(values), m_row_handler(row_handler), m_finish_handler(finish_handler), m_auto_close_command(true)
            {
            }
            template <typename Command, typename Exception>
            void start(const std::shared_ptr<Command> &command)
            {
                auto self = this->shared_from_this();
                command->fetch(std::forward<Values>(m_values), [this, command]()
                               { return qtl::detail::apply<RowHandler, Values>(std::forward<RowHandler>(m_row_handler), std::forward<Values>(m_values)); }, [self, command](const Exception &e)
                               {
            if (e || self->m_auto_close_command)
            {
                command->close([self, command, e](const Exception& new_e) {
@@ -42,43 +42,42 @@
            else 
            {
                self->m_finish_handler(e);
            } });
            }
        });
    }
    void auto_close_command(bool auto_close)
    {
        m_auto_close_command = auto_close;
    }
private:
    Values m_values;
    RowHandler m_row_handler;
    FinishHandler m_finish_handler;
    bool m_auto_close_command;
};
            void auto_close_command(bool auto_close)
            {
                m_auto_close_command = auto_close;
            }
template<typename Values, typename RowHandler, typename FinishHandler>
inline std::shared_ptr<async_fetch_helper<Values, RowHandler, FinishHandler>> make_fetch_helper(const Values& values, const RowHandler& row_handler, const FinishHandler& cpmplete_handler)
{
    return std::make_shared<async_fetch_helper<Values, RowHandler, FinishHandler>>(values, row_handler, cpmplete_handler);
}
        private:
            Values m_values;
            RowHandler m_row_handler;
            FinishHandler m_finish_handler;
            bool m_auto_close_command;
        };
        template <typename Values, typename RowHandler, typename FinishHandler>
        inline std::shared_ptr<async_fetch_helper<Values, RowHandler, FinishHandler>> make_fetch_helper(const Values &values, const RowHandler &row_handler, const FinishHandler &cpmplete_handler)
        {
            return std::make_shared<async_fetch_helper<Values, RowHandler, FinishHandler>>(values, row_handler, cpmplete_handler);
        }
template<typename Exception, typename Command, typename RowHandler, typename FinishHandler>
inline void async_fetch_command(const std::shared_ptr<Command>& command, FinishHandler&& finish_handler, RowHandler&& row_handler)
{
    auto values=make_values(row_handler);
    typedef decltype(values) values_type;
    auto helper = make_fetch_helper(std::forward<values_type>(values), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    helper->auto_close_command(false);
    helper->template start<Command, Exception>(command);
}
        template <typename Exception, typename Command, typename RowHandler, typename FinishHandler>
        inline void async_fetch_command(const std::shared_ptr<Command> &command, FinishHandler &&finish_handler, RowHandler &&row_handler)
        {
            auto values = make_values(row_handler);
            typedef decltype(values) values_type;
            auto helper = make_fetch_helper(std::forward<values_type>(values), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
            helper->auto_close_command(false);
            helper->template start<Command, Exception>(command);
        }
template<typename Exception, typename Command, typename RowHandler, typename FinishHandler, typename... OtherHandler>
inline void async_fetch_command(const std::shared_ptr<Command>& command, FinishHandler&& finish_handler, RowHandler&& row_handler, OtherHandler&&... other)
{
    async_fetch_command<Exception>(command, [command, finish_handler, other...](const Exception& e) mutable {
        template <typename Exception, typename Command, typename RowHandler, typename FinishHandler, typename... OtherHandler>
        inline void async_fetch_command(const std::shared_ptr<Command> &command, FinishHandler &&finish_handler, RowHandler &&row_handler, OtherHandler &&...other)
        {
            async_fetch_command<Exception>(command, [command, finish_handler, other...](const Exception &e) mutable
                                           {
        if (e)
        {
            finish_handler(e);
@@ -91,76 +90,76 @@
                else
                    async_fetch_command<Exception>(command, std::forward<FinishHandler>(finish_handler), std::forward<OtherHandler>(other)...);
            });
        } }, std::forward<RowHandler>(row_handler));
        }
    }, std::forward<RowHandler>(row_handler));
}
}
    }
struct event
{
    enum io_flags
    struct event
    {
        ef_read = 0x1,
        ef_write = 0x2,
        ef_exception = 0x4,
        ef_timeout =0x8,
        ev_all = ef_read | ef_write | ef_exception
        enum io_flags
        {
            ef_read = 0x1,
            ef_write = 0x2,
            ef_exception = 0x4,
            ef_timeout = 0x8,
            ev_all = ef_read | ef_write | ef_exception
        };
        virtual ~event() {}
        virtual void set_io_handler(int flags, long timeout, std::function<void(int)> &&) = 0;
        virtual void remove() = 0;
        virtual bool is_busying() = 0;
    };
    virtual ~event() { }
    virtual void set_io_handler(int flags, long timeout, std::function<void(int)>&&) = 0;
    virtual void remove() = 0;
    virtual bool is_busying() = 0;
};
template<typename T, class Command>
class async_connection
{
public:
    template<typename EventLoop>
    bool bind(EventLoop& ev)
    template <typename T, class Command>
    class async_connection
    {
        T* pThis = static_cast<T*>(this);
        if(m_event_handler)
    public:
        template <typename EventLoop>
        bool bind(EventLoop &ev)
        {
            if(m_event_handler->is_busying())
                return false;
            unbind();
            T *pThis = static_cast<T *>(this);
            if (m_event_handler)
            {
                if (m_event_handler->is_busying())
                    return false;
                unbind();
            }
            m_event_handler = ev.add(pThis);
            return m_event_handler != nullptr;
        }
        m_event_handler = ev.add(pThis);
        return m_event_handler!=nullptr;
    }
    qtl::event* event() const
    {
        return m_event_handler;
    }
    bool unbind()
    {
        if(m_event_handler)
        qtl::event *event() const
        {
            if(m_event_handler->is_busying())
                return false;
            m_event_handler->remove();
            m_event_handler=nullptr;
            return m_event_handler;
        }
        return true;
    }
    /*
        ResultHandler defines as:
            void handler(const exception_type& e, uint64_t affected=0);
        Copies will be made of the handler as required.
        If an error occurred, value of affected is undefined.
        Note: parameter affected must has default value.
    */
    template<typename Params, typename ResultHandler>
    void execute(ResultHandler handler, const char* query_text, size_t text_length, const Params& params)
    {
        T* pThis = static_cast<T*>(this);
        pThis->open_command(query_text, text_length, [handler, params](const typename T::exception_type& e, std::shared_ptr<Command>& command) mutable {
        bool unbind()
        {
            if (m_event_handler)
            {
                if (m_event_handler->is_busying())
                    return false;
                m_event_handler->remove();
                m_event_handler = nullptr;
            }
            return true;
        }
        /*
            ResultHandler defines as:
                void handler(const exception_type& e, uint64_t affected=0);
            Copies will be made of the handler as required.
            If an error occurred, value of affected is undefined.
            Note: parameter affected must has default value.
        */
        template <typename Params, typename ResultHandler>
        void execute(ResultHandler handler, const char *query_text, size_t text_length, const Params &params)
        {
            T *pThis = static_cast<T *>(this);
            pThis->open_command(query_text, text_length, [handler, params](const typename T::exception_type &e, std::shared_ptr<Command> &command) mutable
                                {
            if(e)
            {
                command->close([command, e, handler](const typename T::exception_type& ae) mutable {
@@ -172,49 +171,49 @@
                command->close([command, handler, e, affected](const typename T::exception_type& ae) mutable {
                    handler(e ? e : ae, affected);
                });
            });
        });
    }
    template<typename Params, typename ResultHandler>
    void execute(ResultHandler handler, const char* query_text, const Params& params)
    {
        return execute(std::forward<ResultHandler>(handler), query_text, strlen(query_text), params);
    }
    template<typename Params, typename ResultHandler>
    void execute(ResultHandler handler, const std::string& query_text, const Params& params)
    {
        return execute(std::forward<ResultHandler>(handler), query_text.data(), query_text.length(), params);
    }
            }); });
        }
        template <typename Params, typename ResultHandler>
        void execute(ResultHandler handler, const char *query_text, const Params &params)
        {
            return execute(std::forward<ResultHandler>(handler), query_text, strlen(query_text), params);
        }
        template <typename Params, typename ResultHandler>
        void execute(ResultHandler handler, const std::string &query_text, const Params &params)
        {
            return execute(std::forward<ResultHandler>(handler), query_text.data(), query_text.length(), params);
        }
    template<typename... Params, typename ResultHandler>
    void execute_direct(ResultHandler handler, const char* query_text, size_t text_length, const Params&... params)
    {
        execute(std::forward<ResultHandler>(handler), query_text, text_length, std::forward_as_tuple(params...));
    }
    template<typename... Params, typename ResultHandler>
    void execute_direct(ResultHandler handler, const char* query_text, const Params&... params)
    {
        execute(std::forward<ResultHandler>(handler), query_text, std::forward_as_tuple(params...));
    }
    template<typename... Params, typename ResultHandler>
    void execute_direct(ResultHandler handler, const std::string& query_text, const Params&... params)
    {
        execute(std::forward<ResultHandler>(handler), query_text, query_text, std::forward_as_tuple(params...));
    }
        template <typename... Params, typename ResultHandler>
        void execute_direct(ResultHandler handler, const char *query_text, size_t text_length, const Params &...params)
        {
            execute(std::forward<ResultHandler>(handler), query_text, text_length, std::forward_as_tuple(params...));
        }
        template <typename... Params, typename ResultHandler>
        void execute_direct(ResultHandler handler, const char *query_text, const Params &...params)
        {
            execute(std::forward<ResultHandler>(handler), query_text, std::forward_as_tuple(params...));
        }
        template <typename... Params, typename ResultHandler>
        void execute_direct(ResultHandler handler, const std::string &query_text, const Params &...params)
        {
            execute(std::forward<ResultHandler>(handler), query_text, query_text, std::forward_as_tuple(params...));
        }
    /*
        ResultHandler defines as:
            void handler(const exception_type& e, uint64_t insert_id=0);
        Copies will be made of the handler as required.
        If an error occurred, value of insert_id is undefined.
        If the command is not insert statement, value of insert_id is zero.
        Note: parameter insert_id must has default value.
    */
    template<typename Params, typename ResultHandler>
    void insert(ResultHandler handler, const char* query_text, size_t text_length, const Params& params)
    {
        T* pThis = static_cast<T*>(this);
        pThis->open_command(query_text, text_length, [handler, params](const typename T::exception_type& e, std::shared_ptr<Command>& command) {
        /*
            ResultHandler defines as:
                void handler(const exception_type& e, uint64_t insert_id=0);
            Copies will be made of the handler as required.
            If an error occurred, value of insert_id is undefined.
            If the command is not insert statement, value of insert_id is zero.
            Note: parameter insert_id must has default value.
        */
        template <typename Params, typename ResultHandler>
        void insert(ResultHandler handler, const char *query_text, size_t text_length, const Params &params)
        {
            T *pThis = static_cast<T *>(this);
            pThis->open_command(query_text, text_length, [handler, params](const typename T::exception_type &e, std::shared_ptr<Command> &command)
                                {
            if(e)
            {
                command->close([command, e, handler](const typename T::exception_type& ae) mutable {
@@ -231,53 +230,53 @@
                        handler(e ? e : ae, insert_id);
                    });
                });
            }
        });
    }
            } });
        }
    template<typename Params, typename ResultHandler>
    void insert(ResultHandler&& handler, const char* query_text, const Params& params)
    {
        insert(std::forward<ResultHandler>(handler), query_text, strlen(query_text), params);
    }
        template <typename Params, typename ResultHandler>
        void insert(ResultHandler &&handler, const char *query_text, const Params &params)
        {
            insert(std::forward<ResultHandler>(handler), query_text, strlen(query_text), params);
        }
    template<typename Params, typename ResultHandler>
    void insert(ResultHandler&& handler, const std::string& query_text, const Params& params)
    {
        insert(std::forward<ResultHandler>(handler), query_text.data(), query_text.length(), params);
    }
        template <typename Params, typename ResultHandler>
        void insert(ResultHandler &&handler, const std::string &query_text, const Params &params)
        {
            insert(std::forward<ResultHandler>(handler), query_text.data(), query_text.length(), params);
        }
    template<typename... Params, typename ResultHandler>
    void insert_direct(ResultHandler&& handler, const char* query_text, size_t text_length, const Params&... params)
    {
        insert(std::forward<ResultHandler>(handler), query_text, text_length, std::forward_as_tuple(params...));
    }
        template <typename... Params, typename ResultHandler>
        void insert_direct(ResultHandler &&handler, const char *query_text, size_t text_length, const Params &...params)
        {
            insert(std::forward<ResultHandler>(handler), query_text, text_length, std::forward_as_tuple(params...));
        }
    template<typename... Params, typename ResultHandler>
    void insert_direct(ResultHandler&& handler, const char* query_text, const Params&... params)
    {
        insert(std::forward<ResultHandler>(handler), query_text, strlen(query_text), std::forward_as_tuple(params...));
    }
        template <typename... Params, typename ResultHandler>
        void insert_direct(ResultHandler &&handler, const char *query_text, const Params &...params)
        {
            insert(std::forward<ResultHandler>(handler), query_text, strlen(query_text), std::forward_as_tuple(params...));
        }
    template<typename... Params, typename ResultHandler>
    void insert_direct(ResultHandler&& handler, const std::string& query_text, const Params&... params)
    {
        insert(std::forward<ResultHandler>(handler), query_text.data(), query_text.length(), std::forward_as_tuple(params...));
    }
        template <typename... Params, typename ResultHandler>
        void insert_direct(ResultHandler &&handler, const std::string &query_text, const Params &...params)
        {
            insert(std::forward<ResultHandler>(handler), query_text.data(), query_text.length(), std::forward_as_tuple(params...));
        }
    /*
        RowHandler defines as:
            void row_handler(const Values& values);
        FinishHandler defines as:
            void finish_handler(const exception_type& e);
        If a row is fetched, the row handler is called.
        If an error occurred or the operation is completed, the result handler is called.
    */
    template<typename Params, typename Values, typename RowHandler, typename FinishHandler>
    void query_explicit(const char* query_text, size_t text_length, const Params& params, Values&& values, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        T* pThis = static_cast<T*>(this);
        pThis->open_command(query_text, text_length, [values, row_handler, finish_handler, params](const typename T::exception_type& e, const std::shared_ptr<Command>& command) mutable {
        /*
            RowHandler defines as:
                void row_handler(const Values& values);
            FinishHandler defines as:
                void finish_handler(const exception_type& e);
            If a row is fetched, the row handler is called.
            If an error occurred or the operation is completed, the result handler is called.
        */
        template <typename Params, typename Values, typename RowHandler, typename FinishHandler>
        void query_explicit(const char *query_text, size_t text_length, const Params &params, Values &&values, RowHandler &&row_handler, FinishHandler &&finish_handler)
        {
            T *pThis = static_cast<T *>(this);
            pThis->open_command(query_text, text_length, [values, row_handler, finish_handler, params](const typename T::exception_type &e, const std::shared_ptr<Command> &command) mutable
                                {
            if(e)
            {
                finish_handler(e);
@@ -288,72 +287,72 @@
                    auto helper=detail::make_fetch_helper(values, row_handler, finish_handler);
                    helper->template start<Command, typename T::exception_type>(command);
                });
            }
        });
    }
            } });
        }
    template<typename Params, typename Values, typename RowHandler, typename FinishHandler>
    void query_explicit(const char* query_text, const Params& params, Values&& values, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        query_explicit(query_text, strlen(query_text), params, std::forward<Values>(values), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
    template<typename Params, typename Values, typename RowHandler, typename FinishHandler>
    void query_explicit(const std::string& query_text, const Params& params, Values&& values, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        query_explicit(query_text.data(), query_text.size(), params, std::forward<Values>(values), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
    template<typename Values, typename RowHandler, typename FinishHandler>
    void query_explicit(const char* query_text, size_t text_length, Values&& values, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        query_explicit(query_text, text_length, std::make_tuple(), std::forward<Values>(values), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
    template<typename Values, typename RowHandler, typename FinishHandler>
    void query_explicit(const char* query_text, Values&& values, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        query_explicit(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
    template<typename Values, typename RowHandler, typename FinishHandler>
    void query_explicit(const std::string& query_text, Values&& values, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        query_explicit(query_text, std::make_tuple(), std::forward<Values>(values), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
        template <typename Params, typename Values, typename RowHandler, typename FinishHandler>
        void query_explicit(const char *query_text, const Params &params, Values &&values, RowHandler &&row_handler, FinishHandler &&finish_handler)
        {
            query_explicit(query_text, strlen(query_text), params, std::forward<Values>(values), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        }
        template <typename Params, typename Values, typename RowHandler, typename FinishHandler>
        void query_explicit(const std::string &query_text, const Params &params, Values &&values, RowHandler &&row_handler, FinishHandler &&finish_handler)
        {
            query_explicit(query_text.data(), query_text.size(), params, std::forward<Values>(values), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        }
        template <typename Values, typename RowHandler, typename FinishHandler>
        void query_explicit(const char *query_text, size_t text_length, Values &&values, RowHandler &&row_handler, FinishHandler &&finish_handler)
        {
            query_explicit(query_text, text_length, std::make_tuple(), std::forward<Values>(values), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        }
        template <typename Values, typename RowHandler, typename FinishHandler>
        void query_explicit(const char *query_text, Values &&values, RowHandler &&row_handler, FinishHandler &&finish_handler)
        {
            query_explicit(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        }
        template <typename Values, typename RowHandler, typename FinishHandler>
        void query_explicit(const std::string &query_text, Values &&values, RowHandler &&row_handler, FinishHandler &&finish_handler)
        {
            query_explicit(query_text, std::make_tuple(), std::forward<Values>(values), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        }
    template<typename Params, typename RowHandler, typename FinishHandler>
    void query(const char* query_text, size_t text_length, const Params& params, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        query_explicit(query_text, text_length, params, detail::make_values(row_handler), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
    template<typename Params, typename RowHandler, typename FinishHandler>
    void query(const char* query_text, const Params& params, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        query_explicit(query_text, params, detail::make_values(row_handler), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
    template<typename Params, typename RowHandler, typename FinishHandler>
    void query(const std::string& query_text, const Params& params, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        query_explicit(query_text, params, detail::make_values(row_handler), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
    template<typename RowHandler, typename FinishHandler>
    void query(const char* query_text, size_t text_length, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        query_explicit(query_text, text_length, detail::make_values(row_handler), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
    template<typename RowHandler, typename FinishHandler>
    void query(const char* query_text, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        query_explicit(query_text, detail::make_values(row_handler), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
    template<typename RowHandler, typename FinishHandler>
    void query(const std::string& query_text, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        query_explicit(query_text, detail::make_values(row_handler), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
        template <typename Params, typename RowHandler, typename FinishHandler>
        void query(const char *query_text, size_t text_length, const Params &params, RowHandler &&row_handler, FinishHandler &&finish_handler)
        {
            query_explicit(query_text, text_length, params, detail::make_values(row_handler), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        }
        template <typename Params, typename RowHandler, typename FinishHandler>
        void query(const char *query_text, const Params &params, RowHandler &&row_handler, FinishHandler &&finish_handler)
        {
            query_explicit(query_text, params, detail::make_values(row_handler), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        }
        template <typename Params, typename RowHandler, typename FinishHandler>
        void query(const std::string &query_text, const Params &params, RowHandler &&row_handler, FinishHandler &&finish_handler)
        {
            query_explicit(query_text, params, detail::make_values(row_handler), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        }
        template <typename RowHandler, typename FinishHandler>
        void query(const char *query_text, size_t text_length, RowHandler &&row_handler, FinishHandler &&finish_handler)
        {
            query_explicit(query_text, text_length, detail::make_values(row_handler), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        }
        template <typename RowHandler, typename FinishHandler>
        void query(const char *query_text, RowHandler &&row_handler, FinishHandler &&finish_handler)
        {
            query_explicit(query_text, detail::make_values(row_handler), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        }
        template <typename RowHandler, typename FinishHandler>
        void query(const std::string &query_text, RowHandler &&row_handler, FinishHandler &&finish_handler)
        {
            query_explicit(query_text, detail::make_values(row_handler), std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        }
    template<typename Params, typename FinishHandler, typename... RowHandlers>
    void query_multi_with_params(const char* query_text, size_t text_length, const Params& params, FinishHandler&& finish_handler, RowHandlers&&... row_handlers)
    {
        T* pThis = static_cast<T*>(this);
        pThis->open_command(query_text, text_length, [params, finish_handler, row_handlers...](const typename T::exception_type& e, const std::shared_ptr<Command>& command) mutable {
        template <typename Params, typename FinishHandler, typename... RowHandlers>
        void query_multi_with_params(const char *query_text, size_t text_length, const Params &params, FinishHandler &&finish_handler, RowHandlers &&...row_handlers)
        {
            T *pThis = static_cast<T *>(this);
            pThis->open_command(query_text, text_length, [params, finish_handler, row_handlers...](const typename T::exception_type &e, const std::shared_ptr<Command> &command) mutable
                                {
            if (e)
            {
                finish_handler(e);
@@ -366,38 +365,37 @@
                    else
                        qtl::detail::async_fetch_command<typename T::exception_type>(command, std::forward<FinishHandler>(finish_handler), std::forward<RowHandlers>(row_handlers)...);
                });
            }
        });
    }
    template<typename Params, typename FinishHandler, typename... RowHandlers>
    void query_multi_with_params(const char* query_text, const Params& params, FinishHandler&& finish_handler, RowHandlers&&... row_handlers)
    {
        query_multi_with_params(query_text, strlen(query_text), params, std::forward<FinishHandler>(finish_handler), std::forward<RowHandlers>(row_handlers)...);
    }
    template<typename Params, typename FinishHandler, typename... RowHandlers>
    void query_multi_with_params(const std::string& query_text, const Params& params, FinishHandler&& finish_handler, RowHandlers&&... row_handlers)
    {
        query_multi_with_params(query_text.data(), query_text.size(), params, std::forward<FinishHandler>(finish_handler), std::forward<RowHandlers>(row_handlers)...);
    }
    template<typename FinishHandler, typename... RowHandlers>
    void query_multi(const char* query_text, size_t text_length, FinishHandler&& finish_handler, RowHandlers&&... row_handlers)
    {
        query_multi_with_params<std::tuple<>, FinishHandler, RowHandlers...>(query_text, text_length, std::make_tuple(), std::forward<FinishHandler>(finish_handler), std::forward<RowHandlers>(row_handlers)...);
    }
    template<typename FinishHandler, typename... RowHandlers>
    void query_multi(const char* query_text, FinishHandler&& finish_handler, RowHandlers&&... row_handlers)
    {
        query_multi_with_params<std::tuple<>, FinishHandler, RowHandlers...>(query_text, strlen(query_text), std::make_tuple(), std::forward<FinishHandler>(finish_handler), std::forward<RowHandlers>(row_handlers)...);
    }
    template<typename FinishHandler, typename... RowHandlers>
    void query_multi(const std::string& query_text, FinishHandler&& finish_handler, RowHandlers&&... row_handlers)
    {
        query_multi_with_params<std::tuple<>, FinishHandler, RowHandlers...>(query_text.data(), query_text.size(), std::make_tuple(), std::forward<FinishHandler>(finish_handler), std::forward<RowHandlers>(row_handlers)...);
    }
            } });
        }
        template <typename Params, typename FinishHandler, typename... RowHandlers>
        void query_multi_with_params(const char *query_text, const Params &params, FinishHandler &&finish_handler, RowHandlers &&...row_handlers)
        {
            query_multi_with_params(query_text, strlen(query_text), params, std::forward<FinishHandler>(finish_handler), std::forward<RowHandlers>(row_handlers)...);
        }
        template <typename Params, typename FinishHandler, typename... RowHandlers>
        void query_multi_with_params(const std::string &query_text, const Params &params, FinishHandler &&finish_handler, RowHandlers &&...row_handlers)
        {
            query_multi_with_params(query_text.data(), query_text.size(), params, std::forward<FinishHandler>(finish_handler), std::forward<RowHandlers>(row_handlers)...);
        }
        template <typename FinishHandler, typename... RowHandlers>
        void query_multi(const char *query_text, size_t text_length, FinishHandler &&finish_handler, RowHandlers &&...row_handlers)
        {
            query_multi_with_params<std::tuple<>, FinishHandler, RowHandlers...>(query_text, text_length, std::make_tuple(), std::forward<FinishHandler>(finish_handler), std::forward<RowHandlers>(row_handlers)...);
        }
        template <typename FinishHandler, typename... RowHandlers>
        void query_multi(const char *query_text, FinishHandler &&finish_handler, RowHandlers &&...row_handlers)
        {
            query_multi_with_params<std::tuple<>, FinishHandler, RowHandlers...>(query_text, strlen(query_text), std::make_tuple(), std::forward<FinishHandler>(finish_handler), std::forward<RowHandlers>(row_handlers)...);
        }
        template <typename FinishHandler, typename... RowHandlers>
        void query_multi(const std::string &query_text, FinishHandler &&finish_handler, RowHandlers &&...row_handlers)
        {
            query_multi_with_params<std::tuple<>, FinishHandler, RowHandlers...>(query_text.data(), query_text.size(), std::make_tuple(), std::forward<FinishHandler>(finish_handler), std::forward<RowHandlers>(row_handlers)...);
        }
protected:
    qtl::event* m_event_handler { nullptr };
};
    protected:
        qtl::event *m_event_handler{nullptr};
    };
}
include/qtl_common.hpp
@@ -1,15 +1,15 @@
#ifndef _QTL_COMMON_H_
#define _QTL_COMMON_H_
#if __cplusplus<201103L && _MSC_VER<1800
#if __cplusplus < 201103L && _MSC_VER < 1800
#error QTL need C++11 compiler
#endif //C++11
#endif // C++11
#if _MSC_VER>=1800 && _MSC_VER<1900
#if _MSC_VER >= 1800 && _MSC_VER < 1900
#define NOEXCEPT throw()
#else
#define NOEXCEPT noexcept
#endif //NOEXCEPT
#endif // NOEXCEPT
#include <stdint.h>
#include <string.h>
@@ -31,1426 +31,1457 @@
namespace qtl
{
struct null { };
struct blob_data
{
    void* data;
    size_t size;
    blob_data() : data(NULL), size(0) { }
    blob_data(void* d, size_t n) : data(d), size(n) { }
};
struct const_blob_data
{
    const void* data;
    size_t size;
    const_blob_data() : data(NULL), size(0) { }
    const_blob_data(const void* d, size_t n) : data(d), size(n) { }
};
const size_t blob_buffer_size=64*1024;
inline std::string& trim_string(std::string& str, const char* target)
{
    str.erase(0, str.find_first_not_of(target));
    str.erase(str.find_last_not_of(target)+1);
    return str;
}
template<typename T>
struct indicator
{
    typedef T data_type;
    data_type data;
    size_t length;
    bool is_null;
    bool is_truncated;
    indicator()
        : is_null(false), is_truncated(false), length(0) { }
    explicit indicator(const data_type& src)
        : is_null(false), is_truncated(false), length(0), data(src) { }
    explicit indicator(data_type&& src)
        : is_null(false), is_truncated(false), length(0), data(std::move(src)) { }
    operator bool() const { return is_null; }
    operator data_type&() { return data; }
    operator const data_type&() const { return data; }
};
template<typename StringT>
struct bind_string_helper
{
    typedef StringT string_type;
    typedef typename string_type::value_type char_type;
    bind_string_helper(string_type&& value) : m_value(std::forward<string_type>(value)) { }
    bind_string_helper(const bind_string_helper& src)
        : m_value(std::forward<StringT>(src.m_value))
    struct null
    {
    };
    struct blob_data
    {
        void *data;
        size_t size;
        blob_data() : data(NULL), size(0) {}
        blob_data(void *d, size_t n) : data(d), size(n) {}
    };
    struct const_blob_data
    {
        const void *data;
        size_t size;
        const_blob_data() : data(NULL), size(0) {}
        const_blob_data(const void *d, size_t n) : data(d), size(n) {}
    };
    const size_t blob_buffer_size = 64 * 1024;
    inline std::string &trim_string(std::string &str, const char *target)
    {
        str.erase(0, str.find_first_not_of(target));
        str.erase(str.find_last_not_of(target) + 1);
        return str;
    }
    bind_string_helper(bind_string_helper&& src)
        : m_value(std::forward<StringT>(src.m_value))
    template <typename T>
    struct indicator
    {
    }
    bind_string_helper& operator=(const bind_string_helper& src)
        typedef T data_type;
        data_type data;
        size_t length;
        bool is_null;
        bool is_truncated;
        indicator()
            : is_null(false), is_truncated(false), length(0) {}
        explicit indicator(const data_type &src)
            : is_null(false), is_truncated(false), length(0), data(src) {}
        explicit indicator(data_type &&src)
            : is_null(false), is_truncated(false), length(0), data(std::move(src)) {}
        operator bool() const { return is_null; }
        operator data_type &() { return data; }
        operator const data_type &() const { return data; }
    };
    template <typename StringT>
    struct bind_string_helper
    {
        if (this != &src)
        typedef StringT string_type;
        typedef typename string_type::value_type char_type;
        bind_string_helper(string_type &&value) : m_value(std::forward<string_type>(value)) {}
        bind_string_helper(const bind_string_helper &src)
            : m_value(std::forward<StringT>(src.m_value))
        {
            m_value = std::forward<StringT>(src.m_value);
        }
        return *this;
    }
    bind_string_helper& operator=(bind_string_helper&& src)
    {
        if (this != &src)
        bind_string_helper(bind_string_helper &&src)
            : m_value(std::forward<StringT>(src.m_value))
        {
            m_value = std::forward<StringT>(src.m_value);
        }
        return *this;
        bind_string_helper &operator=(const bind_string_helper &src)
        {
            if (this != &src)
            {
                m_value = std::forward<StringT>(src.m_value);
            }
            return *this;
        }
        bind_string_helper &operator=(bind_string_helper &&src)
        {
            if (this != &src)
            {
                m_value = std::forward<StringT>(src.m_value);
            }
            return *this;
        }
        void clear() { m_value.clear(); }
        char_type *alloc(size_t n)
        {
            m_value.resize(n);
            return (char_type *)m_value.data();
        }
        void truncate(size_t n) { m_value.resize(n); }
        void assign(const char_type *str, size_t n) { m_value.assign(str, n); }
        const char_type *data() const { return m_value.data(); }
        size_t size() const { return m_value.size(); }
    private:
        string_type &&m_value;
    };
    template <typename StringT>
    inline bind_string_helper<typename std::decay<StringT>::type> bind_string(StringT &&value)
    {
        typedef typename std::decay<StringT>::type string_type;
        return bind_string_helper<string_type>(std::forward<string_type>(value));
    }
    void clear() { m_value.clear(); }
    char_type* alloc(size_t n) { m_value.resize(n); return (char_type*)m_value.data(); }
    void truncate(size_t n) { m_value.resize(n); }
    void assign(const char_type* str, size_t n) { m_value.assign(str, n); }
    const char_type* data() const { return m_value.data(); }
    size_t size() const { return m_value.size(); }
private:
    string_type&& m_value;
};
    template <typename Command>
    inline void bind_param(Command &command, size_t index, const std::string &param)
    {
        command.bind_param(index, param.data(), param.size());
    }
template<typename StringT>
inline bind_string_helper<typename std::decay<StringT>::type> bind_string(StringT&& value)
{
    typedef typename std::decay<StringT>::type string_type;
    return bind_string_helper<string_type>(std::forward<string_type>(value));
}
    template <typename Command>
    inline void bind_param(Command &command, size_t index, std::istream &param)
    {
        command.bind_param(index, param);
    }
template<typename Command>
inline void bind_param(Command& command, size_t index, const std::string& param)
{
    command.bind_param(index, param.data(), param.size());
}
template<typename Command>
inline void bind_param(Command& command, size_t index, std::istream& param)
{
    command.bind_param(index, param);
}
template<typename Command, typename T>
inline void bind_param(Command& command, size_t index, const T& param)
{
    command.bind_param(index, param);
}
    template <typename Command, typename T>
    inline void bind_param(Command &command, size_t index, const T &param)
    {
        command.bind_param(index, param);
    }
#ifdef _QTL_ENABLE_CPP17
template<typename Command, typename T>
inline void bind_param(Command& command, size_t index, const std::optional<T>& param)
{
    if(param)
        command.bind_param(index, *param);
    else
        command.bind_param(index, nullptr);
}
    template <typename Command, typename T>
    inline void bind_param(Command &command, size_t index, const std::optional<T> &param)
    {
        if (param)
            command.bind_param(index, *param);
        else
            command.bind_param(index, nullptr);
    }
#endif // C++17
// The order of the overloaded functions 'bind_field' is very important
// The version with the most generic parameters is at the end
    // The order of the overloaded functions 'bind_field' is very important
    // The version with the most generic parameters is at the end
template<typename Command, size_t N>
inline void bind_field(Command& command, size_t index, char (&value)[N])
{
    command.bind_field(index, value, N);
}
    template <typename Command, size_t N>
    inline void bind_field(Command &command, size_t index, char (&value)[N])
    {
        command.bind_field(index, value, N);
    }
template<typename Command, size_t N>
inline void bind_field(Command& command, size_t index, wchar_t (&value)[N])
{
    command.bind_field(index, value, N);
}
    template <typename Command, size_t N>
    inline void bind_field(Command &command, size_t index, wchar_t (&value)[N])
    {
        command.bind_field(index, value, N);
    }
template<typename Command>
inline void bind_field(Command& command, size_t index, char* value, size_t length)
{
    command.bind_field(index, value, length);
}
    template <typename Command>
    inline void bind_field(Command &command, size_t index, char *value, size_t length)
    {
        command.bind_field(index, value, length);
    }
template<typename Command>
inline void bind_field(Command& command, size_t index, wchar_t* value, size_t length)
{
    command.bind_field(index, value, length);
}
    template <typename Command>
    inline void bind_field(Command &command, size_t index, wchar_t *value, size_t length)
    {
        command.bind_field(index, value, length);
    }
template<typename Command>
inline void bind_field(Command& command, size_t index, std::string&& value)
{
    command.bind_field(index, bind_string(std::forward<std::string>(value)));
}
    template <typename Command>
    inline void bind_field(Command &command, size_t index, std::string &&value)
    {
        command.bind_field(index, bind_string(std::forward<std::string>(value)));
    }
template<typename Command>
inline void bind_field(Command& command, size_t index, std::wstring&& value)
{
    command.bind_field(index, bind_string(std::forward<std::wstring>(value)));
}
    template <typename Command>
    inline void bind_field(Command &command, size_t index, std::wstring &&value)
    {
        command.bind_field(index, bind_string(std::forward<std::wstring>(value)));
    }
template<typename Command>
inline void bind_field(Command& command, size_t index, std::vector<char>&& value)
{
    command.bind_field(index, bind_string(std::forward<std::vector<char>>(value)));
}
    template <typename Command>
    inline void bind_field(Command &command, size_t index, std::vector<char> &&value)
    {
        command.bind_field(index, bind_string(std::forward<std::vector<char>>(value)));
    }
template<typename Command>
inline void bind_field(Command& command, size_t index, std::vector<wchar_t>&& value)
{
    command.bind_field(index, bind_string(std::forward<std::vector<wchar_t>>(value)));
}
    template <typename Command>
    inline void bind_field(Command &command, size_t index, std::vector<wchar_t> &&value)
    {
        command.bind_field(index, bind_string(std::forward<std::vector<wchar_t>>(value)));
    }
template<typename Command, typename T, typename=typename std::enable_if<!std::is_reference<T>::value>::type>
inline void bind_field(Command& command, size_t index, T&& value)
{
    command.bind_field(index, std::forward<T>(value));
}
template<typename Command, typename T>
inline void bind_field(Command& command, size_t index, T& value)
{
    bind_field(command, index, std::forward<T>(value));
}
template<typename Command, typename T, typename=typename std::enable_if<!std::is_reference<T>::value>::type>
inline void bind_field(Command& command, const char* name, T&& value)
{
    size_t index=command.find_field(name);
    if(index==-1)
        value=T();
    else
    template <typename Command, typename T, typename = typename std::enable_if<!std::is_reference<T>::value>::type>
    inline void bind_field(Command &command, size_t index, T &&value)
    {
        command.bind_field(index, std::forward<T>(value));
}
    }
template<typename Command, typename T>
inline void bind_field(Command& command, const char* name, T& value)
{
    size_t index=command.find_field(name);
    if(index==-1)
        value=T();
    else
    template <typename Command, typename T>
    inline void bind_field(Command &command, size_t index, T &value)
    {
        bind_field(command, index, std::forward<T>(value));
}
template<typename Command>
inline void bind_field(Command& command, const char* name, char* value, size_t length)
{
    size_t index=command.find_field(name);
    if (index == -1)
    {
        if (length > 0) value[0] = '\0';
    }
    else
        command.bind_field(index, value, length);
}
template<typename Command>
inline void bind_field(Command& command, const char* name, wchar_t* value, size_t length)
{
    size_t index=command.find_field(name);
    if (index == -1)
    template <typename Command, typename T, typename = typename std::enable_if<!std::is_reference<T>::value>::type>
    inline void bind_field(Command &command, const char *name, T &&value)
    {
        if (length > 0) value[0] = '\0';
        size_t index = command.find_field(name);
        if (index == -1)
            value = T();
        else
            command.bind_field(index, std::forward<T>(value));
    }
    else
        command.bind_field(index, value, length);
}
template<typename Command, typename T>
inline void bind_field(Command& command, const char* name, std::reference_wrapper<T>&& value)
{
    return bind_field(command, value.get());
}
    template <typename Command, typename T>
    inline void bind_field(Command &command, const char *name, T &value)
    {
        size_t index = command.find_field(name);
        if (index == -1)
            value = T();
        else
            bind_field(command, index, std::forward<T>(value));
    }
    template <typename Command>
    inline void bind_field(Command &command, const char *name, char *value, size_t length)
    {
        size_t index = command.find_field(name);
        if (index == -1)
        {
            if (length > 0)
                value[0] = '\0';
        }
        else
            command.bind_field(index, value, length);
    }
    template <typename Command>
    inline void bind_field(Command &command, const char *name, wchar_t *value, size_t length)
    {
        size_t index = command.find_field(name);
        if (index == -1)
        {
            if (length > 0)
                value[0] = '\0';
        }
        else
            command.bind_field(index, value, length);
    }
    template <typename Command, typename T>
    inline void bind_field(Command &command, const char *name, std::reference_wrapper<T> &&value)
    {
        return bind_field(command, value.get());
    }
#ifdef _QTL_ENABLE_CPP17
template<typename Command, typename T>
inline void bind_field(Command& command, size_t index, std::optional<T>& value)
{
    value.emplace();
    command.bind_field(index, std::forward<T>(value));
}
    template <typename Command, typename T>
    inline void bind_field(Command &command, size_t index, std::optional<T> &value)
    {
        value.emplace();
        command.bind_field(index, std::forward<T>(value));
    }
template<typename Command, typename T>
inline void bind_field(Command& command, const char* name, std::optional<T>& value)
{
    size_t index = command.find_field(name);
    if (index == -1)
        value.reset();
    else
        bind_field(command, index, value);
}
    template <typename Command, typename T>
    inline void bind_field(Command &command, const char *name, std::optional<T> &value)
    {
        size_t index = command.find_field(name);
        if (index == -1)
            value.reset();
        else
            bind_field(command, index, value);
    }
#endif // C++17
template<typename Command, typename T>
inline size_t bind_fields(Command& command, size_t index, T&& value)
{
    bind_field(command, index, std::forward<T>(value));
    return index+1;
}
    template <typename Command, typename T>
    inline size_t bind_fields(Command &command, size_t index, T &&value)
    {
        bind_field(command, index, std::forward<T>(value));
        return index + 1;
    }
template<typename Command, typename T, typename... Other>
inline size_t bind_fields(Command& command, size_t start, T&& value, Other&&... other)
{
    bind_field(command, start, std::forward<T>(value));
    return bind_fields(command, start+1, std::forward<Other>(other)...);
}
    template <typename Command, typename T, typename... Other>
    inline size_t bind_fields(Command &command, size_t start, T &&value, Other &&...other)
    {
        bind_field(command, start, std::forward<T>(value));
        return bind_fields(command, start + 1, std::forward<Other>(other)...);
    }
template<typename Command, typename... Fields>
inline size_t bind_fields(Command& command, Fields&&... fields)
{
    return bind_fields(command, (size_t)0, std::forward<Fields>(fields)...);
}
    template <typename Command, typename... Fields>
    inline size_t bind_fields(Command &command, Fields &&...fields)
    {
        return bind_fields(command, (size_t)0, std::forward<Fields>(fields)...);
    }
namespace detail
{
    namespace detail
    {
template<typename F, typename T>
struct apply_impl
{
private:
        template <typename F, typename T>
        struct apply_impl
        {
        private:
#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L
    typedef typename std::invoke_result<F, T>::type raw_result_type;
            typedef typename std::invoke_result<F, T>::type raw_result_type;
#else
    typedef typename std::result_of<F(T)>::type raw_result_type;
            typedef typename std::result_of<F(T)>::type raw_result_type;
#endif
    template<typename Ret, bool>
    struct impl {};
    template<typename Ret>
    struct impl<Ret, true>
    {
        typedef bool result_type;
        result_type operator()(F&& f, T&& v)
        {
            f(std::forward<T>(v));
            return true;
        }
    };
    template<typename Ret>
    struct impl<Ret, false>
    {
        typedef Ret result_type;
        result_type operator()(F&& f, T&& v)
        {
            return f(std::forward<T>(v));
        }
    };
            template <typename Ret, bool>
            struct impl
            {
            };
            template <typename Ret>
            struct impl<Ret, true>
            {
                typedef bool result_type;
                result_type operator()(F &&f, T &&v)
                {
                    f(std::forward<T>(v));
                    return true;
                }
            };
            template <typename Ret>
            struct impl<Ret, false>
            {
                typedef Ret result_type;
                result_type operator()(F &&f, T &&v)
                {
                    return f(std::forward<T>(v));
                }
            };
public:
    typedef typename impl<raw_result_type, std::is_void<raw_result_type>::value>::result_type result_type;
    result_type operator()(F&& f, T&& v)
    {
        return impl<raw_result_type, std::is_void<raw_result_type>::value>()(std::forward<F>(f), std::forward<T>(v));
    }
};
        public:
            typedef typename impl<raw_result_type, std::is_void<raw_result_type>::value>::result_type result_type;
            result_type operator()(F &&f, T &&v)
            {
                return impl<raw_result_type, std::is_void<raw_result_type>::value>()(std::forward<F>(f), std::forward<T>(v));
            }
        };
template<typename F, typename... Types>
struct apply_impl<F, std::tuple<Types...>>
{
private:
    typedef typename std::remove_reference<F>::type fun_type;
    typedef std::tuple<Types...> arg_type;
        template <typename F, typename... Types>
        struct apply_impl<F, std::tuple<Types...>>
        {
        private:
            typedef typename std::remove_reference<F>::type fun_type;
            typedef std::tuple<Types...> arg_type;
#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L
    typedef typename std::invoke_result<F, Types...>::type raw_result_type;
            typedef typename std::invoke_result<F, Types...>::type raw_result_type;
#else
    typedef typename std::result_of<F(Types...)>::type raw_result_type;
            typedef typename std::result_of<F(Types...)>::type raw_result_type;
#endif
    template<typename Ret, bool>
    struct impl {};
    template<typename Ret>
    struct impl<Ret, true>
    {
        typedef bool result_type;
        result_type operator()(F&& f, arg_type&& v)
            template <typename Ret, bool>
            struct impl
            {
            };
            template <typename Ret>
            struct impl<Ret, true>
            {
                typedef bool result_type;
                result_type operator()(F &&f, arg_type &&v)
                {
                    qtl::detail::apply_tuple(std::forward<F>(f), std::forward<arg_type>(v));
                    return true;
                }
            };
            template <typename Ret>
            struct impl<Ret, false>
            {
                typedef Ret result_type;
                result_type operator()(F &&f, arg_type &&v)
                {
                    return qtl::detail::apply_tuple(std::forward<F>(f), std::forward<arg_type>(v));
                }
            };
        public:
            typedef typename impl<raw_result_type, std::is_void<raw_result_type>::value>::result_type result_type;
            result_type operator()(F &&f, arg_type &&v)
            {
                return impl<raw_result_type, std::is_void<raw_result_type>::value>()(std::forward<F>(f), std::forward<arg_type>(v));
            }
        };
        template <typename Type, typename R>
        struct apply_impl<R (Type::*)(), Type>
        {
            qtl::detail::apply_tuple(std::forward<F>(f), std::forward<arg_type>(v));
            return true;
        }
    };
    template<typename Ret>
    struct impl<Ret, false>
    {
        typedef Ret result_type;
        result_type operator()(F&& f, arg_type&& v)
        private:
            typedef R (Type::*fun_type)();
            typedef R raw_result_type;
            template <typename Ret, bool>
            struct impl
            {
            };
            template <typename Ret>
            struct impl<Ret, true>
            {
                typedef bool result_type;
                result_type operator()(fun_type f, Type &&v)
                {
                    (v.*f)();
                    return true;
                }
            };
            template <typename Ret>
            struct impl<Ret, false>
            {
                typedef Ret result_type;
                result_type operator()(fun_type f, Type &&v)
                {
                    return (v.*f)();
                }
            };
        public:
            typedef typename impl<raw_result_type, std::is_void<raw_result_type>::value>::result_type result_type;
            result_type operator()(R (Type::*f)(), Type &&v)
            {
                return impl<raw_result_type, std::is_void<raw_result_type>::value>()(f, std::forward<Type>(v));
            }
        };
        template <typename Type, typename R>
        struct apply_impl<R (Type::*)() const, Type>
        {
            return qtl::detail::apply_tuple(std::forward<F>(f), std::forward<arg_type>(v));
        }
    };
        private:
            typedef R (Type::*fun_type)() const;
            typedef R raw_result_type;
            template <typename Ret, bool>
            struct impl
            {
            };
            template <typename Ret>
            struct impl<Ret, true>
            {
                typedef bool result_type;
                result_type operator()(fun_type f, Type &&v)
                {
                    (v.*f)();
                    return true;
                }
            };
            template <typename Ret>
            struct impl<Ret, false>
            {
                typedef Ret result_type;
                result_type operator()(fun_type f, Type &&v)
                {
                    return (v.*f)();
                }
            };
public:
    typedef typename impl<raw_result_type, std::is_void<raw_result_type>::value>::result_type result_type;
    result_type operator()(F&& f, arg_type&& v)
    {
        return impl<raw_result_type, std::is_void<raw_result_type>::value>()(std::forward<F>(f), std::forward<arg_type>(v));
    }
};
        public:
            typedef typename impl<raw_result_type, std::is_void<raw_result_type>::value>::result_type result_type;
            result_type operator()(fun_type f, Type &&v)
            {
                return impl<raw_result_type, std::is_void<raw_result_type>::value>()(f, std::forward<Type>(v));
            }
        };
template<typename Type, typename R>
struct apply_impl<R (Type::*)(), Type>
{
private:
    typedef R (Type::*fun_type)();
    typedef R raw_result_type;
    template<typename Ret, bool>
    struct impl {};
    template<typename Ret>
    struct impl<Ret, true>
    {
        typedef bool result_type;
        result_type operator()(fun_type f, Type&& v)
        template <typename F, typename T>
        typename apply_impl<F, T>::result_type apply(F &&f, T &&v)
        {
            (v.*f)();
            return true;
            return apply_impl<F, T>()(std::forward<F>(f), std::forward<T>(v));
        }
    };
    template<typename Ret>
    struct impl<Ret, false>
    {
        typedef Ret result_type;
        result_type operator()(fun_type f, Type&& v)
        template <typename Command, size_t N, typename... Types>
        struct bind_helper
        {
            return (v.*f)();
        }
    };
        public:
            explicit bind_helper(Command &command) : m_command(command) {}
            void operator()(const std::tuple<Types...> &params) const
            {
                bind_param(m_command, N - 1, std::get<N - 1>(params));
                (bind_helper<Command, N - 1, Types...>(m_command))(params);
            }
            void operator()(std::tuple<Types...> &&params) const
            {
                typedef typename std::remove_reference<typename std::tuple_element<N - 1, tuple_type>::type>::type param_type;
                bind_field(m_command, N - 1, std::forward<param_type>(std::get<N - 1>(std::forward<tuple_type>(params))));
                (bind_helper<Command, N - 1, Types...>(m_command))(std::forward<tuple_type>(params));
            }
public:
    typedef typename impl<raw_result_type, std::is_void<raw_result_type>::value>::result_type result_type;
    result_type operator()(R (Type::*f)(), Type&& v)
    {
        return impl<raw_result_type, std::is_void<raw_result_type>::value>()(f, std::forward<Type>(v));
    }
};
        private:
            typedef std::tuple<Types...> tuple_type;
            Command &m_command;
        };
template<typename Type, typename R>
struct apply_impl<R (Type::*)() const, Type>
{
private:
    typedef R (Type::*fun_type)() const;
    typedef R raw_result_type;
    template<typename Ret, bool>
    struct impl {};
    template<typename Ret>
    struct impl<Ret, true>
    {
        typedef bool result_type;
        result_type operator()(fun_type f, Type&& v)
        template <typename Command, typename... Types>
        struct bind_helper<Command, 1, Types...>
        {
            (v.*f)();
            return true;
        }
    };
    template<typename Ret>
    struct impl<Ret, false>
    {
        typedef Ret result_type;
        result_type operator()(fun_type f, Type&& v)
        public:
            explicit bind_helper(Command &command) : m_command(command) {}
            void operator()(const std::tuple<Types...> &params) const
            {
                bind_param(m_command, 0, std::get<0>(params));
            }
            void operator()(std::tuple<Types...> &&params) const
            {
                typedef typename std::remove_reference<typename std::tuple_element<0, tuple_type>::type>::type param_type;
                bind_field(m_command, static_cast<size_t>(0), std::forward<param_type>(std::get<0>(std::forward<tuple_type>(params))));
            }
        private:
            typedef std::tuple<Types...> tuple_type;
            Command &m_command;
        };
        template <typename Command, typename... Types>
        struct bind_helper<Command, 0, Types...>
        {
            return (v.*f)();
        }
    };
public:
    typedef typename impl<raw_result_type, std::is_void<raw_result_type>::value>::result_type result_type;
    result_type operator()(fun_type f, Type&& v)
    {
        return impl<raw_result_type, std::is_void<raw_result_type>::value>()(f, std::forward<Type>(v));
    }
};
template<typename F, typename T>
typename apply_impl<F, T>::result_type apply(F&& f, T&& v)
{
    return apply_impl<F, T>()(std::forward<F>(f),  std::forward<T>(v));
}
template<typename Command, size_t N, typename... Types>
struct bind_helper
{
public:
    explicit bind_helper(Command& command) : m_command(command) { }
    void operator()(const std::tuple<Types...>& params) const
    {
        bind_param(m_command, N-1, std::get<N-1>(params));
        (bind_helper<Command, N-1, Types...>(m_command))(params);
    }
    void operator()(std::tuple<Types...>&& params) const
    {
        typedef typename std::remove_reference<typename std::tuple_element<N-1, tuple_type>::type>::type param_type;
        bind_field(m_command, N-1, std::forward<param_type>(std::get<N-1>(std::forward<tuple_type>(params))));
        (bind_helper<Command, N-1, Types...>(m_command))(std::forward<tuple_type>(params));
    }
private:
    typedef std::tuple<Types...> tuple_type;
    Command& m_command;
};
template<typename Command, typename... Types>
struct bind_helper<Command, 1, Types...>
{
public:
    explicit bind_helper(Command& command) : m_command(command) { }
    void operator()(const std::tuple<Types...>& params) const
    {
        bind_param(m_command, 0, std::get<0>(params));
    }
    void operator()(std::tuple<Types...>&& params) const
    {
        typedef typename std::remove_reference<typename std::tuple_element<0, tuple_type>::type>::type param_type;
        bind_field(m_command, static_cast<size_t>(0), std::forward<param_type>(std::get<0>(std::forward<tuple_type>(params))));
    }
private:
    typedef std::tuple<Types...> tuple_type;
    Command& m_command;
};
template<typename Command, typename... Types>
struct bind_helper<Command, 0, Types...>
{
public:
    explicit bind_helper(Command& command) { }
    void operator()(const std::tuple<Types...>& params) const
    {
    }
    void operator()(std::tuple<Types...>&& params) const
    {
    }
};
        public:
            explicit bind_helper(Command &command) {}
            void operator()(const std::tuple<Types...> &params) const
            {
            }
            void operator()(std::tuple<Types...> &&params) const
            {
            }
        };
#define QTL_ARGS_TUPLE(Arg, Others) \
    typename std::tuple<typename std::decay<Arg>::type, typename std::decay<Others>::type...>
template<typename Ret, typename Arg>
inline typename std::decay<Arg>::type make_values(Ret (*)(Arg))
{
    return typename std::decay<Arg>::type();
};
template<typename Ret, typename Arg, typename... Others>
inline auto make_values(Ret (*)(Arg, Others...)) -> QTL_ARGS_TUPLE(Arg, Others)
{
    return QTL_ARGS_TUPLE(Arg, Others)();
};
template<typename Type, typename Ret>
inline Type make_values(Ret (Type::*)())
{
    return Type();
};
template<typename Type, typename Ret>
inline Type make_values(Ret (Type::*)() const)
{
    return Type();
};
template<typename Type, typename Ret, typename... Args>
inline auto make_values(Ret (Type::*)(Args...)) -> QTL_ARGS_TUPLE(Type, Args)
{
    return QTL_ARGS_TUPLE(Type, Args)();
};
template<typename Type, typename Ret, typename... Args>
inline auto make_values(Ret (Type::*)(Args...) const) -> QTL_ARGS_TUPLE(Type, Args)
{
    return QTL_ARGS_TUPLE(Type, Args)();
};
template<typename Type, typename Ret, typename Arg>
inline typename std::decay<Arg>::type make_values_noclass(Ret (Type::*)(Arg))
{
    return typename std::decay<Arg>::type();
};
template<typename Type, typename Ret, typename Arg>
inline typename std::decay<Arg>::type make_values_noclass(Ret (Type::*)(Arg) const)
{
    return typename std::decay<Arg>::type();
};
template<typename Type, typename Ret, typename Arg, typename... Others>
inline auto make_values_noclass(Ret (Type::*)(Arg, Others...)) -> QTL_ARGS_TUPLE(Arg, Others)
{
    return QTL_ARGS_TUPLE(Arg, Others)();
};
template<typename Type, typename Ret, typename Arg, typename... Others>
inline auto make_values_noclass(Ret (Type::*)(Arg, Others...) const) -> QTL_ARGS_TUPLE(Arg, Others)
{
    return QTL_ARGS_TUPLE(Arg, Others)();
};
template<typename Functor, typename=typename std::enable_if<std::is_member_function_pointer<decltype(&Functor::operator())>::value>::type>
inline auto make_values(const Functor&)
-> decltype(make_values_noclass(&Functor::operator()))
{
    return make_values_noclass(&Functor::operator());
}
template<typename Command, typename ValueProc>
inline void fetch_command(Command& command, ValueProc&& proc)
{
    auto values=make_values(proc);
    typedef decltype(values) values_type;
    while(command.fetch(std::forward<values_type>(values)))
    {
        if(!apply(std::forward<ValueProc>(proc), std::forward<values_type>(values)))
            break;
    }
}
template<typename Command, typename ValueProc, typename... OtherProc>
inline void fetch_command(Command& command, ValueProc&& proc, OtherProc&&... other)
{
    fetch_command(command, std::forward<ValueProc>(proc));
    if(command.next_result())
    {
        fetch_command(command, std::forward<OtherProc>(other)...);
    }
}
}
template<typename Command, typename T>
struct params_binder
{
    enum { size = 1 };
    inline void operator()(Command& command, const T& param) const
    {
        qtl::bind_param(command, 0, param);
    }
};
template<typename Command, typename... Types>
struct params_binder<Command, std::tuple<Types...>>
{
    enum { size = sizeof...(Types) };
    void operator()(Command& command, const std::tuple<Types...>& params) const
    {
        (detail::bind_helper<Command, std::tuple_size<std::tuple<Types...>>::value, Types...>(command))(params);
    }
};
template<typename Command, typename Type1, typename Type2>
struct params_binder<Command, std::pair<Type1, Type2>>
{
    enum { size = 2 };
    void operator()(Command& command, std::pair<Type1, Type2>&& values) const
    {
        qtl::bind_param(command, 0, std::forward<Type1>(values.first));
        qtl::bind_param(command, 1, std::forward<Type2>(values.second));
    }
};
template<typename Command, typename T>
inline void bind_params(Command& command, const T& param)
{
    params_binder<Command, T> binder;
    binder(command, param);
}
template<typename Command, typename T>
struct record_binder
{
    inline void operator()(Command& command, T&& value) const
    {
        bind_field(command, static_cast<size_t>(0), std::forward<typename std::remove_reference<T>::type>(value));
    }
};
template<typename Command, typename T>
struct record_binder<Command, std::reference_wrapper<T>>
{
    inline void operator()(Command& command, std::reference_wrapper<T>&& value) const
    {
        bind_field(command, static_cast<size_t>(0), std::forward<typename std::remove_reference<T>::type>(value.get()));
    }
};
template<typename Command, typename... Types>
struct record_binder<Command, std::tuple<Types...>>
{
    void operator()(Command& command, std::tuple<Types...>&& values) const
    {
        (detail::bind_helper<Command, std::tuple_size<std::tuple<Types...>>::value, Types...>(command))
            (std::forward<std::tuple<Types...>>(values));
    }
};
template<typename Command, typename Type1, typename Type2>
struct record_binder<Command, std::pair<Type1, Type2>>
{
    void operator()(Command& command, std::pair<Type1, Type2>&& values) const
    {
        bind_field(command, static_cast<size_t>(0), std::forward<Type1>(values.first));
        bind_field(command, static_cast<size_t>(1), std::forward<Type2>(values.second));
    }
};
template<typename T, typename Tag>
struct record_with_tag : public T
{
};
template<typename T, typename Pred>
struct custom_binder_type : public T
{
    typedef T value_type;
    explicit custom_binder_type(Pred pred) : m_pred(pred) { }
    custom_binder_type(value_type&& v, Pred pred)
        : value_type(std::forward<value_type>(v)), m_pred(pred)
    {
    }
    template<typename... Args>
    custom_binder_type(Pred pred, Args&&... args)
        : value_type(std::forward<Args>(args)...), m_pred(pred)
    {
    }
    template<typename Command>
    void bind(Command& command)
    {
        m_pred(std::forward<T>(*this), command); // Pred maybe member function
    }
private:
    Pred m_pred;
};
template<typename T, typename Pred>
struct custom_binder_type<std::reference_wrapper<T>, Pred> : public std::reference_wrapper<T>
{
    typedef std::reference_wrapper<T> value_type;
    explicit custom_binder_type(Pred pred) : m_pred(pred) { }
    custom_binder_type(value_type&& v, Pred pred)
        : value_type(std::forward<value_type>(v)), m_pred(pred)
    {
    }
    template<typename... Args>
    custom_binder_type(Pred pred, Args&&... args)
        : T(std::forward<Args>(args)...), m_pred(pred)
    {
    }
    template<typename Command>
    void bind(Command& command)
    {
        m_pred(std::forward<T>(*this), command); // Pred maybe member function
    }
private:
    Pred m_pred;
};
template<typename T, typename Pred>
inline custom_binder_type<T, Pred> custom_bind(T&& v, Pred pred)
{
    return custom_binder_type<T, Pred>(std::forward<T>(v), pred);
}
template<typename Command, typename T, typename Pred>
struct record_binder<Command, custom_binder_type<T, Pred>>
{
    void operator()(Command& command, custom_binder_type<T, Pred>&& values) const
    {
        values.bind(command);
    }
};
template<typename Command, typename T>
inline void bind_record(Command& command, T&& value)
{
    record_binder<Command, T> binder;
    binder(command, std::forward<T>(value));
}
template<typename Command, typename Record>
class query_iterator final
{
public:
    using iterator_category = std::forward_iterator_tag;
    using value_type = Record;
    using difference_type = ptrdiff_t;
    using pointer = Record*;
    using reference = Record&;
    explicit query_iterator(Command& command)
        : m_command(command) { }
    Record* operator->() const { return m_record.get(); }
    Record& operator*() const { return *m_record; }
    query_iterator& operator++()
    {
        if(!m_record)
            m_record=std::make_shared<Record>();
        if(m_record.use_count()==1)
        template <typename Ret, typename Arg>
        inline typename std::decay<Arg>::type make_values(Ret (*)(Arg))
        {
            if(!m_command.fetch(std::forward<Record>(*m_record)))
                m_record.reset();
        }
        else
            return typename std::decay<Arg>::type();
        };
        template <typename Ret, typename Arg, typename... Others>
        inline auto make_values(Ret (*)(Arg, Others...)) -> QTL_ARGS_TUPLE(Arg, Others)
        {
            std::shared_ptr<Record> record=std::make_shared<Record>();
            if(m_command.fetch(std::forward<Record>(*record)))
                m_record=record;
            else
                m_record.reset();
        }
        return *this;
    }
    query_iterator operator++(int)
    {
        query_iterator temp=*this;
        m_record=std::make_shared<Record>();
        if(!m_command.fetch(std::forward<Record>(*m_record)))
            m_record.reset();
        return temp;
    }
            return QTL_ARGS_TUPLE(Arg, Others)();
        };
    bool operator ==(const query_iterator& rhs)
    {
        return &this->m_command==&rhs.m_command &&
            this->m_record==rhs.m_record;
    }
    bool operator !=(const query_iterator& rhs)
    {
        return !(*this==rhs);
    }
private:
    Command& m_command;
    std::shared_ptr<Record> m_record;
};
template<typename Command, typename Record>
class query_result final
{
public:
    typedef typename query_iterator<Command, Record>::value_type value_type;
    typedef typename query_iterator<Command, Record>::pointer pointer;
    typedef typename query_iterator<Command, Record>::reference reference;
    typedef query_iterator<Command, Record> iterator;
    explicit query_result(Command&& command) : m_command(std::move(command)) { }
    query_result(query_result&& src) : m_command(std::move(src.m_command)) { }
    query_result& operator=(query_result&& src)
    {
        if(this!=&src)
            m_command=std::move(src.m_command);
        return *this;
    }
    template<typename Params>
    iterator begin(const Params& params)
    {
        query_iterator<Command, Record> it(m_command);
        ++it;
        return it;
    }
    iterator begin()
    {
        return begin(std::make_tuple());
    }
    iterator end()
    {
        return query_iterator<Command, Record>(m_command);
    }
private:
    Command m_command;
};
template<typename T, class Command>
class base_database
{
public:
    template<typename Params>
    base_database& execute(const char* query_text, size_t text_length, const Params& params, uint64_t* affected=NULL)
    {
        T* pThis=static_cast<T*>(this);
        Command command=pThis->open_command(query_text, text_length);
        command.execute(params);
        if(affected) *affected=command.affetced_rows();
        command.close();
        return *this;
    }
    template<typename Params>
    base_database& execute(const char* query_text, const Params& params, uint64_t* affected=NULL)
    {
        return execute(query_text, strlen(query_text), params, affected);
    }
    template<typename Params>
    base_database& execute(const std::string& query_text, const Params& params, uint64_t* affected=NULL)
    {
        return execute(query_text.data(), query_text.length(), params, affected);
    }
    template<typename... Params>
    base_database& execute_direct(const char* query_text, size_t text_length, uint64_t* affected, const Params&... params)
    {
        return execute(query_text, text_length, std::forward_as_tuple(params...), affected);
    }
    template<typename... Params>
    base_database& execute_direct(const char* query_text, uint64_t* affected, const Params&... params)
    {
        return execute(query_text, std::forward_as_tuple(params...), affected);
    }
    template<typename... Params>
    base_database& execute_direct(const std::string& query_text, uint64_t* affected, const Params&... params)
    {
        return execute(query_text, std::forward_as_tuple(params...), affected);
    }
    template<typename Params>
    uint64_t insert(const char* query_text, size_t text_length, const Params& params)
    {
        uint64_t id=0;
        T* pThis=static_cast<T*>(this);
        Command command=pThis->open_command(query_text, text_length);
        command.execute(params);
        if(command.affetced_rows()>0)
            id=command.insert_id();
        return id;
    }
    template<typename Params>
    uint64_t insert(const char* query_text, const Params& params)
    {
        return insert(query_text, strlen(query_text), params);
    }
    template<typename Params>
    uint64_t insert(const std::string& query_text, const Params& params)
    {
        return insert(query_text.data(), query_text.length(), params);
    }
    template<typename... Params>
    uint64_t insert_direct(const char* query_text, size_t text_length, const Params&... params)
    {
        return insert(query_text, text_length, std::forward_as_tuple(params...));
    }
    template<typename... Params>
    uint64_t insert_direct(const char* query_text, const Params&... params)
    {
        return insert(query_text, strlen(query_text), std::forward_as_tuple(params...));
    }
    template<typename... Params>
    uint64_t insert_direct(const std::string& query_text, const Params&... params)
    {
        return insert(query_text.data(), query_text.length(), std::forward_as_tuple(params...));
    }
    template<typename Record, typename Params>
    query_result<Command, Record> result(const char* query_text, size_t text_length, const Params& params)
    {
        T* pThis=static_cast<T*>(this);
        Command command=pThis->open_command(query_text, text_length);
        command.execute(params);
        return query_result<Command, Record>(std::move(command));
    }
    template<typename Record, typename Params>
    query_result<Command, Record> result(const char* query_text, const Params& params)
    {
        return result<Record, Params>(query_text, strlen(query_text), params);
    }
    template<typename Record, typename Params>
    query_result<Command, Record> result(const std::string& query_text, const Params& params)
    {
        return result<Record, Params>(query_text.data(), query_text.length(), params);
    }
    template<typename Record>
    query_result<Command, Record> result(const char* query_text, size_t text_length)
    {
        return result<Record>(query_text, text_length, std::make_tuple());
    }
    template<typename Record>
    query_result<Command, Record> result(const char* query_text)
    {
        return result<Record>(query_text, strlen(query_text), std::make_tuple());
    }
    template<typename Record>
    query_result<Command, Record> result(const std::string& query_text)
    {
        return result<Record>(query_text.data(), query_text.length(), std::make_tuple());
    }
    template<typename Params, typename Values, typename ValueProc>
    base_database& query_explicit(const char* query_text, size_t text_length, const Params& params, Values&& values, ValueProc&& proc)
    {
        T* pThis=static_cast<T*>(this);
        Command command=pThis->open_command(query_text, text_length);
        command.execute(params);
        while(command.fetch(std::forward<Values>(values)))
        template <typename Type, typename Ret>
        inline Type make_values(Ret (Type::*)())
        {
            if(!detail::apply(std::forward<ValueProc>(proc), std::forward<Values>(values))) break;
            return Type();
        };
        template <typename Type, typename Ret>
        inline Type make_values(Ret (Type::*)() const)
        {
            return Type();
        };
        template <typename Type, typename Ret, typename... Args>
        inline auto make_values(Ret (Type::*)(Args...)) -> QTL_ARGS_TUPLE(Type, Args)
        {
            return QTL_ARGS_TUPLE(Type, Args)();
        };
        template <typename Type, typename Ret, typename... Args>
        inline auto make_values(Ret (Type::*)(Args...) const) -> QTL_ARGS_TUPLE(Type, Args)
        {
            return QTL_ARGS_TUPLE(Type, Args)();
        };
        template <typename Type, typename Ret, typename Arg>
        inline typename std::decay<Arg>::type make_values_noclass(Ret (Type::*)(Arg))
        {
            return typename std::decay<Arg>::type();
        };
        template <typename Type, typename Ret, typename Arg>
        inline typename std::decay<Arg>::type make_values_noclass(Ret (Type::*)(Arg) const)
        {
            return typename std::decay<Arg>::type();
        };
        template <typename Type, typename Ret, typename Arg, typename... Others>
        inline auto make_values_noclass(Ret (Type::*)(Arg, Others...)) -> QTL_ARGS_TUPLE(Arg, Others)
        {
            return QTL_ARGS_TUPLE(Arg, Others)();
        };
        template <typename Type, typename Ret, typename Arg, typename... Others>
        inline auto make_values_noclass(Ret (Type::*)(Arg, Others...) const) -> QTL_ARGS_TUPLE(Arg, Others)
        {
            return QTL_ARGS_TUPLE(Arg, Others)();
        };
        template <typename Functor, typename = typename std::enable_if<std::is_member_function_pointer<decltype(&Functor::operator())>::value>::type>
        inline auto make_values(const Functor &)
            -> decltype(make_values_noclass(&Functor::operator()))
        {
            return make_values_noclass(&Functor::operator());
        }
        command.close();
        return *this;
        template <typename Command, typename ValueProc>
        inline void fetch_command(Command &command, ValueProc &&proc)
        {
            auto values = make_values(proc);
            typedef decltype(values) values_type;
            while (command.fetch(std::forward<values_type>(values)))
            {
                if (!apply(std::forward<ValueProc>(proc), std::forward<values_type>(values)))
                    break;
            }
        }
        template <typename Command, typename ValueProc, typename... OtherProc>
        inline void fetch_command(Command &command, ValueProc &&proc, OtherProc &&...other)
        {
            fetch_command(command, std::forward<ValueProc>(proc));
            if (command.next_result())
            {
                fetch_command(command, std::forward<OtherProc>(other)...);
            }
        }
    }
    template<typename Params, typename Values, typename ValueProc>
    base_database& query_explicit(const char* query_text, const Params& params, Values&& values, ValueProc&& proc)
    template <typename Command, typename T>
    struct params_binder
    {
        return query_explicit(query_text, strlen(query_text), params, std::forward<Values>(values), std::forward<ValueProc>(proc));
    }
    template<typename Params, typename Values, typename ValueProc>
    base_database& query_explicit(const std::string& query_text, const Params& params, Values&& values, ValueProc&& proc)
    {
        return query_explicit(query_text.data(), query_text.size(), params, std::forward<Values>(values), std::forward<ValueProc>(proc));
    }
    template<typename Values, typename ValueProc>
    base_database& query_explicit(const char* query_text, size_t text_length, Values&& values, ValueProc&& proc)
    {
        return query_explicit(query_text, text_length, std::make_tuple(), std::forward<Values>(values), std::forward<ValueProc>(proc));
    }
    template<typename Values, typename ValueProc>
    base_database& query_explicit(const char* query_text, Values&& values, ValueProc&& proc)
    {
        return query_explicit(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), std::forward<ValueProc>(proc));
    }
    template<typename Values, typename ValueProc>
    base_database& query_explicit(const std::string& query_text, Values&& values, ValueProc&& proc)
    {
        return query_explicit(query_text, std::make_tuple(), std::forward<Values>(values), std::forward<ValueProc>(proc));
    }
    template<typename Params, typename ValueProc>
    base_database& query(const char* query_text, size_t text_length, const Params& params, ValueProc&& proc)
    {
        return query_explicit(query_text, text_length, params, detail::make_values(proc),  std::forward<ValueProc>(proc));
    }
    template<typename Params, typename ValueProc>
    base_database& query(const char* query_text, const Params& params, ValueProc&& proc)
    {
        return query_explicit(query_text, params, detail::make_values(proc),  std::forward<ValueProc>(proc));
    }
    template<typename Params, typename ValueProc>
    base_database& query(const std::string& query_text, const Params& params, ValueProc&& proc)
    {
        return query_explicit(query_text, params, detail::make_values(proc),  std::forward<ValueProc>(proc));
    }
    template<typename ValueProc>
    base_database& query(const char* query_text, size_t text_length, ValueProc&& proc)
    {
        return query_explicit(query_text, text_length, detail::make_values(proc),  std::forward<ValueProc>(proc));
    }
    template<typename ValueProc>
    base_database& query(const char* query_text, ValueProc&& proc)
    {
        return query_explicit(query_text, detail::make_values(proc),  std::forward<ValueProc>(proc));
    }
    template<typename ValueProc>
    base_database& query(const std::string& query_text, ValueProc&& proc)
    {
        return query_explicit(query_text, detail::make_values(proc), std::forward<ValueProc>(proc));
    }
    template<typename Params, typename... ValueProc>
    base_database& query_multi_with_params(const char* query_text, size_t text_length, const Params& params, ValueProc&&... proc)
    {
        T* pThis=static_cast<T*>(this);
        Command command=pThis->open_command(query_text, text_length);
        command.execute(params);
        detail::fetch_command(command, std::forward<ValueProc>(proc)...);
        command.close();
        return *this;
    }
    template<typename Params, typename... ValueProc>
    base_database& query_multi_with_params(const char* query_text, const Params& params, ValueProc&&... proc)
    {
        return query_multi_with_params(query_text, strlen(query_text), params, std::forward<ValueProc>(proc)...);
    }
    template<typename Params, typename... ValueProc>
    base_database& query_multi_with_params(const std::string& query_text, const Params& params, ValueProc&&... proc)
    {
        return query_multi_with_params(query_text.data(), query_text.size(), params, std::forward<ValueProc>(proc)...);
    }
    template<typename... ValueProc>
    base_database& query_multi(const char* query_text, size_t text_length, ValueProc&&... proc)
    {
        return query_multi_with_params<std::tuple<>, ValueProc...>(query_text, text_length, std::make_tuple(), std::forward<ValueProc>(proc)...);
    }
    template<typename... ValueProc>
    base_database& query_multi(const char* query_text, ValueProc&&... proc)
    {
        return query_multi_with_params<std::tuple<>, ValueProc...>(query_text, strlen(query_text), std::make_tuple(), std::forward<ValueProc>(proc)...);
    }
    template<typename... ValueProc>
    base_database& query_multi(const std::string& query_text, ValueProc&&... proc)
    {
        return query_multi_with_params<std::tuple<>, ValueProc...>(query_text.data(), query_text.size(), std::make_tuple(), std::forward<ValueProc>(proc)...);
    }
    template<typename Params, typename Values>
    bool query_first(const char* query_text, size_t text_length, const Params& params, Values&& values)
    {
        first_record fetcher;
        query_explicit(query_text, text_length, params, std::forward<Values>(values), std::ref(fetcher));
        return fetcher;
    }
    template<typename Params, typename Values>
    bool query_first(const char* query_text, const Params& params, Values&& values)
    {
        first_record fetcher;
        query_explicit(query_text, strlen(query_text), params, std::forward<Values>(values), std::ref(fetcher));
        return fetcher;
    }
    template<typename Params, typename Values>
    bool query_first(const std::string& query_text, const Params& params, Values&& values)
    {
        first_record fetcher;
        return query_explicit(query_text, params, values, std::ref(fetcher));
        return fetcher;
    }
    template<typename Values>
    bool query_first(const char* query_text, size_t text_length, Values&& values)
    {
        first_record fetcher;
        return query_explicit(query_text, text_length, std::make_tuple(), std::forward<Values>(values), std::ref(fetcher));
        return fetcher;
    }
    template<typename Values>
    bool query_first(const char* query_text, Values&& values)
    {
        first_record fetcher;
        query_explicit(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), std::ref(fetcher));
        return fetcher;
    }
    template<typename Values>
    bool query_first(const std::string& query_text, Values&& values)
    {
        first_record fetcher;
        return query_explicit(query_text, std::make_tuple(), std::forward<Values>(values), std::ref(fetcher));
        return fetcher;
    }
    template<typename... Values>
    bool query_first_direct(const char* query_text, size_t text_length, Values&... values)
    {
        return query_first(query_text, text_length, std::tie(values...));
    }
    template<typename... Values>
    bool query_first_direct(const char* query_text, Values&... values)
    {
        return query_first(query_text, std::tie(values...));
    }
    template<typename... Values>
    bool query_first_direct(const std::string& query_text, Values&... values)
    {
        return query_first(query_text, std::tie(values...));
    }
protected:
    struct nothing
    {
        template<typename... Values> bool operator()(Values&&...) const {  return true; }
        enum
        {
            size = 1
        };
        inline void operator()(Command &command, const T &param) const
        {
            qtl::bind_param(command, 0, param);
        }
    };
    struct first_record
    template <typename Command, typename... Types>
    struct params_binder<Command, std::tuple<Types...>>
    {
        first_record() : _found(false) { }
        template<typename... Values> bool operator()(Values&&...) { _found = true; return false; }
        operator bool() const { return _found; }
        enum
        {
            size = sizeof...(Types)
        };
        void operator()(Command &command, const std::tuple<Types...> &params) const
        {
            (detail::bind_helper<Command, std::tuple_size<std::tuple<Types...>>::value, Types...>(command))(params);
        }
    };
    template <typename Command, typename Type1, typename Type2>
    struct params_binder<Command, std::pair<Type1, Type2>>
    {
        enum
        {
            size = 2
        };
        void operator()(Command &command, std::pair<Type1, Type2> &&values) const
        {
            qtl::bind_param(command, 0, std::forward<Type1>(values.first));
            qtl::bind_param(command, 1, std::forward<Type2>(values.second));
        }
    };
    template <typename Command, typename T>
    inline void bind_params(Command &command, const T &param)
    {
        params_binder<Command, T> binder;
        binder(command, param);
    }
    template <typename Command, typename T>
    struct record_binder
    {
        inline void operator()(Command &command, T &&value) const
        {
            bind_field(command, static_cast<size_t>(0), std::forward<typename std::remove_reference<T>::type>(value));
        }
    };
    template <typename Command, typename T>
    struct record_binder<Command, std::reference_wrapper<T>>
    {
        inline void operator()(Command &command, std::reference_wrapper<T> &&value) const
        {
            bind_field(command, static_cast<size_t>(0), std::forward<typename std::remove_reference<T>::type>(value.get()));
        }
    };
    template <typename Command, typename... Types>
    struct record_binder<Command, std::tuple<Types...>>
    {
        void operator()(Command &command, std::tuple<Types...> &&values) const
        {
            (detail::bind_helper<Command, std::tuple_size<std::tuple<Types...>>::value, Types...>(command))(std::forward<std::tuple<Types...>>(values));
        }
    };
    template <typename Command, typename Type1, typename Type2>
    struct record_binder<Command, std::pair<Type1, Type2>>
    {
        void operator()(Command &command, std::pair<Type1, Type2> &&values) const
        {
            bind_field(command, static_cast<size_t>(0), std::forward<Type1>(values.first));
            bind_field(command, static_cast<size_t>(1), std::forward<Type2>(values.second));
        }
    };
    template <typename T, typename Tag>
    struct record_with_tag : public T
    {
    };
    template <typename T, typename Pred>
    struct custom_binder_type : public T
    {
        typedef T value_type;
        explicit custom_binder_type(Pred pred) : m_pred(pred) {}
        custom_binder_type(value_type &&v, Pred pred)
            : value_type(std::forward<value_type>(v)), m_pred(pred)
        {
        }
        template <typename... Args>
        custom_binder_type(Pred pred, Args &&...args)
            : value_type(std::forward<Args>(args)...), m_pred(pred)
        {
        }
        template <typename Command>
        void bind(Command &command)
        {
            m_pred(std::forward<T>(*this), command); // Pred maybe member function
        }
    private:
        bool _found;
        Pred m_pred;
    };
};
class blobbuf : public std::streambuf
{
public:
    blobbuf() = default;
    blobbuf(const blobbuf&) = default;
    blobbuf& operator=(const blobbuf&) = default;
    virtual ~blobbuf()
    template <typename T, typename Pred>
    struct custom_binder_type<std::reference_wrapper<T>, Pred> : public std::reference_wrapper<T>
    {
        overflow();
    }
        typedef std::reference_wrapper<T> value_type;
    void swap(blobbuf& other)
    {
        std::swap(m_buf, other.m_buf);
        std::swap(m_size, other.m_size);
        std::swap(m_pos, other.m_pos);
        std::streambuf::swap(other);
    }
protected:
    virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir,
        std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
    {
        if (which&std::ios_base::in)
        explicit custom_binder_type(Pred pred) : m_pred(pred) {}
        custom_binder_type(value_type &&v, Pred pred)
            : value_type(std::forward<value_type>(v)), m_pred(pred)
        {
            pos_type pos = 0;
            pos = seekoff(m_pos, off, dir);
            return seekpos(pos, which);
        }
        return std::streambuf::seekoff(off, dir, which);
        template <typename... Args>
        custom_binder_type(Pred pred, Args &&...args)
            : T(std::forward<Args>(args)...), m_pred(pred)
        {
        }
        template <typename Command>
        void bind(Command &command)
        {
            m_pred(std::forward<T>(*this), command); // Pred maybe member function
        }
    private:
        Pred m_pred;
    };
    template <typename T, typename Pred>
    inline custom_binder_type<T, Pred> custom_bind(T &&v, Pred pred)
    {
        return custom_binder_type<T, Pred>(std::forward<T>(v), pred);
    }
    virtual pos_type seekpos(pos_type pos,
        std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
    template <typename Command, typename T, typename Pred>
    struct record_binder<Command, custom_binder_type<T, Pred>>
    {
        if (pos >= m_size)
            return pos_type(off_type(-1));
        if (which&std::ios_base::out)
        void operator()(Command &command, custom_binder_type<T, Pred> &&values) const
        {
            if (pos < m_pos || pos >= m_pos + off_type(egptr() - pbase()))
            values.bind(command);
        }
    };
    template <typename Command, typename T>
    inline void bind_record(Command &command, T &&value)
    {
        record_binder<Command, T> binder;
        binder(command, std::forward<T>(value));
    }
    template <typename Command, typename Record>
    class query_iterator final
    {
    public:
        using iterator_category = std::forward_iterator_tag;
        using value_type = Record;
        using difference_type = ptrdiff_t;
        using pointer = Record *;
        using reference = Record &;
        explicit query_iterator(Command &command)
            : m_command(command) {}
        Record *operator->() const { return m_record.get(); }
        Record &operator*() const { return *m_record; }
        query_iterator &operator++()
        {
            if (!m_record)
                m_record = std::make_shared<Record>();
            if (m_record.use_count() == 1)
            {
                overflow();
                m_pos = pos;
                setp(m_buf.data(), m_buf.data() + m_buf.size());
                if (!m_command.fetch(std::forward<Record>(*m_record)))
                    m_record.reset();
            }
            else
            {
                pbump(off_type(pos - pabs()));
                std::shared_ptr<Record> record = std::make_shared<Record>();
                if (m_command.fetch(std::forward<Record>(*record)))
                    m_record = record;
                else
                    m_record.reset();
            }
            return *this;
        }
        query_iterator operator++(int)
        {
            query_iterator temp = *this;
            m_record = std::make_shared<Record>();
            if (!m_command.fetch(std::forward<Record>(*m_record)))
                m_record.reset();
            return temp;
        }
        bool operator==(const query_iterator &rhs)
        {
            return &this->m_command == &rhs.m_command &&
                   this->m_record == rhs.m_record;
        }
        bool operator!=(const query_iterator &rhs)
        {
            return !(*this == rhs);
        }
    private:
        Command &m_command;
        std::shared_ptr<Record> m_record;
    };
    template <typename Command, typename Record>
    class query_result final
    {
    public:
        typedef typename query_iterator<Command, Record>::value_type value_type;
        typedef typename query_iterator<Command, Record>::pointer pointer;
        typedef typename query_iterator<Command, Record>::reference reference;
        typedef query_iterator<Command, Record> iterator;
        explicit query_result(Command &&command) : m_command(std::move(command)) {}
        query_result(query_result &&src) : m_command(std::move(src.m_command)) {}
        query_result &operator=(query_result &&src)
        {
            if (this != &src)
                m_command = std::move(src.m_command);
            return *this;
        }
        template <typename Params>
        iterator begin(const Params &params)
        {
            query_iterator<Command, Record> it(m_command);
            ++it;
            return it;
        }
        iterator begin()
        {
            return begin(std::make_tuple());
        }
        iterator end()
        {
            return query_iterator<Command, Record>(m_command);
        }
    private:
        Command m_command;
    };
    template <typename T, class Command>
    class base_database
    {
    public:
        template <typename Params>
        base_database &execute(const char *query_text, size_t text_length, const Params &params, uint64_t *affected = NULL)
        {
            T *pThis = static_cast<T *>(this);
            Command command = pThis->open_command(query_text, text_length);
            command.execute(params);
            if (affected)
                *affected = command.affetced_rows();
            command.close();
            return *this;
        }
        template <typename Params>
        base_database &execute(const char *query_text, const Params &params, uint64_t *affected = NULL)
        {
            return execute(query_text, strlen(query_text), params, affected);
        }
        template <typename Params>
        base_database &execute(const std::string &query_text, const Params &params, uint64_t *affected = NULL)
        {
            return execute(query_text.data(), query_text.length(), params, affected);
        }
        template <typename... Params>
        base_database &execute_direct(const char *query_text, size_t text_length, uint64_t *affected, const Params &...params)
        {
            return execute(query_text, text_length, std::forward_as_tuple(params...), affected);
        }
        template <typename... Params>
        base_database &execute_direct(const char *query_text, uint64_t *affected, const Params &...params)
        {
            return execute(query_text, std::forward_as_tuple(params...), affected);
        }
        template <typename... Params>
        base_database &execute_direct(const std::string &query_text, uint64_t *affected, const Params &...params)
        {
            return execute(query_text, std::forward_as_tuple(params...), affected);
        }
        template <typename Params>
        uint64_t insert(const char *query_text, size_t text_length, const Params &params)
        {
            uint64_t id = 0;
            T *pThis = static_cast<T *>(this);
            Command command = pThis->open_command(query_text, text_length);
            command.execute(params);
            if (command.affetced_rows() > 0)
                id = command.insert_id();
            return id;
        }
        template <typename Params>
        uint64_t insert(const char *query_text, const Params &params)
        {
            return insert(query_text, strlen(query_text), params);
        }
        template <typename Params>
        uint64_t insert(const std::string &query_text, const Params &params)
        {
            return insert(query_text.data(), query_text.length(), params);
        }
        template <typename... Params>
        uint64_t insert_direct(const char *query_text, size_t text_length, const Params &...params)
        {
            return insert(query_text, text_length, std::forward_as_tuple(params...));
        }
        template <typename... Params>
        uint64_t insert_direct(const char *query_text, const Params &...params)
        {
            return insert(query_text, strlen(query_text), std::forward_as_tuple(params...));
        }
        template <typename... Params>
        uint64_t insert_direct(const std::string &query_text, const Params &...params)
        {
            return insert(query_text.data(), query_text.length(), std::forward_as_tuple(params...));
        }
        template <typename Record, typename Params>
        query_result<Command, Record> result(const char *query_text, size_t text_length, const Params &params)
        {
            T *pThis = static_cast<T *>(this);
            Command command = pThis->open_command(query_text, text_length);
            command.execute(params);
            return query_result<Command, Record>(std::move(command));
        }
        template <typename Record, typename Params>
        query_result<Command, Record> result(const char *query_text, const Params &params)
        {
            return result<Record, Params>(query_text, strlen(query_text), params);
        }
        template <typename Record, typename Params>
        query_result<Command, Record> result(const std::string &query_text, const Params &params)
        {
            return result<Record, Params>(query_text.data(), query_text.length(), params);
        }
        template <typename Record>
        query_result<Command, Record> result(const char *query_text, size_t text_length)
        {
            return result<Record>(query_text, text_length, std::make_tuple());
        }
        template <typename Record>
        query_result<Command, Record> result(const char *query_text)
        {
            return result<Record>(query_text, strlen(query_text), std::make_tuple());
        }
        template <typename Record>
        query_result<Command, Record> result(const std::string &query_text)
        {
            return result<Record>(query_text.data(), query_text.length(), std::make_tuple());
        }
        template <typename Params, typename Values, typename ValueProc>
        base_database &query_explicit(const char *query_text, size_t text_length, const Params &params, Values &&values, ValueProc &&proc)
        {
            T *pThis = static_cast<T *>(this);
            Command command = pThis->open_command(query_text, text_length);
            command.execute(params);
            while (command.fetch(std::forward<Values>(values)))
            {
                if (!detail::apply(std::forward<ValueProc>(proc), std::forward<Values>(values)))
                    break;
            }
            command.close();
            return *this;
        }
        template <typename Params, typename Values, typename ValueProc>
        base_database &query_explicit(const char *query_text, const Params &params, Values &&values, ValueProc &&proc)
        {
            return query_explicit(query_text, strlen(query_text), params, std::forward<Values>(values), std::forward<ValueProc>(proc));
        }
        template <typename Params, typename Values, typename ValueProc>
        base_database &query_explicit(const std::string &query_text, const Params &params, Values &&values, ValueProc &&proc)
        {
            return query_explicit(query_text.data(), query_text.size(), params, std::forward<Values>(values), std::forward<ValueProc>(proc));
        }
        template <typename Values, typename ValueProc>
        base_database &query_explicit(const char *query_text, size_t text_length, Values &&values, ValueProc &&proc)
        {
            return query_explicit(query_text, text_length, std::make_tuple(), std::forward<Values>(values), std::forward<ValueProc>(proc));
        }
        template <typename Values, typename ValueProc>
        base_database &query_explicit(const char *query_text, Values &&values, ValueProc &&proc)
        {
            return query_explicit(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), std::forward<ValueProc>(proc));
        }
        template <typename Values, typename ValueProc>
        base_database &query_explicit(const std::string &query_text, Values &&values, ValueProc &&proc)
        {
            return query_explicit(query_text, std::make_tuple(), std::forward<Values>(values), std::forward<ValueProc>(proc));
        }
        template <typename Params, typename ValueProc>
        base_database &query(const char *query_text, size_t text_length, const Params &params, ValueProc &&proc)
        {
            return query_explicit(query_text, text_length, params, detail::make_values(proc), std::forward<ValueProc>(proc));
        }
        template <typename Params, typename ValueProc>
        base_database &query(const char *query_text, const Params &params, ValueProc &&proc)
        {
            return query_explicit(query_text, params, detail::make_values(proc), std::forward<ValueProc>(proc));
        }
        template <typename Params, typename ValueProc>
        base_database &query(const std::string &query_text, const Params &params, ValueProc &&proc)
        {
            return query_explicit(query_text, params, detail::make_values(proc), std::forward<ValueProc>(proc));
        }
        template <typename ValueProc>
        base_database &query(const char *query_text, size_t text_length, ValueProc &&proc)
        {
            return query_explicit(query_text, text_length, detail::make_values(proc), std::forward<ValueProc>(proc));
        }
        template <typename ValueProc>
        base_database &query(const char *query_text, ValueProc &&proc)
        {
            return query_explicit(query_text, detail::make_values(proc), std::forward<ValueProc>(proc));
        }
        template <typename ValueProc>
        base_database &query(const std::string &query_text, ValueProc &&proc)
        {
            return query_explicit(query_text, detail::make_values(proc), std::forward<ValueProc>(proc));
        }
        template <typename Params, typename... ValueProc>
        base_database &query_multi_with_params(const char *query_text, size_t text_length, const Params &params, ValueProc &&...proc)
        {
            T *pThis = static_cast<T *>(this);
            Command command = pThis->open_command(query_text, text_length);
            command.execute(params);
            detail::fetch_command(command, std::forward<ValueProc>(proc)...);
            command.close();
            return *this;
        }
        template <typename Params, typename... ValueProc>
        base_database &query_multi_with_params(const char *query_text, const Params &params, ValueProc &&...proc)
        {
            return query_multi_with_params(query_text, strlen(query_text), params, std::forward<ValueProc>(proc)...);
        }
        template <typename Params, typename... ValueProc>
        base_database &query_multi_with_params(const std::string &query_text, const Params &params, ValueProc &&...proc)
        {
            return query_multi_with_params(query_text.data(), query_text.size(), params, std::forward<ValueProc>(proc)...);
        }
        template <typename... ValueProc>
        base_database &query_multi(const char *query_text, size_t text_length, ValueProc &&...proc)
        {
            return query_multi_with_params<std::tuple<>, ValueProc...>(query_text, text_length, std::make_tuple(), std::forward<ValueProc>(proc)...);
        }
        template <typename... ValueProc>
        base_database &query_multi(const char *query_text, ValueProc &&...proc)
        {
            return query_multi_with_params<std::tuple<>, ValueProc...>(query_text, strlen(query_text), std::make_tuple(), std::forward<ValueProc>(proc)...);
        }
        template <typename... ValueProc>
        base_database &query_multi(const std::string &query_text, ValueProc &&...proc)
        {
            return query_multi_with_params<std::tuple<>, ValueProc...>(query_text.data(), query_text.size(), std::make_tuple(), std::forward<ValueProc>(proc)...);
        }
        template <typename Params, typename Values>
        bool query_first(const char *query_text, size_t text_length, const Params &params, Values &&values)
        {
            first_record fetcher;
            query_explicit(query_text, text_length, params, std::forward<Values>(values), std::ref(fetcher));
            return fetcher;
        }
        template <typename Params, typename Values>
        bool query_first(const char *query_text, const Params &params, Values &&values)
        {
            first_record fetcher;
            query_explicit(query_text, strlen(query_text), params, std::forward<Values>(values), std::ref(fetcher));
            return fetcher;
        }
        template <typename Params, typename Values>
        bool query_first(const std::string &query_text, const Params &params, Values &&values)
        {
            first_record fetcher;
            return query_explicit(query_text, params, values, std::ref(fetcher));
            return fetcher;
        }
        template <typename Values>
        bool query_first(const char *query_text, size_t text_length, Values &&values)
        {
            first_record fetcher;
            return query_explicit(query_text, text_length, std::make_tuple(), std::forward<Values>(values), std::ref(fetcher));
            return fetcher;
        }
        template <typename Values>
        bool query_first(const char *query_text, Values &&values)
        {
            first_record fetcher;
            query_explicit(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), std::ref(fetcher));
            return fetcher;
        }
        template <typename Values>
        bool query_first(const std::string &query_text, Values &&values)
        {
            first_record fetcher;
            return query_explicit(query_text, std::make_tuple(), std::forward<Values>(values), std::ref(fetcher));
            return fetcher;
        }
        template <typename... Values>
        bool query_first_direct(const char *query_text, size_t text_length, Values &...values)
        {
            return query_first(query_text, text_length, std::tie(values...));
        }
        template <typename... Values>
        bool query_first_direct(const char *query_text, Values &...values)
        {
            return query_first(query_text, std::tie(values...));
        }
        template <typename... Values>
        bool query_first_direct(const std::string &query_text, Values &...values)
        {
            return query_first(query_text, std::tie(values...));
        }
    protected:
        struct nothing
        {
            template <typename... Values>
            bool operator()(Values &&...) const { return true; }
        };
        struct first_record
        {
            first_record() : _found(false) {}
            template <typename... Values>
            bool operator()(Values &&...)
            {
                _found = true;
                return false;
            }
            operator bool() const { return _found; }
        private:
            bool _found;
        };
    };
    class blobbuf : public std::streambuf
    {
    public:
        blobbuf() = default;
        blobbuf(const blobbuf &) = default;
        blobbuf &operator=(const blobbuf &) = default;
        virtual ~blobbuf()
        {
            overflow();
        }
        void swap(blobbuf &other)
        {
            std::swap(m_buf, other.m_buf);
            std::swap(m_size, other.m_size);
            std::swap(m_pos, other.m_pos);
            std::streambuf::swap(other);
        }
    protected:
        virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir,
                                 std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
        {
            if (which & std::ios_base::in)
            {
                pos_type pos = 0;
                pos = seekoff(m_pos, off, dir);
                return seekpos(pos, which);
            }
            return std::streambuf::seekoff(off, dir, which);
        }
        virtual pos_type seekpos(pos_type pos,
                                 std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
        {
            if (pos >= m_size)
                return pos_type(off_type(-1));
            if (which & std::ios_base::out)
            {
                if (pos < m_pos || pos >= m_pos + off_type(egptr() - pbase()))
                {
                    overflow();
                    m_pos = pos;
                    setp(m_buf.data(), m_buf.data() + m_buf.size());
                }
                else
                {
                    pbump(off_type(pos - pabs()));
                }
            }
            else if (which & std::ios_base::in)
            {
                if (pos < m_pos || pos >= m_pos + off_type(epptr() - eback()))
                {
                    m_pos = pos;
                    setg(m_buf.data(), m_buf.data(), m_buf.data());
                }
                else
                {
                    gbump(off_type(pos - gabs()));
                }
            }
            return pos;
        }
        virtual std::streamsize showmanyc() override
        {
            return m_size - pabs();
        }
        virtual int_type underflow() override
        {
            if (pptr() > pbase())
                overflow();
            off_type count = egptr() - eback();
            pos_type next_pos = 0;
            if (count == 0 && eback() == m_buf.data())
            {
                setg(m_buf.data(), m_buf.data(), m_buf.data() + m_buf.size());
                count = m_buf.size();
            }
            else
            {
                next_pos = m_pos + pos_type(count);
            }
            if (next_pos >= m_size)
                return traits_type::eof();
            count = std::min(count, m_size - next_pos);
            m_pos = next_pos;
            if (read_blob(m_buf.data(), count, m_pos))
            {
                setg(eback(), eback(), eback() + count);
                return traits_type::to_int_type(*gptr());
            }
            else
            {
                return traits_type::eof();
            }
        }
        else if (which&std::ios_base::in)
        virtual int_type overflow(int_type ch = traits_type::eof()) override
        {
            if (pos < m_pos || pos >= m_pos + off_type(epptr() - eback()))
            if (pptr() != pbase())
            {
                m_pos = pos;
                size_t count = pptr() - pbase();
                write_blob(pbase(), count);
                // auto intersection = interval_intersection(m_pos, egptr() - eback(), m_pos, epptr() - pbase());
                // if (intersection.first != intersection.second)
                //{
                //    commit(intersection.first, intersection.second);
                // }
                m_pos += count;
                setp(pbase(), epptr());
            }
            if (!traits_type::eq_int_type(ch, traits_type::eof()))
            {
                char_type c = traits_type::to_char_type(ch);
                if (m_pos >= m_size)
                    return traits_type::eof();
                write_blob(&c, 1);
                // auto intersection = interval_intersection(m_pos, egptr() - eback(), m_pos, 1);
                // if (intersection.first != intersection.second)
                //{
                //    eback()[intersection.first - m_pos] = c;
                // }
                m_pos += 1;
            }
            return ch;
        }
        virtual int_type pbackfail(int_type c = traits_type::eof()) override
        {
            if (gptr() == 0 || gptr() <= eback() || (!traits_type::eq_int_type(traits_type::eof(), c) && !traits_type::eq(traits_type::to_char_type(c), gptr()[-1])))
            {
                return (traits_type::eof()); // can't put back, fail
            }
            else
            { // back up one position and store put-back character
                gbump(-1);
                if (!traits_type::eq_int_type(traits_type::eof(), c))
                    *gptr() = traits_type::to_char_type(c);
                return (traits_type::not_eof(c));
            }
        }
    private:
        off_type seekoff(off_type position, off_type off, std::ios_base::seekdir dir)
        {
            off_type result = 0;
            switch (dir)
            {
            case std::ios_base::beg:
                result = off;
                break;
            case std::ios_base::cur:
                result = position + off;
                break;
            case std::ios_base::end:
                result = m_size - off;
            }
            if (result > m_size)
                result = m_size;
            return result;
        }
        pos_type gabs() const // absolute offset of input pointer in blob field
        {
            return m_pos + off_type(gptr() - eback());
        }
        pos_type pabs() const // absolute offset of output pointer in blob field
        {
            return m_pos + off_type(pptr() - pbase());
        }
    protected:
        std::vector<char> m_buf;
        pos_type m_size;
        pos_type m_pos; // position in the input sequence
        virtual bool read_blob(char *buffer, off_type &count, pos_type position) = 0;
        virtual void write_blob(const char *buffer, size_t count) = 0;
        void init_buffer(std::ios_base::openmode mode)
        {
            size_t bufsize;
            if (m_size > 0)
                bufsize = std::min<size_t>(blob_buffer_size, m_size);
            else
                bufsize = blob_buffer_size;
            if (mode & std::ios_base::in)
            {
                m_buf.resize(bufsize);
                m_pos = 0;
                setg(m_buf.data(), m_buf.data(), m_buf.data());
            }
            else
            else if (mode & std::ios_base::out)
            {
                gbump(off_type(pos - gabs()));
                m_buf.resize(bufsize);
                m_pos = 0;
                setp(m_buf.data(), m_buf.data() + bufsize);
            }
        }
        return pos;
    }
    };
    virtual std::streamsize showmanyc() override
    typedef std::function<void(std::ostream &)> blob_writer;
    template <typename Database>
    struct transaction
    {
        return m_size - pabs();
    }
        transaction(Database &db) : m_db(db), m_commited(true)
        {
            begin();
        }
        ~transaction()
        {
            rollback();
        }
        void begin()
        {
            if (m_commited)
            {
                m_db.begin_transaction();
                m_commited = false;
            }
        }
        void rollback()
        {
            if (!m_commited)
            {
                m_db.rollback();
                m_commited = true;
            }
        }
        void commit()
        {
            if (!m_commited)
            {
                m_db.commit();
                m_commited = true;
            }
        }
    virtual int_type underflow() override
    private:
        bool m_commited;
        Database &m_db;
    };
    template <typename Command, typename Params, typename... Others>
    inline void execute(Command &command, uint64_t *affected, const Params &params)
    {
        if (pptr() > pbase())
            overflow();
        off_type count = egptr() - eback();
        pos_type next_pos = 0;
        if (count == 0 && eback() == m_buf.data())
        {
            setg(m_buf.data(), m_buf.data(), m_buf.data() + m_buf.size());
            count = m_buf.size();
        }
        else
        {
            next_pos = m_pos + pos_type(count);
        }
        if (next_pos >= m_size)
            return traits_type::eof();
        count = std::min(count, m_size - next_pos);
        m_pos = next_pos;
        if (read_blob(m_buf.data(), count, m_pos))
        {
            setg(eback(), eback(), eback() + count);
            return traits_type::to_int_type(*gptr());
        }
        else
        {
            return traits_type::eof();
        }
        command.reset();
        command.execute(params);
        if (affected)
            *affected += command.affetced_rows();
    }
    virtual int_type overflow(int_type ch = traits_type::eof()) override
    template <typename Command, typename Params, typename... Others>
    inline void execute(Command &command, uint64_t *affected, const Params &params, const Others &...others)
    {
        if (pptr() != pbase())
        {
            size_t count = pptr() - pbase();
            write_blob(pbase(), count);
            //auto intersection = interval_intersection(m_pos, egptr() - eback(), m_pos, epptr() - pbase());
            //if (intersection.first != intersection.second)
            //{
            //    commit(intersection.first, intersection.second);
            //}
            m_pos += count;
            setp(pbase(), epptr());
        }
        if (!traits_type::eq_int_type(ch, traits_type::eof()))
        {
            char_type c = traits_type::to_char_type(ch);
            if (m_pos >= m_size)
                return traits_type::eof();
            write_blob(&c, 1);
            //auto intersection = interval_intersection(m_pos, egptr() - eback(), m_pos, 1);
            //if (intersection.first != intersection.second)
            //{
            //    eback()[intersection.first - m_pos] = c;
            //}
            m_pos += 1;
        }
        return ch;
        execute(command, affected, params);
        execute(command, affected, others...);
    }
    virtual int_type pbackfail(int_type c = traits_type::eof()) override
    {
        if (gptr() == 0
            || gptr() <= eback()
            || (!traits_type::eq_int_type(traits_type::eof(), c)
                && !traits_type::eq(traits_type::to_char_type(c), gptr()[-1])))
        {
            return (traits_type::eof());    // can't put back, fail
        }
        else
        {    // back up one position and store put-back character
            gbump(-1);
            if (!traits_type::eq_int_type(traits_type::eof(), c))
                *gptr() = traits_type::to_char_type(c);
            return (traits_type::not_eof(c));
        }
    }
private:
    off_type seekoff(off_type position, off_type off, std::ios_base::seekdir dir)
    {
        off_type result = 0;
        switch (dir)
        {
        case std::ios_base::beg:
            result = off;
            break;
        case std::ios_base::cur:
            result = position + off;
            break;
        case std::ios_base::end:
            result = m_size - off;
        }
        if (result > m_size)
            result = m_size;
        return result;
    }
    pos_type gabs() const // absolute offset of input pointer in blob field
    {
        return m_pos + off_type(gptr() - eback());
    }
    pos_type pabs() const // absolute offset of output pointer in blob field
    {
        return m_pos + off_type(pptr() - pbase());
    }
protected:
    std::vector<char> m_buf;
    pos_type m_size;
    pos_type m_pos;    //position in the input sequence
    virtual bool read_blob(char* buffer, off_type& count, pos_type position) = 0;
    virtual void write_blob(const char* buffer, size_t count) = 0;
    void init_buffer(std::ios_base::openmode mode)
    {
        size_t bufsize;
        if (m_size > 0)
            bufsize = std::min<size_t>(blob_buffer_size, m_size);
        else
            bufsize = blob_buffer_size;
        if (mode&std::ios_base::in)
        {
            m_buf.resize(bufsize);
            m_pos = 0;
            setg(m_buf.data(), m_buf.data(), m_buf.data());
        }
        else if (mode&std::ios_base::out)
        {
            m_buf.resize(bufsize);
            m_pos = 0;
            setp(m_buf.data(), m_buf.data() + bufsize);
        }
    }
};
typedef std::function<void(std::ostream&)> blob_writer;
template<typename Database>
struct transaction
{
    transaction(Database& db) : m_db(db), m_commited(true)
    {
        begin();
    }
    ~transaction()
    {
        rollback();
    }
    void begin()
    {
        if(m_commited)
        {
            m_db.begin_transaction();
            m_commited=false;
        }
    }
    void rollback()
    {
        if(!m_commited)
        {
            m_db.rollback();
            m_commited=true;
        }
    }
    void commit()
    {
        if(!m_commited)
        {
            m_db.commit();
            m_commited=true;
        }
    }
private:
    bool m_commited;
    Database& m_db;
};
template<typename Command, typename Params, typename... Others>
inline void execute(Command& command, uint64_t* affected, const Params& params)
{
    command.reset();
    command.execute(params);
    if(affected) *affected+=command.affetced_rows();
}
template<typename Command, typename Params, typename... Others>
inline void execute(Command& command, uint64_t* affected, const Params& params, const Others&... others)
{
    execute(command, affected, params);
    execute(command, affected, others...);
}
}
include/qtl_database_pool.hpp
@@ -12,224 +12,228 @@
namespace qtl
{
template<typename Database>
class database_pool
{
public:
    typedef Database value_type;
    typedef std::shared_ptr<Database> pointer;
    database_pool()
        : m_trying_connection(false), m_stop_thread(false)
    template <typename Database>
    class database_pool
    {
    }
    public:
        typedef Database value_type;
        typedef std::shared_ptr<Database> pointer;
    virtual ~database_pool()
    {
        if(m_background_thread.joinable())
        database_pool()
            : m_trying_connection(false), m_stop_thread(false)
        {
            m_stop_thread=true;
            try
            {
                m_background_thread.join();
            }
            catch (std::system_error&)
            {
                //igore the error
            }
        }
        clear();
    }
    pointer get()
    {
        Database* db=popup();
        if(db==NULL && m_trying_connection==false)
            db=create_database();
        return pointer(db, [this](Database* db) {
            recovery(db);
        });
    }
    bool test_alive()
    {
        if(m_databases.empty())
            return false;
        std::unique_lock<std::mutex> lock(m_pool_mutex);
        auto it=m_databases.begin();
        while(it!=m_databases.end())
        virtual ~database_pool()
        {
            Database* db=*it;
            if(!db->is_alive())
            if (m_background_thread.joinable())
            {
                delete db;
                it=m_databases.erase(it);
                m_stop_thread = true;
                try
                {
                    m_background_thread.join();
                }
                catch (std::system_error &)
                {
                    // igore the error
                }
            }
            clear();
        }
        pointer get()
        {
            Database *db = popup();
            if (db == NULL && m_trying_connection == false)
                db = create_database();
            return pointer(db, [this](Database *db)
                           { recovery(db); });
        }
        bool test_alive()
        {
            if (m_databases.empty())
                return false;
            std::unique_lock<std::mutex> lock(m_pool_mutex);
            auto it = m_databases.begin();
            while (it != m_databases.end())
            {
                Database *db = *it;
                if (!db->is_alive())
                {
                    delete db;
                    it = m_databases.erase(it);
                }
                else
                {
                    ++it;
                }
            }
            if (m_databases.empty())
            {
                lock.unlock();
                try_connect();
                return false;
            }
            else
                return true;
        }
    private:
        std::vector<Database *> m_databases;
        std::mutex m_pool_mutex;
        std::atomic<bool> m_trying_connection;
        std::thread m_background_thread;
        bool m_stop_thread;
        virtual Database *new_database() throw() = 0;
        void recovery(Database *db)
        {
            if (db == NULL)
                return;
            if (db->is_alive())
            {
                std::lock_guard<std::mutex> lock(m_pool_mutex);
                m_databases.push_back(db);
            }
            else
            {
                ++it;
                delete db;
                {
                    std::lock_guard<std::mutex> lock(m_pool_mutex);
                    clear();
                }
                try_connect();
            }
        }
        if(m_databases.empty())
        {
            lock.unlock();
            try_connect();
            return false;
        }
        else return true;
    }
private:
    std::vector<Database*> m_databases;
    std::mutex m_pool_mutex;
    std::atomic<bool> m_trying_connection;
    std::thread m_background_thread;
    bool m_stop_thread;
        Database *create_database()
        {
            Database *db = new_database();
            if (db)
                return db;
    virtual Database* new_database() throw()=0;
    void recovery(Database* db)
    {
        if(db==NULL) return;
        if(db->is_alive())
        {
            std::lock_guard<std::mutex> lock(m_pool_mutex);
            m_databases.push_back(db);
        }
        else
        {
            delete db;
            {
                std::lock_guard<std::mutex> lock(m_pool_mutex);
                clear();
            }
            try_connect();
            return NULL;
        }
    }
    Database* create_database()
    {
        Database* db=new_database();
        if(db) return db;
        Database *popup()
        {
            Database *db = NULL;
            std::lock_guard<std::mutex> lock(m_pool_mutex);
            clear();
        }
        try_connect();
        return NULL;
    }
    Database* popup()
    {
        Database* db=NULL;
        std::lock_guard<std::mutex> lock(m_pool_mutex);
        if(!m_databases.empty())
        {
            db=m_databases.back();
            m_databases.pop_back();
        }
        return db;
    }
    void try_connect()
    {
        if(m_trying_connection)
            return;
        m_trying_connection=true;
        try
        {
            m_background_thread=std::thread(&database_pool<Database>::background_connect, this);
        }
        catch (std::system_error&)
        {
            m_trying_connection=false;
        }
    }
    void background_connect()
    {
        Database* db=NULL;
        int interval=1;
        while(db==NULL && m_stop_thread==false)
        {
            db=create_database();
            if(db==NULL)
            if (!m_databases.empty())
            {
                std::this_thread::sleep_for(std::chrono::seconds(interval));
                if(interval<60) interval<<=1;
                db = m_databases.back();
                m_databases.pop_back();
            }
            return db;
        }
        void try_connect()
        {
            if (m_trying_connection)
                return;
            m_trying_connection = true;
            try
            {
                m_background_thread = std::thread(&database_pool<Database>::background_connect, this);
            }
            catch (std::system_error &)
            {
                m_trying_connection = false;
            }
        }
        if(db)
        void background_connect()
        {
            recovery(db);
            Database *db = NULL;
            int interval = 1;
            while (db == NULL && m_stop_thread == false)
            {
                db = create_database();
                if (db == NULL)
                {
                    std::this_thread::sleep_for(std::chrono::seconds(interval));
                    if (interval < 60)
                        interval <<= 1;
                }
            }
            if (db)
            {
                recovery(db);
            }
            m_background_thread.detach();
            m_trying_connection = false;
        }
        m_background_thread.detach();
        m_trying_connection=false;
    }
    void clear()
    {
        std::for_each(m_databases.begin(), m_databases.end(), std::default_delete<Database>());
        m_databases.clear();
    }
};
template<typename T, typename EventLoop, typename Connection>
class async_pool
{
public:
    typedef Connection value_type;
    typedef std::shared_ptr<Connection> pointer;
    async_pool(EventLoop& ev)
        : m_ev(ev), m_trying_connecting(false)
    {
    }
    virtual ~async_pool()
    {
        clear();
    }
    /*
        Handler defines as:
        void handler(const pointer& ptr);
    */
    template<typename Handler>
    void get(Handler&& handler, EventLoop* ev=nullptr)
    {
        Connection* db = popup();
        if(ev==nullptr) ev=&m_ev;
        if(db)
        void clear()
        {
            db->bind(*ev);
            handler(typename Connection::exception_type(), wrap(db));
            std::for_each(m_databases.begin(), m_databases.end(), std::default_delete<Database>());
            m_databases.clear();
        }
        else if (m_trying_connecting == false)
        {
            create_connection(ev, [this, handler](const typename Connection::exception_type& e,  Connection* db) {
                handler(e, wrap(db));
            });
        }
        else
        {
            handler(typename Connection::exception_type(), nullptr);
        }
    }
    };
    void test_alive()
    template <typename T, typename EventLoop, typename Connection>
    class async_pool
    {
        if (m_connections.empty())
            return;
        std::unique_lock<std::mutex> lock(m_pool_mutex);
        auto it = m_connections.begin();
        while (it != m_connections.end())
    public:
        typedef Connection value_type;
        typedef std::shared_ptr<Connection> pointer;
        async_pool(EventLoop &ev)
            : m_ev(ev), m_trying_connecting(false)
        {
            Connection* db = *it;
            db->is_alive([this, db](const typename Connection::exception_type& e) {
        }
        virtual ~async_pool()
        {
            clear();
        }
        /*
            Handler defines as:
            void handler(const pointer& ptr);
        */
        template <typename Handler>
        void get(Handler &&handler, EventLoop *ev = nullptr)
        {
            Connection *db = popup();
            if (ev == nullptr)
                ev = &m_ev;
            if (db)
            {
                db->bind(*ev);
                handler(typename Connection::exception_type(), wrap(db));
            }
            else if (m_trying_connecting == false)
            {
                create_connection(ev, [this, handler](const typename Connection::exception_type &e, Connection *db)
                                  { handler(e, wrap(db)); });
            }
            else
            {
                handler(typename Connection::exception_type(), nullptr);
            }
        }
        void test_alive()
        {
            if (m_connections.empty())
                return;
            std::unique_lock<std::mutex> lock(m_pool_mutex);
            auto it = m_connections.begin();
            while (it != m_connections.end())
            {
                Connection *db = *it;
                db->is_alive([this, db](const typename Connection::exception_type &e)
                             {
                if (e)
                {
                    std::unique_lock<std::mutex> lock(m_pool_mutex);
@@ -238,22 +242,23 @@
                    m_connections.erase(it);
                    if (m_connections.empty())
                        try_connect();
                }
            });
            ++it;
                } });
                ++it;
            }
        }
    }
private:
    EventLoop& m_ev;
    std::vector<Connection*> m_connections;
    std::recursive_mutex m_pool_mutex;
    std::atomic<bool> m_trying_connecting;
    private:
        EventLoop &m_ev;
        std::vector<Connection *> m_connections;
        std::recursive_mutex m_pool_mutex;
        std::atomic<bool> m_trying_connecting;
    void recovery(Connection* db)
    {
        if (db == NULL) return;
        db->is_alive([this, db](const typename Connection::exception_type& e) {
        void recovery(Connection *db)
        {
            if (db == NULL)
                return;
            db->is_alive([this, db](const typename Connection::exception_type &e)
                         {
            if (e)
            {
                {
@@ -268,15 +273,15 @@
                    throw std::runtime_error("destroy a busysing connection.");
                std::lock_guard<std::recursive_mutex> lock(m_pool_mutex);
                m_connections.push_back(db);
            }
        });
    }
            } });
        }
    template<typename Handler>
    void create_connection(EventLoop* ev, Handler&& handler)
    {
        T* pThis = static_cast<T*>(this);
        pThis->new_connection(*ev, [this, handler](const typename Connection::exception_type& e, Connection* db) {
        template <typename Handler>
        void create_connection(EventLoop *ev, Handler &&handler)
        {
            T *pThis = static_cast<T *>(this);
            pThis->new_connection(*ev, [this, handler](const typename Connection::exception_type &e, Connection *db)
                                  {
            handler(e, db);
            if (!db)
            {
@@ -287,29 +292,29 @@
                m_ev.set_timeout(tv, [this]() {
                    try_connect();
                });
            }
        });
    }
    Connection* popup()
    {
        Connection* db = nullptr;
        std::lock_guard<std::recursive_mutex> lock(m_pool_mutex);
        if (!m_connections.empty())
        {
            db = m_connections.back();
            m_connections.pop_back();
            } });
        }
        return db;
    }
    void try_connect()
    {
        if (m_trying_connecting)
            return;
        Connection *popup()
        {
            Connection *db = nullptr;
            std::lock_guard<std::recursive_mutex> lock(m_pool_mutex);
            if (!m_connections.empty())
            {
                db = m_connections.back();
                m_connections.pop_back();
            }
            return db;
        }
        m_trying_connecting = true;
        create_connection(&m_ev, [this](const typename Connection::exception_type& e, Connection* db) {
        void try_connect()
        {
            if (m_trying_connecting)
                return;
            m_trying_connecting = true;
            create_connection(&m_ev, [this](const typename Connection::exception_type &e, Connection *db)
                              {
            if (db)
            {
                std::lock_guard<std::recursive_mutex> lock(m_pool_mutex);
@@ -318,27 +323,26 @@
            else
            {
                m_trying_connecting = false;
            }
        });
    }
    void clear()
    {
        std::for_each(m_connections.begin(), m_connections.end(), std::default_delete<Connection>());
        m_connections.clear();
    }
    pointer wrap(Connection* db)
    {
        if (db)
        {
            return pointer(db, [this](Connection* db) {
                recovery(db);
            });
            } });
        }
        else return nullptr;
    }
};
        void clear()
        {
            std::for_each(m_connections.begin(), m_connections.end(), std::default_delete<Connection>());
            m_connections.clear();
        }
        pointer wrap(Connection *db)
        {
            if (db)
            {
                return pointer(db, [this](Connection *db)
                               { recovery(db); });
            }
            else
                return nullptr;
        }
    };
}
include/qtl_mysql.hpp
@@ -18,1722 +18,1760 @@
#include "qtl_common.hpp"
#include "qtl_async.hpp"
#if LIBMYSQL_VERSION_ID >=80000
#if LIBMYSQL_VERSION_ID >= 80000
typedef bool my_bool;
#endif //MySQL 8
#endif // MySQL 8
#ifdef MARIADB_VERSION_ID
#define IS_LONGDATA(t) ((t) >= MYSQL_TYPE_TINY_BLOB && (t) <= MYSQL_TYPE_STRING)
#endif //MariaDB
#endif // MariaDB
namespace qtl
{
namespace mysql
{
    namespace mysql
    {
struct init
{
    init(int argc=-1, char **argv=nullptr, char **groups=nullptr)
    {
        //my_init();
        mysql_library_init(argc, argv, groups);
    }
    ~init()
    {
        mysql_library_end();
    }
};
struct thread_init
{
    thread_init()
    {
        mysql_thread_init();
    }
    ~thread_init()
    {
        mysql_thread_end();
    }
};
class binder : public MYSQL_BIND
{
    friend class statement;
public:
    binder()
    {
        init();
    }
    void init()
    {
        memset(this, 0, sizeof(MYSQL_BIND));
    }
    void bind()
    {
        init();
        buffer_type=MYSQL_TYPE_NULL;
    }
    void bind(null)
    {
        bind();
    }
    void bind(std::nullptr_t)
    {
        bind();
    }
    void bind(bool& v)
    {
        init();
        buffer_type = MYSQL_TYPE_BIT;
        buffer = &v;
        buffer_length = 1;
    }
    void bind(int8_t& v)
    {
        init();
        buffer_type=MYSQL_TYPE_TINY;
        buffer=&v;
    }
    void bind(uint8_t& v)
    {
        init();
        buffer_type=MYSQL_TYPE_TINY;
        buffer=&v;
        is_unsigned=true;
    }
    void bind(int16_t& v)
    {
        init();
        buffer_type=MYSQL_TYPE_SHORT;
        buffer=&v;
    }
    void bind(uint16_t& v)
    {
        init();
        buffer_type=MYSQL_TYPE_SHORT;
        buffer=&v;
        is_unsigned=true;
    }
    void bind(int32_t& v)
    {
        init();
        buffer_type=MYSQL_TYPE_LONG;
        buffer=&v;
    }
    void bind(uint32_t& v)
    {
        init();
        buffer_type=MYSQL_TYPE_LONG;
        buffer=&v;
        is_unsigned=true;
    }
    void bind(int64_t& v)
    {
        init();
        buffer_type=MYSQL_TYPE_LONGLONG;
        buffer=&v;
    }
    void bind(uint64_t& v)
    {
        init();
        buffer_type=MYSQL_TYPE_LONGLONG;
        buffer=&v;
        is_unsigned=true;
    }
    void bind(double& v)
    {
        init();
        buffer_type=MYSQL_TYPE_DOUBLE;
        buffer=&v;
    }
    void bind(float& v)
    {
        init();
        buffer_type=MYSQL_TYPE_FLOAT;
        buffer=&v;
    }
    void bind(MYSQL_TIME& v, enum_field_types type=MYSQL_TYPE_TIMESTAMP)
    {
        init();
        buffer_type=type;
        buffer=&v;
    }
    void bind(void* data, unsigned long length, enum_field_types type=MYSQL_TYPE_BLOB)
    {
        init();
        buffer_type=type;
        buffer=data;
        buffer_length=length;
    }
    void bind(const const_blob_data& data, enum_field_types type=MYSQL_TYPE_BLOB)
    {
        init();
        buffer_type=type;
        buffer=const_cast<void*>(data.data);
        buffer_length=data.size;
    }
    void bind(blob_data& data, enum_field_types type=MYSQL_TYPE_BLOB)
    {
        init();
        buffer_type=type;
        buffer=data.data;
        buffer_length=data.size;
    }
};
template<typename T>
inline void bind(binder& binder, const T& v)
{
    binder.bind(const_cast<T&>(v));
}
template<typename T>
inline void bind(binder& binder, T&& v)
{
    binder.bind(v);
}
inline void bind(binder& binder, const char* str, size_t length=0)
{
    if(length==0) length=strlen(str);
    binder.bind(const_cast<char*>(str), static_cast<unsigned long>(length), MYSQL_TYPE_VAR_STRING);
}
class base_statement;
class basic_database;
class error : public std::exception
{
public:
    error() : m_error(0) { }
    error(unsigned int err, const char* errmsg) : m_error(err), m_errmsg(errmsg) { }
    explicit error(const base_statement& stmt);
    explicit error(const basic_database& db);
    error(const error& src) = default;
    virtual ~error() throw() { }
    int code() const throw() { return m_error; }
    operator bool() const { return m_error != 0;  }
    virtual const char* what() const NOEXCEPT override { return m_errmsg.data(); }
private:
    unsigned int m_error;
    std::string m_errmsg;
};
class blobbuf : public qtl::blobbuf
{
public:
    blobbuf() : m_stmt(nullptr), m_field(0)
    {
    }
    blobbuf(const blobbuf&) = default;
    blobbuf& operator=(const blobbuf&) = default;
    virtual ~blobbuf() { overflow();  }
    void open(MYSQL_STMT* stmt, int field, const binder& b, std::ios_base::openmode mode)
    {
        if (m_stmt && m_field)
        struct init
        {
            overflow();
        }
        assert(stmt != nullptr);
        m_stmt = stmt;
        m_field = field;
        m_binder = b;
        if (b.length) m_size = *b.length;
        init_buffer(mode);
    }
    void swap(blobbuf& other)
    {
        std::swap(m_stmt, other.m_stmt);
        std::swap(m_binder, other.m_binder);
        std::swap(m_field, other.m_field);
        qtl::blobbuf::swap(other);
    }
private:
    MYSQL_STMT* m_stmt;
    binder m_binder;
    int m_field;
protected:
    virtual bool read_blob(char* buffer, off_type& count, pos_type position) override
    {
        m_binder.buffer = buffer;
        m_binder.buffer_length = count;
        int ret = mysql_stmt_fetch_column(m_stmt, &m_binder, m_field, position);
        switch (ret)
        {
        case 0:
            count = std::min(m_binder.buffer_length, *m_binder.length);
            return true;
        case CR_NO_DATA:
            return false;
        default:
            throw error(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt));
        }
    }
    virtual void write_blob(const char* buffer, size_t count) override
    {
        int ret = mysql_stmt_send_long_data(m_stmt, m_field, buffer, count);
        if (ret != 0)
            throw error(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt));
    }
};
struct time : public MYSQL_TIME
{
    time()
    {
        memset(this, 0, sizeof(MYSQL_TIME));
        time_type = MYSQL_TIMESTAMP_NONE;
    }
    time(const struct tm& tm)
    {
        memset(this, 0, sizeof(MYSQL_TIME));
        year = tm.tm_year + 1900;
        month = tm.tm_mon + 1;
        day = tm.tm_mday;
        hour = tm.tm_hour;
        minute = tm.tm_min;
        second = tm.tm_sec;
        time_type = MYSQL_TIMESTAMP_DATETIME;
    }
    time(time_t value)
    {
        struct tm tm;
#if defined(_MSC_VER)
        localtime_s(&tm, &value);
#elif defined(_POSIX_VERSION)
        localtime_r(&value, &tm);
#else
        tm = *localtime(&value);
#endif
        new(this)time(tm);
    }
    time(const time& src)
    {
        memcpy(this, &src, sizeof(MYSQL_TIME));
    }
    time& operator=(const time& src)
    {
        if (this != &src)
            memcpy(this, &src, sizeof(MYSQL_TIME));
        return *this;
    }
    static time now()
    {
        time_t value;
        ::time(&value);
        return time(value);
    }
    time_t as_tm(struct tm& tm) const
    {
        tm.tm_year = year - 1900;
        tm.tm_mon = month - 1;
        tm.tm_mday = day;
        tm.tm_hour = hour;
        tm.tm_min = minute;
        tm.tm_sec = second;
        return mktime(&tm);
    }
    time_t get_time() const
    {
        struct tm tm;
        return as_tm(tm);
    }
};
class base_statement
{
protected:
    base_statement() : m_stmt(nullptr), m_result(nullptr) {}
    base_statement(const base_statement&) = delete;
    explicit base_statement(basic_database& db);
    base_statement(base_statement&& src)
        : m_stmt(src.m_stmt), m_result(src.m_result),
        m_binders(std::move(src.m_binders)), m_binderAddins(std::move(src.m_binderAddins))
    {
        src.m_stmt=nullptr;
        src.m_result=nullptr;
    }
    base_statement& operator=(const base_statement&) = delete;
    base_statement& operator=(base_statement&& src)
    {
        if(this!=&src)
        {
            m_stmt=src.m_stmt;
            m_result=src.m_result;
            src.m_stmt=nullptr;
            src.m_result=nullptr;
            m_binders=std::move(src.m_binders);
            m_binderAddins=std::move(src.m_binderAddins);
        }
        return *this;
    }
public:
    operator MYSQL_STMT*() { return m_stmt; }
    unsigned int get_parameter_count() const { return mysql_stmt_param_count(m_stmt); }
    unsigned int get_column_count() const { return mysql_stmt_field_count(m_stmt); }
    unsigned long length(unsigned int index) const
    {
        return m_binderAddins[index].m_length;
    }
    bool is_null(unsigned int index) const
    {
        return m_binderAddins[index].m_isNull!=0;
    }
    size_t find_field(const char* name) const
    {
        if(m_result)
        {
            for(size_t i=0; i!=m_result->field_count; i++)
            init(int argc = -1, char **argv = nullptr, char **groups = nullptr)
            {
                if(strncmp(m_result->fields[i].name, name, m_result->fields[i].name_length)==0)
                    return i;
                // my_init();
                mysql_library_init(argc, argv, groups);
            }
        }
        return -1;
    }
    my_ulonglong affetced_rows()
    {
        return mysql_stmt_affected_rows(m_stmt);
    }
    my_ulonglong insert_id()
    {
        return mysql_stmt_insert_id(m_stmt);
    }
    binder* get_binder(unsigned long index)
    {
        return &m_binders[index];
    }
    unsigned int error() const
    {
        return mysql_stmt_errno(m_stmt);
    }
    const char* errmsg() const
    {
        return mysql_stmt_error(m_stmt);
    }
    MYSQL_RES* result() { return m_result; }
    void bind_param(size_t index, const char* param, size_t length)
    {
        bind(m_binders[index], param, length);
    }
    void bind_param(size_t index, const std::nullptr_t&)
    {
        m_binders[index].bind();
    }
    void bind_param(size_t index, std::istream& param)
    {
        m_binders[index].bind(nullptr, 0, MYSQL_TYPE_LONG_BLOB);
        m_binderAddins[index].m_after_fetch = [this, index, &param](const binder&) {
            std::array<char, blob_buffer_size> buffer;
            unsigned long readed = 0;
            while (!param.eof() && !param.fail())
            ~init()
            {
                param.read(buffer.data(), buffer.size());
                readed = (unsigned long)param.gcount();
                if (readed > 0)
                mysql_library_end();
            }
        };
        struct thread_init
        {
            thread_init()
            {
                mysql_thread_init();
            }
            ~thread_init()
            {
                mysql_thread_end();
            }
        };
        class binder : public MYSQL_BIND
        {
            friend class statement;
        public:
            binder()
            {
                init();
            }
            void init()
            {
                memset(this, 0, sizeof(MYSQL_BIND));
            }
            void bind()
            {
                init();
                buffer_type = MYSQL_TYPE_NULL;
            }
            void bind(null)
            {
                bind();
            }
            void bind(std::nullptr_t)
            {
                bind();
            }
            void bind(bool &v)
            {
                init();
                buffer_type = MYSQL_TYPE_BIT;
                buffer = &v;
                buffer_length = 1;
            }
            void bind(int8_t &v)
            {
                init();
                buffer_type = MYSQL_TYPE_TINY;
                buffer = &v;
            }
            void bind(uint8_t &v)
            {
                init();
                buffer_type = MYSQL_TYPE_TINY;
                buffer = &v;
                is_unsigned = true;
            }
            void bind(int16_t &v)
            {
                init();
                buffer_type = MYSQL_TYPE_SHORT;
                buffer = &v;
            }
            void bind(uint16_t &v)
            {
                init();
                buffer_type = MYSQL_TYPE_SHORT;
                buffer = &v;
                is_unsigned = true;
            }
            void bind(int32_t &v)
            {
                init();
                buffer_type = MYSQL_TYPE_LONG;
                buffer = &v;
            }
            void bind(uint32_t &v)
            {
                init();
                buffer_type = MYSQL_TYPE_LONG;
                buffer = &v;
                is_unsigned = true;
            }
            void bind(int64_t &v)
            {
                init();
                buffer_type = MYSQL_TYPE_LONGLONG;
                buffer = &v;
            }
            void bind(uint64_t &v)
            {
                init();
                buffer_type = MYSQL_TYPE_LONGLONG;
                buffer = &v;
                is_unsigned = true;
            }
            void bind(double &v)
            {
                init();
                buffer_type = MYSQL_TYPE_DOUBLE;
                buffer = &v;
            }
            void bind(float &v)
            {
                init();
                buffer_type = MYSQL_TYPE_FLOAT;
                buffer = &v;
            }
            void bind(MYSQL_TIME &v, enum_field_types type = MYSQL_TYPE_TIMESTAMP)
            {
                init();
                buffer_type = type;
                buffer = &v;
            }
            void bind(void *data, unsigned long length, enum_field_types type = MYSQL_TYPE_BLOB)
            {
                init();
                buffer_type = type;
                buffer = data;
                buffer_length = length;
            }
            void bind(const const_blob_data &data, enum_field_types type = MYSQL_TYPE_BLOB)
            {
                init();
                buffer_type = type;
                buffer = const_cast<void *>(data.data);
                buffer_length = data.size;
            }
            void bind(blob_data &data, enum_field_types type = MYSQL_TYPE_BLOB)
            {
                init();
                buffer_type = type;
                buffer = data.data;
                buffer_length = data.size;
            }
        };
        template <typename T>
        inline void bind(binder &binder, const T &v)
        {
            binder.bind(const_cast<T &>(v));
        }
        template <typename T>
        inline void bind(binder &binder, T &&v)
        {
            binder.bind(v);
        }
        inline void bind(binder &binder, const char *str, size_t length = 0)
        {
            if (length == 0)
                length = strlen(str);
            binder.bind(const_cast<char *>(str), static_cast<unsigned long>(length), MYSQL_TYPE_VAR_STRING);
        }
        class base_statement;
        class basic_database;
        class error : public std::exception
        {
        public:
            error() : m_error(0) {}
            error(unsigned int err, const char *errmsg) : m_error(err), m_errmsg(errmsg) {}
            explicit error(const base_statement &stmt);
            explicit error(const basic_database &db);
            error(const error &src) = default;
            virtual ~error() throw() {}
            int code() const throw() { return m_error; }
            operator bool() const { return m_error != 0; }
            virtual const char *what() const NOEXCEPT override { return m_errmsg.data(); }
        private:
            unsigned int m_error;
            std::string m_errmsg;
        };
        class blobbuf : public qtl::blobbuf
        {
        public:
            blobbuf() : m_stmt(nullptr), m_field(0)
            {
            }
            blobbuf(const blobbuf &) = default;
            blobbuf &operator=(const blobbuf &) = default;
            virtual ~blobbuf() { overflow(); }
            void open(MYSQL_STMT *stmt, int field, const binder &b, std::ios_base::openmode mode)
            {
                if (m_stmt && m_field)
                {
                    if (mysql_stmt_send_long_data(m_stmt, index, buffer.data(), readed) != 0)
                        throw_exception();
                    overflow();
                }
                assert(stmt != nullptr);
                m_stmt = stmt;
                m_field = field;
                m_binder = b;
                if (b.length)
                    m_size = *b.length;
                init_buffer(mode);
            }
            void swap(blobbuf &other)
            {
                std::swap(m_stmt, other.m_stmt);
                std::swap(m_binder, other.m_binder);
                std::swap(m_field, other.m_field);
                qtl::blobbuf::swap(other);
            }
        private:
            MYSQL_STMT *m_stmt;
            binder m_binder;
            int m_field;
        protected:
            virtual bool read_blob(char *buffer, off_type &count, pos_type position) override
            {
                m_binder.buffer = buffer;
                m_binder.buffer_length = count;
                int ret = mysql_stmt_fetch_column(m_stmt, &m_binder, m_field, position);
                switch (ret)
                {
                case 0:
                    count = std::min(m_binder.buffer_length, *m_binder.length);
                    return true;
                case CR_NO_DATA:
                    return false;
                default:
                    throw error(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt));
                }
            }
        };
    }
    void bind_param(size_t index, const blob_writer& param)
    {
        m_binders[index].bind(nullptr, 0, MYSQL_TYPE_LONG_BLOB);
        m_binderAddins[index].m_after_fetch = [this, index, &param](const binder& b) {
            blobbuf buf;
            buf.open(m_stmt, index, b, std::ios::out);
            std::ostream s(&buf);
            param(s);
        };
    }
    template<class Param>
    void bind_param(size_t index, const Param& param)
    {
        bind(m_binders[index], param);
    }
    template<class Type>
    void bind_field(size_t index, Type&& value)
    {
        if (m_result)
        {
            bind(m_binders[index], std::forward<Type>(value));
            m_binderAddins[index].m_after_fetch =
                if_null<typename std::remove_reference<Type>::type>(value);
        }
    }
    void bind_field(size_t index, char* value, size_t length)
    {
        m_binders[index].bind(value, length - 1, MYSQL_TYPE_VAR_STRING);
        m_binderAddins[index].m_after_fetch = [](const binder& bind) {
            if (*bind.is_null)
                memset(bind.buffer, 0, bind.buffer_length + 1);
            else
            virtual void write_blob(const char *buffer, size_t count) override
            {
                char* text = reinterpret_cast<char*>(bind.buffer);
                text[*bind.length] = '\0';
                int ret = mysql_stmt_send_long_data(m_stmt, m_field, buffer, count);
                if (ret != 0)
                    throw error(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt));
            }
        };
    }
    template<size_t N>
    void bind_field(size_t index, std::array<char, N>&& value)
    {
        bind_field(index, value.data(), value.size());
    }
    template<typename T>
    void bind_field(size_t index, bind_string_helper<T>&& value)
    {
        if (m_result)
        struct time : public MYSQL_TIME
        {
            MYSQL_FIELD* field = mysql_fetch_field_direct(m_result, (unsigned int)index);
            if (field == nullptr) throw_exception();
            value.clear();
            typename bind_string_helper<T>::char_type* data = value.alloc(field->length);
            m_binderAddins[index].m_before_fetch = [this, value](binder& b) mutable {
                if (value.size() < b.buffer_length)
            time()
            {
                memset(this, 0, sizeof(MYSQL_TIME));
                time_type = MYSQL_TIMESTAMP_NONE;
            }
            time(const struct tm &tm)
            {
                memset(this, 0, sizeof(MYSQL_TIME));
                year = tm.tm_year + 1900;
                month = tm.tm_mon + 1;
                day = tm.tm_mday;
                hour = tm.tm_hour;
                minute = tm.tm_min;
                second = tm.tm_sec;
                time_type = MYSQL_TIMESTAMP_DATETIME;
            }
            time(time_t value)
            {
                struct tm tm;
#if defined(_MSC_VER)
                localtime_s(&tm, &value);
#elif defined(_POSIX_VERSION)
                localtime_r(&value, &tm);
#else
                tm = *localtime(&value);
#endif
                new (this) time(tm);
            }
            time(const time &src)
            {
                memcpy(this, &src, sizeof(MYSQL_TIME));
            }
            time &operator=(const time &src)
            {
                if (this != &src)
                    memcpy(this, &src, sizeof(MYSQL_TIME));
                return *this;
            }
            static time now()
            {
                time_t value;
                ::time(&value);
                return time(value);
            }
            time_t as_tm(struct tm &tm) const
            {
                tm.tm_year = year - 1900;
                tm.tm_mon = month - 1;
                tm.tm_mday = day;
                tm.tm_hour = hour;
                tm.tm_min = minute;
                tm.tm_sec = second;
                return mktime(&tm);
            }
            time_t get_time() const
            {
                struct tm tm;
                return as_tm(tm);
            }
        };
        class base_statement
        {
        protected:
            base_statement() : m_stmt(nullptr), m_result(nullptr) {}
            base_statement(const base_statement &) = delete;
            explicit base_statement(basic_database &db);
            base_statement(base_statement &&src)
                : m_stmt(src.m_stmt), m_result(src.m_result),
                  m_binders(std::move(src.m_binders)), m_binderAddins(std::move(src.m_binderAddins))
            {
                src.m_stmt = nullptr;
                src.m_result = nullptr;
            }
            base_statement &operator=(const base_statement &) = delete;
            base_statement &operator=(base_statement &&src)
            {
                if (this != &src)
                {
                    value.alloc(b.buffer_length);
                    if (b.buffer != value.data())
                    m_stmt = src.m_stmt;
                    m_result = src.m_result;
                    src.m_stmt = nullptr;
                    src.m_result = nullptr;
                    m_binders = std::move(src.m_binders);
                    m_binderAddins = std::move(src.m_binderAddins);
                }
                return *this;
            }
        public:
            operator MYSQL_STMT *() { return m_stmt; }
            unsigned int get_parameter_count() const { return mysql_stmt_param_count(m_stmt); }
            unsigned int get_column_count() const { return mysql_stmt_field_count(m_stmt); }
            unsigned long length(unsigned int index) const
            {
                return m_binderAddins[index].m_length;
            }
            bool is_null(unsigned int index) const
            {
                return m_binderAddins[index].m_isNull != 0;
            }
            size_t find_field(const char *name) const
            {
                if (m_result)
                {
                    for (size_t i = 0; i != m_result->field_count; i++)
                    {
                        b.buffer = const_cast<char*>(value.data());
                        mysql_stmt_bind_result(m_stmt, &m_binders.front());
                        if (strncmp(m_result->fields[i].name, name, m_result->fields[i].name_length) == 0)
                            return i;
                    }
                }
            };
            m_binderAddins[index].m_after_fetch = [value](const binder& b) mutable {
                if (*b.is_null) value.clear();
                else value.truncate(*b.length);
            };
            m_binders[index].bind((void*)data, field->length, field->type);
        }
    }
                return -1;
            }
    void bind_field(size_t index, std::ostream&& value)
    {
        if (m_result)
        {
            MYSQL_FIELD* field = mysql_fetch_field_direct(m_result, (unsigned int)index);
            assert(IS_LONGDATA(field->type));
            m_binders[index].bind(nullptr, 0, field->type);
            m_binderAddins[index].m_after_fetch = [this, index, &value](const binder& b) {
                unsigned long readed = 0;
                std::array<char, blob_buffer_size> buffer;
                binder& bb = const_cast<binder&>(b);
                if (*b.is_null) return;
                bb.buffer = const_cast<char*>(buffer.data());
                bb.buffer_length = buffer.size();
                while (readed <= *b.length)
            my_ulonglong affetced_rows()
            {
                return mysql_stmt_affected_rows(m_stmt);
            }
            my_ulonglong insert_id()
            {
                return mysql_stmt_insert_id(m_stmt);
            }
            binder *get_binder(unsigned long index)
            {
                return &m_binders[index];
            }
            unsigned int error() const
            {
                return mysql_stmt_errno(m_stmt);
            }
            const char *errmsg() const
            {
                return mysql_stmt_error(m_stmt);
            }
            MYSQL_RES *result() { return m_result; }
            void bind_param(size_t index, const char *param, size_t length)
            {
                bind(m_binders[index], param, length);
            }
            void bind_param(size_t index, const std::nullptr_t &)
            {
                m_binders[index].bind();
            }
            void bind_param(size_t index, std::istream &param)
            {
                m_binders[index].bind(nullptr, 0, MYSQL_TYPE_LONG_BLOB);
                m_binderAddins[index].m_after_fetch = [this, index, &param](const binder &)
                {
                    int ret = mysql_stmt_fetch_column(m_stmt, &bb, index, readed);
                    if (ret != 0)
                        throw_exception();
                    value.write(buffer.data(), std::min(b.buffer_length, *b.length - b.offset));
                    readed += bb.buffer_length;
                    std::array<char, blob_buffer_size> buffer;
                    unsigned long readed = 0;
                    while (!param.eof() && !param.fail())
                    {
                        param.read(buffer.data(), buffer.size());
                        readed = (unsigned long)param.gcount();
                        if (readed > 0)
                        {
                            if (mysql_stmt_send_long_data(m_stmt, index, buffer.data(), readed) != 0)
                                throw_exception();
                        }
                    }
                };
            }
            void bind_param(size_t index, const blob_writer &param)
            {
                m_binders[index].bind(nullptr, 0, MYSQL_TYPE_LONG_BLOB);
                m_binderAddins[index].m_after_fetch = [this, index, &param](const binder &b)
                {
                    blobbuf buf;
                    buf.open(m_stmt, index, b, std::ios::out);
                    std::ostream s(&buf);
                    param(s);
                };
            }
            template <class Param>
            void bind_param(size_t index, const Param &param)
            {
                bind(m_binders[index], param);
            }
            template <class Type>
            void bind_field(size_t index, Type &&value)
            {
                if (m_result)
                {
                    bind(m_binders[index], std::forward<Type>(value));
                    m_binderAddins[index].m_after_fetch =
                        if_null<typename std::remove_reference<Type>::type>(value);
                }
            };
        }
    }
            }
    void bind_field(size_t index, blobbuf&& value)
    {
        if (m_result)
        {
            MYSQL_FIELD* field = mysql_fetch_field_direct(m_result, (unsigned int)index);
            assert(IS_LONGDATA(field->type));
            m_binders[index].bind(nullptr, 0, field->type);
            m_binderAddins[index].m_after_fetch = [this, index, &value](const binder& b) {
                if (*b.is_null) return;
                value.open(m_stmt, index, b, std::ios::in);
            };
        }
    }
            void bind_field(size_t index, char *value, size_t length)
            {
                m_binders[index].bind(value, length - 1, MYSQL_TYPE_VAR_STRING);
                m_binderAddins[index].m_after_fetch = [](const binder &bind)
                {
                    if (*bind.is_null)
                        memset(bind.buffer, 0, bind.buffer_length + 1);
                    else
                    {
                        char *text = reinterpret_cast<char *>(bind.buffer);
                        text[*bind.length] = '\0';
                    }
                };
            }
    template<typename Type>
    void bind_field(size_t index, indicator<Type>&& value)
    {
        if (m_result)
        {
            qtl::bind_field(*this, index, value.data);
            binder_addin& addin = m_binderAddins[index];
            auto fetch_fun = addin.m_after_fetch;
            addin.m_after_fetch = [&addin, fetch_fun, &value](const binder& b) {
                value.is_null = *b.is_null != 0;
                value.length = *b.length;
                value.is_truncated = addin.is_truncated;
                if (fetch_fun) fetch_fun(b);
            };
        }
    }
            template <size_t N>
            void bind_field(size_t index, std::array<char, N> &&value)
            {
                bind_field(index, value.data(), value.size());
            }
            template <typename T>
            void bind_field(size_t index, bind_string_helper<T> &&value)
            {
                if (m_result)
                {
                    MYSQL_FIELD *field = mysql_fetch_field_direct(m_result, (unsigned int)index);
                    if (field == nullptr)
                        throw_exception();
                    value.clear();
                    typename bind_string_helper<T>::char_type *data = value.alloc(field->length);
                    m_binderAddins[index].m_before_fetch = [this, value](binder &b) mutable
                    {
                        if (value.size() < b.buffer_length)
                        {
                            value.alloc(b.buffer_length);
                            if (b.buffer != value.data())
                            {
                                b.buffer = const_cast<char *>(value.data());
                                mysql_stmt_bind_result(m_stmt, &m_binders.front());
                            }
                        }
                    };
                    m_binderAddins[index].m_after_fetch = [value](const binder &b) mutable
                    {
                        if (*b.is_null)
                            value.clear();
                        else
                            value.truncate(*b.length);
                    };
                    m_binders[index].bind((void *)data, field->length, field->type);
                }
            }
            void bind_field(size_t index, std::ostream &&value)
            {
                if (m_result)
                {
                    MYSQL_FIELD *field = mysql_fetch_field_direct(m_result, (unsigned int)index);
                    assert(IS_LONGDATA(field->type));
                    m_binders[index].bind(nullptr, 0, field->type);
                    m_binderAddins[index].m_after_fetch = [this, index, &value](const binder &b)
                    {
                        unsigned long readed = 0;
                        std::array<char, blob_buffer_size> buffer;
                        binder &bb = const_cast<binder &>(b);
                        if (*b.is_null)
                            return;
                        bb.buffer = const_cast<char *>(buffer.data());
                        bb.buffer_length = buffer.size();
                        while (readed <= *b.length)
                        {
                            int ret = mysql_stmt_fetch_column(m_stmt, &bb, index, readed);
                            if (ret != 0)
                                throw_exception();
                            value.write(buffer.data(), std::min(b.buffer_length, *b.length - b.offset));
                            readed += bb.buffer_length;
                        }
                    };
                }
            }
            void bind_field(size_t index, blobbuf &&value)
            {
                if (m_result)
                {
                    MYSQL_FIELD *field = mysql_fetch_field_direct(m_result, (unsigned int)index);
                    assert(IS_LONGDATA(field->type));
                    m_binders[index].bind(nullptr, 0, field->type);
                    m_binderAddins[index].m_after_fetch = [this, index, &value](const binder &b)
                    {
                        if (*b.is_null)
                            return;
                        value.open(m_stmt, index, b, std::ios::in);
                    };
                }
            }
            template <typename Type>
            void bind_field(size_t index, indicator<Type> &&value)
            {
                if (m_result)
                {
                    qtl::bind_field(*this, index, value.data);
                    binder_addin &addin = m_binderAddins[index];
                    auto fetch_fun = addin.m_after_fetch;
                    addin.m_after_fetch = [&addin, fetch_fun, &value](const binder &b)
                    {
                        value.is_null = *b.is_null != 0;
                        value.length = *b.length;
                        value.is_truncated = addin.is_truncated;
                        if (fetch_fun)
                            fetch_fun(b);
                    };
                }
            }
#ifdef _QTL_ENABLE_CPP17
    template<typename T>
    inline void bind_field(size_t index, std::optional<T>&& value)
    {
        if (m_result)
        {
            qtl::bind_field(*this, index, *value);
            binder_addin& addin = m_binderAddins[index];
            auto fetch_fun = addin.m_after_fetch;
            addin.m_after_fetch = [&addin, fetch_fun, &value](const binder& b) {
                if (fetch_fun) fetch_fun(b);
                if (*b.is_null) value.reset();
            };
        }
    }
    inline void bind_field(size_t index, std::any&& value)
    {
        if (m_result)
        {
            MYSQL_FIELD* field = mysql_fetch_field_direct(m_result, (unsigned int)index);
            if (field == nullptr) throw_exception();
            switch (field->type)
            template <typename T>
            inline void bind_field(size_t index, std::optional<T> &&value)
            {
            case MYSQL_TYPE_NULL:
                value.reset();
                break;
            case MYSQL_TYPE_BIT:
                value.emplace<bool>();
                bind_field(index, std::any_cast<bool&>(value));
                break;
            case MYSQL_TYPE_TINY:
                value.emplace<int8_t>();
                bind_field(index, std::any_cast<int8_t&>(value));
                break;
            case MYSQL_TYPE_SHORT:
                value.emplace<int16_t>();
                bind_field(index, std::any_cast<int16_t&>(value));
                break;
            case MYSQL_TYPE_LONG:
                value.emplace<int32_t>();
                bind_field(index, std::any_cast<int32_t&>(value));
                break;
            case MYSQL_TYPE_LONGLONG:
                value.emplace<int64_t>();
                bind_field(index, std::any_cast<int64_t&>(value));
                break;
            case MYSQL_TYPE_FLOAT:
                value.emplace<float>();
                bind_field(index, std::any_cast<float&>(value));
                break;
            case MYSQL_TYPE_DOUBLE:
                value.emplace<double>();
                bind_field(index, std::any_cast<double&>(value));
                break;
            case MYSQL_TYPE_DATE:
            case MYSQL_TYPE_TIME:
            case MYSQL_TYPE_DATETIME:
            case MYSQL_TYPE_TIMESTAMP:
            case MYSQL_TYPE_TIMESTAMP2:
            case MYSQL_TYPE_DATETIME2:
            case MYSQL_TYPE_TIME2:
                value.emplace<qtl::mysql::time>();
                bind_field(index, std::any_cast<qtl::mysql::time&>(value));
                break;
            case MYSQL_TYPE_VARCHAR:
            case MYSQL_TYPE_VAR_STRING:
            case MYSQL_TYPE_STRING:
            case MYSQL_TYPE_ENUM:
#if LIBMYSQL_VERSION_ID >= 50700
            case MYSQL_TYPE_JSON:
#endif
            case MYSQL_TYPE_DECIMAL:
            case MYSQL_TYPE_NEWDECIMAL:
            case MYSQL_TYPE_GEOMETRY:
                value.emplace<std::string>();
                bind_field(index, qtl::bind_string(std::any_cast<std::string&>(value)));
                break;
            case MYSQL_TYPE_TINY_BLOB:
            case MYSQL_TYPE_MEDIUM_BLOB:
            case MYSQL_TYPE_BLOB:
            case MYSQL_TYPE_LONG_BLOB:
                value.emplace<blobbuf>();
                bind_field(index, std::forward<blobbuf>(std::any_cast<blobbuf&>(value)));
                break;
            default:
                throw mysql::error(CR_UNSUPPORTED_PARAM_TYPE, "Unsupported field type");
                if (m_result)
                {
                    qtl::bind_field(*this, index, *value);
                    binder_addin &addin = m_binderAddins[index];
                    auto fetch_fun = addin.m_after_fetch;
                    addin.m_after_fetch = [&addin, fetch_fun, &value](const binder &b)
                    {
                        if (fetch_fun)
                            fetch_fun(b);
                        if (*b.is_null)
                            value.reset();
                    };
                }
            }
            binder_addin& addin = m_binderAddins[index];
            auto fetch_fun = addin.m_after_fetch;
            addin.m_after_fetch = [&addin, fetch_fun, &value](const binder& b) {
                if (fetch_fun) fetch_fun(b);
                if (*b.is_null) value.reset();
            };
        }
    }
            inline void bind_field(size_t index, std::any &&value)
            {
                if (m_result)
                {
                    MYSQL_FIELD *field = mysql_fetch_field_direct(m_result, (unsigned int)index);
                    if (field == nullptr)
                        throw_exception();
                    switch (field->type)
                    {
                    case MYSQL_TYPE_NULL:
                        value.reset();
                        break;
                    case MYSQL_TYPE_BIT:
                        value.emplace<bool>();
                        bind_field(index, std::any_cast<bool &>(value));
                        break;
                    case MYSQL_TYPE_TINY:
                        value.emplace<int8_t>();
                        bind_field(index, std::any_cast<int8_t &>(value));
                        break;
                    case MYSQL_TYPE_SHORT:
                        value.emplace<int16_t>();
                        bind_field(index, std::any_cast<int16_t &>(value));
                        break;
                    case MYSQL_TYPE_LONG:
                        value.emplace<int32_t>();
                        bind_field(index, std::any_cast<int32_t &>(value));
                        break;
                    case MYSQL_TYPE_LONGLONG:
                        value.emplace<int64_t>();
                        bind_field(index, std::any_cast<int64_t &>(value));
                        break;
                    case MYSQL_TYPE_FLOAT:
                        value.emplace<float>();
                        bind_field(index, std::any_cast<float &>(value));
                        break;
                    case MYSQL_TYPE_DOUBLE:
                        value.emplace<double>();
                        bind_field(index, std::any_cast<double &>(value));
                        break;
                    case MYSQL_TYPE_DATE:
                    case MYSQL_TYPE_TIME:
                    case MYSQL_TYPE_DATETIME:
                    case MYSQL_TYPE_TIMESTAMP:
                    case MYSQL_TYPE_TIMESTAMP2:
                    case MYSQL_TYPE_DATETIME2:
                    case MYSQL_TYPE_TIME2:
                        value.emplace<qtl::mysql::time>();
                        bind_field(index, std::any_cast<qtl::mysql::time &>(value));
                        break;
                    case MYSQL_TYPE_VARCHAR:
                    case MYSQL_TYPE_VAR_STRING:
                    case MYSQL_TYPE_STRING:
                    case MYSQL_TYPE_ENUM:
#if LIBMYSQL_VERSION_ID >= 50700
                    case MYSQL_TYPE_JSON:
#endif
                    case MYSQL_TYPE_DECIMAL:
                    case MYSQL_TYPE_NEWDECIMAL:
                    case MYSQL_TYPE_GEOMETRY:
                        value.emplace<std::string>();
                        bind_field(index, qtl::bind_string(std::any_cast<std::string &>(value)));
                        break;
                    case MYSQL_TYPE_TINY_BLOB:
                    case MYSQL_TYPE_MEDIUM_BLOB:
                    case MYSQL_TYPE_BLOB:
                    case MYSQL_TYPE_LONG_BLOB:
                        value.emplace<blobbuf>();
                        bind_field(index, std::forward<blobbuf>(std::any_cast<blobbuf &>(value)));
                        break;
                    default:
                        throw mysql::error(CR_UNSUPPORTED_PARAM_TYPE, "Unsupported field type");
                    }
                    binder_addin &addin = m_binderAddins[index];
                    auto fetch_fun = addin.m_after_fetch;
                    addin.m_after_fetch = [&addin, fetch_fun, &value](const binder &b)
                    {
                        if (fetch_fun)
                            fetch_fun(b);
                        if (*b.is_null)
                            value.reset();
                    };
                }
            }
#endif // C++17
    void close()
    {
        if (m_result)
        {
            mysql_free_result(m_result);
            m_result = nullptr;
        }
        if (m_stmt)
        {
            mysql_stmt_close(m_stmt);
            m_stmt = nullptr;
        }
    }
protected:
    MYSQL_STMT* m_stmt;
    MYSQL_RES* m_result;
    std::vector<binder> m_binders;
    struct binder_addin
    {
        unsigned long m_length;
        my_bool m_isNull;
        my_bool m_error;
        bool is_truncated;
        std::function<void(binder&)> m_before_fetch;
        std::function<void(const binder&)> m_after_fetch;
    };
    std::vector<binder_addin> m_binderAddins;
    void resize_binders(size_t n)
    {
        m_binders.resize(n);
        m_binderAddins.resize(n);
    }
    void set_binders()
    {
        for(size_t i=0; i!=m_binders.size(); i++)
        {
            m_binderAddins[i].m_length=0;
            m_binders[i].length=&m_binderAddins[i].m_length;
            m_binderAddins[i].m_isNull=0;
            m_binders[i].is_null=&m_binderAddins[i].m_isNull;
            m_binderAddins[i].m_error=0;
            m_binders[i].error=&m_binderAddins[i].m_error;
        }
    }
    void throw_exception() const { throw mysql::error(*this); }
    template<typename Value>
    struct if_null
    {
        explicit if_null(Value& value, Value&& def=Value()) : m_value(value), m_def(std::move(def)) { }
        void operator()(const binder& b)
        {
            if(*b.is_null) m_value=m_def;
        }
        Value& m_value;
        Value m_def;
    };
};
class statement : public base_statement
{
public:
    statement() = default;
    explicit statement(basic_database& db) : base_statement(db) { }
    statement(statement&& src) : base_statement(std::move(src)) { }
    statement& operator=(statement&& src)
    {
        base_statement::operator =(std::move(src));
        return *this;
    }
    ~statement()
    {
        close();
    }
    void open(const char *query_text, unsigned long text_length=0)
    {
        mysql_stmt_reset(m_stmt);
        if(text_length==0) text_length=(unsigned long)strlen(query_text);
        if(mysql_stmt_prepare(m_stmt, query_text, text_length)!=0)
            throw_exception();
    }
    void execute()
    {
        resize_binders(0);
        if(mysql_stmt_execute(m_stmt)!=0)
            throw_exception();
    }
    template<typename BindProc>
    void execute_custom(BindProc&& bind_proc)
    {
        unsigned long count = mysql_stmt_param_count(m_stmt);
        if (count > 0)
        {
            resize_binders(count);
            bind_proc(*this);
            if (mysql_stmt_bind_param(m_stmt, &m_binders.front()))
                throw_exception();
            for (size_t i = 0; i != count; i++)
            void close()
            {
                if (m_binderAddins[i].m_after_fetch)
                    m_binderAddins[i].m_after_fetch(m_binders[i]);
                if (m_result)
                {
                    mysql_free_result(m_result);
                    m_result = nullptr;
                }
                if (m_stmt)
                {
                    mysql_stmt_close(m_stmt);
                    m_stmt = nullptr;
                }
            }
        }
        if (mysql_stmt_execute(m_stmt) != 0)
            throw_exception();
    }
    template<typename Types>
    void execute(const Types& params)
    {
        execute_custom([&params](statement& stmt) {
            qtl::bind_params(stmt, params);
        });
    }
        protected:
            MYSQL_STMT *m_stmt;
            MYSQL_RES *m_result;
            std::vector<binder> m_binders;
    template<typename Types>
    bool fetch(Types&& values)
    {
        if(m_result==nullptr)
        {
            unsigned long count=mysql_stmt_field_count(m_stmt);
            if(count>0)
            struct binder_addin
            {
                m_result=mysql_stmt_result_metadata(m_stmt);
                if(m_result==nullptr) throw_exception();
                resize_binders(count);
                qtl::bind_record(*this, std::forward<Types>(values));
                set_binders();
                if(mysql_stmt_bind_result(m_stmt, m_binders.data())!=0)
                unsigned long m_length;
                my_bool m_isNull;
                my_bool m_error;
                bool is_truncated;
                std::function<void(binder &)> m_before_fetch;
                std::function<void(const binder &)> m_after_fetch;
            };
            std::vector<binder_addin> m_binderAddins;
            void resize_binders(size_t n)
            {
                m_binders.resize(n);
                m_binderAddins.resize(n);
            }
            void set_binders()
            {
                for (size_t i = 0; i != m_binders.size(); i++)
                {
                    m_binderAddins[i].m_length = 0;
                    m_binders[i].length = &m_binderAddins[i].m_length;
                    m_binderAddins[i].m_isNull = 0;
                    m_binders[i].is_null = &m_binderAddins[i].m_isNull;
                    m_binderAddins[i].m_error = 0;
                    m_binders[i].error = &m_binderAddins[i].m_error;
                }
            }
            void throw_exception() const { throw mysql::error(*this); }
            template <typename Value>
            struct if_null
            {
                explicit if_null(Value &value, Value &&def = Value()) : m_value(value), m_def(std::move(def)) {}
                void operator()(const binder &b)
                {
                    if (*b.is_null)
                        m_value = m_def;
                }
                Value &m_value;
                Value m_def;
            };
        };
        class statement : public base_statement
        {
        public:
            statement() = default;
            explicit statement(basic_database &db) : base_statement(db) {}
            statement(statement &&src) : base_statement(std::move(src)) {}
            statement &operator=(statement &&src)
            {
                base_statement::operator=(std::move(src));
                return *this;
            }
            ~statement()
            {
                close();
            }
            void open(const char *query_text, unsigned long text_length = 0)
            {
                mysql_stmt_reset(m_stmt);
                if (text_length == 0)
                    text_length = (unsigned long)strlen(query_text);
                if (mysql_stmt_prepare(m_stmt, query_text, text_length) != 0)
                    throw_exception();
            }
        }
        return fetch();
    }
    bool fetch()
    {
        for (size_t i = 0; i != m_binders.size(); i++)
        {
            if (m_binderAddins[i].m_before_fetch)
                m_binderAddins[i].m_before_fetch(m_binders[i]);
        }
        int err=mysql_stmt_fetch(m_stmt);
        if(err==0 || err==MYSQL_DATA_TRUNCATED)
        {
            for(size_t i=0; i!=m_binders.size(); i++)
            void execute()
            {
                m_binderAddins[i].is_truncated = (err==MYSQL_DATA_TRUNCATED);
                if(m_binderAddins[i].m_after_fetch)
                    m_binderAddins[i].m_after_fetch(m_binders[i]);
                resize_binders(0);
                if (mysql_stmt_execute(m_stmt) != 0)
                    throw_exception();
            }
            return true;
        }
        else if(err==1)
            throw_exception();
        return false;
    }
    bool next_result()
    {
        if(m_result)
            template <typename BindProc>
            void execute_custom(BindProc &&bind_proc)
            {
                unsigned long count = mysql_stmt_param_count(m_stmt);
                if (count > 0)
                {
                    resize_binders(count);
                    bind_proc(*this);
                    if (mysql_stmt_bind_param(m_stmt, &m_binders.front()))
                        throw_exception();
                    for (size_t i = 0; i != count; i++)
                    {
                        if (m_binderAddins[i].m_after_fetch)
                            m_binderAddins[i].m_after_fetch(m_binders[i]);
                    }
                }
                if (mysql_stmt_execute(m_stmt) != 0)
                    throw_exception();
            }
            template <typename Types>
            void execute(const Types &params)
            {
                execute_custom([&params](statement &stmt)
                               { qtl::bind_params(stmt, params); });
            }
            template <typename Types>
            bool fetch(Types &&values)
            {
                if (m_result == nullptr)
                {
                    unsigned long count = mysql_stmt_field_count(m_stmt);
                    if (count > 0)
                    {
                        m_result = mysql_stmt_result_metadata(m_stmt);
                        if (m_result == nullptr)
                            throw_exception();
                        resize_binders(count);
                        qtl::bind_record(*this, std::forward<Types>(values));
                        set_binders();
                        if (mysql_stmt_bind_result(m_stmt, m_binders.data()) != 0)
                            throw_exception();
                    }
                }
                return fetch();
            }
            bool fetch()
            {
                for (size_t i = 0; i != m_binders.size(); i++)
                {
                    if (m_binderAddins[i].m_before_fetch)
                        m_binderAddins[i].m_before_fetch(m_binders[i]);
                }
                int err = mysql_stmt_fetch(m_stmt);
                if (err == 0 || err == MYSQL_DATA_TRUNCATED)
                {
                    for (size_t i = 0; i != m_binders.size(); i++)
                    {
                        m_binderAddins[i].is_truncated = (err == MYSQL_DATA_TRUNCATED);
                        if (m_binderAddins[i].m_after_fetch)
                            m_binderAddins[i].m_after_fetch(m_binders[i]);
                    }
                    return true;
                }
                else if (err == 1)
                    throw_exception();
                return false;
            }
            bool next_result()
            {
                if (m_result)
                {
                    mysql_free_result(m_result);
                    m_result = nullptr;
                    mysql_stmt_free_result(m_stmt);
                }
                int ret = 0;
                do
                {
                    ret = mysql_stmt_next_result(m_stmt);
                    if (ret > 0)
                        throw_exception();
                } while (ret == 0 && mysql_stmt_field_count(m_stmt) <= 0);
                return ret == 0;
            }
            bool reset() { return mysql_stmt_reset(m_stmt) != 0; }
        };
        /*
        struct LocalInfile
        {
            mysql_free_result(m_result);
            m_result=nullptr;
            mysql_stmt_free_result(m_stmt);
        }
        int ret=0;
        do
            int read(char *buf, unsigned int buf_len);
            void close();
            int error(char *error_msg, unsigned int error_msg_len);
        };
        */
        template <typename LocalInfile>
        struct local_infile_factory
        {
            ret=mysql_stmt_next_result(m_stmt);
            if(ret>0) throw_exception();
        }while(ret==0 && mysql_stmt_field_count(m_stmt)<=0);
        return ret==0;
    }
            static int local_infile_init(void **ptr, const char *filename, void *userdata)
            {
                LocalInfile *object = nullptr;
                try
                {
                    object = new LocalInfile(filename);
                }
                catch (...)
                {
                    return -1;
                }
                return ptr ? 0 : -1;
            }
    bool reset() { return mysql_stmt_reset(m_stmt)!=0; }
};
            static int local_infile_read(void *ptr, char *buf, unsigned int buf_len)
            {
                LocalInfile *object = reinterpret_cast<LocalInfile>(ptr);
                return object->read(buf, buf_len);
            }
/*
struct LocalInfile
{
    int read(char *buf, unsigned int buf_len);
    void close();
    int error(char *error_msg, unsigned int error_msg_len);
};
*/
template<typename LocalInfile>
struct local_infile_factory
{
    static int local_infile_init(void **ptr, const char *filename, void *userdata)
    {
        LocalInfile* object = nullptr;
        try
            static void local_infile_end(void *ptr)
            {
                LocalInfile *object = reinterpret_cast<LocalInfile>(ptr);
                object->close();
                delete object;
            }
            static int local_infile_error(void *ptr, char *error_msg, unsigned int error_msg_len)
            {
                LocalInfile *object = reinterpret_cast<LocalInfile>(ptr);
                return object->error(error_msg, error_msg_len);
            }
        };
        class local_file
        {
            object = new LocalInfile(filename);
        }
        catch (...)
        public:
            local_file(const char *filename)
            {
                m_fp = fopen(filename, "rb");
                if (m_fp == nullptr)
                    throw std::system_error(std::make_error_code(std::errc(errno)));
            }
            int read(char *buf, unsigned int buf_len)
            {
                return fread(buf, 1, buf_len, m_fp);
            }
            void close()
            {
                fclose(m_fp);
            }
            int error(char *error_msg, unsigned int error_msg_len)
            {
                int errcode = errno;
                memset(error_msg, 0, error_msg_len);
                strncpy(error_msg, strerror(errcode), error_msg_len - 1);
                return errcode;
            }
        private:
            FILE *m_fp;
        };
        class basic_database
        {
            return -1;
        }
        return ptr ? 0 : -1;
    }
        protected:
            basic_database()
            {
                m_mysql = mysql_init(nullptr);
            }
    static int local_infile_read(void *ptr, char *buf, unsigned int buf_len)
    {
        LocalInfile* object = reinterpret_cast<LocalInfile>(ptr);
        return object->read(buf, buf_len);
    }
        public:
            typedef mysql::error exception_type;
    static void local_infile_end(void *ptr)
    {
        LocalInfile* object = reinterpret_cast<LocalInfile>(ptr);
        object->close();
        delete object;
    }
            ~basic_database()
            {
                if (m_mysql)
                    mysql_close(m_mysql);
            }
            basic_database(const basic_database &) = delete;
            basic_database(basic_database &&src)
            {
                m_mysql = src.m_mysql;
                src.m_mysql = nullptr;
            }
            basic_database &operator==(const basic_database &) = delete;
            basic_database &operator==(basic_database &&src)
            {
                if (this != &src)
                {
                    if (m_mysql)
                        mysql_close(m_mysql);
                    m_mysql = src.m_mysql;
                    src.m_mysql = nullptr;
                }
                return *this;
            }
    static int local_infile_error(void *ptr, char *error_msg, unsigned int error_msg_len)
    {
        LocalInfile* object = reinterpret_cast<LocalInfile>(ptr);
        return object->error(error_msg, error_msg_len);
    }
};
            MYSQL *handle() { return m_mysql; }
class local_file
{
public:
    local_file(const char* filename)
    {
        m_fp = fopen(filename, "rb");
        if (m_fp == nullptr)
            throw std::system_error(std::make_error_code(std::errc(errno)));
    }
    int read(char *buf, unsigned int buf_len)
    {
        return fread(buf, 1, buf_len, m_fp);
    }
    void close()
    {
        fclose(m_fp);
    }
    int error(char *error_msg, unsigned int error_msg_len)
    {
        int errcode = errno;
        memset(error_msg, 0, error_msg_len);
        strncpy(error_msg, strerror(errcode), error_msg_len - 1);
        return errcode;
    }
            void options(enum mysql_option option, const void *arg)
            {
                if (mysql_options(m_mysql, option, arg) != 0)
                    throw_exception();
            }
            void charset_name(const char *charset)
            {
                if (mysql_set_character_set(m_mysql, charset) != 0)
                    throw_exception();
            }
            void protocol(mysql_protocol_type type)
            {
                return options(MYSQL_OPT_PROTOCOL, &type);
            }
            void reconnect(my_bool re)
            {
                return options(MYSQL_OPT_RECONNECT, &re);
            }
private:
    FILE* m_fp;
};
            const char *current() const
            {
                return m_mysql->db;
            }
class basic_database
{
protected:
    basic_database()
    {
        m_mysql = mysql_init(nullptr);
    }
            unsigned int error() const
            {
                return mysql_errno(m_mysql);
            }
            const char *errmsg() const
            {
                return mysql_error(m_mysql);
            }
public:
    typedef mysql::error exception_type;
            uint64_t affected_rows()
            {
                return mysql_affected_rows(m_mysql);
            }
    ~basic_database()
    {
        if(m_mysql)
            mysql_close(m_mysql);
    }
    basic_database(const basic_database&) = delete;
    basic_database(basic_database&& src)
    {
        m_mysql=src.m_mysql;
        src.m_mysql=nullptr;
    }
    basic_database& operator==(const basic_database&) = delete;
    basic_database& operator==(basic_database&& src)
    {
        if(this!=&src)
            unsigned int field_count()
            {
                return mysql_field_count(m_mysql);
            }
            uint64_t insert_id()
            {
                return mysql_insert_id(m_mysql);
            }
        protected:
            MYSQL *m_mysql;
            void throw_exception() { throw mysql::error(*this); }
        };
#if MARIADB_VERSION_ID >= 050500
        class async_connection;
#endif // MariaDB
        class database : public basic_database, public qtl::base_database<database, statement>
        {
            if(m_mysql)
        public:
            database() = default;
            bool open(const char *host, const char *user, const char *passwd, const char *db,
                      unsigned long clientflag = 0, unsigned int port = 0, const char *unix_socket = nullptr)
            {
                if (m_mysql == nullptr)
                    m_mysql = mysql_init(nullptr);
                return mysql_real_connect(m_mysql, host, user, passwd, db, port, unix_socket, clientflag) != nullptr;
            }
            void close()
            {
                mysql_close(m_mysql);
            m_mysql=src.m_mysql;
            src.m_mysql=nullptr;
        }
        return *this;
    }
    MYSQL* handle() { return m_mysql; }
    void options(enum mysql_option option, const void *arg)
    {
        if(mysql_options(m_mysql, option, arg)!=0)
        throw_exception();
    }
    void charset_name(const char* charset)
    {
        if(mysql_set_character_set(m_mysql, charset)!=0)
            throw_exception();
    }
    void protocol(mysql_protocol_type type)
    {
        return options(MYSQL_OPT_PROTOCOL, &type);
    }
    void reconnect(my_bool re)
    {
        return options(MYSQL_OPT_RECONNECT, &re);
    }
    const char* current() const
    {
        return m_mysql->db;
    }
    unsigned int error() const
    {
        return mysql_errno(m_mysql);
    }
    const char* errmsg() const
    {
        return mysql_error(m_mysql);
    }
    uint64_t affected_rows()
    {
        return mysql_affected_rows(m_mysql);
    }
    unsigned int field_count()
    {
        return mysql_field_count(m_mysql);
    }
    uint64_t insert_id()
    {
        return mysql_insert_id(m_mysql);
    }
protected:
    MYSQL* m_mysql;
    void throw_exception() { throw mysql::error(*this); }
};
#if MARIADB_VERSION_ID >= 050500
class async_connection;
#endif //MariaDB
class database : public basic_database, public qtl::base_database<database, statement>
{
public:
    database() = default;
    bool open(const char *host, const char *user, const char *passwd, const char *db,
        unsigned long clientflag = 0, unsigned int port = 0, const char *unix_socket = nullptr)
    {
        if (m_mysql == nullptr) m_mysql = mysql_init(nullptr);
        return mysql_real_connect(m_mysql, host, user, passwd, db, port, unix_socket, clientflag) != nullptr;
    }
    void close()
    {
        mysql_close(m_mysql);
        m_mysql = nullptr;
    }
    statement open_command(const char* query_text, size_t text_length)
    {
        statement stmt(*this);
        stmt.open(query_text, text_length);
        return stmt;
    }
    statement open_command(const char* query_text)
    {
        return open_command(query_text, strlen(query_text));
    }
    statement open_command(const std::string& query_text)
    {
        return open_command(query_text.data(), query_text.length());
    }
    void refresh(unsigned int options)
    {
        if (mysql_refresh(m_mysql, options) < 0)
            throw_exception();
    }
    void select(const char* db)
    {
        if (mysql_select_db(m_mysql, db) != 0)
            throw_exception();
    }
    void simple_execute(const char* query_text, uint64_t* paffected = nullptr)
    {
        if (mysql_query(m_mysql, query_text) != 0)
            throw_exception();
        if (paffected) *paffected = affected_rows();
    }
    void simple_execute(const char* query_text, unsigned long text_length, uint64_t* paffected = nullptr)
    {
        if (text_length == 0) text_length = (unsigned long)strlen(query_text);
        if (mysql_real_query(m_mysql, query_text, text_length) != 0)
            throw_exception();
        if (paffected) *paffected = affected_rows();
    }
    void auto_commit(bool on)
    {
        if (mysql_autocommit(m_mysql, on ? 1 : 0) != 0)
            throw_exception();
    }
    void begin_transaction()
    {
        auto_commit(false);
    }
    void rollback()
    {
        if (mysql_rollback(m_mysql) != 0)
            throw_exception();
        auto_commit(true);
    }
    void commit()
    {
        if (mysql_commit(m_mysql) != 0)
            throw_exception();
        auto_commit(true);
    }
    bool is_alive()
    {
        return mysql_ping(m_mysql) == 0;
    }
    template<typename Pred>
    bool simple_query(const char* query, unsigned long length, Pred&& pred)
    {
        simple_execute(query, length);
        unsigned int fieldCount = mysql_field_count(m_mysql);
        MYSQL_RES* result = mysql_store_result(m_mysql);
        if (fieldCount > 0 && result)
        {
            MYSQL_RES* result = mysql_store_result(m_mysql);
            MYSQL_ROW row;
            while (row = mysql_fetch_row(result))
            {
                if (!pred(*this, row, fieldCount))
                    break;
                m_mysql = nullptr;
            }
            mysql_free_result(result);
            return true;
        }
        return false;
    }
    template<typename LocalInfile>
    void set_local_infile_factory(local_infile_factory<LocalInfile>* factory)
    {
        typedef local_infile_factory<LocalInfile> factory_type;
        if (factory == nullptr)
        {
            reset_local_infile();
        }
        else
        {
            mysql_set_local_infile_handler(m_mysql, &factory_type::local_infile_init,
                &factory_type::local_infile_read,
                &factory_type::local_infile_end,
                &factory_type::local_infile_error, factory);
        }
    }
    void reset_local_infile()
    {
        mysql_set_local_infile_default(m_mysql);
    }
            statement open_command(const char *query_text, size_t text_length)
            {
                statement stmt(*this);
                stmt.open(query_text, text_length);
                return stmt;
            }
            statement open_command(const char *query_text)
            {
                return open_command(query_text, strlen(query_text));
            }
            statement open_command(const std::string &query_text)
            {
                return open_command(query_text.data(), query_text.length());
            }
            void refresh(unsigned int options)
            {
                if (mysql_refresh(m_mysql, options) < 0)
                    throw_exception();
            }
            void select(const char *db)
            {
                if (mysql_select_db(m_mysql, db) != 0)
                    throw_exception();
            }
            void simple_execute(const char *query_text, uint64_t *paffected = nullptr)
            {
                if (mysql_query(m_mysql, query_text) != 0)
                    throw_exception();
                if (paffected)
                    *paffected = affected_rows();
            }
            void simple_execute(const char *query_text, unsigned long text_length, uint64_t *paffected = nullptr)
            {
                if (text_length == 0)
                    text_length = (unsigned long)strlen(query_text);
                if (mysql_real_query(m_mysql, query_text, text_length) != 0)
                    throw_exception();
                if (paffected)
                    *paffected = affected_rows();
            }
            void auto_commit(bool on)
            {
                if (mysql_autocommit(m_mysql, on ? 1 : 0) != 0)
                    throw_exception();
            }
            void begin_transaction()
            {
                auto_commit(false);
            }
            void rollback()
            {
                if (mysql_rollback(m_mysql) != 0)
                    throw_exception();
                auto_commit(true);
            }
            void commit()
            {
                if (mysql_commit(m_mysql) != 0)
                    throw_exception();
                auto_commit(true);
            }
            bool is_alive()
            {
                return mysql_ping(m_mysql) == 0;
            }
            template <typename Pred>
            bool simple_query(const char *query, unsigned long length, Pred &&pred)
            {
                simple_execute(query, length);
                unsigned int fieldCount = mysql_field_count(m_mysql);
                MYSQL_RES *result = mysql_store_result(m_mysql);
                if (fieldCount > 0 && result)
                {
                    MYSQL_RES *result = mysql_store_result(m_mysql);
                    MYSQL_ROW row;
                    while (row = mysql_fetch_row(result))
                    {
                        if (!pred(*this, row, fieldCount))
                            break;
                    }
                    mysql_free_result(result);
                    return true;
                }
                return false;
            }
            template <typename LocalInfile>
            void set_local_infile_factory(local_infile_factory<LocalInfile> *factory)
            {
                typedef local_infile_factory<LocalInfile> factory_type;
                if (factory == nullptr)
                {
                    reset_local_infile();
                }
                else
                {
                    mysql_set_local_infile_handler(m_mysql, &factory_type::local_infile_init,
                                                   &factory_type::local_infile_read,
                                                   &factory_type::local_infile_end,
                                                   &factory_type::local_infile_error, factory);
                }
            }
            void reset_local_infile()
            {
                mysql_set_local_infile_default(m_mysql);
            }
#if MARIADB_VERSION_ID >= 050500
    async_connection async_mode();
            async_connection async_mode();
#endif //MariaDB
};
#endif // MariaDB
        };
#if MARIADB_VERSION_ID >= 100000
inline int event_flags(int status) NOEXCEPT
{
    int flags = 0;
    if (status&MYSQL_WAIT_READ)
        flags |= event::ef_read;
    if (status&MYSQL_WAIT_WRITE)
        flags |= event::ef_write;
    if (status&MYSQL_WAIT_EXCEPT)
        flags |= event::ef_exception;
    return flags;
}
inline int mysql_status(int flags) NOEXCEPT
{
    int status = 0;
    if (flags&event::ef_read)
        status |= MYSQL_WAIT_READ;
    if (flags&event::ef_write)
        status |= MYSQL_WAIT_WRITE;
    if (flags&event::ef_exception)
        status |= MYSQL_WAIT_EXCEPT;
    if (flags&event::ef_timeout)
        status |= MYSQL_WAIT_TIMEOUT;
    return status;
}
class async_connection;
class async_statement : public base_statement
{
public:
    async_statement() = default;
    async_statement(async_connection& db);
    async_statement(async_statement&& src)
        : base_statement(std::move(src))
    {
        m_event=src.m_event;
        src.m_event=nullptr;
    }
    async_statement& operator=(async_statement&& src)
    {
        base_statement::operator =(std::move(src));
        m_event=src.m_event;
        src.m_event=nullptr;
        return *this;
    }
    ~async_statement()
    {
        close();
    }
    /*
        Handler defiens as:
        void handler(const qtl::mysql::error& e);
     */
    template<typename Handler>
    void open(Handler&& handler, const char *query_text, size_t text_length=0)
    {
        if(text_length==0) text_length=strlen(query_text);
        if(m_stmt)
        inline int event_flags(int status) NOEXCEPT
        {
            std::string text(query_text, text_length);
            reset([this, text, handler](const mysql::error& e) mutable {
            int flags = 0;
            if (status & MYSQL_WAIT_READ)
                flags |= event::ef_read;
            if (status & MYSQL_WAIT_WRITE)
                flags |= event::ef_write;
            if (status & MYSQL_WAIT_EXCEPT)
                flags |= event::ef_exception;
            return flags;
        }
        inline int mysql_status(int flags) NOEXCEPT
        {
            int status = 0;
            if (flags & event::ef_read)
                status |= MYSQL_WAIT_READ;
            if (flags & event::ef_write)
                status |= MYSQL_WAIT_WRITE;
            if (flags & event::ef_exception)
                status |= MYSQL_WAIT_EXCEPT;
            if (flags & event::ef_timeout)
                status |= MYSQL_WAIT_TIMEOUT;
            return status;
        }
        class async_connection;
        class async_statement : public base_statement
        {
        public:
            async_statement() = default;
            async_statement(async_connection &db);
            async_statement(async_statement &&src)
                : base_statement(std::move(src))
            {
                m_event = src.m_event;
                src.m_event = nullptr;
            }
            async_statement &operator=(async_statement &&src)
            {
                base_statement::operator=(std::move(src));
                m_event = src.m_event;
                src.m_event = nullptr;
                return *this;
            }
            ~async_statement()
            {
                close();
            }
            /*
                Handler defiens as:
                void handler(const qtl::mysql::error& e);
             */
            template <typename Handler>
            void open(Handler &&handler, const char *query_text, size_t text_length = 0)
            {
                if (text_length == 0)
                    text_length = strlen(query_text);
                if (m_stmt)
                {
                    std::string text(query_text, text_length);
                    reset([this, text, handler](const mysql::error &e) mutable
                          {
                if(e)
                {
                    handler(e);
                    return;
                }
                prepare(text.data(), text.size(), std::forward<Handler>(handler));
            });
        }
        else
        {
            prepare(query_text, text_length, std::forward<Handler>(handler));
        }
    }
    /*
        ExecuteHandler defiens as:
        void handler(const qtl::mysql::error& e, uint64_t affected);
     */
    template<typename ExecuteHandler>
    void execute(ExecuteHandler&& handler)
    {
        resize_binders(0);
        int ret=0;
        int status = mysql_stmt_execute_start(&ret, m_stmt);
        if(status)
            wait_execute(status, std::forward<ExecuteHandler>(handler));
        else if(ret)
            handler(mysql::error(*this), 0);
        else
            handler(mysql::error(), affetced_rows());
    }
    template<typename Types, typename Handler>
    void execute(const Types& params, Handler&& handler)
    {
        unsigned long count=mysql_stmt_param_count(m_stmt);
        if(count>0)
        {
            resize_binders(count);
            qtl::bind_params(*this, params);
            if(mysql_stmt_bind_param(m_stmt, &m_binders.front()))
                throw_exception();
            for(size_t i=0; i!=count; i++)
            {
                if(m_binderAddins[i].m_after_fetch)
                    m_binderAddins[i].m_after_fetch(m_binders[i]);
            }
        }
        execute(std::forward<Handler>(handler));
    }
    template<typename Types, typename RowHandler, typename FinishHandler>
    void fetch(Types&& values, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        if(m_result==nullptr)
        {
            unsigned long count=mysql_stmt_field_count(m_stmt);
            if(count>0)
            {
                m_result=mysql_stmt_result_metadata(m_stmt);
                if(m_result==nullptr) throw_exception();
                resize_binders(count);
                qtl::bind_record(*this, std::forward<Types>(values));
                set_binders();
                if(mysql_stmt_bind_result(m_stmt, m_binders.data())!=0)
                prepare(text.data(), text.size(), std::forward<Handler>(handler)); });
                }
                else
                {
                    finish_handler(mysql::error(*this));
                    return;
                    prepare(query_text, text_length, std::forward<Handler>(handler));
                }
            }
        }
        fetch(std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
    template<typename RowHandler, typename FinishHandler>
    void fetch(RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        int ret = 0;
        int status = start_fetch(&ret);
        if (status == 0)
            status = after_fetch(status, ret, std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        if (status)
            wait_fetch(status, std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
    template<typename Handler>
    void reset(Handler&& handler)
    {
        my_bool ret=false;
        int status = mysql_stmt_reset_start(&ret, m_stmt);
        if(status)
            wait_operation<my_bool>(status, &mysql_stmt_reset_cont, handler);
        else
            handler( (ret) ? mysql::error(*this) : mysql::error());
    }
    void close()
    {
        base_statement::close();
    }
    template<typename Handler>
    void close(Handler&& handler)
    {
        if(m_result)
        {
            my_bool ret=0;
            int status = mysql_stmt_free_result_start(&ret, m_stmt);
            if(status)
            /*
                ExecuteHandler defiens as:
                void handler(const qtl::mysql::error& e, uint64_t affected);
             */
            template <typename ExecuteHandler>
            void execute(ExecuteHandler &&handler)
            {
                this->wait_free_result(status, [this, handler](const mysql::error& e) mutable {
                    m_result=nullptr;
                    if (e)
                        handler(e);
                    else
                        close(handler);
                });
            }
            else if(ret)
                handler(mysql::error(*this));
            else
                m_result=nullptr;
        }
        if(m_stmt)
        {
            my_bool ret=0;
            int status = mysql_stmt_close_start(&ret, m_stmt);
            if(status)
            {
                wait_operation<my_bool>(status, &mysql_stmt_close_cont, [this, handler](const mysql::error& e) mutable {
                    m_stmt=nullptr;
                    handler(e);
                });;
            }
            else
            {
                m_stmt = nullptr;
                handler((ret) ? mysql::error(*this) : mysql::error());
            }
        }
    }
    template<typename Handler>
    void next_result(Handler&& handler)
    {
        if(m_result)
        {
            mysql_free_result(m_result);
            m_result = nullptr;
        }
        int ret=0;
        do
        {
            int status = mysql_stmt_next_result_start(&ret, m_stmt);
            if (status)
            {
                wait_next_result(status, std::forward<Handler>(handler));
                return;
            }
        }while(ret==0 && mysql_stmt_field_count(m_stmt)<=0);
        handler((ret) ? mysql::error(*this) : mysql::error());
    }
private:
    event* m_event;
    template<typename Handler>
    void prepare(const char *query_text, size_t text_length, Handler&& handler)
    {
        int ret;
        int status=mysql_stmt_prepare_start(&ret, m_stmt, query_text, (unsigned long)text_length);
        if(status)
            wait_operation<int>(status, &mysql_stmt_prepare_cont, handler);
        else
            handler((ret) ? mysql::error(*this) : mysql::error());
    }
    template<typename Ret, typename Func, typename Handler>
    void wait_operation(int status, Func func, Handler&& handler)
    {
        m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql),
            [this, func, handler](int flags) mutable {
                Ret ret = 0;
                int status = func(&ret, m_stmt, mysql_status(flags));
                if (status)
                    wait_operation<Ret>(status, func, handler);
                else
                    handler((ret) ? mysql::error(*this) : mysql::error());
        });
    }
    template<typename ExecuteHandler>
    void wait_execute(int status, ExecuteHandler&& handler)
    {
        m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql),
            [this, handler](int flags) mutable {
                resize_binders(0);
                int ret = 0;
                int status = mysql_stmt_execute_cont(&ret, m_stmt, mysql_status(flags));
                int status = mysql_stmt_execute_start(&ret, m_stmt);
                if (status)
                    wait_execute(status, handler);
                else if(ret)
                    wait_execute(status, std::forward<ExecuteHandler>(handler));
                else if (ret)
                    handler(mysql::error(*this), 0);
                else
                    handler(mysql::error(), affetced_rows());
        });
    }
            }
    template<typename RowHandler, typename FinishHandler>
    void wait_fetch(int status, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql),
            [this, row_handler, finish_handler](int flags) mutable {
            template <typename Types, typename Handler>
            void execute(const Types &params, Handler &&handler)
            {
                unsigned long count = mysql_stmt_param_count(m_stmt);
                if (count > 0)
                {
                    resize_binders(count);
                    qtl::bind_params(*this, params);
                    if (mysql_stmt_bind_param(m_stmt, &m_binders.front()))
                        throw_exception();
                    for (size_t i = 0; i != count; i++)
                    {
                        if (m_binderAddins[i].m_after_fetch)
                            m_binderAddins[i].m_after_fetch(m_binders[i]);
                    }
                }
                execute(std::forward<Handler>(handler));
            }
            template <typename Types, typename RowHandler, typename FinishHandler>
            void fetch(Types &&values, RowHandler &&row_handler, FinishHandler &&finish_handler)
            {
                if (m_result == nullptr)
                {
                    unsigned long count = mysql_stmt_field_count(m_stmt);
                    if (count > 0)
                    {
                        m_result = mysql_stmt_result_metadata(m_stmt);
                        if (m_result == nullptr)
                            throw_exception();
                        resize_binders(count);
                        qtl::bind_record(*this, std::forward<Types>(values));
                        set_binders();
                        if (mysql_stmt_bind_result(m_stmt, m_binders.data()) != 0)
                        {
                            finish_handler(mysql::error(*this));
                            return;
                        }
                    }
                }
                fetch(std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
            }
            template <typename RowHandler, typename FinishHandler>
            void fetch(RowHandler &&row_handler, FinishHandler &&finish_handler)
            {
                int ret = 0;
                int status = mysql_stmt_fetch_cont(&ret, m_stmt, mysql_status(flags));
                int status = start_fetch(&ret);
                if (status == 0)
                    status = after_fetch(status, ret, std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
                if (status)
                    wait_fetch(status, std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
        });
    }
            }
    int start_fetch(int* ret)
    {
        for (size_t i = 0; i != m_binders.size(); i++)
        {
            if (m_binderAddins[i].m_before_fetch)
                m_binderAddins[i].m_before_fetch(m_binders[i]);
        }
        return mysql_stmt_fetch_start(ret, m_stmt);
    }
            template <typename Handler>
            void reset(Handler &&handler)
            {
                my_bool ret = false;
                int status = mysql_stmt_reset_start(&ret, m_stmt);
                if (status)
                    wait_operation<my_bool>(status, &mysql_stmt_reset_cont, handler);
                else
                    handler((ret) ? mysql::error(*this) : mysql::error());
            }
    template<typename RowHandler, typename FinishHandler>
    int after_fetch(int status, int ret, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        while (status == 0)
        {
            if (ret == 0 || ret == MYSQL_DATA_TRUNCATED)
            void close()
            {
                base_statement::close();
            }
            template <typename Handler>
            void close(Handler &&handler)
            {
                if (m_result)
                {
                    my_bool ret = 0;
                    int status = mysql_stmt_free_result_start(&ret, m_stmt);
                    if (status)
                    {
                        this->wait_free_result(status, [this, handler](const mysql::error &e) mutable
                                               {
                    m_result=nullptr;
                    if (e)
                        handler(e);
                    else
                        close(handler); });
                    }
                    else if (ret)
                        handler(mysql::error(*this));
                    else
                        m_result = nullptr;
                }
                if (m_stmt)
                {
                    my_bool ret = 0;
                    int status = mysql_stmt_close_start(&ret, m_stmt);
                    if (status)
                    {
                        wait_operation<my_bool>(status, &mysql_stmt_close_cont, [this, handler](const mysql::error &e) mutable
                                                {
                    m_stmt=nullptr;
                    handler(e); });
                        ;
                    }
                    else
                    {
                        m_stmt = nullptr;
                        handler((ret) ? mysql::error(*this) : mysql::error());
                    }
                }
            }
            template <typename Handler>
            void next_result(Handler &&handler)
            {
                if (m_result)
                {
                    mysql_free_result(m_result);
                    m_result = nullptr;
                }
                int ret = 0;
                do
                {
                    int status = mysql_stmt_next_result_start(&ret, m_stmt);
                    if (status)
                    {
                        wait_next_result(status, std::forward<Handler>(handler));
                        return;
                    }
                } while (ret == 0 && mysql_stmt_field_count(m_stmt) <= 0);
                handler((ret) ? mysql::error(*this) : mysql::error());
            }
        private:
            event *m_event;
            template <typename Handler>
            void prepare(const char *query_text, size_t text_length, Handler &&handler)
            {
                int ret;
                int status = mysql_stmt_prepare_start(&ret, m_stmt, query_text, (unsigned long)text_length);
                if (status)
                    wait_operation<int>(status, &mysql_stmt_prepare_cont, handler);
                else
                    handler((ret) ? mysql::error(*this) : mysql::error());
            }
            template <typename Ret, typename Func, typename Handler>
            void wait_operation(int status, Func func, Handler &&handler)
            {
                m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql),
                                        [this, func, handler](int flags) mutable
                                        {
                                            Ret ret = 0;
                                            int status = func(&ret, m_stmt, mysql_status(flags));
                                            if (status)
                                                wait_operation<Ret>(status, func, handler);
                                            else
                                                handler((ret) ? mysql::error(*this) : mysql::error());
                                        });
            }
            template <typename ExecuteHandler>
            void wait_execute(int status, ExecuteHandler &&handler)
            {
                m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql),
                                        [this, handler](int flags) mutable
                                        {
                                            int ret = 0;
                                            int status = mysql_stmt_execute_cont(&ret, m_stmt, mysql_status(flags));
                                            if (status)
                                                wait_execute(status, handler);
                                            else if (ret)
                                                handler(mysql::error(*this), 0);
                                            else
                                                handler(mysql::error(), affetced_rows());
                                        });
            }
            template <typename RowHandler, typename FinishHandler>
            void wait_fetch(int status, RowHandler &&row_handler, FinishHandler &&finish_handler)
            {
                m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql),
                                        [this, row_handler, finish_handler](int flags) mutable
                                        {
                                            int ret = 0;
                                            int status = mysql_stmt_fetch_cont(&ret, m_stmt, mysql_status(flags));
                                            if (status == 0)
                                                status = after_fetch(status, ret, std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
                                            if (status)
                                                wait_fetch(status, std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
                                        });
            }
            int start_fetch(int *ret)
            {
                for (size_t i = 0; i != m_binders.size(); i++)
                {
                    m_binderAddins[i].is_truncated = (ret == MYSQL_DATA_TRUNCATED);
                    if (m_binderAddins[i].m_after_fetch)
                        m_binderAddins[i].m_after_fetch(m_binders[i]);
                    if (m_binderAddins[i].m_before_fetch)
                        m_binderAddins[i].m_before_fetch(m_binders[i]);
                }
                if (row_handler())
                    status = start_fetch(&ret);
                else
                return mysql_stmt_fetch_start(ret, m_stmt);
            }
            template <typename RowHandler, typename FinishHandler>
            int after_fetch(int status, int ret, RowHandler &&row_handler, FinishHandler &&finish_handler)
            {
                while (status == 0)
                {
                    finish_handler(mysql::error());
                    break;
                }
                    if (ret == 0 || ret == MYSQL_DATA_TRUNCATED)
                    {
                        for (size_t i = 0; i != m_binders.size(); i++)
                        {
                            m_binderAddins[i].is_truncated = (ret == MYSQL_DATA_TRUNCATED);
                            if (m_binderAddins[i].m_after_fetch)
                                m_binderAddins[i].m_after_fetch(m_binders[i]);
                        }
                        if (row_handler())
                            status = start_fetch(&ret);
                        else
                        {
                            finish_handler(mysql::error());
                            break;
                        }
                    }
                    else if (ret == 1)
                    {
                        finish_handler(mysql::error(*this));
                        break;
                    }
                    else
                    {
                        finish_handler(mysql::error());
                        break;
                    }
                };
                return status;
            }
            else if (ret == 1)
            template <typename ResultHandler>
            void wait_free_result(int status, ResultHandler &&handler) NOEXCEPT
            {
                finish_handler(mysql::error(*this));
                break;
                m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql),
                                        [this, handler](int flags) mutable
                                        {
                                            my_bool ret = false;
                                            int status = mysql_stmt_free_result_cont(&ret, m_stmt, mysql_status(flags));
                                            if (status)
                                                wait_free_result(status, handler);
                                            else
                                                handler(mysql::error());
                                        });
            }
            else
            template <typename Handler>
            void wait_next_result(int status, Handler &&handler)
            {
                finish_handler(mysql::error());
                break;
                m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql),
                                        [this, handler](int flags) mutable
                                        {
                                            int ret = 0;
                                            int status = mysql_stmt_next_result_cont(&ret, m_stmt, mysql_status(flags));
                                            if (status)
                                                wait_next_result(status, handler);
                                            else if (ret)
                                                handler(mysql::error(*this));
                                            else if (mysql_stmt_field_count(m_stmt) > 0)
                                                handler(mysql::error());
                                            else
                                                next_result(std::forward<Handler>(handler));
                                        });
            }
        };
        return status;
    }
    template<typename ResultHandler>
    void wait_free_result(int status, ResultHandler&& handler) NOEXCEPT
    {
        m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql),
            [this, handler](int flags) mutable {
                my_bool ret = false;
                int status = mysql_stmt_free_result_cont(&ret, m_stmt, mysql_status(flags));
        class async_connection : public basic_database, public qtl::async_connection<async_connection, async_statement>
        {
        public:
            async_connection() = default;
            /*
                OpenHandler defines as:
                    void handler(const qtl::mysql::error& e) NOEXCEPT;
            */
            template <typename EventLoop, typename OpenHandler>
            void open(EventLoop &ev, OpenHandler &&handler, const char *host, const char *user, const char *passwd, const char *db,
                      unsigned long clientflag = 0, unsigned int port = 0, const char *unix_socket = nullptr)
            {
                if (m_mysql == nullptr)
                    m_mysql = mysql_init(nullptr);
                mysql_options(m_mysql, MYSQL_OPT_NONBLOCK, 0);
                MYSQL *ret = nullptr;
                int status = mysql_real_connect_start(&ret, m_mysql, host, user, passwd, db, port, unix_socket, clientflag);
                bind(ev);
                if (status)
                    wait_free_result(status, handler);
                    wait_connect(status, std::forward<OpenHandler>(handler));
                else
                    handler(mysql::error());
        });
    }
                    handler((ret == nullptr) ? mysql::error(*this) : mysql::error());
            }
    template<typename Handler>
    void wait_next_result(int status, Handler&& handler)
    {
        m_event->set_io_handler(event_flags(status), mysql_get_timeout_value(m_stmt->mysql),
            [this, handler](int flags) mutable {
            int ret = 0;
            int status = mysql_stmt_next_result_cont(&ret, m_stmt, mysql_status(flags));
            if (status)
                wait_next_result(status, handler);
            else if (ret)
                handler(mysql::error(*this));
            else if (mysql_stmt_field_count(m_stmt) > 0)
                handler(mysql::error());
            else
                next_result(std::forward<Handler>(handler));
        });
    }
};
class async_connection : public basic_database, public qtl::async_connection<async_connection, async_statement>
{
public:
    async_connection() = default;
    /*
        OpenHandler defines as:
            void handler(const qtl::mysql::error& e) NOEXCEPT;
    */
    template<typename EventLoop, typename OpenHandler>
    void open(EventLoop& ev, OpenHandler&& handler, const char *host, const char *user, const char *passwd, const char *db,
        unsigned long clientflag = 0, unsigned int port = 0, const char *unix_socket = nullptr)
    {
        if (m_mysql == nullptr)
            m_mysql = mysql_init(nullptr);
        mysql_options(m_mysql, MYSQL_OPT_NONBLOCK, 0);
        MYSQL* ret = nullptr;
        int status = mysql_real_connect_start(&ret, m_mysql, host, user, passwd, db, port, unix_socket, clientflag);
        bind(ev);
        if (status)
            wait_connect(status, std::forward<OpenHandler>(handler));
        else
            handler((ret == nullptr) ? mysql::error(*this) : mysql::error());
    }
    /*
        CloseHandler defines as:
            void handler() NOEXCEPT;
    */
    template<typename CloseHandler >
    void close(CloseHandler&& handler) NOEXCEPT
    {
        int status  = mysql_close_start(m_mysql);
        if (status)
        {
            wait_close(status, [this, handler]() mutable {
            /*
                CloseHandler defines as:
                    void handler() NOEXCEPT;
            */
            template <typename CloseHandler>
            void close(CloseHandler &&handler) NOEXCEPT
            {
                int status = mysql_close_start(m_mysql);
                if (status)
                {
                    wait_close(status, [this, handler]() mutable
                               {
                handler();
                m_mysql = nullptr;
            });
        }
        else
        {
            handler();
            m_mysql = nullptr;
        }
    }
                m_mysql = nullptr; });
                }
                else
                {
                    handler();
                    m_mysql = nullptr;
                }
            }
    /*
        Handler defines as:
            void handler(const qtl::mysql::error& e) NOEXCEPT;
    */
    template<typename Handler>
    void refresh(Handler&& handler, unsigned int options) NOEXCEPT
    {
        int ret = 0;
        int status = mysql_refresh_start(&ret, m_mysql, options);
        if (status)
            wait_operation(status, &mysql_refresh_cont, std::forward<Handler>(handler));
        else
            handler((ret) ? mysql::error(*this) : mysql::error());
    }
            /*
                Handler defines as:
                    void handler(const qtl::mysql::error& e) NOEXCEPT;
            */
            template <typename Handler>
            void refresh(Handler &&handler, unsigned int options) NOEXCEPT
            {
                int ret = 0;
                int status = mysql_refresh_start(&ret, m_mysql, options);
                if (status)
                    wait_operation(status, &mysql_refresh_cont, std::forward<Handler>(handler));
                else
                    handler((ret) ? mysql::error(*this) : mysql::error());
            }
    template<typename Handler>
    void select(Handler&& handler, const char* db) NOEXCEPT
    {
        int ret = 0;
        int status = mysql_select_db_start(&ret, m_mysql, db);
        if (status)
            wait_operation(status, &mysql_select_db_cont, std::forward<Handler>(handler));
        else
            handler((ret) ? mysql::error(*this) : mysql::error());
    }
            template <typename Handler>
            void select(Handler &&handler, const char *db) NOEXCEPT
            {
                int ret = 0;
                int status = mysql_select_db_start(&ret, m_mysql, db);
                if (status)
                    wait_operation(status, &mysql_select_db_cont, std::forward<Handler>(handler));
                else
                    handler((ret) ? mysql::error(*this) : mysql::error());
            }
    template<typename Handler>
    void is_alive(Handler&& handler) NOEXCEPT
    {
        int ret = 0;
        int status = mysql_ping_start(&ret, m_mysql);
        if (status)
            wait_operation(status, &mysql_ping_cont, std::forward<Handler>(handler));
        else
            handler((ret) ? mysql::error(*this) : mysql::error());
    }
            template <typename Handler>
            void is_alive(Handler &&handler) NOEXCEPT
            {
                int ret = 0;
                int status = mysql_ping_start(&ret, m_mysql);
                if (status)
                    wait_operation(status, &mysql_ping_cont, std::forward<Handler>(handler));
                else
                    handler((ret) ? mysql::error(*this) : mysql::error());
            }
    /*
        Handler defines as:
            void handler(const qtl::mysql::error& e, uint64_t affected) NOEXCEPT;
    */
    template<typename ExecuteHandler>
    void simple_execute(ExecuteHandler&& handler, const char* query_text) NOEXCEPT
    {
        int ret = 0;
        int status = mysql_query_start(&ret, m_mysql, query_text);
        if (status)
        {
            wait_operation(status, &mysql_query_cont, [this, handler](const mysql::error& e) {
            /*
                Handler defines as:
                    void handler(const qtl::mysql::error& e, uint64_t affected) NOEXCEPT;
            */
            template <typename ExecuteHandler>
            void simple_execute(ExecuteHandler &&handler, const char *query_text) NOEXCEPT
            {
                int ret = 0;
                int status = mysql_query_start(&ret, m_mysql, query_text);
                if (status)
                {
                    wait_operation(status, &mysql_query_cont, [this, handler](const mysql::error &e)
                                   {
                uint64_t affected = 0;
                if (!e) affected = affected_rows();
                handler(e, affected);
            });
        }
        else
        {
            uint64_t affected = 0;
            if (ret >= 0) affected = affected_rows();
            handler((ret) ? mysql::error(*this) : mysql::error(), affected);
        }
    }
                handler(e, affected); });
                }
                else
                {
                    uint64_t affected = 0;
                    if (ret >= 0)
                        affected = affected_rows();
                    handler((ret) ? mysql::error(*this) : mysql::error(), affected);
                }
            }
    template<typename ExecuteHandler>
    void simple_execute(ExecuteHandler&& handler, const char* query_text, unsigned long text_length) NOEXCEPT
    {
        int ret = 0;
        int status = mysql_real_query_start(&ret, m_mysql, query_text, text_length);
        if (status)
        {
            wait_operation(status, &mysql_real_query_cont, [this, handler](const mysql::error& e) mutable {
            template <typename ExecuteHandler>
            void simple_execute(ExecuteHandler &&handler, const char *query_text, unsigned long text_length) NOEXCEPT
            {
                int ret = 0;
                int status = mysql_real_query_start(&ret, m_mysql, query_text, text_length);
                if (status)
                {
                    wait_operation(status, &mysql_real_query_cont, [this, handler](const mysql::error &e) mutable
                                   {
                uint64_t affected = 0;
                if (!e) affected = affected_rows();
                handler(e, affected);
            });
        }
        else
        {
            uint64_t affected = 0;
            if (ret >= 0) affected = affected_rows();
            handler((ret) ? mysql::error(*this) : mysql::error(), affected);
        }
    }
                handler(e, affected); });
                }
                else
                {
                    uint64_t affected = 0;
                    if (ret >= 0)
                        affected = affected_rows();
                    handler((ret) ? mysql::error(*this) : mysql::error(), affected);
                }
            }
    template<typename Handler>
    void auto_commit(Handler&& handler, bool on) NOEXCEPT
    {
        my_bool ret;
        int status = mysql_autocommit_start(&ret, m_mysql, on ? 1 : 0);
        if(status)
            wait_operation(status, &mysql_autocommit_cont, std::forward<Handler>(handler));
        else
            handler((ret) ? mysql::error(*this) : mysql::error());
    }
            template <typename Handler>
            void auto_commit(Handler &&handler, bool on) NOEXCEPT
            {
                my_bool ret;
                int status = mysql_autocommit_start(&ret, m_mysql, on ? 1 : 0);
                if (status)
                    wait_operation(status, &mysql_autocommit_cont, std::forward<Handler>(handler));
                else
                    handler((ret) ? mysql::error(*this) : mysql::error());
            }
    template<typename Handler>
    void begin_transaction(Handler&& handler) NOEXCEPT
    {
        auto_commit(handler, false);
    }
            template <typename Handler>
            void begin_transaction(Handler &&handler) NOEXCEPT
            {
                auto_commit(handler, false);
            }
    template<typename Handler>
    void rollback(Handler&& handler) NOEXCEPT
    {
        my_bool ret;
        int status = mysql_rollback_start(&ret, m_mysql);
        if (status)
            wait_operation(status, &mysql_rollback_cont, std::forward<Handler>(handler));
        else
            handler((ret) ? mysql::error(*this) : mysql::error());
    }
            template <typename Handler>
            void rollback(Handler &&handler) NOEXCEPT
            {
                my_bool ret;
                int status = mysql_rollback_start(&ret, m_mysql);
                if (status)
                    wait_operation(status, &mysql_rollback_cont, std::forward<Handler>(handler));
                else
                    handler((ret) ? mysql::error(*this) : mysql::error());
            }
    template<typename Handler>
    void commit(Handler&& handler) NOEXCEPT
    {
        my_bool ret;
        int status = mysql_commit_start(&ret, m_mysql);
        if (status)
            wait_operation(status, &mysql_commit_cont, std::forward<Handler>(handler));
        else
            handler((ret) ? mysql::error(*this) : mysql::error());
    }
            template <typename Handler>
            void commit(Handler &&handler) NOEXCEPT
            {
                my_bool ret;
                int status = mysql_commit_start(&ret, m_mysql);
                if (status)
                    wait_operation(status, &mysql_commit_cont, std::forward<Handler>(handler));
                else
                    handler((ret) ? mysql::error(*this) : mysql::error());
            }
    /*
        RowHandler defines as:
            bool row_handler(MYSQL_ROW row, int field_count) NOEXCEPT;
        ResultHandler defines as:
            void result_handler(const qtl::mysql::error& e, size_t row_count) NOEXCEPT;
    */
    template<typename RowHandler, typename ResultHandler>
    void simple_query(const char* query, unsigned long length, RowHandler&& row_handler, ResultHandler&& result_handler) NOEXCEPT
    {
        simple_execute([this, row_handler, result_handler](const mysql::error& e, uint64_t affected) mutable {
            /*
                RowHandler defines as:
                    bool row_handler(MYSQL_ROW row, int field_count) NOEXCEPT;
                ResultHandler defines as:
                    void result_handler(const qtl::mysql::error& e, size_t row_count) NOEXCEPT;
            */
            template <typename RowHandler, typename ResultHandler>
            void simple_query(const char *query, unsigned long length, RowHandler &&row_handler, ResultHandler &&result_handler) NOEXCEPT
            {
                simple_execute([this, row_handler, result_handler](const mysql::error &e, uint64_t affected) mutable
                               {
            if (e)
            {
                result_handler(e, 0);
@@ -1755,180 +1793,185 @@
            else
            {
                result_handler(mysql::error(), 0);
            } }, query, length);
            }
        }, query, length);
            template <typename Handler>
            void open_command(const char *query_text, size_t text_length, Handler &&handler)
            {
                std::shared_ptr<async_statement> stmt = std::make_shared<async_statement>(*this);
                stmt->open([stmt, handler](const mysql::error &e) mutable
                           { handler(e, stmt); }, query_text, (unsigned long)text_length);
            }
            socket_type socket() const NOEXCEPT { return mysql_get_socket(m_mysql); }
        private:
            template <typename OpenHandler>
            void wait_connect(int status, OpenHandler &&handler) NOEXCEPT
            {
                m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql),
                                                [this, handler](int flags) mutable
                                                {
                                                    MYSQL *ret = nullptr;
                                                    int status = mysql_real_connect_cont(&ret, m_mysql, mysql_status(flags));
                                                    if (status)
                                                        wait_connect(status, handler);
                                                    else
                                                        handler((ret == nullptr) ? mysql::error(*this) : mysql::error());
                                                });
            }
            template <typename CloseHandler>
            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) mutable
                                                {
                                                    MYSQL *ret = nullptr;
                                                    int status = mysql_close_cont(m_mysql, mysql_status(flags));
                                                    if (status)
                                                        wait_close(status, handler);
                                                    else
                                                        handler();
                                                });
            }
            template <typename Func, typename Handler>
            void wait_operation(int status, Func func, Handler &&handler) NOEXCEPT
            {
                m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql),
                                                [this, func, handler](int flags) mutable
                                                {
                                                    int ret = 0;
                                                    int status = func(&ret, m_mysql, mysql_status(flags));
                                                    if (status)
                                                        wait_operation(status, func, handler);
                                                    else
                                                        handler((ret) ? mysql::error(*this) : mysql::error());
                                                });
            }
            template <typename RowHandler, typename ResultHandler>
            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) mutable
                                                {
                                                    MYSQL_RES *result = 0;
                                                    int status = mysql_store_result_cont(&result, m_mysql, mysql_status(flags));
                                                    if (status)
                                                        wait_query(status, field_count, row_handler, result_handler);
                                                    else if (result)
                                                        fetch_rows(result, field_count, 0, row_handler, result_handler);
                                                    else
                                                        result_handler(mysql::error(*this), 0);
                                                });
            }
            template <typename RowHandler, typename ResultHandler>
            void fetch_rows(MYSQL_RES *result, int field_count, size_t row_count, RowHandler &&row_handler, ResultHandler &&result_handler) NOEXCEPT
            {
                MYSQL_ROW row;
                int status = mysql_fetch_row_start(&row, result);
                if (status)
                    wait_fetch(status, result, field_count, row_count, row_handler, result_handler);
                else if (row && row_handler(row, field_count))
                    fetch_rows(result, field_count, row_count + 1, row_handler, result_handler);
                else
                    free_result(result, row_count, result_handler);
            }
            template <typename ResultHandler>
            void free_result(MYSQL_RES *result, size_t row_count, ResultHandler &&result_handler) NOEXCEPT
            {
                int status = mysql_free_result_start(result);
                if (status)
                    wait_free_result(status, result, row_count, result_handler);
                else
                    result_handler(mysql::error(), row_count);
            }
            template <typename RowHandler, typename ResultHandler>
            void wait_fetch(int status, MYSQL_RES *result, int field_count, size_t row_count, RowHandler &&row_handler, ResultHandler &&result_handler)
            {
                m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql),
                                                [this, result, field_count, row_count, row_handler, result_handler](int flags) mutable
                                                {
                                                    MYSQL_ROW row;
                                                    int status = mysql_fetch_row_cont(&row, result, mysql_status(flags));
                                                    if (status)
                                                        wait_fetch(status, result, field_count, row_count, row_handler, result_handler);
                                                    else if (result && row_handler(row, field_count))
                                                        fetch_rows(result, field_count, row_count + 1, row_handler, result_handler);
                                                    else
                                                        free_result(result, row_count, result_handler);
                                                });
            }
            template <typename ResultHandler>
            void wait_free_result(int status, MYSQL_RES *result, size_t row_count, ResultHandler &&handler) NOEXCEPT
            {
                m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql),
                                                [this, result, row_count, handler](int flags) mutable
                                                {
                                                    MYSQL *ret = nullptr;
                                                    int status = mysql_free_result_cont(result, mysql_status(flags));
                                                    if (status)
                                                        wait_free_result(status, result, row_count, handler);
                                                    else
                                                        handler(mysql::error(), row_count);
                                                });
            }
        };
        inline async_statement::async_statement(async_connection &db)
            : base_statement(static_cast<basic_database &>(db))
        {
            m_event = db.event();
        }
#endif // MariaDB 10.0
        typedef qtl::transaction<database> transaction;
        template <typename Record>
        using query_iterator = qtl::query_iterator<statement, Record>;
        template <typename Record>
        using query_result = qtl::query_result<statement, Record>;
        template <typename Params>
        inline statement &operator<<(statement &stmt, const Params &params)
        {
            stmt.reset();
            stmt.execute(params);
            return stmt;
        }
        inline error::error(const base_statement &stmt)
        {
            const char *errmsg = stmt.errmsg();
            m_error = stmt.error();
            if (errmsg)
                m_errmsg = errmsg;
        }
        inline error::error(const basic_database &db)
        {
            const char *errmsg = db.errmsg();
            m_error = db.error();
            if (errmsg)
                m_errmsg = errmsg;
        }
        inline base_statement::base_statement(basic_database &db)
        {
            m_stmt = mysql_stmt_init(db.handle());
            m_result = nullptr;
        }
    }
    template<typename Handler>
    void open_command(const char* query_text, size_t text_length, Handler&& handler)
    {
        std::shared_ptr<async_statement> stmt=std::make_shared<async_statement>(*this);
        stmt->open([stmt, handler](const mysql::error& e) mutable {
            handler(e, stmt);
        }, query_text, (unsigned long)text_length);
    }
    socket_type socket() const NOEXCEPT { return mysql_get_socket(m_mysql); }
private:
    template<typename OpenHandler>
    void wait_connect(int status, OpenHandler&& handler) NOEXCEPT
    {
        m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql),
            [this, handler](int flags) mutable {
            MYSQL* ret = nullptr;
            int status = mysql_real_connect_cont(&ret, m_mysql, mysql_status(flags));
            if (status)
                wait_connect(status, handler);
            else
                handler((ret == nullptr) ? mysql::error(*this) : mysql::error());
        });
    }
    template<typename CloseHandler>
    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) mutable {
            MYSQL* ret = nullptr;
            int status = mysql_close_cont(m_mysql, mysql_status(flags));
            if (status)
                wait_close(status, handler);
            else
                handler();
        });
    }
    template<typename Func, typename Handler>
    void wait_operation(int status, Func func, Handler&& handler) NOEXCEPT
    {
        m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql),
            [this, func, handler](int flags) mutable {
            int ret = 0;
            int status = func(&ret, m_mysql, mysql_status(flags));
            if (status)
                wait_operation(status, func, handler);
            else
                handler((ret) ? mysql::error(*this) : mysql::error());
        });
    }
    template<typename RowHandler, typename ResultHandler>
    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) mutable {
            MYSQL_RES* result = 0;
            int status = mysql_store_result_cont(&result, m_mysql, mysql_status(flags));
            if (status)
                wait_query(status, field_count, row_handler, result_handler);
            else if (result)
                fetch_rows(result, field_count, 0, row_handler, result_handler);
            else
                result_handler(mysql::error(*this), 0);
        });
    }
    template<typename RowHandler, typename ResultHandler>
    void fetch_rows(MYSQL_RES* result, int field_count, size_t row_count, RowHandler&& row_handler, ResultHandler&& result_handler) NOEXCEPT
    {
        MYSQL_ROW row;
        int status = mysql_fetch_row_start(&row, result);
        if (status)
            wait_fetch(status, result, field_count, row_count, row_handler, result_handler);
        else if(row && row_handler(row, field_count))
            fetch_rows(result, field_count, row_count + 1, row_handler, result_handler);
        else
            free_result(result, row_count, result_handler);
    }
    template<typename ResultHandler>
    void free_result(MYSQL_RES* result, size_t row_count, ResultHandler&& result_handler) NOEXCEPT
    {
        int status = mysql_free_result_start(result);
        if (status)
            wait_free_result(status, result, row_count, result_handler);
        else
            result_handler(mysql::error(), row_count);
    }
    template<typename RowHandler, typename ResultHandler>
    void wait_fetch(int status, MYSQL_RES* result, int field_count,  size_t row_count, RowHandler&& row_handler, ResultHandler&& result_handler)
    {
        m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql),
            [this, result, field_count, row_count, row_handler, result_handler](int flags) mutable {
            MYSQL_ROW row;
            int status = mysql_fetch_row_cont(&row, result, mysql_status(flags));
            if (status)
                wait_fetch(status, result, field_count, row_count, row_handler, result_handler);
            else if (result && row_handler(row, field_count))
                fetch_rows(result, field_count, row_count+1, row_handler, result_handler);
            else
                free_result(result, row_count, result_handler);
        });
    }
    template<typename ResultHandler>
    void wait_free_result(int status, MYSQL_RES* result, size_t row_count, ResultHandler&& handler) NOEXCEPT
    {
        m_event_handler->set_io_handler(event_flags(status), mysql_get_timeout_value(m_mysql),
            [this, result, row_count, handler](int flags) mutable {
            MYSQL* ret = nullptr;
            int status = mysql_free_result_cont(result, mysql_status(flags));
            if (status)
                wait_free_result(status, result, row_count, handler);
            else
                handler(mysql::error(), row_count);
        });
    }
};
inline     async_statement::async_statement(async_connection& db)
: base_statement(static_cast<basic_database&>(db))
{
    m_event=db.event();
}
#endif //MariaDB 10.0
typedef qtl::transaction<database> transaction;
template<typename Record>
using query_iterator = qtl::query_iterator<statement, Record>;
template<typename Record>
using query_result = qtl::query_result<statement, Record>;
template<typename Params>
inline statement& operator<<(statement& stmt, const Params& params)
{
    stmt.reset();
    stmt.execute(params);
    return stmt;
}
inline error::error(const base_statement& stmt)
{
    const char* errmsg=stmt.errmsg();
    m_error=stmt.error();
    if(errmsg) m_errmsg=errmsg;
}
inline error::error(const basic_database& db)
{
    const char* errmsg=db.errmsg();
    m_error=db.error();
    if(errmsg) m_errmsg=errmsg;
}
inline base_statement::base_statement(basic_database& db)
{
    m_stmt=mysql_stmt_init(db.handle());
    m_result=nullptr;
}
}
}
include/qtl_mysql_pool.hpp
@@ -7,52 +7,54 @@
namespace qtl
{
namespace mysql
{
class database_pool : public qtl::database_pool<database>
{
public:
    database_pool() : m_port(0) { }
    virtual ~database_pool() { }
    virtual database* new_database() throw() override
    namespace mysql
    {
        database* db=new database;
        if(!db->open(m_host.data(), m_user.data(), m_password.data(), m_database.data(), 0, m_port))
        {
            delete db;
            db=NULL;
        }
        else
        {
            db->charset_name("utf8");
        }
        return db;
    }
protected:
    std::string m_host;
    unsigned short m_port;
    std::string m_database;
    std::string m_user;
    std::string m_password;
};
        class database_pool : public qtl::database_pool<database>
        {
        public:
            database_pool() : m_port(0) {}
            virtual ~database_pool() {}
            virtual database *new_database() throw() override
            {
                database *db = new database;
                if (!db->open(m_host.data(), m_user.data(), m_password.data(), m_database.data(), 0, m_port))
                {
                    delete db;
                    db = NULL;
                }
                else
                {
                    db->charset_name("utf8");
                }
                return db;
            }
        protected:
            std::string m_host;
            unsigned short m_port;
            std::string m_database;
            std::string m_user;
            std::string m_password;
        };
#if MARIADB_VERSION_ID >= 050500
template<typename EventLoop>
class async_pool : public qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection>
{
    typedef qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection> base_class;
public:
    async_pool(EventLoop& ev) : base_class(ev), m_port(0) { }
    virtual ~async_pool() { }
        template <typename EventLoop>
        class async_pool : public qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection>
        {
            typedef qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection> base_class;
    template<typename Handler>
    void new_connection(EventLoop& ev, Handler&& handler) throw()
    {
        async_connection* db = new async_connection;
        db->open(ev, [this, handler, db](const mysql::error& e) mutable {
        public:
            async_pool(EventLoop &ev) : base_class(ev), m_port(0) {}
            virtual ~async_pool() {}
            template <typename Handler>
            void new_connection(EventLoop &ev, Handler &&handler) throw()
            {
                async_connection *db = new async_connection;
                db->open(ev, [this, handler, db](const mysql::error &e) mutable
                         {
            if (e)
            {
                delete db;
@@ -62,21 +64,20 @@
            {
                db->charset_name("utf8");
            }
            handler(e, db);
        }, m_host.data(), m_user.data(), m_password.data(), m_database.data(), 0, m_port);
            handler(e, db); }, m_host.data(), m_user.data(), m_password.data(), m_database.data(), 0, m_port);
            }
        protected:
            std::string m_host;
            unsigned short m_port;
            std::string m_database;
            std::string m_user;
            std::string m_password;
        };
#endif // MariaDB
    }
protected:
    std::string m_host;
    unsigned short m_port;
    std::string m_database;
    std::string m_user;
    std::string m_password;
};
#endif //MariaDB
}
}
include/qtl_odbc.hpp
@@ -18,8 +18,7 @@
#if (ODBCVER >= 0x0380) && (_WIN32_WINNT >= 0x0602)
#define QTL_ODBC_ENABLE_ASYNC_MODE 1
#endif //ODBC 3.80 && Windows
#endif // ODBC 3.80 && Windows
#include "qtl_common.hpp"
#include "qtl_async.hpp"
@@ -27,1343 +26,1365 @@
namespace qtl
{
namespace odbc
{
    namespace odbc
    {
template<SQLSMALLINT> class object;
class base_database;
        template <SQLSMALLINT>
        class object;
        class base_database;
class error : public std::exception
{
public:
    error() : m_errno(SQL_SUCCESS) { }
    template<SQLSMALLINT Type>
    error(const object<Type>& h, SQLINTEGER code);
    error(SQLINTEGER code, const char* msg) : m_errno(code), m_errmsg(msg) { }
    SQLINTEGER code() const { return m_errno; }
    operator bool() const { return m_errno!=SQL_SUCCESS && m_errno!=SQL_SUCCESS_WITH_INFO; }
    virtual const char* what() const throw() override { return m_errmsg.data(); }
private:
    SQLINTEGER m_errno;
    std::string m_errmsg;
};
template<SQLSMALLINT Type>
class object
{
public:
    enum { handler_type=Type };
    object() : m_handle(SQL_NULL_HANDLE) { };
    object(const object&) = delete;
    object(object&& src) : m_handle(src.m_handle)
    {
        src.m_handle=SQL_NULL_HANDLE;
    }
    explicit object(SQLHANDLE parent)
    {
        verify_error(SQLAllocHandle(handler_type, parent, &m_handle));
    }
    ~object()
    {
        close();
    }
    object& operator=(const object&) = delete;
    object& operator=(object&& src)
    {
        if(this!=&src)
        class error : public std::exception
        {
            close();
            m_handle=src.m_handle;
            src.m_handle=NULL;
        }
        return *this;
    }
    SQLHANDLE handle() const { return m_handle; }
        public:
            error() : m_errno(SQL_SUCCESS) {}
            template <SQLSMALLINT Type>
            error(const object<Type> &h, SQLINTEGER code);
            error(SQLINTEGER code, const char *msg) : m_errno(code), m_errmsg(msg) {}
            SQLINTEGER code() const { return m_errno; }
            operator bool() const { return m_errno != SQL_SUCCESS && m_errno != SQL_SUCCESS_WITH_INFO; }
            virtual const char *what() const throw() override { return m_errmsg.data(); }
    void close()
    {
        if(m_handle)
        private:
            SQLINTEGER m_errno;
            std::string m_errmsg;
        };
        template <SQLSMALLINT Type>
        class object
        {
            verify_error(SQLFreeHandle(handler_type, m_handle));
            m_handle=SQL_NULL_HANDLE;
        }
    }
    void verify_error(SQLINTEGER code) const
    {
        if (code < 0)
            throw odbc::error(*this, code);
    }
protected:
    SQLHANDLE m_handle;
};
class blobbuf : public qtl::blobbuf
{
public:
    blobbuf() : m_stmt(nullptr), m_field(0)
    {
    }
    blobbuf(const blobbuf&) = default;
    blobbuf& operator=(const blobbuf&) = default;
    virtual ~blobbuf() { overflow(); }
    void open(object<SQL_HANDLE_STMT>* stmt, SQLSMALLINT field, std::ios_base::openmode mode)
    {
        if (m_stmt && m_field)
        {
            overflow();
        }
        assert(stmt != SQL_NULL_HANDLE);
        m_stmt = stmt;
        m_field = field;
        m_size = INTMAX_MAX;
        init_buffer(mode);
    }
private:
    object<SQL_HANDLE_STMT>* m_stmt;
    SQLSMALLINT m_field;
protected:
    virtual bool read_blob(char* buffer, off_type& count, pos_type position) override
    {
        SQLLEN indicator=0;
        SQLRETURN ret = SQLGetData(m_stmt->handle(), m_field + 1, SQL_C_BINARY, buffer, static_cast<SQLINTEGER>(count), const_cast<SQLLEN*>(&indicator));
        if (ret != SQL_NO_DATA)
        {
            count = (indicator > count) || (indicator == SQL_NO_TOTAL) ?
                count : indicator;
            m_stmt->verify_error(ret);
            return true;
        }
        else return false;
    }
    virtual void write_blob(const char* buffer, size_t count) override
    {
        m_stmt->verify_error(SQLPutData(m_stmt->handle(), (SQLPOINTER)buffer, count));
    }
};
class environment final : public object<SQL_HANDLE_ENV>
{
public:
    environment() : object(SQL_NULL_HANDLE)
    {
#if ODBCVER >= 0x0380
        const SQLPOINTER version = (SQLPOINTER)SQL_OV_ODBC3_80;
#else
        const SQLPOINTER version = (SQLPOINTER)SQL_OV_ODBC3;
#endif
        verify_error(SQLSetEnvAttr(m_handle, SQL_ATTR_ODBC_VERSION, version, SQL_IS_INTEGER));
    }
    environment(environment&& src) : object(std::forward<environment>(src)) { }
    int32_t version() const
    {
        int32_t ver = 0;
        verify_error(SQLGetEnvAttr(m_handle, SQL_ATTR_ODBC_VERSION, &ver, sizeof(DWORD), NULL));
        return ver;
    }
};
class base_statement : public object<SQL_HANDLE_STMT>
{
public:
    explicit base_statement(base_database& db);
    base_statement(base_statement&& src)
        : object(std::forward<base_statement>(src)), m_params(std::forward<std::vector<param_data>>(src.m_params))
    {
        m_binded_cols=src.m_binded_cols;
        src.m_binded_cols=false;
        m_blob_buffer=src.m_blob_buffer;
        src.m_blob_buffer=NULL;
    }
    ~base_statement()
    {
        if(m_blob_buffer)
            free(m_blob_buffer);
    }
    base_statement& operator=(base_statement&& src)
    {
        if(this!=&src)
        {
            object::operator =(std::forward<base_statement>(src));
            m_params=std::forward<std::vector<param_data>>(src.m_params);
            m_binded_cols=src.m_binded_cols;
            src.m_binded_cols=false;
            m_blob_buffer=src.m_blob_buffer;
            src.m_blob_buffer=NULL;
        }
        return *this;
    }
    void bind_param(size_t index, const std::nullptr_t&)
    {
        m_params[index].m_indicator=SQL_NULL_DATA;
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1),
            SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_DEFAULT, 0, 0, NULL, 0, &m_params[index].m_indicator));
    }
    void bind_param(size_t index, const qtl::null&)
    {
        bind_param(index, nullptr);
    }
    void bind_param(size_t index, const int8_t& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_STINYINT, SQL_TINYINT,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const uint8_t& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_UTINYINT, SQL_TINYINT,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const int16_t& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_SMALLINT,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const uint16_t& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_USHORT, SQL_SMALLINT,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const int32_t& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const uint32_t& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const int64_t& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const uint64_t& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const double& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const float& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const bool& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const DATE_STRUCT& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_DATE, SQL_DATE,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const TIME_STRUCT& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_TIME, SQL_TIME,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const TIMESTAMP_STRUCT& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const SQLGUID& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_GUID, SQL_GUID,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const SQL_NUMERIC_STRUCT& v)
    {
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_NUMERIC, SQL_NUMERIC,
            0, 0, (SQLPOINTER)&v, 0, NULL));
    }
    void bind_param(size_t index, const char* v, size_t n=SQL_NTS, SQLULEN size=0)
    {
        m_params[index].m_indicator=n;
        if(size==0) size=strlen(v);
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
            size, 0, (SQLPOINTER)v, 0, &m_params[index].m_indicator));
    }
    void bind_param(size_t index, const wchar_t* v, size_t n=SQL_NTS, SQLULEN size=0)
    {
        m_params[index].m_indicator=n;
        if(size==0) size=wcslen(v);
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR,
            size, 0, (SQLPOINTER)v, 0, &m_params[index].m_indicator));
    }
    void bind_param(size_t index, const std::string& v)
    {
        bind_param(index, v.data(), v.size(), v.size());
    }
    void bind_param(size_t index, const std::wstring& v)
    {
        bind_param(index, v.data(), v.size(), v.size());
    }
    void bind_param(size_t index, const const_blob_data& v)
    {
        m_params[index].m_indicator=v.size;
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY,
            v.size, 0, (SQLPOINTER)v.data, 0, &m_params[index].m_indicator));
    }
    void bind_param(size_t index, std::istream& s)
    {
        if(m_blob_buffer==NULL)
            m_blob_buffer=malloc(blob_buffer_size);
        m_params[index].m_data=m_blob_buffer;
        m_params[index].m_size=blob_buffer_size;
        m_params[index].m_indicator=SQL_LEN_DATA_AT_EXEC(m_params[index].m_size);
        verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
            INT_MAX, 0, &m_params[index], 0, &m_params[index].m_indicator));
        m_params[index].m_after_fetch=[this, &s](const param_data& p) {
            SQLLEN readed=SQL_NULL_DATA;
            while(!s.eof() && !s.fail())
        public:
            enum
            {
                s.read((char*)p.m_data, p.m_size);
                readed=(unsigned long)s.gcount();
                if(readed>0)
                handler_type = Type
            };
            object() : m_handle(SQL_NULL_HANDLE){};
            object(const object &) = delete;
            object(object &&src) : m_handle(src.m_handle)
            {
                src.m_handle = SQL_NULL_HANDLE;
            }
            explicit object(SQLHANDLE parent)
            {
                verify_error(SQLAllocHandle(handler_type, parent, &m_handle));
            }
            ~object()
            {
                close();
            }
            object &operator=(const object &) = delete;
            object &operator=(object &&src)
            {
                if (this != &src)
                {
                    verify_error(SQLPutData(m_handle, p.m_data, readed));
                    close();
                    m_handle = src.m_handle;
                    src.m_handle = NULL;
                }
                return *this;
            }
            SQLHANDLE handle() const { return m_handle; }
            void close()
            {
                if (m_handle)
                {
                    verify_error(SQLFreeHandle(handler_type, m_handle));
                    m_handle = SQL_NULL_HANDLE;
                }
            }
        };
    }
    void bind_param(size_t index, const blob_writer& param)
    {
        m_params[index].m_data = nullptr;
        m_params[index].m_size = blob_buffer_size;
        m_params[index].m_indicator = SQL_LEN_DATA_AT_EXEC(m_params[index].m_size);
        verify_error(SQLBindParameter(m_handle, static_cast<SQLSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
            INT_MAX, 0, &m_params[index], 0, &m_params[index].m_indicator));
        m_params[index].m_after_fetch = [this, index, &param](const param_data& b) {
            blobbuf buf;
            buf.open(this, static_cast<SQLSMALLINT>(index), std::ios::out);
            std::ostream s(&buf);
            param(s);
        };
    }
            void verify_error(SQLINTEGER code) const
            {
                if (code < 0)
                    throw odbc::error(*this, code);
            }
    void bind_field(size_t index, bool&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_BIT, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, int8_t&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_STINYINT, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, uint8_t&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_UTINYINT, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, int16_t&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_SSHORT, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, uint16_t&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_USHORT, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, int32_t&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_SLONG, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, uint32_t&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_ULONG, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, int64_t&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_SBIGINT, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, uint64_t&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_UBIGINT, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, float&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_FLOAT, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, double&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_DOUBLE, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, DATE_STRUCT&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_TYPE_DATE, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, TIME_STRUCT&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_TYPE_TIME, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, TIMESTAMP_STRUCT&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_TYPE_TIMESTAMP, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, SQLGUID&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_GUID, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, SQL_NUMERIC_STRUCT&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_NUMERIC, &v, 0, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, char* v, size_t n)
    {
        m_params[index].m_data=v;
        m_params[index].m_size=n;
        m_params[index].m_after_fetch=[](const param_data& p) {
            if(p.m_indicator==SQL_NULL_DATA)
                memset(p.m_data, 0, p.m_size*sizeof(char));
            else
            {
                char* text=reinterpret_cast<char*>(p.m_data);
                text[p.m_indicator]='\0';
            }
        protected:
            SQLHANDLE m_handle;
        };
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_CHAR, v, n, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, wchar_t* v, size_t n)
    {
        m_params[index].m_data=v;
        m_params[index].m_size=n;
        m_params[index].m_after_fetch=[](const param_data& p) {
            if(p.m_indicator==SQL_NULL_DATA)
                memset(p.m_data, 0, p.m_size*sizeof(wchar_t));
            else
            {
                wchar_t* text=reinterpret_cast<wchar_t*>(p.m_data);
                text[p.m_indicator]='\0';
            }
        };
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_WCHAR, v, n, &m_params[index].m_indicator));
    }
    template<typename T>
    void bind_field(size_t index, qtl::bind_string_helper<T>&& v)
    {
        SQLLEN length=0;
        verify_error(SQLColAttribute(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_DESC_LENGTH, NULL, 0, NULL, &length));
        typename qtl::bind_string_helper<T>::char_type* data=v.alloc(length);
        bind_field(index, data, length+1);
        m_params[index].m_after_fetch=[v](const param_data& p) mutable {
            if(p.m_indicator==SQL_NULL_DATA)
                v.clear();
            else
                v.truncate(p.m_indicator);
        };
    }
    template<size_t N>
    void bind_field(size_t index, std::array<char, N>&& value)
    {
        bind_field(index, value.data(), value.size());
    }
    template<size_t N>
    void bind_field(size_t index, std::array<wchar_t, N>&& value)
    {
        bind_field(index, value.data(), value.size());
    }
    void bind_field(size_t index, qtl::blob_data&& v)
    {
        verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_BINARY, v.data, v.size, &m_params[index].m_indicator));
    }
    void bind_field(size_t index, std::ostream&& v)
    {
        if(m_blob_buffer==NULL)
            m_blob_buffer=malloc(blob_buffer_size);
        m_params[index].m_data=m_blob_buffer;
        m_params[index].m_size=blob_buffer_size;
        m_params[index].m_after_fetch=[this, index, &v](const param_data& p) {
            SQLRETURN ret=SQLGetData(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN*>(&p.m_indicator));
            while(ret!=SQL_NO_DATA)
            {
                size_t n = (p.m_indicator > blob_buffer_size) || (p.m_indicator == SQL_NO_TOTAL) ?
                    blob_buffer_size : p.m_indicator;
                verify_error(ret);
                v.write((const char*)p.m_data, n);
                ret=SQLGetData(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN*>(&p.m_indicator));
            }
        };
    }
    void bind_field(size_t index, blobbuf&& value)
    {
        m_params[index].m_data = nullptr;
        m_params[index].m_size = 0;
        m_params[index].m_after_fetch = [this, index, &value](const param_data& p) {
            value.open(this, static_cast<SQLSMALLINT>(index), std::ios::in);
        };
    }
        class blobbuf : public qtl::blobbuf
        {
        public:
            blobbuf() : m_stmt(nullptr), m_field(0)
            {
            }
            blobbuf(const blobbuf &) = default;
            blobbuf &operator=(const blobbuf &) = default;
            virtual ~blobbuf() { overflow(); }
    template<typename Type>
    void bind_field(size_t index, indicator<Type>&& value)
    {
        qtl::bind_field(*this, index, value.data);
        param_data& param=m_params[index];
        auto fetch_fun=param.m_after_fetch;
        param.m_after_fetch=[fetch_fun, &value](const param_data& p) {
            value.is_truncated=false;
            if(p.m_indicator==SQL_NULL_DATA)
            void open(object<SQL_HANDLE_STMT> *stmt, SQLSMALLINT field, std::ios_base::openmode mode)
            {
                value.is_null=true;
                value.length=0;
                if (m_stmt && m_field)
                {
                    overflow();
                }
                assert(stmt != SQL_NULL_HANDLE);
                m_stmt = stmt;
                m_field = field;
                m_size = INTMAX_MAX;
                init_buffer(mode);
            }
            else if(p.m_indicator>=0)
        private:
            object<SQL_HANDLE_STMT> *m_stmt;
            SQLSMALLINT m_field;
        protected:
            virtual bool read_blob(char *buffer, off_type &count, pos_type position) override
            {
                value.is_null=false;
                value.length=p.m_indicator;
                if(p.m_size>0 && p.m_indicator>=p.m_size)
                    value.is_truncated=true;
                SQLLEN indicator = 0;
                SQLRETURN ret = SQLGetData(m_stmt->handle(), m_field + 1, SQL_C_BINARY, buffer, static_cast<SQLINTEGER>(count), const_cast<SQLLEN *>(&indicator));
                if (ret != SQL_NO_DATA)
                {
                    count = (indicator > count) || (indicator == SQL_NO_TOTAL) ? count : indicator;
                    m_stmt->verify_error(ret);
                    return true;
                }
                else
                    return false;
            }
            if(fetch_fun) fetch_fun(p);
            virtual void write_blob(const char *buffer, size_t count) override
            {
                m_stmt->verify_error(SQLPutData(m_stmt->handle(), (SQLPOINTER)buffer, count));
            }
        };
    }
        class environment final : public object<SQL_HANDLE_ENV>
        {
        public:
            environment() : object(SQL_NULL_HANDLE)
            {
#if ODBCVER >= 0x0380
                const SQLPOINTER version = (SQLPOINTER)SQL_OV_ODBC3_80;
#else
                const SQLPOINTER version = (SQLPOINTER)SQL_OV_ODBC3;
#endif
                verify_error(SQLSetEnvAttr(m_handle, SQL_ATTR_ODBC_VERSION, version, SQL_IS_INTEGER));
            }
            environment(environment &&src) : object(std::forward<environment>(src)) {}
            int32_t version() const
            {
                int32_t ver = 0;
                verify_error(SQLGetEnvAttr(m_handle, SQL_ATTR_ODBC_VERSION, &ver, sizeof(DWORD), NULL));
                return ver;
            }
        };
        class base_statement : public object<SQL_HANDLE_STMT>
        {
        public:
            explicit base_statement(base_database &db);
            base_statement(base_statement &&src)
                : object(std::forward<base_statement>(src)), m_params(std::forward<std::vector<param_data>>(src.m_params))
            {
                m_binded_cols = src.m_binded_cols;
                src.m_binded_cols = false;
                m_blob_buffer = src.m_blob_buffer;
                src.m_blob_buffer = NULL;
            }
            ~base_statement()
            {
                if (m_blob_buffer)
                    free(m_blob_buffer);
            }
            base_statement &operator=(base_statement &&src)
            {
                if (this != &src)
                {
                    object::operator=(std::forward<base_statement>(src));
                    m_params = std::forward<std::vector<param_data>>(src.m_params);
                    m_binded_cols = src.m_binded_cols;
                    src.m_binded_cols = false;
                    m_blob_buffer = src.m_blob_buffer;
                    src.m_blob_buffer = NULL;
                }
                return *this;
            }
            void bind_param(size_t index, const std::nullptr_t &)
            {
                m_params[index].m_indicator = SQL_NULL_DATA;
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1),
                                              SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_DEFAULT, 0, 0, NULL, 0, &m_params[index].m_indicator));
            }
            void bind_param(size_t index, const qtl::null &)
            {
                bind_param(index, nullptr);
            }
            void bind_param(size_t index, const int8_t &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_STINYINT, SQL_TINYINT,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const uint8_t &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_UTINYINT, SQL_TINYINT,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const int16_t &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_SMALLINT,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const uint16_t &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_USHORT, SQL_SMALLINT,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const int32_t &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const uint32_t &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const int64_t &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const uint64_t &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const double &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const float &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const bool &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const DATE_STRUCT &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_DATE, SQL_DATE,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const TIME_STRUCT &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_TIME, SQL_TIME,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const TIMESTAMP_STRUCT &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const SQLGUID &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_GUID, SQL_GUID,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const SQL_NUMERIC_STRUCT &v)
            {
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_NUMERIC, SQL_NUMERIC,
                                              0, 0, (SQLPOINTER)&v, 0, NULL));
            }
            void bind_param(size_t index, const char *v, size_t n = SQL_NTS, SQLULEN size = 0)
            {
                m_params[index].m_indicator = n;
                if (size == 0)
                    size = strlen(v);
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
                                              size, 0, (SQLPOINTER)v, 0, &m_params[index].m_indicator));
            }
            void bind_param(size_t index, const wchar_t *v, size_t n = SQL_NTS, SQLULEN size = 0)
            {
                m_params[index].m_indicator = n;
                if (size == 0)
                    size = wcslen(v);
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR,
                                              size, 0, (SQLPOINTER)v, 0, &m_params[index].m_indicator));
            }
            void bind_param(size_t index, const std::string &v)
            {
                bind_param(index, v.data(), v.size(), v.size());
            }
            void bind_param(size_t index, const std::wstring &v)
            {
                bind_param(index, v.data(), v.size(), v.size());
            }
            void bind_param(size_t index, const const_blob_data &v)
            {
                m_params[index].m_indicator = v.size;
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY,
                                              v.size, 0, (SQLPOINTER)v.data, 0, &m_params[index].m_indicator));
            }
            void bind_param(size_t index, std::istream &s)
            {
                if (m_blob_buffer == NULL)
                    m_blob_buffer = malloc(blob_buffer_size);
                m_params[index].m_data = m_blob_buffer;
                m_params[index].m_size = blob_buffer_size;
                m_params[index].m_indicator = SQL_LEN_DATA_AT_EXEC(m_params[index].m_size);
                verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
                                              INT_MAX, 0, &m_params[index], 0, &m_params[index].m_indicator));
                m_params[index].m_after_fetch = [this, &s](const param_data &p)
                {
                    SQLLEN readed = SQL_NULL_DATA;
                    while (!s.eof() && !s.fail())
                    {
                        s.read((char *)p.m_data, p.m_size);
                        readed = (unsigned long)s.gcount();
                        if (readed > 0)
                        {
                            verify_error(SQLPutData(m_handle, p.m_data, readed));
                        }
                    }
                };
            }
            void bind_param(size_t index, const blob_writer &param)
            {
                m_params[index].m_data = nullptr;
                m_params[index].m_size = blob_buffer_size;
                m_params[index].m_indicator = SQL_LEN_DATA_AT_EXEC(m_params[index].m_size);
                verify_error(SQLBindParameter(m_handle, static_cast<SQLSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
                                              INT_MAX, 0, &m_params[index], 0, &m_params[index].m_indicator));
                m_params[index].m_after_fetch = [this, index, &param](const param_data &b)
                {
                    blobbuf buf;
                    buf.open(this, static_cast<SQLSMALLINT>(index), std::ios::out);
                    std::ostream s(&buf);
                    param(s);
                };
            }
            void bind_field(size_t index, bool &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_BIT, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, int8_t &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_STINYINT, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, uint8_t &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_UTINYINT, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, int16_t &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_SSHORT, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, uint16_t &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_USHORT, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, int32_t &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_SLONG, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, uint32_t &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_ULONG, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, int64_t &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_SBIGINT, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, uint64_t &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_UBIGINT, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, float &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_FLOAT, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, double &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_DOUBLE, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, DATE_STRUCT &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_TYPE_DATE, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, TIME_STRUCT &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_TYPE_TIME, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, TIMESTAMP_STRUCT &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_TYPE_TIMESTAMP, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, SQLGUID &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_GUID, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, SQL_NUMERIC_STRUCT &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_NUMERIC, &v, 0, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, char *v, size_t n)
            {
                m_params[index].m_data = v;
                m_params[index].m_size = n;
                m_params[index].m_after_fetch = [](const param_data &p)
                {
                    if (p.m_indicator == SQL_NULL_DATA)
                        memset(p.m_data, 0, p.m_size * sizeof(char));
                    else
                    {
                        char *text = reinterpret_cast<char *>(p.m_data);
                        text[p.m_indicator] = '\0';
                    }
                };
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_CHAR, v, n, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, wchar_t *v, size_t n)
            {
                m_params[index].m_data = v;
                m_params[index].m_size = n;
                m_params[index].m_after_fetch = [](const param_data &p)
                {
                    if (p.m_indicator == SQL_NULL_DATA)
                        memset(p.m_data, 0, p.m_size * sizeof(wchar_t));
                    else
                    {
                        wchar_t *text = reinterpret_cast<wchar_t *>(p.m_data);
                        text[p.m_indicator] = '\0';
                    }
                };
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_WCHAR, v, n, &m_params[index].m_indicator));
            }
            template <typename T>
            void bind_field(size_t index, qtl::bind_string_helper<T> &&v)
            {
                SQLLEN length = 0;
                verify_error(SQLColAttribute(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_DESC_LENGTH, NULL, 0, NULL, &length));
                typename qtl::bind_string_helper<T>::char_type *data = v.alloc(length);
                bind_field(index, data, length + 1);
                m_params[index].m_after_fetch = [v](const param_data &p) mutable
                {
                    if (p.m_indicator == SQL_NULL_DATA)
                        v.clear();
                    else
                        v.truncate(p.m_indicator);
                };
            }
            template <size_t N>
            void bind_field(size_t index, std::array<char, N> &&value)
            {
                bind_field(index, value.data(), value.size());
            }
            template <size_t N>
            void bind_field(size_t index, std::array<wchar_t, N> &&value)
            {
                bind_field(index, value.data(), value.size());
            }
            void bind_field(size_t index, qtl::blob_data &&v)
            {
                verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_BINARY, v.data, v.size, &m_params[index].m_indicator));
            }
            void bind_field(size_t index, std::ostream &&v)
            {
                if (m_blob_buffer == NULL)
                    m_blob_buffer = malloc(blob_buffer_size);
                m_params[index].m_data = m_blob_buffer;
                m_params[index].m_size = blob_buffer_size;
                m_params[index].m_after_fetch = [this, index, &v](const param_data &p)
                {
                    SQLRETURN ret = SQLGetData(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN *>(&p.m_indicator));
                    while (ret != SQL_NO_DATA)
                    {
                        size_t n = (p.m_indicator > blob_buffer_size) || (p.m_indicator == SQL_NO_TOTAL) ? blob_buffer_size : p.m_indicator;
                        verify_error(ret);
                        v.write((const char *)p.m_data, n);
                        ret = SQLGetData(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN *>(&p.m_indicator));
                    }
                };
            }
            void bind_field(size_t index, blobbuf &&value)
            {
                m_params[index].m_data = nullptr;
                m_params[index].m_size = 0;
                m_params[index].m_after_fetch = [this, index, &value](const param_data &p)
                {
                    value.open(this, static_cast<SQLSMALLINT>(index), std::ios::in);
                };
            }
            template <typename Type>
            void bind_field(size_t index, indicator<Type> &&value)
            {
                qtl::bind_field(*this, index, value.data);
                param_data &param = m_params[index];
                auto fetch_fun = param.m_after_fetch;
                param.m_after_fetch = [fetch_fun, &value](const param_data &p)
                {
                    value.is_truncated = false;
                    if (p.m_indicator == SQL_NULL_DATA)
                    {
                        value.is_null = true;
                        value.length = 0;
                    }
                    else if (p.m_indicator >= 0)
                    {
                        value.is_null = false;
                        value.length = p.m_indicator;
                        if (p.m_size > 0 && p.m_indicator >= p.m_size)
                            value.is_truncated = true;
                    }
                    if (fetch_fun)
                        fetch_fun(p);
                };
            }
#ifdef _QTL_ENABLE_CPP17
    template<typename Type>
    void bind_field(size_t index, std::optional<Type>&& value)
    {
        qtl::bind_field(*this, index, *value);
        param_data& param = m_params[index];
        auto fetch_fun = param.m_after_fetch;
        param.m_after_fetch = [fetch_fun, &value](const param_data& p) {
            if (fetch_fun) fetch_fun(p);
            if (p.m_indicator == SQL_NULL_DATA)
                value.reset();
        };
    }
            template <typename Type>
            void bind_field(size_t index, std::optional<Type> &&value)
            {
                qtl::bind_field(*this, index, *value);
                param_data &param = m_params[index];
                auto fetch_fun = param.m_after_fetch;
                param.m_after_fetch = [fetch_fun, &value](const param_data &p)
                {
                    if (fetch_fun)
                        fetch_fun(p);
                    if (p.m_indicator == SQL_NULL_DATA)
                        value.reset();
                };
            }
    void bind_field(size_t index, std::any&& value)
    {
        SQLLEN type = 0, isUnsigned=SQL_FALSE;
        verify_error(SQLColAttribute(m_handle, index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &type));
        verify_error(SQLColAttribute(m_handle, index + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL, &isUnsigned));
        switch (type)
        {
        case SQL_BIT:
            value.emplace<bool>();
            bind_field(index, std::forward<bool>(std::any_cast<bool&>(value)));
            break;
        case SQL_TINYINT:
            if (isUnsigned)
            void bind_field(size_t index, std::any &&value)
            {
                value.emplace<uint8_t>();
                bind_field(index, std::forward<uint8_t>(std::any_cast<uint8_t&>(value)));
                SQLLEN type = 0, isUnsigned = SQL_FALSE;
                verify_error(SQLColAttribute(m_handle, index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &type));
                verify_error(SQLColAttribute(m_handle, index + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL, &isUnsigned));
                switch (type)
                {
                case SQL_BIT:
                    value.emplace<bool>();
                    bind_field(index, std::forward<bool>(std::any_cast<bool &>(value)));
                    break;
                case SQL_TINYINT:
                    if (isUnsigned)
                    {
                        value.emplace<uint8_t>();
                        bind_field(index, std::forward<uint8_t>(std::any_cast<uint8_t &>(value)));
                    }
                    else
                    {
                        value.emplace<int8_t>();
                        bind_field(index, std::forward<int8_t>(std::any_cast<int8_t &>(value)));
                    }
                    break;
                case SQL_SMALLINT:
                    if (isUnsigned)
                    {
                        value.emplace<uint16_t>();
                        bind_field(index, std::forward<uint16_t>(std::any_cast<uint16_t &>(value)));
                    }
                    else
                    {
                        value.emplace<int16_t>();
                        bind_field(index, std::forward<int16_t>(std::any_cast<int16_t &>(value)));
                    }
                    break;
                case SQL_INTEGER:
                    if (isUnsigned)
                    {
                        value.emplace<uint32_t>();
                        bind_field(index, std::forward<uint32_t>(std::any_cast<uint32_t &>(value)));
                    }
                    else
                    {
                        value.emplace<int32_t>();
                        bind_field(index, std::forward<int32_t>(std::any_cast<int32_t &>(value)));
                    }
                    break;
                case SQL_BIGINT:
                    if (isUnsigned)
                    {
                        value.emplace<uint64_t>();
                        bind_field(index, std::forward<uint64_t>(std::any_cast<uint64_t &>(value)));
                    }
                    else
                    {
                        value.emplace<int64_t>();
                        bind_field(index, std::forward<int64_t>(std::any_cast<int64_t &>(value)));
                    }
                    break;
                case SQL_FLOAT:
                    value.emplace<float>();
                    bind_field(index, std::forward<float>(std::any_cast<float &>(value)));
                    break;
                case SQL_DOUBLE:
                    value.emplace<double>();
                    bind_field(index, std::forward<double>(std::any_cast<double &>(value)));
                    break;
                case SQL_NUMERIC:
                    value.emplace<SQL_NUMERIC_STRUCT>();
                    bind_field(index, std::forward<SQL_NUMERIC_STRUCT>(std::any_cast<SQL_NUMERIC_STRUCT &>(value)));
                    break;
                case SQL_TIME:
                    value.emplace<SQL_TIME_STRUCT>();
                    bind_field(index, std::forward<SQL_TIME_STRUCT>(std::any_cast<SQL_TIME_STRUCT &>(value)));
                    break;
                case SQL_DATE:
                    value.emplace<SQL_DATE_STRUCT>();
                    bind_field(index, std::forward<SQL_DATE_STRUCT>(std::any_cast<SQL_DATE_STRUCT &>(value)));
                    break;
                case SQL_TIMESTAMP:
                    value.emplace<SQL_TIMESTAMP_STRUCT>();
                    bind_field(index, std::forward<SQL_TIMESTAMP_STRUCT>(std::any_cast<SQL_TIMESTAMP_STRUCT &>(value)));
                    break;
                case SQL_INTERVAL_MONTH:
                case SQL_INTERVAL_YEAR:
                case SQL_INTERVAL_YEAR_TO_MONTH:
                case SQL_INTERVAL_DAY:
                case SQL_INTERVAL_HOUR:
                case SQL_INTERVAL_MINUTE:
                case SQL_INTERVAL_SECOND:
                case SQL_INTERVAL_DAY_TO_HOUR:
                case SQL_INTERVAL_DAY_TO_MINUTE:
                case SQL_INTERVAL_DAY_TO_SECOND:
                case SQL_INTERVAL_HOUR_TO_MINUTE:
                case SQL_INTERVAL_HOUR_TO_SECOND:
                case SQL_INTERVAL_MINUTE_TO_SECOND:
                    value.emplace<SQL_INTERVAL_STRUCT>();
                    bind_field(index, std::forward<SQL_INTERVAL_STRUCT>(std::any_cast<SQL_INTERVAL_STRUCT &>(value)));
                    break;
                case SQL_CHAR:
                    value.emplace<std::string>();
                    bind_field(index, qtl::bind_string(std::any_cast<std::string &>(value)));
                    break;
                case SQL_GUID:
                    value.emplace<SQLGUID>();
                    bind_field(index, std::forward<SQLGUID>(std::any_cast<SQLGUID &>(value)));
                    break;
                case SQL_BINARY:
                    value.emplace<blobbuf>();
                    bind_field(index, std::forward<blobbuf>(std::any_cast<blobbuf &>(value)));
                    break;
                default:
                    throw odbc::error(*this, SQL_ERROR);
                }
                param_data &param = m_params[index];
                auto fetch_fun = param.m_after_fetch;
                param.m_after_fetch = [fetch_fun, &value](const param_data &p)
                {
                    if (fetch_fun)
                        fetch_fun(p);
                    if (p.m_indicator == SQL_NULL_DATA)
                        value.reset();
                };
            }
            else
            {
                value.emplace<int8_t>();
                bind_field(index, std::forward<int8_t>(std::any_cast<int8_t&>(value)));
            }
            break;
        case SQL_SMALLINT:
            if (isUnsigned)
            {
                value.emplace<uint16_t>();
                bind_field(index, std::forward<uint16_t>(std::any_cast<uint16_t&>(value)));
            }
            else
            {
                value.emplace<int16_t>();
                bind_field(index, std::forward<int16_t>(std::any_cast<int16_t&>(value)));
            }
            break;
        case SQL_INTEGER:
            if (isUnsigned)
            {
                value.emplace<uint32_t>();
                bind_field(index, std::forward<uint32_t>(std::any_cast<uint32_t&>(value)));
            }
            else
            {
                value.emplace<int32_t>();
                bind_field(index, std::forward<int32_t>(std::any_cast<int32_t&>(value)));
            }
            break;
        case SQL_BIGINT:
            if (isUnsigned)
            {
                value.emplace<uint64_t>();
                bind_field(index, std::forward<uint64_t>(std::any_cast<uint64_t&>(value)));
            }
            else
            {
                value.emplace<int64_t>();
                bind_field(index, std::forward<int64_t>(std::any_cast<int64_t&>(value)));
            }
            break;
        case SQL_FLOAT:
            value.emplace<float>();
            bind_field(index, std::forward<float>(std::any_cast<float&>(value)));
            break;
        case SQL_DOUBLE:
            value.emplace<double>();
            bind_field(index, std::forward<double>(std::any_cast<double&>(value)));
            break;
        case SQL_NUMERIC:
            value.emplace<SQL_NUMERIC_STRUCT>();
            bind_field(index, std::forward<SQL_NUMERIC_STRUCT>(std::any_cast<SQL_NUMERIC_STRUCT&>(value)));
            break;
        case SQL_TIME:
            value.emplace<SQL_TIME_STRUCT>();
            bind_field(index, std::forward<SQL_TIME_STRUCT>(std::any_cast<SQL_TIME_STRUCT&>(value)));
            break;
        case SQL_DATE:
            value.emplace<SQL_DATE_STRUCT>();
            bind_field(index, std::forward<SQL_DATE_STRUCT>(std::any_cast<SQL_DATE_STRUCT&>(value)));
            break;
        case SQL_TIMESTAMP:
            value.emplace<SQL_TIMESTAMP_STRUCT>();
            bind_field(index, std::forward<SQL_TIMESTAMP_STRUCT>(std::any_cast<SQL_TIMESTAMP_STRUCT&>(value)));
            break;
        case SQL_INTERVAL_MONTH:
        case SQL_INTERVAL_YEAR:
        case SQL_INTERVAL_YEAR_TO_MONTH:
        case SQL_INTERVAL_DAY:
        case SQL_INTERVAL_HOUR:
        case SQL_INTERVAL_MINUTE:
        case SQL_INTERVAL_SECOND:
        case SQL_INTERVAL_DAY_TO_HOUR:
        case SQL_INTERVAL_DAY_TO_MINUTE:
        case SQL_INTERVAL_DAY_TO_SECOND:
        case SQL_INTERVAL_HOUR_TO_MINUTE:
        case SQL_INTERVAL_HOUR_TO_SECOND:
        case SQL_INTERVAL_MINUTE_TO_SECOND:
            value.emplace<SQL_INTERVAL_STRUCT>();
            bind_field(index, std::forward<SQL_INTERVAL_STRUCT>(std::any_cast<SQL_INTERVAL_STRUCT&>(value)));
            break;
        case SQL_CHAR:
            value.emplace<std::string>();
            bind_field(index, qtl::bind_string(std::any_cast<std::string&>(value)));
            break;
        case SQL_GUID:
            value.emplace<SQLGUID>();
            bind_field(index, std::forward<SQLGUID>(std::any_cast<SQLGUID&>(value)));
            break;
        case SQL_BINARY:
            value.emplace<blobbuf>();
            bind_field(index, std::forward<blobbuf>(std::any_cast<blobbuf&>(value)));
            break;
        default:
            throw odbc::error(*this, SQL_ERROR);
        }
        param_data& param = m_params[index];
        auto fetch_fun = param.m_after_fetch;
        param.m_after_fetch = [fetch_fun, &value](const param_data& p) {
            if (fetch_fun) fetch_fun(p);
            if (p.m_indicator == SQL_NULL_DATA)
                value.reset();
        };
    }
#endif // C++17
    SQLLEN affetced_rows()
    {
        SQLLEN count=0;
        verify_error(SQLRowCount(m_handle, &count));
        return count;
    }
    size_t find_field(const char* name) const
    {
        SQLSMALLINT count=0;
        verify_error(SQLNumResultCols(m_handle, &count));
        for(SQLSMALLINT i=0; i!=count; i++)
        {
            SQLCHAR field_name[256]={0};
            SQLSMALLINT name_length=0;
            SQLSMALLINT data_type;
            SQLULEN column_size;
            SQLSMALLINT digits;
            SQLSMALLINT nullable;
            verify_error(SQLDescribeColA(m_handle, i, field_name, sizeof(field_name), &name_length,
                &data_type, &column_size, &digits, &nullable));
            if(strncmp((char*)field_name, name, name_length)==0)
                return i;
        }
        return -1;
    }
    void reset()
    {
        verify_error(SQLFreeStmt(m_handle, SQL_RESET_PARAMS));
    }
    /*
        ODBC do not support this function, but you can use query to instead it:
        For MS SQL Server: SELECT @@IDENTITY;
        For MySQL: SELECT LAST_INSERT_ID();
        For SQLite: SELECT last_insert_rowid();
     */
    /*uint64_t insert_id()
    {
        assert(false);
        return 0;
    }*/
protected:
    struct param_data
    {
        SQLPOINTER m_data;
        SQLLEN m_size;
        SQLLEN m_indicator;
        std::function<void(const param_data&)> m_after_fetch;
        param_data() : m_data(NULL), m_size(0), m_indicator(0) { }
    };
    SQLPOINTER m_blob_buffer;
    std::vector<param_data> m_params;
    bool m_binded_cols;
};
class statement : public base_statement
{
public:
    statement() = default;
    explicit statement(base_database& db) : base_statement(db) { }
    statement(statement&& src) : base_statement(std::move(src)) { }
    statement& operator=(statement&& src)
    {
        base_statement::operator =(std::move(src));
        return *this;
    }
    ~statement()
    {
        close();
    }
    void open(const char* query_text, size_t text_length = SQL_NTS)
    {
        reset();
        verify_error(SQLPrepareA(m_handle, (SQLCHAR*)query_text, text_length));
    }
    void open(const std::string& query_text)
    {
        open(query_text.data(), query_text.size());
    }
    template<typename Types>
    void execute(const Types& params)
    {
        SQLSMALLINT count = 0;
        verify_error(SQLNumParams(m_handle, &count));
        if (count > 0)
        {
            m_params.resize(count);
            qtl::bind_params(*this, params);
        }
        SQLRETURN ret = SQLExecute(m_handle);
        verify_error(ret);
        if (ret == SQL_NEED_DATA)
        {
            SQLPOINTER token;
            size_t i = 0;
            ret = SQLParamData(m_handle, &token);
            verify_error(ret);
            while (ret == SQL_NEED_DATA)
            SQLLEN affetced_rows()
            {
                while (i != count)
                SQLLEN count = 0;
                verify_error(SQLRowCount(m_handle, &count));
                return count;
            }
            size_t find_field(const char *name) const
            {
                SQLSMALLINT count = 0;
                verify_error(SQLNumResultCols(m_handle, &count));
                for (SQLSMALLINT i = 0; i != count; i++)
                {
                    if (&m_params[i] == token)
                    {
                        if (m_params[i].m_after_fetch)
                            m_params[i].m_after_fetch(m_params[i]);
                        break;
                    }
                    ++i;
                    SQLCHAR field_name[256] = {0};
                    SQLSMALLINT name_length = 0;
                    SQLSMALLINT data_type;
                    SQLULEN column_size;
                    SQLSMALLINT digits;
                    SQLSMALLINT nullable;
                    verify_error(SQLDescribeColA(m_handle, i, field_name, sizeof(field_name), &name_length,
                                                 &data_type, &column_size, &digits, &nullable));
                    if (strncmp((char *)field_name, name, name_length) == 0)
                        return i;
                }
                ret = SQLParamData(m_handle, &token);
                verify_error(ret);
                return -1;
            }
        }
    }
    template<typename Types>
    bool fetch(Types&& values)
    {
        if (!m_binded_cols)
        {
            SQLSMALLINT count = 0;
            verify_error(SQLNumResultCols(m_handle, &count));
            if (count > 0)
            void reset()
            {
                m_params.resize(count);
                qtl::bind_record(*this, std::forward<Types>(values));
                verify_error(SQLFreeStmt(m_handle, SQL_RESET_PARAMS));
            }
            m_binded_cols = true;
        }
        return fetch();
    }
    bool fetch()
    {
        SQLRETURN ret = SQLFetch(m_handle);
        if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
        {
            for (const param_data& data : m_params)
            /*
                ODBC do not support this function, but you can use query to instead it:
                For MS SQL Server: SELECT @@IDENTITY;
                For MySQL: SELECT LAST_INSERT_ID();
                For SQLite: SELECT last_insert_rowid();
             */
            /*uint64_t insert_id()
            {
                if (data.m_after_fetch)
                    data.m_after_fetch(data);
                assert(false);
                return 0;
            }*/
        protected:
            struct param_data
            {
                SQLPOINTER m_data;
                SQLLEN m_size;
                SQLLEN m_indicator;
                std::function<void(const param_data &)> m_after_fetch;
                param_data() : m_data(NULL), m_size(0), m_indicator(0) {}
            };
            SQLPOINTER m_blob_buffer;
            std::vector<param_data> m_params;
            bool m_binded_cols;
        };
        class statement : public base_statement
        {
        public:
            statement() = default;
            explicit statement(base_database &db) : base_statement(db) {}
            statement(statement &&src) : base_statement(std::move(src)) {}
            statement &operator=(statement &&src)
            {
                base_statement::operator=(std::move(src));
                return *this;
            }
            return true;
        }
        verify_error(ret);
        return false;
    }
            ~statement()
            {
                close();
            }
    bool next_result()
    {
        SQLRETURN ret;
        SQLSMALLINT count = 0;
        m_binded_cols = false;
        do
        {
            ret = SQLMoreResults(m_handle);
            if (ret == SQL_ERROR || ret == SQL_INVALID_HANDLE)
            void open(const char *query_text, size_t text_length = SQL_NTS)
            {
                reset();
                verify_error(SQLPrepareA(m_handle, (SQLCHAR *)query_text, text_length));
            }
            void open(const std::string &query_text)
            {
                open(query_text.data(), query_text.size());
            }
            template <typename Types>
            void execute(const Types &params)
            {
                SQLSMALLINT count = 0;
                verify_error(SQLNumParams(m_handle, &count));
                if (count > 0)
                {
                    m_params.resize(count);
                    qtl::bind_params(*this, params);
                }
                SQLRETURN ret = SQLExecute(m_handle);
                verify_error(ret);
            verify_error(SQLNumResultCols(m_handle, &count));
        } while (count == 0);
        return ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO;
    }
};
                if (ret == SQL_NEED_DATA)
                {
                    SQLPOINTER token;
                    size_t i = 0;
                    ret = SQLParamData(m_handle, &token);
                    verify_error(ret);
                    while (ret == SQL_NEED_DATA)
                    {
                        while (i != count)
                        {
                            if (&m_params[i] == token)
                            {
                                if (m_params[i].m_after_fetch)
                                    m_params[i].m_after_fetch(m_params[i]);
                                break;
                            }
                            ++i;
                        }
                        ret = SQLParamData(m_handle, &token);
                        verify_error(ret);
                    }
                }
            }
struct connection_parameter
{
    std::string m_name;
    std::string m_prompt;
    std::string m_value;
    std::vector<std::string> m_value_list;
    bool m_optinal;
    bool m_assigned;
            template <typename Types>
            bool fetch(Types &&values)
            {
                if (!m_binded_cols)
                {
                    SQLSMALLINT count = 0;
                    verify_error(SQLNumResultCols(m_handle, &count));
                    if (count > 0)
                    {
                        m_params.resize(count);
                        qtl::bind_record(*this, std::forward<Types>(values));
                    }
                    m_binded_cols = true;
                }
                return fetch();
            }
    connection_parameter() : m_optinal(false), m_assigned(false) { }
    void reset()
    {
        m_name.clear();
        m_prompt.clear();
        m_value.clear();
        m_value_list.clear();
        m_optinal=false;
        m_assigned=false;
    }
};
typedef std::vector<connection_parameter> connection_parameters;
            bool fetch()
            {
                SQLRETURN ret = SQLFetch(m_handle);
                if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
                {
                    for (const param_data &data : m_params)
                    {
                        if (data.m_after_fetch)
                            data.m_after_fetch(data);
                    }
                    return true;
                }
                verify_error(ret);
                return false;
            }
class base_database : public object<SQL_HANDLE_DBC>
{
public:
    typedef odbc::error exception_type;
            bool next_result()
            {
                SQLRETURN ret;
                SQLSMALLINT count = 0;
                m_binded_cols = false;
                do
                {
                    ret = SQLMoreResults(m_handle);
                    if (ret == SQL_ERROR || ret == SQL_INVALID_HANDLE)
                        verify_error(ret);
                    verify_error(SQLNumResultCols(m_handle, &count));
                } while (count == 0);
                return ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO;
            }
        };
    explicit base_database(environment& env) : object(env.handle()), m_opened(false)
    {
    }
    base_database(const base_database&) = delete;
    base_database(base_database&& src)
        : object(std::forward<base_database>(src)), m_connection(std::forward<std::string>(src.m_connection))
    {
        m_opened=src.m_opened;
        src.m_opened=false;
    }
    ~base_database()
    {
        close();
    }
    base_database& operator=(base_database&& src)
    {
        if(this!=&src)
        struct connection_parameter
        {
            object::operator =(std::forward<base_database>(src));
            m_opened=src.m_opened;
            src.m_opened=false;
            m_connection=std::forward<std::string>(src.m_connection);
        }
        return *this;
    }
            std::string m_name;
            std::string m_prompt;
            std::string m_value;
            std::vector<std::string> m_value_list;
            bool m_optinal;
            bool m_assigned;
    void close()
    {
        if(m_opened)
            connection_parameter() : m_optinal(false), m_assigned(false) {}
            void reset()
            {
                m_name.clear();
                m_prompt.clear();
                m_value.clear();
                m_value_list.clear();
                m_optinal = false;
                m_assigned = false;
            }
        };
        typedef std::vector<connection_parameter> connection_parameters;
        class base_database : public object<SQL_HANDLE_DBC>
        {
            verify_error(SQLDisconnect(m_handle));
            m_opened=false;
        }
    }
        public:
            typedef odbc::error exception_type;
    void set_attribute(SQLINTEGER attr, SQLPOINTER value)
    {
        verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_POINTER));
    }
    void set_attribute(SQLINTEGER attr, SQLINTEGER value)
    {
        verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_INTEGER));
    }
    void set_attribute(SQLINTEGER attr, SQLUINTEGER value)
    {
        verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_UINTEGER));
    }
    void set_attribute(SQLINTEGER attr, SQLSMALLINT value)
    {
        verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_SMALLINT));
    }
    void set_attribute(SQLINTEGER attr, SQLUSMALLINT value)
    {
        verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_USMALLINT));
    }
    void set_attribute(SQLINTEGER attr, const char* value)
    {
        verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_NTS));
    }
    void set_attribute(SQLINTEGER attr, const std::string& value)
    {
        verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value.data(), value.size()));
    }
    void set_attribute(SQLINTEGER attr, const void* value, SQLINTEGER length)
    {
        verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_LEN_BINARY_ATTR(length)));
    }
    void get_attribute(SQLINTEGER attr, SQLPOINTER& value) const
    {
        verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_POINTER, 0));
    }
    void get_attribute(SQLINTEGER attr, SQLINTEGER& value) const
    {
        value = 0;
        verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_INTEGER, 0));
    }
    void get_attribute(SQLINTEGER attr, SQLUINTEGER& value) const
    {
        value = 0;
        verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_UINTEGER, 0));
    }
    void get_attribute(SQLINTEGER attr, SQLSMALLINT& value) const
    {
        value = 0;
        verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_SMALLINT, 0));
    }
    void get_attribute(SQLINTEGER attr, SQLUSMALLINT& value) const
    {
        value = 0;
        verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_USMALLINT, 0));
    }
    void get_attribute(SQLINTEGER attr, void* buffer, SQLINTEGER length) const
    {
        verify_error(SQLGetConnectAttrA(m_handle, attr, buffer, SQL_LEN_BINARY_ATTR(length), 0));
    }
    void get_attribute(SQLINTEGER attr, char* buffer, size_t length) const
    {
        verify_error(SQLGetConnectAttrA(m_handle, attr, buffer, length, 0));
    }
    void get_attribute(SQLINTEGER attr, std::string& value) const
    {
        SQLINTEGER length = 0;
        verify_error(SQLGetConnectAttrA(m_handle, attr, NULL, 0, &length));
        value.resize(length);
        if(length>0)
            verify_error(SQLGetConnectAttrA(m_handle, attr, (SQLPOINTER)value.data(), length, 0));
    }
    void get_info(SQLSMALLINT info, std::string& value, SQLSMALLINT size=SQL_MAX_OPTION_STRING_LENGTH) const
    {
        value.resize(size);
        verify_error(SQLGetInfo(m_handle, info, (SQLPOINTER)value.data(), size, &size));
        value.resize(size);
    }
            explicit base_database(environment &env) : object(env.handle()), m_opened(false)
            {
            }
            base_database(const base_database &) = delete;
            base_database(base_database &&src)
                : object(std::forward<base_database>(src)), m_connection(std::forward<std::string>(src.m_connection))
            {
                m_opened = src.m_opened;
                src.m_opened = false;
            }
            ~base_database()
            {
                close();
            }
            base_database &operator=(base_database &&src)
            {
                if (this != &src)
                {
                    object::operator=(std::forward<base_database>(src));
                    m_opened = src.m_opened;
                    src.m_opened = false;
                    m_connection = std::forward<std::string>(src.m_connection);
                }
                return *this;
            }
    std::string dbms_name() const
    {
        std::string name;
        get_info(SQL_DBMS_NAME, name);
        return name;
    }
    std::string server_name() const
    {
        std::string name;
        get_info(SQL_SERVER_NAME, name);
        return name;
    }
    std::string user_name() const
    {
        std::string name;
        get_info(SQL_USER_NAME, name);
        return name;
    }
    std::string db_name() const
    {
        std::string name;
        get_info(SQL_DATABASE_NAME, name);
        return name;
    }
    const std::string& connection_text() const
    {
        return m_connection;
    }
            void close()
            {
                if (m_opened)
                {
                    verify_error(SQLDisconnect(m_handle));
                    m_opened = false;
                }
            }
protected:
    bool m_opened;
    std::string m_connection;
            void set_attribute(SQLINTEGER attr, SQLPOINTER value)
            {
                verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_POINTER));
            }
            void set_attribute(SQLINTEGER attr, SQLINTEGER value)
            {
                verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_INTEGER));
            }
            void set_attribute(SQLINTEGER attr, SQLUINTEGER value)
            {
                verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_UINTEGER));
            }
            void set_attribute(SQLINTEGER attr, SQLSMALLINT value)
            {
                verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_SMALLINT));
            }
            void set_attribute(SQLINTEGER attr, SQLUSMALLINT value)
            {
                verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_USMALLINT));
            }
            void set_attribute(SQLINTEGER attr, const char *value)
            {
                verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_NTS));
            }
            void set_attribute(SQLINTEGER attr, const std::string &value)
            {
                verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value.data(), value.size()));
            }
            void set_attribute(SQLINTEGER attr, const void *value, SQLINTEGER length)
            {
                verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_LEN_BINARY_ATTR(length)));
            }
            void get_attribute(SQLINTEGER attr, SQLPOINTER &value) const
            {
                verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_POINTER, 0));
            }
            void get_attribute(SQLINTEGER attr, SQLINTEGER &value) const
            {
                value = 0;
                verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_INTEGER, 0));
            }
            void get_attribute(SQLINTEGER attr, SQLUINTEGER &value) const
            {
                value = 0;
                verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_UINTEGER, 0));
            }
            void get_attribute(SQLINTEGER attr, SQLSMALLINT &value) const
            {
                value = 0;
                verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_SMALLINT, 0));
            }
            void get_attribute(SQLINTEGER attr, SQLUSMALLINT &value) const
            {
                value = 0;
                verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_USMALLINT, 0));
            }
            void get_attribute(SQLINTEGER attr, void *buffer, SQLINTEGER length) const
            {
                verify_error(SQLGetConnectAttrA(m_handle, attr, buffer, SQL_LEN_BINARY_ATTR(length), 0));
            }
            void get_attribute(SQLINTEGER attr, char *buffer, size_t length) const
            {
                verify_error(SQLGetConnectAttrA(m_handle, attr, buffer, length, 0));
            }
            void get_attribute(SQLINTEGER attr, std::string &value) const
            {
                SQLINTEGER length = 0;
                verify_error(SQLGetConnectAttrA(m_handle, attr, NULL, 0, &length));
                value.resize(length);
                if (length > 0)
                    verify_error(SQLGetConnectAttrA(m_handle, attr, (SQLPOINTER)value.data(), length, 0));
            }
            void get_info(SQLSMALLINT info, std::string &value, SQLSMALLINT size = SQL_MAX_OPTION_STRING_LENGTH) const
            {
                value.resize(size);
                verify_error(SQLGetInfo(m_handle, info, (SQLPOINTER)value.data(), size, &size));
                value.resize(size);
            }
    void parse_browse_string(const char* output_text, size_t text_length, connection_parameters& parameters);
    std::string create_connection_text(const connection_parameters& parameters);
};
            std::string dbms_name() const
            {
                std::string name;
                get_info(SQL_DBMS_NAME, name);
                return name;
            }
            std::string server_name() const
            {
                std::string name;
                get_info(SQL_SERVER_NAME, name);
                return name;
            }
            std::string user_name() const
            {
                std::string name;
                get_info(SQL_USER_NAME, name);
                return name;
            }
            std::string db_name() const
            {
                std::string name;
                get_info(SQL_DATABASE_NAME, name);
                return name;
            }
            const std::string &connection_text() const
            {
                return m_connection;
            }
class database : public base_database, public qtl::base_database<database, statement>
{
public:
    database() = default;
    explicit database(environment& env) : odbc::base_database(env)
    {
    }
    database(database&& src) : odbc::base_database(std::move(src))
    {
    }
        protected:
            bool m_opened;
            std::string m_connection;
    void open(const char* server_name, size_t server_name_length,
        const char* user_name, size_t user_name_length, const char* password, size_t password_length)
    {
        if (m_opened) close();
        verify_error(SQLConnectA(m_handle, (SQLCHAR*)server_name, static_cast<SQLSMALLINT>(server_name_length),
            (SQLCHAR*)user_name, static_cast<SQLSMALLINT>(user_name_length), (SQLCHAR*)password, static_cast<SQLSMALLINT>(password_length)));
        m_opened = true;
    }
    void open(const char* server_name, const char* user_name, const char* password)
    {
        verify_error(SQLConnectA(m_handle, (SQLCHAR*)server_name, SQL_NTS, (SQLCHAR*)user_name, SQL_NTS, (SQLCHAR*)password, SQL_NTS));
    }
    void open(const std::string& server_name, const std::string& user_name, const std::string& password)
    {
        open(server_name.data(), server_name.size(), user_name.data(), user_name.size(), password.data(), password.size());
    }
    void open(const char* input_text, size_t text_length = SQL_NTS, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
    {
        m_connection.resize(512);
        SQLSMALLINT out_len=0;
        if (m_opened) close();
        verify_error(SQLDriverConnectA(m_handle, hwnd, (SQLCHAR*)input_text, (SQLSMALLINT)text_length,
            (SQLCHAR*)m_connection.data(), (SQLSMALLINT)m_connection.size(), &out_len, driver_completion));
        m_connection.resize(out_len);
        m_opened = true;
    }
    void open(const std::string& input_text, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
    {
        open(input_text.data(), input_text.size(), driver_completion, hwnd);
    }
    void open(SQLHWND hwnd, SQLSMALLINT driver_completion = SQL_DRIVER_COMPLETE)
    {
        open("", SQL_NTS, driver_completion, hwnd);
    }
    // InputPred like:
    // bool input_parameters(connection_parameters& parameters);
    template<typename InputPred>
    void open(const char* connection_text, size_t text_length, InputPred&& pred)
    {
        SQLSMALLINT length = 0;
        SQLRETURN ret = SQL_SUCCESS;
        std::string input_text;
        if (m_opened) close();
        if (text_length == SQL_NTS)
            input_text = connection_text;
        else
            input_text.assign(connection_text, text_length);
        m_connection.resize(1024);
        while ((ret = SQLBrowseConnectA(m_handle, (SQLCHAR*)input_text.data(), SQL_NTS,
            (SQLCHAR*)m_connection.data(), m_connection.size(), &length)) == SQL_NEED_DATA)
            void parse_browse_string(const char *output_text, size_t text_length, connection_parameters &parameters);
            std::string create_connection_text(const connection_parameters &parameters);
        };
        class database : public base_database, public qtl::base_database<database, statement>
        {
            connection_parameters parameters;
            parse_browse_string(m_connection.data(), length, parameters);
            if (!pred(parameters))
                throw error(SQL_NEED_DATA, "User cancel operation.");
            input_text = create_connection_text(parameters);
        }
        if (ret == SQL_ERROR || ret == SQL_SUCCESS_WITH_INFO)
            verify_error(ret);
        m_opened = true;
    }
    template<typename InputPred>
    void open(const char* connection_text, InputPred&& pred)
    {
        open(connection_text, SQL_NTS, std::forward<InputPred>(pred));
    }
    template<typename InputPred>
    void open(const std::string& connection_text, InputPred&& pred)
    {
        open(connection_text.data(), connection_text.size(), std::forward<InputPred>(pred));
    }
        public:
            database() = default;
            explicit database(environment &env) : odbc::base_database(env)
            {
            }
            database(database &&src) : odbc::base_database(std::move(src))
            {
            }
    statement open_command(const char* query_text, size_t text_length)
    {
        statement stmt(*this);
        stmt.open(query_text, text_length);
        return stmt;
    }
    statement open_command(const char* query_text)
    {
        return open_command(query_text, strlen(query_text));
    }
    statement open_command(const std::string& query_text)
    {
        return open_command(query_text.data(), query_text.length());
    }
            void open(const char *server_name, size_t server_name_length,
                      const char *user_name, size_t user_name_length, const char *password, size_t password_length)
            {
                if (m_opened)
                    close();
                verify_error(SQLConnectA(m_handle, (SQLCHAR *)server_name, static_cast<SQLSMALLINT>(server_name_length),
                                         (SQLCHAR *)user_name, static_cast<SQLSMALLINT>(user_name_length), (SQLCHAR *)password, static_cast<SQLSMALLINT>(password_length)));
                m_opened = true;
            }
            void open(const char *server_name, const char *user_name, const char *password)
            {
                verify_error(SQLConnectA(m_handle, (SQLCHAR *)server_name, SQL_NTS, (SQLCHAR *)user_name, SQL_NTS, (SQLCHAR *)password, SQL_NTS));
            }
            void open(const std::string &server_name, const std::string &user_name, const std::string &password)
            {
                open(server_name.data(), server_name.size(), user_name.data(), user_name.size(), password.data(), password.size());
            }
            void open(const char *input_text, size_t text_length = SQL_NTS, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
            {
                m_connection.resize(512);
                SQLSMALLINT out_len = 0;
                if (m_opened)
                    close();
                verify_error(SQLDriverConnectA(m_handle, hwnd, (SQLCHAR *)input_text, (SQLSMALLINT)text_length,
                                               (SQLCHAR *)m_connection.data(), (SQLSMALLINT)m_connection.size(), &out_len, driver_completion));
                m_connection.resize(out_len);
                m_opened = true;
            }
            void open(const std::string &input_text, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
            {
                open(input_text.data(), input_text.size(), driver_completion, hwnd);
            }
            void open(SQLHWND hwnd, SQLSMALLINT driver_completion = SQL_DRIVER_COMPLETE)
            {
                open("", SQL_NTS, driver_completion, hwnd);
            }
            // InputPred like:
            // bool input_parameters(connection_parameters& parameters);
            template <typename InputPred>
            void open(const char *connection_text, size_t text_length, InputPred &&pred)
            {
                SQLSMALLINT length = 0;
                SQLRETURN ret = SQL_SUCCESS;
                std::string input_text;
                if (m_opened)
                    close();
                if (text_length == SQL_NTS)
                    input_text = connection_text;
                else
                    input_text.assign(connection_text, text_length);
                m_connection.resize(1024);
                while ((ret = SQLBrowseConnectA(m_handle, (SQLCHAR *)input_text.data(), SQL_NTS,
                                                (SQLCHAR *)m_connection.data(), m_connection.size(), &length)) == SQL_NEED_DATA)
                {
                    connection_parameters parameters;
                    parse_browse_string(m_connection.data(), length, parameters);
                    if (!pred(parameters))
                        throw error(SQL_NEED_DATA, "User cancel operation.");
                    input_text = create_connection_text(parameters);
                }
                if (ret == SQL_ERROR || ret == SQL_SUCCESS_WITH_INFO)
                    verify_error(ret);
                m_opened = true;
            }
            template <typename InputPred>
            void open(const char *connection_text, InputPred &&pred)
            {
                open(connection_text, SQL_NTS, std::forward<InputPred>(pred));
            }
            template <typename InputPred>
            void open(const std::string &connection_text, InputPred &&pred)
            {
                open(connection_text.data(), connection_text.size(), std::forward<InputPred>(pred));
            }
    void simple_execute(const char* query_text, size_t text_length = SQL_NTS)
    {
        statement command(*this);
        SQLRETURN ret = SQLExecDirectA(command.handle(), (SQLCHAR*)query_text, text_length);
        if (ret != SQL_SUCCESS && ret != SQL_NO_DATA)
            verify_error(ret);
    }
    void simple_execute(const std::string& query_text)
    {
        simple_execute(query_text.data(), query_text.size());
    }
            statement open_command(const char *query_text, size_t text_length)
            {
                statement stmt(*this);
                stmt.open(query_text, text_length);
                return stmt;
            }
            statement open_command(const char *query_text)
            {
                return open_command(query_text, strlen(query_text));
            }
            statement open_command(const std::string &query_text)
            {
                return open_command(query_text.data(), query_text.length());
            }
    void auto_commit(bool on)
    {
        set_attribute(SQL_ATTR_AUTOCOMMIT, on ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
    }
    void begin_transaction()
    {
        auto_commit(false);
    }
    void rollback()
    {
        verify_error(SQLEndTran(handler_type, m_handle, SQL_ROLLBACK));
        auto_commit(true);
    }
    void commit()
    {
        verify_error(SQLEndTran(handler_type, m_handle, SQL_COMMIT));
        auto_commit(true);
    }
            void simple_execute(const char *query_text, size_t text_length = SQL_NTS)
            {
                statement command(*this);
                SQLRETURN ret = SQLExecDirectA(command.handle(), (SQLCHAR *)query_text, text_length);
                if (ret != SQL_SUCCESS && ret != SQL_NO_DATA)
                    verify_error(ret);
            }
            void simple_execute(const std::string &query_text)
            {
                simple_execute(query_text.data(), query_text.size());
            }
    bool is_alive()
    {
        SQLINTEGER value;
        get_attribute(SQL_ATTR_CONNECTION_DEAD, value);
        return value == SQL_CD_FALSE;
    }
            void auto_commit(bool on)
            {
                set_attribute(SQL_ATTR_AUTOCOMMIT, on ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
            }
            void begin_transaction()
            {
                auto_commit(false);
            }
            void rollback()
            {
                verify_error(SQLEndTran(handler_type, m_handle, SQL_ROLLBACK));
                auto_commit(true);
            }
            void commit()
            {
                verify_error(SQLEndTran(handler_type, m_handle, SQL_COMMIT));
                auto_commit(true);
            }
            bool is_alive()
            {
                SQLINTEGER value;
                get_attribute(SQL_ATTR_CONNECTION_DEAD, value);
                return value == SQL_CD_FALSE;
            }
#ifdef QTL_ODBC_ENABLE_ASYNC_MODE
    //async_connection async_mode();
            // async_connection async_mode();
#endif //ODBC 3.80
#endif // ODBC 3.80
        };
};
        struct date : public SQL_DATE_STRUCT
        {
            date()
            {
                memset(this, 0, sizeof(SQL_DATE_STRUCT));
            }
        };
struct date : public SQL_DATE_STRUCT
{
    date()
    {
        memset(this, 0, sizeof(SQL_DATE_STRUCT));
    }
};
        struct time : public SQL_TIME_STRUCT
        {
            time()
            {
                memset(this, 0, sizeof(SQL_TIME_STRUCT));
            }
        };
struct time : public SQL_TIME_STRUCT
{
    time()
    {
        memset(this, 0, sizeof(SQL_TIME_STRUCT));
    }
};
struct timestamp : public SQL_TIMESTAMP_STRUCT
{
    timestamp()
    {
        memset(this, 0, sizeof(SQL_TIMESTAMP_STRUCT));
    }
    timestamp(struct tm& tm)
    {
        year=tm.tm_year+1900;
        month=tm.tm_mon+1;
        day=tm.tm_mday;
        hour=tm.tm_hour;
        minute=tm.tm_min;
        second=tm.tm_sec;
    }
    timestamp(time_t value)
    {
        struct tm tm;
        struct timestamp : public SQL_TIMESTAMP_STRUCT
        {
            timestamp()
            {
                memset(this, 0, sizeof(SQL_TIMESTAMP_STRUCT));
            }
            timestamp(struct tm &tm)
            {
                year = tm.tm_year + 1900;
                month = tm.tm_mon + 1;
                day = tm.tm_mday;
                hour = tm.tm_hour;
                minute = tm.tm_min;
                second = tm.tm_sec;
            }
            timestamp(time_t value)
            {
                struct tm tm;
#if defined(_MSC_VER)
        localtime_s(&tm, &value);
                localtime_s(&tm, &value);
#elif defined(_POSIX_VERSION)
        localtime_r(&value, &tm);
                localtime_r(&value, &tm);
#else
        tm=*localtime(&value);
                tm = *localtime(&value);
#endif
        new(this)timestamp(tm);
    }
    timestamp(const timestamp& src)
    {
        memcpy(this, &src, sizeof(SQL_TIMESTAMP_STRUCT));
    }
    timestamp& operator=(const timestamp& src)
    {
        if(this!=&src)
            memcpy(this, &src, sizeof(SQL_TIMESTAMP_STRUCT));
        return *this;
    }
                new (this) timestamp(tm);
            }
            timestamp(const timestamp &src)
            {
                memcpy(this, &src, sizeof(SQL_TIMESTAMP_STRUCT));
            }
            timestamp &operator=(const timestamp &src)
            {
                if (this != &src)
                    memcpy(this, &src, sizeof(SQL_TIMESTAMP_STRUCT));
                return *this;
            }
    static timestamp now()
    {
        time_t value;
        ::time(&value);
        return timestamp(value);
    }
            static timestamp now()
            {
                time_t value;
                ::time(&value);
                return timestamp(value);
            }
    time_t as_tm(struct tm& tm) const
    {
        tm.tm_year=year-1900;
        tm.tm_mon=month-1;
        tm.tm_mday=day;
        tm.tm_hour=hour;
        tm.tm_min=minute;
        tm.tm_sec=second;
        return mktime(&tm);
    }
    time_t get_time() const
    {
        struct tm tm;
        return as_tm(tm);
    }
    timeval get_timeval() const
    {
        timeval tv;
        struct tm tm;
        tv.tv_sec=as_tm(tm);
        tv.tv_usec=fraction/1000;
    }
};
            time_t as_tm(struct tm &tm) const
            {
                tm.tm_year = year - 1900;
                tm.tm_mon = month - 1;
                tm.tm_mday = day;
                tm.tm_hour = hour;
                tm.tm_min = minute;
                tm.tm_sec = second;
                return mktime(&tm);
            }
            time_t get_time() const
            {
                struct tm tm;
                return as_tm(tm);
            }
            timeval get_timeval() const
            {
                timeval tv;
                struct tm tm;
                tv.tv_sec = as_tm(tm);
                tv.tv_usec = fraction / 1000;
            }
        };
#ifdef QTL_ODBC_ENABLE_ASYNC_MODE
class async_connection;
        class async_connection;
inline bool is_still_executing(SQLINTEGER code)
{
    return code == SQL_STILL_EXECUTING;
}
class async_statement : public base_statement
{
public:
    explicit async_statement(async_connection& db);
    async_statement(async_statement&& src)
        : base_statement(std::move(src))
    {
        m_hCompleteEvent = src.m_hCompleteEvent;
        m_event=src.m_event;
        m_nQueryTimeout = src.m_nQueryTimeout;
        src.m_hCompleteEvent = nullptr;
        src.m_event = nullptr;
    }
    async_statement& operator=(async_statement&& src)
    {
        if (this != &src)
        inline bool is_still_executing(SQLINTEGER code)
        {
            base_statement::operator =(std::move(src));
            m_hCompleteEvent = src.m_hCompleteEvent;
            m_event = src.m_event;
            m_nQueryTimeout = src.m_nQueryTimeout;
            src.m_hCompleteEvent = nullptr;
            src.m_event = nullptr;
        }
        return *this;
    }
    ~async_statement()
    {
        close();
    }
    /*
        Handler defiens as:
        void handler(const qtl::odbc::error& e);
     */
    template<typename Handler>
    void open(Handler&& handler, const char *query_text, size_t text_length = 0)
    {
        if (text_length == 0) text_length = strlen(query_text);
        reset();
        SQLRETURN ret = SQLPrepareA(m_handle, (SQLCHAR*)query_text, text_length);
        async_wait(ret, std::forward<Handler>(handler));
    }
    /*
        ExecuteHandler defiens as:
        void handler(const qtl::odbc::error& e, uint64_t affected);
     */
    template<typename Types, typename Handler>
    void execute(const Types& params, Handler&& handler)
    {
        SQLSMALLINT count = 0;
        SQLRETURN ret = SQLNumParams(m_handle, &count);
        if (!SQL_SUCCEEDED(ret))
        {
            handler(error(*this, ret), 0);
            return;
        }
        if (count > 0)
        {
            m_params.resize(count);
            qtl::bind_params(*this, params);
            return code == SQL_STILL_EXECUTING;
        }
        if (m_nQueryTimeout == 0)
            m_nQueryTimeout = query_timeout();
        ret = SQLExecute(m_handle);
        async_wait(ret, [this, count, handler](const error& e) mutable {
        class async_statement : public base_statement
        {
        public:
            explicit async_statement(async_connection &db);
            async_statement(async_statement &&src)
                : base_statement(std::move(src))
            {
                m_hCompleteEvent = src.m_hCompleteEvent;
                m_event = src.m_event;
                m_nQueryTimeout = src.m_nQueryTimeout;
                src.m_hCompleteEvent = nullptr;
                src.m_event = nullptr;
            }
            async_statement &operator=(async_statement &&src)
            {
                if (this != &src)
                {
                    base_statement::operator=(std::move(src));
                    m_hCompleteEvent = src.m_hCompleteEvent;
                    m_event = src.m_event;
                    m_nQueryTimeout = src.m_nQueryTimeout;
                    src.m_hCompleteEvent = nullptr;
                    src.m_event = nullptr;
                }
                return *this;
            }
            ~async_statement()
            {
                close();
            }
            /*
                Handler defiens as:
                void handler(const qtl::odbc::error& e);
             */
            template <typename Handler>
            void open(Handler &&handler, const char *query_text, size_t text_length = 0)
            {
                if (text_length == 0)
                    text_length = strlen(query_text);
                reset();
                SQLRETURN ret = SQLPrepareA(m_handle, (SQLCHAR *)query_text, text_length);
                async_wait(ret, std::forward<Handler>(handler));
            }
            /*
                ExecuteHandler defiens as:
                void handler(const qtl::odbc::error& e, uint64_t affected);
             */
            template <typename Types, typename Handler>
            void execute(const Types &params, Handler &&handler)
            {
                SQLSMALLINT count = 0;
                SQLRETURN ret = SQLNumParams(m_handle, &count);
                if (!SQL_SUCCEEDED(ret))
                {
                    handler(error(*this, ret), 0);
                    return;
                }
                if (count > 0)
                {
                    m_params.resize(count);
                    qtl::bind_params(*this, params);
                }
                if (m_nQueryTimeout == 0)
                    m_nQueryTimeout = query_timeout();
                ret = SQLExecute(m_handle);
                async_wait(ret, [this, count, handler](const error &e) mutable
                           {
            SQLINTEGER ret = e.code();
            if (ret == SQL_NEED_DATA)
                async_param_data(0, count, std::forward<Handler>(handler));
            else if(ret>=0)
                handler(error(*this, ret), affetced_rows());
            else
                handler(error(*this, ret), 0);
        });
    }
    template<typename Types, typename RowHandler, typename FinishHandler>
    void fetch(Types&& values, RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        if (!m_binded_cols)
        {
            SQLSMALLINT count = 0;
            SQLRETURN ret = SQLNumResultCols(m_handle, &count);
            if(!SQL_SUCCEEDED(ret))
            {
                finish_handler(error(*this, ret));
                return;
                handler(error(*this, ret), 0); });
            }
            if (count > 0)
            template <typename Types, typename RowHandler, typename FinishHandler>
            void fetch(Types &&values, RowHandler &&row_handler, FinishHandler &&finish_handler)
            {
                m_params.resize(count);
                qtl::bind_record(*this, std::forward<Types>(values));
                if (!m_binded_cols)
                {
                    SQLSMALLINT count = 0;
                    SQLRETURN ret = SQLNumResultCols(m_handle, &count);
                    if (!SQL_SUCCEEDED(ret))
                    {
                        finish_handler(error(*this, ret));
                        return;
                    }
                    if (count > 0)
                    {
                        m_params.resize(count);
                        qtl::bind_record(*this, std::forward<Types>(values));
                    }
                    m_binded_cols = true;
                }
                return fetch(std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
            }
            m_binded_cols = true;
        }
        return fetch(std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
    }
    template<typename RowHandler, typename FinishHandler>
    void fetch(RowHandler&& row_handler, FinishHandler&& finish_handler)
    {
        SQLRETURN ret = SQLFetch(m_handle);
        async_wait(ret, [this, row_handler, finish_handler](const error& e) mutable {
            template <typename RowHandler, typename FinishHandler>
            void fetch(RowHandler &&row_handler, FinishHandler &&finish_handler)
            {
                SQLRETURN ret = SQLFetch(m_handle);
                async_wait(ret, [this, row_handler, finish_handler](const error &e) mutable
                           {
            SQLINTEGER ret = e.code();
            if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
            {
@@ -1383,17 +1404,17 @@
                    finish_handler(error());
                else
                    finish_handler(e);
            } });
            }
        });
    }
    template<typename Handler>
    void next_result(Handler handler)
    {
        SQLRETURN ret;
        m_binded_cols = false;
        ret = SQLMoreResults(m_handle);
        async_wait(ret, [this, handler](const error& e) mutable {
            template <typename Handler>
            void next_result(Handler handler)
            {
                SQLRETURN ret;
                m_binded_cols = false;
                ret = SQLMoreResults(m_handle);
                async_wait(ret, [this, handler](const error &e) mutable
                           {
            SQLINTEGER ret=e.code();
            SQLSMALLINT count = 0;
            if (ret == SQL_ERROR || ret == SQL_INVALID_HANDLE)
@@ -1412,83 +1433,83 @@
            if (count > 0)
                handler(error());
            else
                next_result(handler);
        });
    }
                next_result(handler); });
            }
    HANDLE event_handle() const { return m_hCompleteEvent; }
            HANDLE event_handle() const { return m_hCompleteEvent; }
    void close()
    {
        close_event();
        base_statement::close();
    }
            void close()
            {
                close_event();
                base_statement::close();
            }
    template<typename CloseHandler>
    void close(CloseHandler&& handler)
    {
        if (m_handle)
        {
            close_event();
            SQLRETURN ret = SQLFreeHandle(handler_type, m_handle);
            if(SQL_SUCCEEDED(ret))
                m_handle = SQL_NULL_HANDLE;
            handler(error(*this, ret));
        }
        else
        {
            handler(error());
        }
    }
            template <typename CloseHandler>
            void close(CloseHandler &&handler)
            {
                if (m_handle)
                {
                    close_event();
                    SQLRETURN ret = SQLFreeHandle(handler_type, m_handle);
                    if (SQL_SUCCEEDED(ret))
                        m_handle = SQL_NULL_HANDLE;
                    handler(error(*this, ret));
                }
                else
                {
                    handler(error());
                }
            }
private:
        private:
            void close_event()
            {
                if (m_hCompleteEvent)
                {
                    if (m_event)
                        m_event->remove();
                    verify_error(SQLCancelHandle(handler_type, m_handle));
                    verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_STMT_EVENT, NULL, SQL_IS_POINTER));
                    verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_OFF, SQL_IS_INTEGER));
                    CloseHandle(m_hCompleteEvent);
                    m_hCompleteEvent = NULL;
                }
            }
    void close_event()
    {
        if (m_hCompleteEvent)
        {
            if (m_event)
                m_event->remove();
            verify_error(SQLCancelHandle(handler_type, m_handle));
            verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_STMT_EVENT, NULL, SQL_IS_POINTER));
            verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_OFF, SQL_IS_INTEGER));
            CloseHandle(m_hCompleteEvent);
            m_hCompleteEvent = NULL;
        }
    }
            template <typename Handler>
            void async_wait(SQLINTEGER ret, Handler &&handler) NOEXCEPT
            {
                if (is_still_executing(ret))
                {
                    m_event->set_io_handler(0, m_nQueryTimeout,
                                            [this, handler](int flags) mutable
                                            {
                                                RETCODE code;
                                                SQLCompleteAsync(SQL_HANDLE_STMT, m_handle, &code);
                                                if (SQL_SUCCEEDED(code))
                                                {
                                                    handler(odbc::error());
                                                }
                                                else
                                                {
                                                    SetEvent(m_hCompleteEvent);
                                                    handler(odbc::error(*this, code));
                                                }
                                            });
                }
                else
                {
                    handler(error(*this, ret));
                }
            }
    template<typename Handler>
    void async_wait(SQLINTEGER ret, Handler&& handler) NOEXCEPT
    {
        if(is_still_executing(ret))
        {
            m_event->set_io_handler(0, m_nQueryTimeout,
                [this, handler](int flags) mutable {
                    RETCODE code;
                    SQLCompleteAsync(SQL_HANDLE_STMT, m_handle, &code);
                    if (SQL_SUCCEEDED(code))
                    {
                        handler(odbc::error());
                    }
                    else
                    {
                        SetEvent(m_hCompleteEvent);
                        handler(odbc::error(*this, code));
                    }
            });
        }
        else
        {
            handler(error(*this, ret));
        }
    }
    template<typename Handler>
    void async_param_data(SQLSMALLINT index, SQLSMALLINT count, Handler&& handler) NOEXCEPT
    {
        SQLPOINTER token;
        SQLRETURN ret = SQLParamData(m_handle, &token);
        async_wait(ret, [this, index, count, token, handler](const error& e) mutable {
            template <typename Handler>
            void async_param_data(SQLSMALLINT index, SQLSMALLINT count, Handler &&handler) NOEXCEPT
            {
                SQLPOINTER token;
                SQLRETURN ret = SQLParamData(m_handle, &token);
                async_wait(ret, [this, index, count, token, handler](const error &e) mutable
                           {
            SQLINTEGER ret = e.code();
            if (ret == SQL_NEED_DATA)
            {
@@ -1507,430 +1528,440 @@
            else
            {
                handler(error(*this, ret), affetced_rows());
            } });
            }
        });
    }
    int query_timeout() const
    {
        SQLULEN timeout = 0;
        verify_error(SQLGetStmtAttr(m_handle, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)&timeout, NULL, NULL));
        return timeout;
    }
            int query_timeout() const
            {
                SQLULEN timeout = 0;
                verify_error(SQLGetStmtAttr(m_handle, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)&timeout, NULL, NULL));
                return timeout;
            }
private:
    HANDLE m_hCompleteEvent;
    qtl::event* m_event;
    SQLULEN m_nQueryTimeout;
};
        private:
            HANDLE m_hCompleteEvent;
            qtl::event *m_event;
            SQLULEN m_nQueryTimeout;
        };
class async_connection : public base_database, public qtl::async_connection<async_connection, async_statement>
{
public:
    async_connection(environment& env) : base_database(env)
    {
        set_attribute(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, SQL_ASYNC_DBC_ENABLE_ON);
        m_hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
        if (m_hCompleteEvent == NULL)
        class async_connection : public base_database, public qtl::async_connection<async_connection, async_statement>
        {
            throw std::system_error(std::error_code(GetLastError(), std::system_category()));
        }
        set_attribute(SQL_ATTR_ASYNC_DBC_EVENT, m_hCompleteEvent);
    }
    async_connection(async_connection&& src) :
        base_database(std::move(src)),
        qtl::async_connection<async_connection, async_statement>(std::move(src)),
        m_BindFunc(std::move(src.m_BindFunc))
    {
        m_hCompleteEvent = src.m_hCompleteEvent;
        src.m_hCompleteEvent = nullptr;
    }
    ~async_connection()
    {
        if (m_hCompleteEvent)
        {
            verify_error(SQLCancelHandle(handler_type, m_handle));
            set_attribute(SQL_ATTR_ASYNC_DBC_EVENT, (SQLPOINTER)NULL);
        }
        if (m_opened)
        {
            set_attribute(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, SQL_ASYNC_DBC_ENABLE_OFF);
            verify_error(SQLDisconnect(m_handle));
            m_opened = false;
        }
        if (m_hCompleteEvent)
        {
            CloseHandle(m_hCompleteEvent);
        }
    }
    /*
        OpenHandler defines as:
            void handler(const qtl::odbc::error& e) NOEXCEPT;
    */
    template<class EventLoop, typename OpenHandler>
    void open(EventLoop& ev, OpenHandler&& handler, const char* server_name, size_t server_name_length,
        const char* user_name, size_t user_name_length, const char* password, size_t password_length)
    {
        if (m_opened) close();
        SQLRETURN err = SQLConnectA(m_handle, (SQLCHAR*)server_name, static_cast<SQLSMALLINT>(server_name_length),
            (SQLCHAR*)user_name, static_cast<SQLSMALLINT>(user_name_length), (SQLCHAR*)password, static_cast<SQLSMALLINT>(password_length));
        async_wait_connect(err, ev, std::forward<OpenHandler>(handler));
    }
    template<class EventLoop, typename OpenHandler>
    void open(EventLoop& ev, OpenHandler&& handler, const char* server_name, const char* user_name, const char* password)
    {
        if (m_opened) close();
        SQLRETURN err = SQLConnectA(m_handle, (SQLCHAR*)server_name, SQL_NTS, (SQLCHAR*)user_name, SQL_NTS, (SQLCHAR*)password, SQL_NTS);
        async_wait_connect(err, ev, std::forward<OpenHandler>(handler));
    }
    template<class EventLoop, typename OpenHandler>
    void open(EventLoop& ev, OpenHandler&& handler, const std::string& server_name, const std::string& user_name, const std::string& password)
    {
        open(ev, std::forward<OpenHandler>(handler), server_name.data(), server_name.size(), user_name.data(), user_name.size(), password.data(), password.size());
    }
    template<class EventLoop, typename OpenHandler>
    void open(EventLoop& ev, OpenHandler&& handler, const char* input_text, size_t text_length = SQL_NTS, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
    {
        m_connection.resize(512);
        SQLSMALLINT out_len=0;
        SQLRETURN err = SQLDriverConnectA(m_handle, hwnd, (SQLCHAR*)input_text, (SQLSMALLINT)text_length,
            (SQLCHAR*)m_connection.data(), (SQLSMALLINT)m_connection.size(), &out_len, driver_completion);
        m_connection.resize(out_len);
        async_wait_connect(err, ev, std::forward<OpenHandler>(handler));
    }
    template<class EventLoop, typename OpenHandler>
    void open(EventLoop& ev, OpenHandler&& handler, const std::string& input_text, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
    {
        open(ev, std::forward<OpenHandler>(handler), input_text.data(), input_text.size(), driver_completion, hwnd);
    }
    template<class EventLoop, typename OpenHandler>
    void open(EventLoop& ev, OpenHandler&& handler, SQLHWND hwnd, SQLSMALLINT driver_completion = SQL_DRIVER_COMPLETE)
    {
        open(ev, std::forward<OpenHandler>(handler), "", SQL_NTS, driver_completion, hwnd);
    }
    /*
        CloseHandler defines as:
            void handler(const qtl::odbc::error& e) NOEXCEPT;
    */
    template<typename CloseHandler >
    void close(CloseHandler&& handler) NOEXCEPT
    {
        SQLRETURN ret = SQLDisconnect(m_handle);
        m_opened = false;
        async_wait(ret, [this, handler](const error& e) {
            if (!e) m_opened = false;
            handler(e);
        });
    }
    /*
        ExecuteHandler defines as:
            void handler(const qtl::odbc::error& e) NOEXCEPT;
    */
    template<typename ExecuteHandler>
    void simple_execute(ExecuteHandler&& handler, const char* query_text, size_t text_length = SQL_NTS) NOEXCEPT
    {
        statement command(*this);
        SQLRETURN ret = SQLExecDirectA(command.handle(), (SQLCHAR*)query_text, static_cast<SQLINTEGER>(text_length));
        async_wait(ret, std::forward<ExecuteHandler>(handler));
    }
    template<typename ExecuteHandler>
    void simple_execute(ExecuteHandler&& handler, const std::string& query_text)
    {
        simple_execute(std::forward<ExecuteHandler>(handler), query_text.data(), query_text.size());
    }
    template<typename Handler>
    void open_command(const char* query_text, size_t text_length, Handler&& handler)
    {
        std::shared_ptr<async_statement> stmt = std::make_shared<async_statement>(*this);
        stmt->open([stmt, handler](const odbc::error& e) mutable {
            handler(e, stmt);
        }, query_text, text_length);
    }
    HANDLE event_handle() const { return m_hCompleteEvent; }
    qtl::event* rebind(HANDLE hEvent)
    {
        return m_BindFunc(hEvent);
    }
private:
    template<typename Handler>
    void async_wait(SQLRETURN ret, Handler&& handler) NOEXCEPT
    {
        if (is_still_executing(ret))
        {
            m_event_handler->set_io_handler(0, connect_timeout(),
                [this, handler](int flags) mutable {
                RETCODE code;
                SQLCompleteAsync(SQL_HANDLE_DBC, m_handle, &code);
                if (SQL_SUCCEEDED(code))
        public:
            async_connection(environment &env) : base_database(env)
            {
                set_attribute(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, SQL_ASYNC_DBC_ENABLE_ON);
                m_hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
                if (m_hCompleteEvent == NULL)
                {
                    handler(odbc::error());
                    throw std::system_error(std::error_code(GetLastError(), std::system_category()));
                }
                set_attribute(SQL_ATTR_ASYNC_DBC_EVENT, m_hCompleteEvent);
            }
            async_connection(async_connection &&src) : base_database(std::move(src)),
                                                       qtl::async_connection<async_connection, async_statement>(std::move(src)),
                                                       m_BindFunc(std::move(src.m_BindFunc))
            {
                m_hCompleteEvent = src.m_hCompleteEvent;
                src.m_hCompleteEvent = nullptr;
            }
            ~async_connection()
            {
                if (m_hCompleteEvent)
                {
                    verify_error(SQLCancelHandle(handler_type, m_handle));
                    set_attribute(SQL_ATTR_ASYNC_DBC_EVENT, (SQLPOINTER)NULL);
                }
                if (m_opened)
                {
                    set_attribute(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, SQL_ASYNC_DBC_ENABLE_OFF);
                    verify_error(SQLDisconnect(m_handle));
                    m_opened = false;
                }
                if (m_hCompleteEvent)
                {
                    CloseHandle(m_hCompleteEvent);
                }
            }
            /*
                OpenHandler defines as:
                    void handler(const qtl::odbc::error& e) NOEXCEPT;
            */
            template <class EventLoop, typename OpenHandler>
            void open(EventLoop &ev, OpenHandler &&handler, const char *server_name, size_t server_name_length,
                      const char *user_name, size_t user_name_length, const char *password, size_t password_length)
            {
                if (m_opened)
                    close();
                SQLRETURN err = SQLConnectA(m_handle, (SQLCHAR *)server_name, static_cast<SQLSMALLINT>(server_name_length),
                                            (SQLCHAR *)user_name, static_cast<SQLSMALLINT>(user_name_length), (SQLCHAR *)password, static_cast<SQLSMALLINT>(password_length));
                async_wait_connect(err, ev, std::forward<OpenHandler>(handler));
            }
            template <class EventLoop, typename OpenHandler>
            void open(EventLoop &ev, OpenHandler &&handler, const char *server_name, const char *user_name, const char *password)
            {
                if (m_opened)
                    close();
                SQLRETURN err = SQLConnectA(m_handle, (SQLCHAR *)server_name, SQL_NTS, (SQLCHAR *)user_name, SQL_NTS, (SQLCHAR *)password, SQL_NTS);
                async_wait_connect(err, ev, std::forward<OpenHandler>(handler));
            }
            template <class EventLoop, typename OpenHandler>
            void open(EventLoop &ev, OpenHandler &&handler, const std::string &server_name, const std::string &user_name, const std::string &password)
            {
                open(ev, std::forward<OpenHandler>(handler), server_name.data(), server_name.size(), user_name.data(), user_name.size(), password.data(), password.size());
            }
            template <class EventLoop, typename OpenHandler>
            void open(EventLoop &ev, OpenHandler &&handler, const char *input_text, size_t text_length = SQL_NTS, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
            {
                m_connection.resize(512);
                SQLSMALLINT out_len = 0;
                SQLRETURN err = SQLDriverConnectA(m_handle, hwnd, (SQLCHAR *)input_text, (SQLSMALLINT)text_length,
                                                  (SQLCHAR *)m_connection.data(), (SQLSMALLINT)m_connection.size(), &out_len, driver_completion);
                m_connection.resize(out_len);
                async_wait_connect(err, ev, std::forward<OpenHandler>(handler));
            }
            template <class EventLoop, typename OpenHandler>
            void open(EventLoop &ev, OpenHandler &&handler, const std::string &input_text, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
            {
                open(ev, std::forward<OpenHandler>(handler), input_text.data(), input_text.size(), driver_completion, hwnd);
            }
            template <class EventLoop, typename OpenHandler>
            void open(EventLoop &ev, OpenHandler &&handler, SQLHWND hwnd, SQLSMALLINT driver_completion = SQL_DRIVER_COMPLETE)
            {
                open(ev, std::forward<OpenHandler>(handler), "", SQL_NTS, driver_completion, hwnd);
            }
            /*
                CloseHandler defines as:
                    void handler(const qtl::odbc::error& e) NOEXCEPT;
            */
            template <typename CloseHandler>
            void close(CloseHandler &&handler) NOEXCEPT
            {
                SQLRETURN ret = SQLDisconnect(m_handle);
                m_opened = false;
                async_wait(ret, [this, handler](const error &e)
                           {
            if (!e) m_opened = false;
            handler(e); });
            }
            /*
                ExecuteHandler defines as:
                    void handler(const qtl::odbc::error& e) NOEXCEPT;
            */
            template <typename ExecuteHandler>
            void simple_execute(ExecuteHandler &&handler, const char *query_text, size_t text_length = SQL_NTS) NOEXCEPT
            {
                statement command(*this);
                SQLRETURN ret = SQLExecDirectA(command.handle(), (SQLCHAR *)query_text, static_cast<SQLINTEGER>(text_length));
                async_wait(ret, std::forward<ExecuteHandler>(handler));
            }
            template <typename ExecuteHandler>
            void simple_execute(ExecuteHandler &&handler, const std::string &query_text)
            {
                simple_execute(std::forward<ExecuteHandler>(handler), query_text.data(), query_text.size());
            }
            template <typename Handler>
            void open_command(const char *query_text, size_t text_length, Handler &&handler)
            {
                std::shared_ptr<async_statement> stmt = std::make_shared<async_statement>(*this);
                stmt->open([stmt, handler](const odbc::error &e) mutable
                           { handler(e, stmt); }, query_text, text_length);
            }
            HANDLE event_handle() const { return m_hCompleteEvent; }
            qtl::event *rebind(HANDLE hEvent)
            {
                return m_BindFunc(hEvent);
            }
        private:
            template <typename Handler>
            void async_wait(SQLRETURN ret, Handler &&handler) NOEXCEPT
            {
                if (is_still_executing(ret))
                {
                    m_event_handler->set_io_handler(0, connect_timeout(),
                                                    [this, handler](int flags) mutable
                                                    {
                                                        RETCODE code;
                                                        SQLCompleteAsync(SQL_HANDLE_DBC, m_handle, &code);
                                                        if (SQL_SUCCEEDED(code))
                                                        {
                                                            handler(odbc::error());
                                                        }
                                                        else
                                                        {
                                                            SetEvent(m_hCompleteEvent);
                                                            handler(odbc::error(*this, code));
                                                        }
                                                    });
                }
                else
                {
                    SetEvent(m_hCompleteEvent);
                    handler(odbc::error(*this, code));
                    handler(odbc::error(*this, ret));
                }
            });
        }
        else
        {
            handler(odbc::error(*this, ret));
        }
    }
    template<typename EventLoop, typename Handler>
    void async_wait_connect(SQLRETURN err, EventLoop& ev, Handler&& handler)
    {
        bind(ev);
        m_BindFunc = [&ev](HANDLE hEvent) {
            return ev.add(hEvent);
        };
        if(is_still_executing(err))
        {
            async_wait(err, [this, handler](const error& e) mutable {
                if (!e) m_opened = true;
                handler(e);
            });
        }
        else
        {
            handler(odbc::error(*this, err));
        }
    }
    int connect_timeout() const
    {
        int timeout=0;
        verify_error(SQLGetConnectAttr(m_handle, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)&timeout, 0, NULL));
        return timeout;
    }
private:
    HANDLE m_hCompleteEvent;
    std::function<qtl::event*(HANDLE)> m_BindFunc;
};
inline     async_statement::async_statement(async_connection& db)
    : base_statement(static_cast<base_database&>(db))
{
    verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER));
    m_hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (m_hCompleteEvent == NULL)
    {
        throw std::system_error(std::error_code(GetLastError(), std::system_category()));
    }
    verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_STMT_EVENT, m_hCompleteEvent, SQL_IS_POINTER));
    m_event = db.rebind(this->m_hCompleteEvent);
    m_nQueryTimeout = query_timeout();
}
#endif //ODBC 3.80
typedef qtl::transaction<database> transaction;
template<typename Record>
using query_iterator = qtl::query_iterator<statement, Record>;
template<typename Record>
using query_result = qtl::query_result<statement, Record>;
template<typename Params>
inline statement& operator<<(statement& stmt, const Params& params)
{
    stmt.reset();
    stmt.execute(params);
    return stmt;
}
template<SQLSMALLINT Type>
inline error::error(const object<Type>& h, SQLINTEGER code)
{
    m_errno=code;
    if(code==SQL_ERROR || code==SQL_SUCCESS_WITH_INFO)
    {
        SQLSMALLINT i=0;
        SQLINTEGER err=SQL_SUCCESS;
        SQLCHAR message[SQL_MAX_MESSAGE_LENGTH];
        SQLCHAR state[SQL_SQLSTATE_SIZE+1];
        std::ostringstream oss;
        SQLRETURN ret = SQLGetDiagRecA(object<Type>::handler_type, h.handle(), ++i, state, &err,
            message, SQL_MAX_MESSAGE_LENGTH, NULL);
        while(ret==SQL_SUCCESS)
        {
            oss<<"["<<state<<"] ("<<err<<") "<<message<<std::endl;
            ret = SQLGetDiagRecA(object<Type>::handler_type, h.handle(), ++i, state, &err,
                message, SQL_MAX_MESSAGE_LENGTH, NULL);
        }
        m_errmsg=oss.str();
    }
    else if(code==SQL_INVALID_HANDLE)
    {
        m_errmsg="Invalid handle.";
    }
}
inline void base_database::parse_browse_string(const char* output_text, size_t text_length, connection_parameters& parameters)
{
    enum { part_name, part_prompt, part_list, part_value };
    const char* sp=output_text;
    const char* token=sp;
    connection_parameter parameter;
    int part_type=part_name;
    while(sp!=output_text+text_length)
    {
        switch(*sp)
        {
        case ';':
            parameters.emplace_back(parameter);
            parameter.reset();
            part_type=part_name;
            token=sp+1;
            break;
        case '=':
            if(part_type==part_prompt)
                parameter.m_prompt.assign(token, sp-token);
            part_type=part_value;
            token=sp+1;
            break;
        case ':':
            if(part_type==part_name)
                parameter.m_name.assign(token, sp-token);
            part_type=part_prompt;
            token=sp+1;
            break;
        case '{':
            part_type=part_list;
            parameter.m_value_list.clear();
            token=sp+1;
            break;;
        case '}':
        case ',':
            if(part_type==part_list)
                parameter.m_value_list.emplace_back(token, sp-token);
            token=sp+1;
            break;
        case '*':
            if(part_type==part_name && token==sp)
            {
                parameter.m_optinal=true;
                token=sp+1;
            }
            break;
        case '?':
            token=sp+1;
            break;
            template <typename EventLoop, typename Handler>
            void async_wait_connect(SQLRETURN err, EventLoop &ev, Handler &&handler)
            {
                bind(ev);
                m_BindFunc = [&ev](HANDLE hEvent)
                {
                    return ev.add(hEvent);
                };
                if (is_still_executing(err))
                {
                    async_wait(err, [this, handler](const error &e) mutable
                               {
                if (!e) m_opened = true;
                handler(e); });
                }
                else
                {
                    handler(odbc::error(*this, err));
                }
            }
            int connect_timeout() const
            {
                int timeout = 0;
                verify_error(SQLGetConnectAttr(m_handle, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)&timeout, 0, NULL));
                return timeout;
            }
        private:
            HANDLE m_hCompleteEvent;
            std::function<qtl::event *(HANDLE)> m_BindFunc;
        };
        inline async_statement::async_statement(async_connection &db)
            : base_statement(static_cast<base_database &>(db))
        {
            verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER));
            m_hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
            if (m_hCompleteEvent == NULL)
            {
                throw std::system_error(std::error_code(GetLastError(), std::system_category()));
            }
            verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_STMT_EVENT, m_hCompleteEvent, SQL_IS_POINTER));
            m_event = db.rebind(this->m_hCompleteEvent);
            m_nQueryTimeout = query_timeout();
        }
        ++sp;
    }
    if(!parameter.m_name.empty())
        parameters.emplace_back(parameter);
}
inline std::string base_database::create_connection_text(const connection_parameters& parameters)
{
    std::ostringstream oss;
    for(auto& parameter : parameters)
    {
        if(parameter.m_assigned)
            oss<<parameter.m_name<<'='<<parameter.m_value<<';';
    }
    return oss.str();
}
#endif // ODBC 3.80
inline base_statement::base_statement(base_database& db)
    : object(db.handle()), m_blob_buffer(NULL), m_binded_cols(false)
{
}
        typedef qtl::transaction<database> transaction;
} //odbc
        template <typename Record>
        using query_iterator = qtl::query_iterator<statement, Record>;
        template <typename Record>
        using query_result = qtl::query_result<statement, Record>;
        template <typename Params>
        inline statement &operator<<(statement &stmt, const Params &params)
        {
            stmt.reset();
            stmt.execute(params);
            return stmt;
        }
        template <SQLSMALLINT Type>
        inline error::error(const object<Type> &h, SQLINTEGER code)
        {
            m_errno = code;
            if (code == SQL_ERROR || code == SQL_SUCCESS_WITH_INFO)
            {
                SQLSMALLINT i = 0;
                SQLINTEGER err = SQL_SUCCESS;
                SQLCHAR message[SQL_MAX_MESSAGE_LENGTH];
                SQLCHAR state[SQL_SQLSTATE_SIZE + 1];
                std::ostringstream oss;
                SQLRETURN ret = SQLGetDiagRecA(object<Type>::handler_type, h.handle(), ++i, state, &err,
                                               message, SQL_MAX_MESSAGE_LENGTH, NULL);
                while (ret == SQL_SUCCESS)
                {
                    oss << "[" << state << "] (" << err << ") " << message << std::endl;
                    ret = SQLGetDiagRecA(object<Type>::handler_type, h.handle(), ++i, state, &err,
                                         message, SQL_MAX_MESSAGE_LENGTH, NULL);
                }
                m_errmsg = oss.str();
            }
            else if (code == SQL_INVALID_HANDLE)
            {
                m_errmsg = "Invalid handle.";
            }
        }
        inline void base_database::parse_browse_string(const char *output_text, size_t text_length, connection_parameters &parameters)
        {
            enum
            {
                part_name,
                part_prompt,
                part_list,
                part_value
            };
            const char *sp = output_text;
            const char *token = sp;
            connection_parameter parameter;
            int part_type = part_name;
            while (sp != output_text + text_length)
            {
                switch (*sp)
                {
                case ';':
                    parameters.emplace_back(parameter);
                    parameter.reset();
                    part_type = part_name;
                    token = sp + 1;
                    break;
                case '=':
                    if (part_type == part_prompt)
                        parameter.m_prompt.assign(token, sp - token);
                    part_type = part_value;
                    token = sp + 1;
                    break;
                case ':':
                    if (part_type == part_name)
                        parameter.m_name.assign(token, sp - token);
                    part_type = part_prompt;
                    token = sp + 1;
                    break;
                case '{':
                    part_type = part_list;
                    parameter.m_value_list.clear();
                    token = sp + 1;
                    break;
                    ;
                case '}':
                case ',':
                    if (part_type == part_list)
                        parameter.m_value_list.emplace_back(token, sp - token);
                    token = sp + 1;
                    break;
                case '*':
                    if (part_type == part_name && token == sp)
                    {
                        parameter.m_optinal = true;
                        token = sp + 1;
                    }
                    break;
                case '?':
                    token = sp + 1;
                    break;
                }
                ++sp;
            }
            if (!parameter.m_name.empty())
                parameters.emplace_back(parameter);
        }
        inline std::string base_database::create_connection_text(const connection_parameters &parameters)
        {
            std::ostringstream oss;
            for (auto &parameter : parameters)
            {
                if (parameter.m_assigned)
                    oss << parameter.m_name << '=' << parameter.m_value << ';';
            }
            return oss.str();
        }
        inline base_statement::base_statement(base_database &db)
            : object(db.handle()), m_blob_buffer(NULL), m_binded_cols(false)
        {
        }
    } // odbc
#ifdef _WIN32
namespace mssql
{
class database : public odbc::database
{
public:
    explicit database(odbc::environment& env) : odbc::database(env) { }
    database(database&& src) : odbc::database(std::move(src)) { }
    void open(const char* server, const char* db=NULL, const char* user=NULL, const char* password=NULL)
    namespace mssql
    {
        std::ostringstream oss;
        oss<<"DRIVER={SQL Server};SERVER="<<server<<";";
        if(user==NULL)
            oss<<"UID=;PWD=;Trusted_Connection=yes;";
        else
        class database : public odbc::database
        {
            oss<<"UID="<<user<<";PWD=";
            if(password) oss<<password;
            oss<<";Trusted_Connection=no;";
        }
        oss<<"DATABASE="<<db;
        odbc::database::open(oss.str());
    }
};
        public:
            explicit database(odbc::environment &env) : odbc::database(env) {}
            database(database &&src) : odbc::database(std::move(src)) {}
            void open(const char *server, const char *db = NULL, const char *user = NULL, const char *password = NULL)
            {
                std::ostringstream oss;
                oss << "DRIVER={SQL Server};SERVER=" << server << ";";
                if (user == NULL)
                    oss << "UID=;PWD=;Trusted_Connection=yes;";
                else
                {
                    oss << "UID=" << user << ";PWD=";
                    if (password)
                        oss << password;
                    oss << ";Trusted_Connection=no;";
                }
                oss << "DATABASE=" << db;
                odbc::database::open(oss.str());
            }
        };
#ifdef QTL_ODBC_ENABLE_ASYNC_MODE
class async_connection : public odbc::async_connection
{
public:
    explicit async_connection(odbc::environment& env) : odbc::async_connection(env) { }
    async_connection(async_connection&& src) : odbc::async_connection(std::move(src)) { }
    template<typename EventLoop, typename OpenHandler>
    void open(EventLoop& ev, const OpenHandler& handler, const char* server, const char* db = NULL, const char* user = NULL, const char* password = NULL)
    {
        std::ostringstream oss;
        oss << "DRIVER={ODBC Driver 11 for SQL Server};SERVER=" << server << ";";
        if (user == NULL)
            oss << "UID=;PWD=;Trusted_Connection=yes;";
        else
        class async_connection : public odbc::async_connection
        {
            oss << "UID=" << user << ";PWD=";
            if (password) oss << password;
            oss << ";Trusted_Connection=no;";
        }
        oss << "DATABASE=" << db;
        odbc::async_connection::open(ev, handler, oss.str());
    }
        public:
            explicit async_connection(odbc::environment &env) : odbc::async_connection(env) {}
            async_connection(async_connection &&src) : odbc::async_connection(std::move(src)) {}
};
            template <typename EventLoop, typename OpenHandler>
            void open(EventLoop &ev, const OpenHandler &handler, const char *server, const char *db = NULL, const char *user = NULL, const char *password = NULL)
            {
                std::ostringstream oss;
                oss << "DRIVER={ODBC Driver 11 for SQL Server};SERVER=" << server << ";";
                if (user == NULL)
                    oss << "UID=;PWD=;Trusted_Connection=yes;";
                else
                {
                    oss << "UID=" << user << ";PWD=";
                    if (password)
                        oss << password;
                    oss << ";Trusted_Connection=no;";
                }
                oss << "DATABASE=" << db;
                odbc::async_connection::open(ev, handler, oss.str());
            }
        };
#endif
} //mssql
    } // mssql
namespace msaccess
{
class database : public odbc::database
{
public:
    explicit database(odbc::environment& env) : odbc::database(env) { }
    database(database&& src) : odbc::database(std::forward<database>(src)) { }
    void open(const char* filename, const char* user=NULL, const char* password=NULL)
    namespace msaccess
    {
        std::ostringstream oss;
        oss<<"DRIVER={Microsoft Access Driver};DBQ="<<filename;
        if(user) oss<<";UID:"<<user;
        if(password) oss<<";PWD="<<password;
        odbc::database::open(oss.str());
    }
};
} //msaccess
        class database : public odbc::database
        {
        public:
            explicit database(odbc::environment &env) : odbc::database(env) {}
            database(database &&src) : odbc::database(std::forward<database>(src)) {}
            void open(const char *filename, const char *user = NULL, const char *password = NULL)
            {
                std::ostringstream oss;
                oss << "DRIVER={Microsoft Access Driver};DBQ=" << filename;
                if (user)
                    oss << ";UID:" << user;
                if (password)
                    oss << ";PWD=" << password;
                odbc::database::open(oss.str());
            }
        };
    } // msaccess
#endif //_WIN32
include/qtl_odbc_pool.hpp
@@ -7,70 +7,71 @@
namespace qtl
{
namespace odbc
{
class database_pool : public qtl::database_pool<database>
{
public:
    database_pool() { }
    virtual ~database_pool() { }
    virtual database* new_database() throw() override
    namespace odbc
    {
        database* db=NULL;
        try
        {
            db=new database(m_env);
            db->open(m_connection);
        }
        catch(error& e)
        {
            if(db)
            {
                delete db;
                db=NULL;
            }
        }
        return db;
    }
protected:
    std::string m_connection;
    environment m_env;
};
        class database_pool : public qtl::database_pool<database>
        {
        public:
            database_pool() {}
            virtual ~database_pool() {}
            virtual database *new_database() throw() override
            {
                database *db = NULL;
                try
                {
                    db = new database(m_env);
                    db->open(m_connection);
                }
                catch (error &e)
                {
                    if (db)
                    {
                        delete db;
                        db = NULL;
                    }
                }
                return db;
            }
        protected:
            std::string m_connection;
            environment m_env;
        };
#ifdef QTL_ODBC_ENABLE_ASYNC_MODE
template<typename EventLoop>
class async_pool : public qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection>
{
    typedef qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection> base_class;
public:
    async_pool(EventLoop& ev) : base_class(ev) { }
    virtual ~async_pool() { }
        template <typename EventLoop>
        class async_pool : public qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection>
        {
            typedef qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection> base_class;
    template<typename Handler>
    void new_connection(EventLoop& ev, Handler&& handler) throw()
    {
        async_connection* db = new async_connection(env);
        db->open(ev, [this, handler, db](const mysql::error& e) mutable {
        public:
            async_pool(EventLoop &ev) : base_class(ev) {}
            virtual ~async_pool() {}
            template <typename Handler>
            void new_connection(EventLoop &ev, Handler &&handler) throw()
            {
                async_connection *db = new async_connection(env);
                db->open(ev, [this, handler, db](const mysql::error &e) mutable
                         {
            if (e)
            {
                delete db;
                db = nullptr;
            }
            handler(e, db);
        }, m_connection);
            handler(e, db); }, m_connection);
            }
        protected:
            std::string m_connection;
            environment m_env;
        };
#endif // QTL_ODBC_ENABLE_ASYNC_MODE
    }
protected:
    std::string m_connection;
    environment m_env;
};
#endif //QTL_ODBC_ENABLE_ASYNC_MODE
}
}
include/qtl_postgres.hpp
Diff too large
include/qtl_postgres_pool.hpp
@@ -7,64 +7,65 @@
namespace qtl
{
namespace postgres
{
class database_pool : public qtl::database_pool<database>
{
public:
    database_pool() : m_port(0) { }
    virtual ~database_pool() { }
    virtual database* new_database() throw() override
    namespace postgres
    {
        database* db=new database;
        if(!db->open(m_host.data(), m_user.data(), m_password.data(), m_port, m_database.data()))
        class database_pool : public qtl::database_pool<database>
        {
            delete db;
            db=NULL;
        }
        else
        public:
            database_pool() : m_port(0) {}
            virtual ~database_pool() {}
            virtual database *new_database() throw() override
            {
                database *db = new database;
                if (!db->open(m_host.data(), m_user.data(), m_password.data(), m_port, m_database.data()))
                {
                    delete db;
                    db = NULL;
                }
                else
                {
                    PQsetClientEncoding(db->handle(), "UTF8");
                }
                return db;
            }
        protected:
            std::string m_host;
            unsigned short m_port;
            std::string m_database;
            std::string m_user;
            std::string m_password;
        };
        template <typename EventLoop>
        class async_pool : public qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection>
        {
            PQsetClientEncoding(db->handle(), "UTF8");
        }
        return db;
    }
            typedef qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection> base_class;
protected:
    std::string m_host;
    unsigned short m_port;
    std::string m_database;
    std::string m_user;
    std::string m_password;
};
        public:
            async_pool(EventLoop &ev) : base_class(ev) {}
            virtual ~async_pool() {}
template<typename EventLoop>
class async_pool : public qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection>
{
    typedef qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection> base_class;
public:
    async_pool(EventLoop& ev) : base_class(ev) { }
    virtual ~async_pool() { }
    template<typename Handler>
    void new_connection(EventLoop& ev, Handler&& handler) throw()
    {
        async_connection* db = new async_connection;
        db->open(ev, [this, handler, db](const postgres::error& e) mutable {
            template <typename Handler>
            void new_connection(EventLoop &ev, Handler &&handler) throw()
            {
                async_connection *db = new async_connection;
                db->open(ev, [this, handler, db](const postgres::error &e) mutable
                         {
            if (e)
            {
                delete db;
                db = nullptr;
            }
            handler(e, db);
        }, m_params);
            handler(e, db); }, m_params);
            }
        protected:
            std::map<std::string, std::string> m_params;
        };
    }
protected:
    std::map<std::string, std::string> m_params;
};
}
}
include/qtl_sqlite.hpp
@@ -10,1123 +10,1124 @@
namespace qtl
{
namespace sqlite
{
    namespace sqlite
    {
class error : public std::exception
{
public:
    explicit error(int error_code) : m_errno(error_code)
    {
        m_errmsg=sqlite3_errstr(error_code);
    }
    explicit error(sqlite3* db)
    {
        m_errno=sqlite3_errcode(db);
        m_errmsg=sqlite3_errmsg(db);
    }
    error(const error& src) = default;
    virtual ~error() throw() { }
    virtual const char* what() const throw() override { return m_errmsg; }
    int code() const throw() { return m_errno; }
private:
    int m_errno;
    const char* m_errmsg;
};
class statement final
{
public:
    statement() : m_stmt(NULL), m_fetch_result(SQLITE_OK) { }
    statement(const statement&) = delete;
    statement(statement&& src)
        : m_stmt(src.m_stmt), m_fetch_result(src.m_fetch_result),
        m_tail_text(std::forward<std::string>(src.m_tail_text))
    {
        src.m_stmt=NULL;
        src.m_fetch_result=SQLITE_OK;
    }
    statement& operator=(const statement&) = delete;
    statement& operator=(statement&& src)
    {
        if(this!=&src)
        class error : public std::exception
        {
            m_stmt=src.m_stmt;
            m_fetch_result=src.m_fetch_result;
            m_tail_text=std::forward<std::string>(src.m_tail_text);
            src.m_stmt=NULL;
            src.m_fetch_result=SQLITE_OK;
        }
        return *this;
    }
    ~statement()
    {
        close();
    }
        public:
            explicit error(int error_code) : m_errno(error_code)
            {
                m_errmsg = sqlite3_errstr(error_code);
            }
            explicit error(sqlite3 *db)
            {
                m_errno = sqlite3_errcode(db);
                m_errmsg = sqlite3_errmsg(db);
            }
            error(const error &src) = default;
            virtual ~error() throw() {}
            virtual const char *what() const throw() override { return m_errmsg; }
            int code() const throw() { return m_errno; }
    void open(sqlite3* db, const char* query_text, size_t text_length=-1)
    {
        const char* tail=NULL;
        close();
        verify_error(sqlite3_prepare_v2(db, query_text, (int)text_length, &m_stmt, &tail));
        if(tail!=NULL)
        {
            if(text_length==-1)
                m_tail_text.assign(tail);
            else
                m_tail_text.assign(tail, query_text+text_length);
        }
        else
            m_tail_text.clear();
    }
        private:
            int m_errno;
            const char *m_errmsg;
        };
    void close()
    {
        if(m_stmt)
        class statement final
        {
            sqlite3_finalize(m_stmt);
            m_stmt=NULL;
        }
    }
        public:
            statement() : m_stmt(NULL), m_fetch_result(SQLITE_OK) {}
            statement(const statement &) = delete;
            statement(statement &&src)
                : m_stmt(src.m_stmt), m_fetch_result(src.m_fetch_result),
                  m_tail_text(std::forward<std::string>(src.m_tail_text))
            {
                src.m_stmt = NULL;
                src.m_fetch_result = SQLITE_OK;
            }
            statement &operator=(const statement &) = delete;
            statement &operator=(statement &&src)
            {
                if (this != &src)
                {
                    m_stmt = src.m_stmt;
                    m_fetch_result = src.m_fetch_result;
                    m_tail_text = std::forward<std::string>(src.m_tail_text);
                    src.m_stmt = NULL;
                    src.m_fetch_result = SQLITE_OK;
                }
                return *this;
            }
            ~statement()
            {
                close();
            }
    void bind_param(int index, int value)
    {
        verify_error(sqlite3_bind_int(m_stmt, index+1, value));
    }
    void bind_param(int index, int64_t value)
    {
        verify_error(sqlite3_bind_int64(m_stmt, index+1, value));
    }
    void bind_param(int index, double value)
    {
        verify_error(sqlite3_bind_double(m_stmt, index+1, value));
    }
    void bind_param(int index, const char* value, size_t n=-1)
    {
        if(value)
            verify_error(sqlite3_bind_text(m_stmt, index+1, value, (int)n, NULL));
        else
            verify_error(sqlite3_bind_null(m_stmt, index+1));
    }
    void bind_param(int index, const wchar_t* value, size_t n=-1)
    {
        if(value)
            verify_error(sqlite3_bind_text16(m_stmt, index+1, value, (int)n, NULL));
        else
            verify_error(sqlite3_bind_null(m_stmt, index+1));
    }
    void bind_param(int index, const std::string& value)
    {
        bind_param(index, value.data(), value.size());
    }
    void bind_param(int index, const std::wstring& value)
    {
        bind_param(index, value.data(), value.size());
    }
    void bind_param(int index, const const_blob_data& value)
    {
        if(value.size)
        {
            if(value.data)
                verify_error(sqlite3_bind_blob(m_stmt, index+1, value.data, (int)value.size, NULL));
            else
                verify_error(sqlite3_bind_zeroblob(m_stmt, index+1, (int)value.size));
        }
        else
            verify_error(sqlite3_bind_null(m_stmt, index+1));
    }
    void bind_zero_blob(int index, int n)
    {
        verify_error(sqlite3_bind_zeroblob(m_stmt, index+1, (int)n));
    }
    //void bind_zero_blob(int index, sqlite3_uint64 n)
    //{
    //    verify_error(sqlite3_bind_zeroblob64(m_stmt, index+1, (int)n));
    //}
    void bind_param(int index, qtl::null)
    {
        verify_error(sqlite3_bind_null(m_stmt, index+1));
    }
    void bind_param(int index, std::nullptr_t)
    {
        verify_error(sqlite3_bind_null(m_stmt, index+1));
    }
    int get_parameter_count() const
    {
        return sqlite3_bind_parameter_count(m_stmt);
    }
    const char* get_parameter_name(int i) const
    {
        return sqlite3_bind_parameter_name(m_stmt, i);
    }
    int get_parameter_index(const char* param_name) const
    {
        return sqlite3_bind_parameter_index(m_stmt, param_name);
    }
            void open(sqlite3 *db, const char *query_text, size_t text_length = -1)
            {
                const char *tail = NULL;
                close();
                verify_error(sqlite3_prepare_v2(db, query_text, (int)text_length, &m_stmt, &tail));
                if (tail != NULL)
                {
                    if (text_length == -1)
                        m_tail_text.assign(tail);
                    else
                        m_tail_text.assign(tail, query_text + text_length);
                }
                else
                    m_tail_text.clear();
            }
    template<class Type>
    void bind_field(size_t index, Type&& value)
    {
        get_value((int)index, std::forward<Type>(value));
    }
    template<class Type>
    void bind_field(size_t index, qtl::indicator<Type>&& value)
    {
        int type=get_column_type(index);
        value.length=0;
        value.is_truncated=false;
        qtl::bind_field(*this, index, value.data);
        if(type==SQLITE_NULL)
        {
            value.is_null=true;
        }
        else
        {
            value.is_null=false;
            if(type==SQLITE_TEXT || type==SQLITE_BLOB)
                value.length=sqlite3_column_bytes(m_stmt, index);
        }
    }
    void bind_field(size_t index, char* value, size_t length)
    {
        size_t col_length=get_column_length((int)index);
        if(col_length>0)
            strncpy(value, (const char*)sqlite3_column_text(m_stmt, (int)index), std::min(length, col_length+1));
        else
            memset(value, 0, length*sizeof(char));
    }
    void bind_field(size_t index, wchar_t* value, size_t length)
    {
        size_t col_length=sqlite3_column_bytes16(m_stmt, (int)index);
        if(col_length>0)
            wcsncpy(value, (const wchar_t*)sqlite3_column_text16(m_stmt, (int)index), std::min(length, col_length+1));
        else
            memset(value, 0, length*sizeof(wchar_t));
    }
    template<size_t N>
    void bind_field(size_t index, char (&&value)[N])
    {
        bind_field(index, value, N);
    }
    template<size_t N>
    void bind_field(size_t index, wchar_t (&&value)[N])
    {
        bind_field(index, value, N);
    }
    template<size_t N>
    void bind_field(size_t index, std::array<char, N>&& value)
    {
        bind_field(index, value.data(), value.size());
    }
    template<size_t N>
    void bind_field(size_t index, std::array<wchar_t, N>&& value)
    {
        bind_field(index, value.data(), value.size());
    }
            void close()
            {
                if (m_stmt)
                {
                    sqlite3_finalize(m_stmt);
                    m_stmt = NULL;
                }
            }
            void bind_param(int index, int value)
            {
                verify_error(sqlite3_bind_int(m_stmt, index + 1, value));
            }
            void bind_param(int index, int64_t value)
            {
                verify_error(sqlite3_bind_int64(m_stmt, index + 1, value));
            }
            void bind_param(int index, double value)
            {
                verify_error(sqlite3_bind_double(m_stmt, index + 1, value));
            }
            void bind_param(int index, const char *value, size_t n = -1)
            {
                if (value)
                    verify_error(sqlite3_bind_text(m_stmt, index + 1, value, (int)n, NULL));
                else
                    verify_error(sqlite3_bind_null(m_stmt, index + 1));
            }
            void bind_param(int index, const wchar_t *value, size_t n = -1)
            {
                if (value)
                    verify_error(sqlite3_bind_text16(m_stmt, index + 1, value, (int)n, NULL));
                else
                    verify_error(sqlite3_bind_null(m_stmt, index + 1));
            }
            void bind_param(int index, const std::string &value)
            {
                bind_param(index, value.data(), value.size());
            }
            void bind_param(int index, const std::wstring &value)
            {
                bind_param(index, value.data(), value.size());
            }
            void bind_param(int index, const const_blob_data &value)
            {
                if (value.size)
                {
                    if (value.data)
                        verify_error(sqlite3_bind_blob(m_stmt, index + 1, value.data, (int)value.size, NULL));
                    else
                        verify_error(sqlite3_bind_zeroblob(m_stmt, index + 1, (int)value.size));
                }
                else
                    verify_error(sqlite3_bind_null(m_stmt, index + 1));
            }
            void bind_zero_blob(int index, int n)
            {
                verify_error(sqlite3_bind_zeroblob(m_stmt, index + 1, (int)n));
            }
            // void bind_zero_blob(int index, sqlite3_uint64 n)
            //{
            //    verify_error(sqlite3_bind_zeroblob64(m_stmt, index+1, (int)n));
            // }
            void bind_param(int index, qtl::null)
            {
                verify_error(sqlite3_bind_null(m_stmt, index + 1));
            }
            void bind_param(int index, std::nullptr_t)
            {
                verify_error(sqlite3_bind_null(m_stmt, index + 1));
            }
            int get_parameter_count() const
            {
                return sqlite3_bind_parameter_count(m_stmt);
            }
            const char *get_parameter_name(int i) const
            {
                return sqlite3_bind_parameter_name(m_stmt, i);
            }
            int get_parameter_index(const char *param_name) const
            {
                return sqlite3_bind_parameter_index(m_stmt, param_name);
            }
            template <class Type>
            void bind_field(size_t index, Type &&value)
            {
                get_value((int)index, std::forward<Type>(value));
            }
            template <class Type>
            void bind_field(size_t index, qtl::indicator<Type> &&value)
            {
                int type = get_column_type(index);
                value.length = 0;
                value.is_truncated = false;
                qtl::bind_field(*this, index, value.data);
                if (type == SQLITE_NULL)
                {
                    value.is_null = true;
                }
                else
                {
                    value.is_null = false;
                    if (type == SQLITE_TEXT || type == SQLITE_BLOB)
                        value.length = sqlite3_column_bytes(m_stmt, index);
                }
            }
            void bind_field(size_t index, char *value, size_t length)
            {
                size_t col_length = get_column_length((int)index);
                if (col_length > 0)
                    strncpy(value, (const char *)sqlite3_column_text(m_stmt, (int)index), std::min(length, col_length + 1));
                else
                    memset(value, 0, length * sizeof(char));
            }
            void bind_field(size_t index, wchar_t *value, size_t length)
            {
                size_t col_length = sqlite3_column_bytes16(m_stmt, (int)index);
                if (col_length > 0)
                    wcsncpy(value, (const wchar_t *)sqlite3_column_text16(m_stmt, (int)index), std::min(length, col_length + 1));
                else
                    memset(value, 0, length * sizeof(wchar_t));
            }
            template <size_t N>
            void bind_field(size_t index, char (&&value)[N])
            {
                bind_field(index, value, N);
            }
            template <size_t N>
            void bind_field(size_t index, wchar_t (&&value)[N])
            {
                bind_field(index, value, N);
            }
            template <size_t N>
            void bind_field(size_t index, std::array<char, N> &&value)
            {
                bind_field(index, value.data(), value.size());
            }
            template <size_t N>
            void bind_field(size_t index, std::array<wchar_t, N> &&value)
            {
                bind_field(index, value.data(), value.size());
            }
#ifdef _QTL_ENABLE_CPP17
    template<typename T>
    inline void bind_field(size_t index, std::optional<T>&& value)
    {
        int type = get_column_type(index);
        if (type == SQLITE_NULL)
        {
            value.reset();
        }
        else
        {
            qtl::bind_field(*this, index, *value);
        }
    }
            template <typename T>
            inline void bind_field(size_t index, std::optional<T> &&value)
            {
                int type = get_column_type(index);
                if (type == SQLITE_NULL)
                {
                    value.reset();
                }
                else
                {
                    qtl::bind_field(*this, index, *value);
                }
            }
    inline void bind_field(size_t index, std::any&& value)
    {
        int type = get_column_type(index);
        switch(type)
        {
        case SQLITE_NULL:
            value.reset();
        case SQLITE_INTEGER:
            value = sqlite3_column_int64(m_stmt, index);
            break;
        case SQLITE_FLOAT:
            value = sqlite3_column_double(m_stmt, index);
            break;
        case SQLITE_TEXT:
            value.emplace<std::string_view>((const char*)sqlite3_column_text(m_stmt, index), sqlite3_column_bytes(m_stmt, index));
            break;
        case SQLITE_BLOB:
            value.emplace<const_blob_data>(sqlite3_column_text(m_stmt, index), sqlite3_column_bytes(m_stmt, index));
            break;
        default:
            throw sqlite::error(SQLITE_MISMATCH);
        }
    }
            inline void bind_field(size_t index, std::any &&value)
            {
                int type = get_column_type(index);
                switch (type)
                {
                case SQLITE_NULL:
                    value.reset();
                case SQLITE_INTEGER:
                    value = sqlite3_column_int64(m_stmt, index);
                    break;
                case SQLITE_FLOAT:
                    value = sqlite3_column_double(m_stmt, index);
                    break;
                case SQLITE_TEXT:
                    value.emplace<std::string_view>((const char *)sqlite3_column_text(m_stmt, index), sqlite3_column_bytes(m_stmt, index));
                    break;
                case SQLITE_BLOB:
                    value.emplace<const_blob_data>(sqlite3_column_text(m_stmt, index), sqlite3_column_bytes(m_stmt, index));
                    break;
                default:
                    throw sqlite::error(SQLITE_MISMATCH);
                }
            }
#endif // C++17
    size_t find_field(const char* name) const
    {
        size_t count=get_column_count();
        for(size_t i=0; i!=count; i++)
        {
            if(strcmp(get_column_name(i), name)==0)
                return i;
        }
        return -1;
    }
    bool fetch()
    {
        m_fetch_result=sqlite3_step(m_stmt);
        switch(m_fetch_result)
        {
        case SQLITE_ROW:
            return true;
        case SQLITE_DONE:
            return false;
        default:
            throw sqlite::error(m_fetch_result);
        }
    }
    int get_column_count() const
    {
        return sqlite3_column_count(m_stmt);
    }
    const char* get_column_name(int col) const
    {
        return sqlite3_column_name(m_stmt, col);
    }
    void get_value(int col, int&& value) const
    {
        value=sqlite3_column_int(m_stmt, col);
    }
    void get_value(int col, int64_t&& value) const
    {
        value=sqlite3_column_int64(m_stmt, col);
    }
    void get_value(int col, double&& value) const
    {
        value=sqlite3_column_double(m_stmt, col);
    }
    const unsigned char* get_value(int col) const
    {
        return sqlite3_column_text(m_stmt, col);
    }
    template<typename CharT>
    const CharT* get_text_value(int col) const;
    template<typename T>
    void get_value(int col, qtl::bind_string_helper<T>&& value) const
    {
        typedef typename qtl::bind_string_helper<T>::char_type char_type;
        int bytes=sqlite3_column_bytes(m_stmt, col);
        if(bytes>0)
            value.assign(get_text_value<char_type>(col), bytes/sizeof(char_type));
        else
            value.clear();
    }
    void get_value(int col, const_blob_data&& value) const
    {
        value.data=sqlite3_column_blob(m_stmt, col);
        value.size=sqlite3_column_bytes(m_stmt, col);
    }
    void get_value(int col, blob_data&& value) const
    {
        const void* data=sqlite3_column_blob(m_stmt, col);
        size_t size=sqlite3_column_bytes(m_stmt, col);
        if(value.size<size)
            throw std::out_of_range("no enough buffer to receive blob data.");
        memcpy(value.data, data, size);
        value.size=size;
    }
    void get_value(int col, std::ostream&& value) const
    {
        const void* data=sqlite3_column_blob(m_stmt, col);
        size_t size=sqlite3_column_bytes(m_stmt, col);
        if(size>0)
            value.write((const char*)data, size);
    }
    int get_column_length(int col) const
    {
        return sqlite3_column_bytes(m_stmt, col);
    }
    int get_column_type(int col) const
    {
        return sqlite3_column_type(m_stmt, col);
    }
    void clear_bindings()
    {
        sqlite3_clear_bindings(m_stmt);
    }
    void reset()
    {
        sqlite3_reset(m_stmt);
    }
    template<typename Types>
    void execute(const Types& params)
    {
        unsigned long count=get_parameter_count();
        if(count>0)
        {
            qtl::bind_params(*this, params);
        }
        fetch();
    }
    template<typename Types>
    bool fetch(Types&& values)
    {
        bool result=false;
        if(m_fetch_result==SQLITE_OK)
            fetch();
        if(m_fetch_result==SQLITE_ROW)
        {
            result=true;
            qtl::bind_record(*this, std::forward<Types>(values));
            m_fetch_result=SQLITE_OK;
        }
        return result;
    }
    bool next_result()
    {
        sqlite3* db=sqlite3_db_handle(m_stmt);
        int count=0;
        do
        {
            trim_string(m_tail_text, " \t\r\n");
            if(!m_tail_text.empty())
            size_t find_field(const char *name) const
            {
                open(db, m_tail_text.data(), m_tail_text.size());
                count=sqlite3_column_count(m_stmt);
                m_fetch_result=SQLITE_OK;
                size_t count = get_column_count();
                for (size_t i = 0; i != count; i++)
                {
                    if (strcmp(get_column_name(i), name) == 0)
                        return i;
                }
                return -1;
            }
        }while(!m_tail_text.empty() && count==0);
        return count>0;;
    }
    int affetced_rows() const
    {
        sqlite3* db=sqlite3_db_handle(m_stmt);
        return db ? sqlite3_changes(db) : 0;
    }
            bool fetch()
            {
                m_fetch_result = sqlite3_step(m_stmt);
                switch (m_fetch_result)
                {
                case SQLITE_ROW:
                    return true;
                case SQLITE_DONE:
                    return false;
                default:
                    throw sqlite::error(m_fetch_result);
                }
            }
            int get_column_count() const
            {
                return sqlite3_column_count(m_stmt);
            }
            const char *get_column_name(int col) const
            {
                return sqlite3_column_name(m_stmt, col);
            }
            void get_value(int col, int &&value) const
            {
                value = sqlite3_column_int(m_stmt, col);
            }
            void get_value(int col, int64_t &&value) const
            {
                value = sqlite3_column_int64(m_stmt, col);
            }
            void get_value(int col, double &&value) const
            {
                value = sqlite3_column_double(m_stmt, col);
            }
            const unsigned char *get_value(int col) const
            {
                return sqlite3_column_text(m_stmt, col);
            }
    int64_t insert_id() const
    {
        sqlite3* db=sqlite3_db_handle(m_stmt);
        return db ? sqlite3_last_insert_rowid(db) : 0;
    }
            template <typename CharT>
            const CharT *get_text_value(int col) const;
protected:
    sqlite3_stmt* m_stmt;
    std::string m_tail_text;
    int m_fetch_result;
    void verify_error(int e)
    {
        if(e!=SQLITE_OK) throw error(e);
    }
};
            template <typename T>
            void get_value(int col, qtl::bind_string_helper<T> &&value) const
            {
                typedef typename qtl::bind_string_helper<T>::char_type char_type;
                int bytes = sqlite3_column_bytes(m_stmt, col);
                if (bytes > 0)
                    value.assign(get_text_value<char_type>(col), bytes / sizeof(char_type));
                else
                    value.clear();
            }
            void get_value(int col, const_blob_data &&value) const
            {
                value.data = sqlite3_column_blob(m_stmt, col);
                value.size = sqlite3_column_bytes(m_stmt, col);
            }
            void get_value(int col, blob_data &&value) const
            {
                const void *data = sqlite3_column_blob(m_stmt, col);
                size_t size = sqlite3_column_bytes(m_stmt, col);
                if (value.size < size)
                    throw std::out_of_range("no enough buffer to receive blob data.");
                memcpy(value.data, data, size);
                value.size = size;
            }
            void get_value(int col, std::ostream &&value) const
            {
                const void *data = sqlite3_column_blob(m_stmt, col);
                size_t size = sqlite3_column_bytes(m_stmt, col);
                if (size > 0)
                    value.write((const char *)data, size);
            }
class database final : public qtl::base_database<database, statement>
{
public:
    typedef sqlite::error exception_type;
            int get_column_length(int col) const
            {
                return sqlite3_column_bytes(m_stmt, col);
            }
            int get_column_type(int col) const
            {
                return sqlite3_column_type(m_stmt, col);
            }
            void clear_bindings()
            {
                sqlite3_clear_bindings(m_stmt);
            }
            void reset()
            {
                sqlite3_reset(m_stmt);
            }
    database() : m_db(NULL) { }
    ~database() { close(); }
    database(const database&) = delete;
    database(database&& src)
    {
        m_db=src.m_db;
        src.m_db=NULL;
    }
    database& operator=(const database&) = delete;
    database& operator=(database&& src)
    {
        if(this!=&src)
            template <typename Types>
            void execute(const Types &params)
            {
                unsigned long count = get_parameter_count();
                if (count > 0)
                {
                    qtl::bind_params(*this, params);
                }
                fetch();
            }
            template <typename Types>
            bool fetch(Types &&values)
            {
                bool result = false;
                if (m_fetch_result == SQLITE_OK)
                    fetch();
                if (m_fetch_result == SQLITE_ROW)
                {
                    result = true;
                    qtl::bind_record(*this, std::forward<Types>(values));
                    m_fetch_result = SQLITE_OK;
                }
                return result;
            }
            bool next_result()
            {
                sqlite3 *db = sqlite3_db_handle(m_stmt);
                int count = 0;
                do
                {
                    trim_string(m_tail_text, " \t\r\n");
                    if (!m_tail_text.empty())
                    {
                        open(db, m_tail_text.data(), m_tail_text.size());
                        count = sqlite3_column_count(m_stmt);
                        m_fetch_result = SQLITE_OK;
                    }
                } while (!m_tail_text.empty() && count == 0);
                return count > 0;
                ;
            }
            int affetced_rows() const
            {
                sqlite3 *db = sqlite3_db_handle(m_stmt);
                return db ? sqlite3_changes(db) : 0;
            }
            int64_t insert_id() const
            {
                sqlite3 *db = sqlite3_db_handle(m_stmt);
                return db ? sqlite3_last_insert_rowid(db) : 0;
            }
        protected:
            sqlite3_stmt *m_stmt;
            std::string m_tail_text;
            int m_fetch_result;
            void verify_error(int e)
            {
                if (e != SQLITE_OK)
                    throw error(e);
            }
        };
        class database final : public qtl::base_database<database, statement>
        {
            close();
            m_db=src.m_db;
            src.m_db=NULL;
        }
        return *this;
    }
        public:
            typedef sqlite::error exception_type;
    void open(const char *filename, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
    {
        int result=sqlite3_open_v2(filename, &m_db, flags, NULL);
        if(result!=SQLITE_OK)
            throw sqlite::error(result);
    }
    void open(const wchar_t *filename)
    {
        int result=sqlite3_open16(filename, &m_db);
        if(result!=SQLITE_OK)
            throw sqlite::error(result);
    }
    void close()
    {
        if(m_db)
        {
            sqlite3_close_v2(m_db);
            m_db=NULL;
        }
    }
            database() : m_db(NULL) {}
            ~database() { close(); }
            database(const database &) = delete;
            database(database &&src)
            {
                m_db = src.m_db;
                src.m_db = NULL;
            }
            database &operator=(const database &) = delete;
            database &operator=(database &&src)
            {
                if (this != &src)
                {
                    close();
                    m_db = src.m_db;
                    src.m_db = NULL;
                }
                return *this;
            }
    statement open_command(const char* query_text, size_t text_length)
    {
        statement stmt;
        stmt.open(handle(), query_text, text_length);
        return stmt;
    }
    statement open_command(const char* query_text)
    {
        return open_command(query_text, strlen(query_text));
    }
    statement open_command(const std::string& query_text)
    {
        return open_command(query_text.data(), query_text.length());
    }
            void open(const char *filename, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
            {
                int result = sqlite3_open_v2(filename, &m_db, flags, NULL);
                if (result != SQLITE_OK)
                    throw sqlite::error(result);
            }
            void open(const wchar_t *filename)
            {
                int result = sqlite3_open16(filename, &m_db);
                if (result != SQLITE_OK)
                    throw sqlite::error(result);
            }
            void close()
            {
                if (m_db)
                {
                    sqlite3_close_v2(m_db);
                    m_db = NULL;
                }
            }
    void simple_execute(const char* lpszSql)
    {
        int result=sqlite3_exec(m_db, lpszSql, NULL, NULL, NULL);
        if(result!=SQLITE_OK)
            throw sqlite::error(result);
    }
            statement open_command(const char *query_text, size_t text_length)
            {
                statement stmt;
                stmt.open(handle(), query_text, text_length);
                return stmt;
            }
            statement open_command(const char *query_text)
            {
                return open_command(query_text, strlen(query_text));
            }
            statement open_command(const std::string &query_text)
            {
                return open_command(query_text.data(), query_text.length());
            }
    void begin_transaction()
    {
        simple_execute("BEGIN TRANSACTION");
    }
    void commit()
    {
        simple_execute("COMMIT TRANSACTION");
    }
    void rollback()
    {
        simple_execute("ROLLBACK TRANSACTION");
    }
            void simple_execute(const char *lpszSql)
            {
                int result = sqlite3_exec(m_db, lpszSql, NULL, NULL, NULL);
                if (result != SQLITE_OK)
                    throw sqlite::error(result);
            }
    bool is_alive()
    {
            void begin_transaction()
            {
                simple_execute("BEGIN TRANSACTION");
            }
            void commit()
            {
                simple_execute("COMMIT TRANSACTION");
            }
            void rollback()
            {
                simple_execute("ROLLBACK TRANSACTION");
            }
            bool is_alive()
            {
#ifdef _WIN32
        return true;
                return true;
#else
        int has_moved=0;
        int result=sqlite3_file_control(m_db, NULL, SQLITE_FCNTL_HAS_MOVED, &has_moved);
        if(result!=SQLITE_OK)
            throw sqlite::error(result);
        return has_moved==0;
                int has_moved = 0;
                int result = sqlite3_file_control(m_db, NULL, SQLITE_FCNTL_HAS_MOVED, &has_moved);
                if (result != SQLITE_OK)
                    throw sqlite::error(result);
                return has_moved == 0;
#endif //_WIN32
    }
    const char* errmsg() const { return sqlite3_errmsg(m_db); }
    int error() const { return sqlite3_errcode(m_db); }
    uint64_t insert_id() { return sqlite3_last_insert_rowid(m_db); }
    sqlite3* handle() { return m_db; }
protected:
    sqlite3* m_db;
};
// stream for blob field
class blobbuf : public std::streambuf
{
public:
    blobbuf()
    {
        init();
    }
    blobbuf(const blobbuf&) = delete;
    blobbuf(blobbuf&& src) : std::streambuf(std::move(src))
    {
        init();
        swap(src);
    }
    virtual ~blobbuf()
    {
        if(m_blob)
        {
            close();
        }
    }
    blobbuf& operator=(const blobbuf&) = delete;
    blobbuf& operator=(blobbuf&& src)
    {
        if(this!=&src)
        {
            reset_back();
            close();
            swap(src);
        }
        return *this;
    }
    void swap( blobbuf& other )
    {
        std::swap(m_blob, other.m_blob);
        std::swap(m_inbuf, other.m_inbuf);
        std::swap(m_outbuf, other.m_outbuf);
        std::swap(m_size, other.m_size);
        std::swap(m_inpos, other.m_inpos);
        std::swap(m_outpos, other.m_outpos);
        std::streambuf::swap(other);
        std::swap(m_back_char, other.m_back_char);
        if(eback() == &other.m_back_char)
            set_back();
        else
            reset_back();
        if(other.eback()==&m_back_char)
            other.set_back();
        else
            other.reset_back();
    }
    static void init_blob(database& db, const char* table, const char* column, int64_t row, int length)
    {
        statement stmt;
        std::ostringstream oss;
        oss<< "UPDATE " << table << " SET " << column << "=? WHERE rowid=?";
        stmt.open(db.handle(), oss.str().data());
        stmt.bind_zero_blob(0, length);
        stmt.bind_param(1, row);
        stmt.fetch();
    }
    static void init_blob(database& db, const std::string& table, const std::string& column, int64_t row, int length)
    {
        return init_blob(db, table.c_str(), column.c_str(), row, length);
    }
    bool is_open() const { return m_blob!=nullptr; }
    blobbuf* open(database& db, const char* table, const char* column, sqlite3_int64 row,
        std::ios_base::openmode mode, const char* dbname="main")
    {
        int flags=0;
        if(mode&std::ios_base::out) flags=1;
        if(sqlite3_blob_open(db.handle(), dbname, table, column, row, flags, &m_blob)==SQLITE_OK)
        {
            m_size=sqlite3_blob_bytes(m_blob)/sizeof(char);
            // prepare buffer
            size_t bufsize=std::min<size_t>(default_buffer_size, m_size);
            if(mode&std::ios_base::in)
            {
                m_inbuf.resize(bufsize);
                m_inpos=0;
                setg(m_inbuf.data(), m_inbuf.data(), m_inbuf.data());
            }
            if(mode&std::ios_base::out)
            const char *errmsg() const { return sqlite3_errmsg(m_db); }
            int error() const { return sqlite3_errcode(m_db); }
            uint64_t insert_id() { return sqlite3_last_insert_rowid(m_db); }
            sqlite3 *handle() { return m_db; }
        protected:
            sqlite3 *m_db;
        };
        // stream for blob field
        class blobbuf : public std::streambuf
        {
        public:
            blobbuf()
            {
                m_outbuf.resize(bufsize);
                m_outpos=0;
                setp(m_outbuf.data(), m_outbuf.data()+bufsize);
                init();
            }
        }
        return this;
    }
    blobbuf* open(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
        std::ios_base::openmode mode, const char* dbname="main")
    {
        return open(db, table.c_str(), column.c_str(), row, mode, dbname);
    }
    blobbuf* close()
    {
        if(m_blob==nullptr)
            return nullptr;
        overflow();
        sqlite3_blob_close(m_blob);
        init();
        return this;
    }
    std::streamoff size() const { return std::streamoff(m_size); }
    void flush()
    {
        if(m_blob)
            overflow();
    }
protected:
    enum { default_buffer_size = 4096 };
    virtual pos_type seekoff( off_type off, std::ios_base::seekdir dir,
        std::ios_base::openmode which = std::ios_base::in | std::ios_base::out ) override
    {
        if(!is_open())
            return pos_type(off_type(-1));
        pos_type pos=0;
        if(which&std::ios_base::out)
        {
            pos=seekoff(m_outpos, off, dir);
        }
        else if(which&std::ios_base::in)
        {
            pos=seekoff(m_inpos, off, dir);
        }
        return seekpos(pos, which);
    }
    virtual pos_type seekpos( pos_type pos,
        std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
    {
        if(!is_open())
            return pos_type(off_type(-1));
        if(pos>=m_size)
            return pos_type(off_type(-1));
        if(which&std::ios_base::out)
        {
            if(pos<m_outpos || pos>=m_outpos+off_type(egptr()-pbase()))
            blobbuf(const blobbuf &) = delete;
            blobbuf(blobbuf &&src) : std::streambuf(std::move(src))
            {
                init();
                swap(src);
            }
            virtual ~blobbuf()
            {
                if (m_blob)
                {
                    close();
                }
            }
            blobbuf &operator=(const blobbuf &) = delete;
            blobbuf &operator=(blobbuf &&src)
            {
                if (this != &src)
                {
                    reset_back();
                    close();
                    swap(src);
                }
                return *this;
            }
            void swap(blobbuf &other)
            {
                std::swap(m_blob, other.m_blob);
                std::swap(m_inbuf, other.m_inbuf);
                std::swap(m_outbuf, other.m_outbuf);
                std::swap(m_size, other.m_size);
                std::swap(m_inpos, other.m_inpos);
                std::swap(m_outpos, other.m_outpos);
                std::streambuf::swap(other);
                std::swap(m_back_char, other.m_back_char);
                if (eback() == &other.m_back_char)
                    set_back();
                else
                    reset_back();
                if (other.eback() == &m_back_char)
                    other.set_back();
                else
                    other.reset_back();
            }
            static void init_blob(database &db, const char *table, const char *column, int64_t row, int length)
            {
                statement stmt;
                std::ostringstream oss;
                oss << "UPDATE " << table << " SET " << column << "=? WHERE rowid=?";
                stmt.open(db.handle(), oss.str().data());
                stmt.bind_zero_blob(0, length);
                stmt.bind_param(1, row);
                stmt.fetch();
            }
            static void init_blob(database &db, const std::string &table, const std::string &column, int64_t row, int length)
            {
                return init_blob(db, table.c_str(), column.c_str(), row, length);
            }
            bool is_open() const { return m_blob != nullptr; }
            blobbuf *open(database &db, const char *table, const char *column, sqlite3_int64 row,
                          std::ios_base::openmode mode, const char *dbname = "main")
            {
                int flags = 0;
                if (mode & std::ios_base::out)
                    flags = 1;
                if (sqlite3_blob_open(db.handle(), dbname, table, column, row, flags, &m_blob) == SQLITE_OK)
                {
                    m_size = sqlite3_blob_bytes(m_blob) / sizeof(char);
                    // prepare buffer
                    size_t bufsize = std::min<size_t>(default_buffer_size, m_size);
                    if (mode & std::ios_base::in)
                    {
                        m_inbuf.resize(bufsize);
                        m_inpos = 0;
                        setg(m_inbuf.data(), m_inbuf.data(), m_inbuf.data());
                    }
                    if (mode & std::ios_base::out)
                    {
                        m_outbuf.resize(bufsize);
                        m_outpos = 0;
                        setp(m_outbuf.data(), m_outbuf.data() + bufsize);
                    }
                }
                return this;
            }
            blobbuf *open(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                          std::ios_base::openmode mode, const char *dbname = "main")
            {
                return open(db, table.c_str(), column.c_str(), row, mode, dbname);
            }
            blobbuf *close()
            {
                if (m_blob == nullptr)
                    return nullptr;
                overflow();
                m_outpos=pos;
                setp(m_outbuf.data(), m_outbuf.data()+m_outbuf.size());
                sqlite3_blob_close(m_blob);
                init();
                return this;
            }
            else
            std::streamoff size() const { return std::streamoff(m_size); }
            void flush()
            {
                pbump(off_type(pos-pabs()));
                if (m_blob)
                    overflow();
            }
        }
        else if(which&std::ios_base::in)
        {
            if(pos<m_inpos || pos>=m_inpos+off_type(epptr()-eback()))
        protected:
            enum
            {
                m_inpos=pos;
                setg(m_inbuf.data(), m_inbuf.data(), m_inbuf.data());
            }
            else
                default_buffer_size = 4096
            };
            virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir,
                                     std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
            {
                gbump(off_type(pos-gabs()));
                if (!is_open())
                    return pos_type(off_type(-1));
                pos_type pos = 0;
                if (which & std::ios_base::out)
                {
                    pos = seekoff(m_outpos, off, dir);
                }
                else if (which & std::ios_base::in)
                {
                    pos = seekoff(m_inpos, off, dir);
                }
                return seekpos(pos, which);
            }
        }
        return pos;
    }
    virtual std::streamsize showmanyc() override
    {
        return m_size-pabs();
    }
    //reads characters from the associated input sequence to the get area
    virtual int_type underflow() override
    {
        if(!is_open())
            return traits_type::eof();
        if(pptr()>pbase())
            overflow();
        off_type count=egptr()-eback();
        pos_type next_pos=0;
        if(count==0 && eback()==m_inbuf.data())
        {
            setg(m_inbuf.data(), m_inbuf.data(), m_inbuf.data()+m_inbuf.size());
            count=m_inbuf.size();
        }
        else
        {
            next_pos=m_inpos+pos_type(count);
        }
        if(next_pos>=m_size)
            return traits_type::eof();
        count=std::min(count, m_size-next_pos);
        m_inpos = next_pos;
        if(sqlite3_blob_read(m_blob, eback(), count, m_inpos)!=SQLITE_OK)
            return traits_type::eof();
        setg(eback(), eback(), eback()+count);
        return traits_type::to_int_type(*gptr());
    }
    /*//reads characters from the associated input sequence to the get area and advances the next pointer
    virtual int_type uflow() override
    {
    }*/
    //writes characters to the associated output sequence from the put area
    virtual int_type overflow( int_type ch = traits_type::eof() ) override
    {
        if(!is_open())
            return traits_type::eof();
        if(pptr()!=pbase())
        {
            size_t count = pptr()-pbase();
            if(sqlite3_blob_write(m_blob, pbase(), count, m_outpos)!=SQLITE_OK)
                return traits_type::eof();
            auto intersection = interval_intersection(m_inpos, egptr()-eback(), m_outpos, epptr()-pbase());
            if(intersection.first!=intersection.second)
            virtual pos_type seekpos(pos_type pos,
                                     std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override
            {
                commit(intersection.first, intersection.second);
                if (!is_open())
                    return pos_type(off_type(-1));
                if (pos >= m_size)
                    return pos_type(off_type(-1));
                if (which & std::ios_base::out)
                {
                    if (pos < m_outpos || pos >= m_outpos + off_type(egptr() - pbase()))
                    {
                        overflow();
                        m_outpos = pos;
                        setp(m_outbuf.data(), m_outbuf.data() + m_outbuf.size());
                    }
                    else
                    {
                        pbump(off_type(pos - pabs()));
                    }
                }
                else if (which & std::ios_base::in)
                {
                    if (pos < m_inpos || pos >= m_inpos + off_type(epptr() - eback()))
                    {
                        m_inpos = pos;
                        setg(m_inbuf.data(), m_inbuf.data(), m_inbuf.data());
                    }
                    else
                    {
                        gbump(off_type(pos - gabs()));
                    }
                }
                return pos;
            }
            m_outpos+=count;
            setp(pbase(), epptr());
        }
        if(!traits_type::eq_int_type(ch, traits_type::eof()))
        {
            char_type c = traits_type::to_char_type(ch);
            if(m_outpos>=m_size)
                return traits_type::eof();
            if(sqlite3_blob_write(m_blob, &c, 1, m_outpos)!=SQLITE_OK)
                return traits_type::eof();
            auto intersection = interval_intersection(m_inpos, egptr()-eback(), m_outpos, 1);
            if(intersection.first!=intersection.second)
            virtual std::streamsize showmanyc() override
            {
                eback()[intersection.first-m_inpos]=c;
                return m_size - pabs();
            }
            m_outpos+=1;
        }
        return ch;
    }
    virtual int_type pbackfail( int_type c = traits_type::eof() ) override
    {
        if (gptr() == 0
            || gptr() <= eback()
            || (!traits_type::eq_int_type(traits_type::eof(), c)
            && !traits_type::eq(traits_type::to_char_type(c), gptr()[-1])))
            // reads characters from the associated input sequence to the get area
            virtual int_type underflow() override
            {
                if (!is_open())
                    return traits_type::eof();
                if (pptr() > pbase())
                    overflow();
                off_type count = egptr() - eback();
                pos_type next_pos = 0;
                if (count == 0 && eback() == m_inbuf.data())
                {
                    setg(m_inbuf.data(), m_inbuf.data(), m_inbuf.data() + m_inbuf.size());
                    count = m_inbuf.size();
                }
                else
                {
                    next_pos = m_inpos + pos_type(count);
                }
                if (next_pos >= m_size)
                    return traits_type::eof();
                count = std::min(count, m_size - next_pos);
                m_inpos = next_pos;
                if (sqlite3_blob_read(m_blob, eback(), count, m_inpos) != SQLITE_OK)
                    return traits_type::eof();
                setg(eback(), eback(), eback() + count);
                return traits_type::to_int_type(*gptr());
            }
            /*//reads characters from the associated input sequence to the get area and advances the next pointer
            virtual int_type uflow() override
            {
            }*/
            // writes characters to the associated output sequence from the put area
            virtual int_type overflow(int_type ch = traits_type::eof()) override
            {
                if (!is_open())
                    return traits_type::eof();
                if (pptr() != pbase())
                {
                    size_t count = pptr() - pbase();
                    if (sqlite3_blob_write(m_blob, pbase(), count, m_outpos) != SQLITE_OK)
                        return traits_type::eof();
                    auto intersection = interval_intersection(m_inpos, egptr() - eback(), m_outpos, epptr() - pbase());
                    if (intersection.first != intersection.second)
                    {
                        commit(intersection.first, intersection.second);
                    }
                    m_outpos += count;
                    setp(pbase(), epptr());
                }
                if (!traits_type::eq_int_type(ch, traits_type::eof()))
                {
                    char_type c = traits_type::to_char_type(ch);
                    if (m_outpos >= m_size)
                        return traits_type::eof();
                    if (sqlite3_blob_write(m_blob, &c, 1, m_outpos) != SQLITE_OK)
                        return traits_type::eof();
                    auto intersection = interval_intersection(m_inpos, egptr() - eback(), m_outpos, 1);
                    if (intersection.first != intersection.second)
                    {
                        eback()[intersection.first - m_inpos] = c;
                    }
                    m_outpos += 1;
                }
                return ch;
            }
            virtual int_type pbackfail(int_type c = traits_type::eof()) override
            {
                if (gptr() == 0 || gptr() <= eback() || (!traits_type::eq_int_type(traits_type::eof(), c) && !traits_type::eq(traits_type::to_char_type(c), gptr()[-1])))
                {
                    return (traits_type::eof()); // can't put back, fail
                }
                else
                { // back up one position and store put-back character
                    gbump(-1);
                    if (!traits_type::eq_int_type(traits_type::eof(), c))
                        *gptr() = traits_type::to_char_type(c);
                    return (traits_type::not_eof(c));
                }
            }
        private:
            sqlite3_blob *m_blob;
            std::vector<char> m_inbuf;
            std::vector<char> m_outbuf;
            pos_type m_size;
            pos_type m_inpos;  // position in the input sequence
            pos_type m_outpos; // position in the output sequence
            void init()
            {
                m_blob = nullptr;
                m_size = 0;
                m_inpos = m_outpos = 0;
                m_set_eback = m_set_egptr = nullptr;
                m_back_char = 0;
                setg(nullptr, nullptr, nullptr);
                setp(nullptr, nullptr);
            }
            off_type seekoff(off_type position, off_type off, std::ios_base::seekdir dir)
            {
                off_type result = 0;
                switch (dir)
                {
                case std::ios_base::beg:
                    result = off;
                    break;
                case std::ios_base::cur:
                    result = position + off;
                    break;
                case std::ios_base::end:
                    result = m_size - off;
                    break;
                }
                if (result > m_size)
                    result = m_size;
                return result;
            }
            void reset_back()
            { // restore buffer after putback
                if (this->eback() == &m_back_char)
                    this->setg(m_set_eback, m_set_eback, m_set_egptr);
            }
            void set_back()
            { // set up putback area
                if (this->eback() != &m_back_char)
                { // save current get buffer
                    m_set_eback = this->eback();
                    m_set_egptr = this->egptr();
                }
                this->setg(&m_back_char, &m_back_char, &m_back_char + 1);
            }
            char_type *m_set_eback{nullptr}; // saves eback() during one-element putback
            char_type *m_set_egptr{nullptr}; // saves egptr()
            char_type m_back_char{0};
            void move_data(blobbuf &other)
            {
                m_blob = other.m_blob;
                other.m_blob = nullptr;
                m_size = other.m_size;
                other.m_size = 0;
                m_inpos = other.m_inpos;
                other.m_inpos = 0;
                m_outpos = other.m_outpos;
                other.m_outpos = 0;
            }
            static std::pair<pos_type, pos_type> interval_intersection(pos_type first1, pos_type last1, pos_type first2, pos_type last2)
            {
                if (first1 > first2)
                {
                    std::swap(first1, first2);
                    std::swap(last1, last2);
                }
                if (first2 < last1)
                    return std::make_pair(first2, std::min(last1, last2));
                else
                    return std::make_pair(0, 0);
            }
            static std::pair<pos_type, pos_type> interval_intersection(pos_type first1, off_type count1, pos_type first2, off_type count2)
            {
                return interval_intersection(first1, first1 + count1, first2, first2 + count2);
            }
            void commit(off_type first, off_type last)
            {
                char_type *src = pbase() + (first - m_outpos);
                char_type *dest = eback() + (first - m_inpos);
                memmove(dest, src, last - first);
            }
            pos_type gabs() const // absolute offset of input pointer in blob field
            {
                return m_inpos + off_type(gptr() - eback());
            }
            pos_type pabs() const // absolute offset of output pointer in blob field
            {
                return m_outpos + off_type(pptr() - pbase());
            }
        };
        class iblobstream : public std::istream
        {
            return (traits_type::eof());    // can't put back, fail
        }
        else
        {    // back up one position and store put-back character
            gbump(-1);
            if (!traits_type::eq_int_type(traits_type::eof(), c))
                *gptr() = traits_type::to_char_type(c);
            return (traits_type::not_eof(c));
        }
    }
        public:
            iblobstream() : std::istream(&m_buffer) {}
            iblobstream(database &db, const char *table, const char *column, sqlite3_int64 row,
                        std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
                : std::istream(&m_buffer)
            {
                open(db, table, column, row, mode, dbname);
            }
            iblobstream(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                        std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
                : std::istream(&m_buffer)
            {
                open(db, table, column, row, mode, dbname);
            }
            iblobstream(const iblobstream &) = delete;
            iblobstream(iblobstream &&src) : std::istream(&m_buffer), m_buffer(std::move(src.m_buffer)) {}
private:
    sqlite3_blob* m_blob;
    std::vector<char> m_inbuf;
    std::vector<char> m_outbuf;
    pos_type m_size;
    pos_type m_inpos;    //position in the input sequence
    pos_type m_outpos;    //position in the output sequence
            iblobstream &operator=(const iblobstream &) = delete;
            iblobstream &operator=(iblobstream &&src)
            {
                m_buffer.operator=(std::move(src.m_buffer));
            }
    void init()
    {
        m_blob=nullptr;
        m_size=0;
        m_inpos=m_outpos=0;
        m_set_eback=m_set_egptr=nullptr;
        m_back_char=0;
        setg(nullptr, nullptr, nullptr);
        setp(nullptr, nullptr);
    }
            bool is_open() const { return m_buffer.is_open(); }
    off_type seekoff(off_type position, off_type off, std::ios_base::seekdir dir)
    {
        off_type result=0;
        switch(dir)
            void open(database &db, const char *table, const char *column, sqlite3_int64 row,
                      std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
            {
                if (m_buffer.open(db, table, column, row, mode | std::ios_base::in, dbname) == nullptr)
                    this->setstate(std::ios_base::failbit);
                else
                    this->clear();
            }
            void open(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                      std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
            {
                open(db, table.c_str(), column.c_str(), row, mode, dbname);
            }
            void close()
            {
                if (m_buffer.close() == nullptr)
                    this->setstate(std::ios_base::failbit);
            }
            blobbuf *rdbuf() const
            {
                return const_cast<blobbuf *>(&m_buffer);
            }
            std::streamoff blob_size() const { return m_buffer.size(); }
        private:
            blobbuf m_buffer;
        };
        class oblobstream : public std::ostream
        {
        case std::ios_base::beg:
            result=off;
            break;
        case std::ios_base::cur:
            result=position+off;
            break;
        case std::ios_base::end:
            result=m_size-off;
            break;
        }
        if(result>m_size)
            result=m_size;
        return result;
    }
        public:
            oblobstream() : std::ostream(&m_buffer) {}
            oblobstream(database &db, const char *table, const char *column, sqlite3_int64 row,
                        std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
                : std::ostream(&m_buffer)
            {
                open(db, table, column, row, mode, dbname);
            }
            oblobstream(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                        std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
                : std::ostream(&m_buffer)
            {
                open(db, table, column, row, mode, dbname);
            }
            oblobstream(const oblobstream &) = delete;
            oblobstream(oblobstream &&src) : std::ostream(&m_buffer), m_buffer(std::move(src.m_buffer)) {}
    void reset_back()
    {    // restore buffer after putback
        if (this->eback() == &m_back_char)
            this->setg(m_set_eback, m_set_eback, m_set_egptr);
    }
            oblobstream &operator=(const oblobstream &) = delete;
            oblobstream &operator=(oblobstream &&src)
            {
                m_buffer.operator=(std::move(src.m_buffer));
            }
    void set_back()
    {    // set up putback area
        if (this->eback() != &m_back_char)
        {    // save current get buffer
            m_set_eback = this->eback();
            m_set_egptr = this->egptr();
        }
        this->setg(&m_back_char, &m_back_char, &m_back_char + 1);
    }
            bool is_open() const { return m_buffer.is_open(); }
    char_type *m_set_eback { nullptr };    // saves eback() during one-element putback
    char_type *m_set_egptr { nullptr };    // saves egptr()
    char_type m_back_char { 0 };
            void open(database &db, const char *table, const char *column, sqlite3_int64 row,
                      std::ios_base::openmode mode = std::ios_base::out, const char *dbname = "main")
            {
                if (m_buffer.open(db, table, column, row, mode | std::ios_base::out, dbname) == nullptr)
                    this->setstate(std::ios_base::failbit);
                else
                    this->clear();
            }
            void open(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                      std::ios_base::openmode mode = std::ios_base::out, const char *dbname = "main")
            {
                open(db, table.c_str(), column.c_str(), row, mode, dbname);
            }
    void move_data(blobbuf& other)
    {
        m_blob=other.m_blob;
        other.m_blob=nullptr;
        m_size=other.m_size;
        other.m_size=0;
        m_inpos=other.m_inpos;
        other.m_inpos=0;
        m_outpos=other.m_outpos;
        other.m_outpos=0;
    }
            void close()
            {
                if (m_buffer.close() == nullptr)
                    this->setstate(std::ios_base::failbit);
            }
    static std::pair<pos_type, pos_type> interval_intersection(pos_type first1, pos_type last1, pos_type first2, pos_type last2)
    {
        if(first1>first2)
            blobbuf *rdbuf() const
            {
                return const_cast<blobbuf *>(&m_buffer);
            }
            std::streamoff blob_size() const { return m_buffer.size(); }
        private:
            blobbuf m_buffer;
        };
        class blobstream : public std::iostream
        {
            std::swap(first1, first2);
            std::swap(last1, last2);
        public:
            blobstream() : std::iostream(&m_buffer) {}
            blobstream(database &db, const char *table, const char *column, sqlite3_int64 row,
                       std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
                : std::iostream(&m_buffer)
            {
                open(db, table, column, row, mode, dbname);
            }
            blobstream(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                       std::ios_base::openmode mode = std::ios_base::in, const char *dbname = "main")
                : std::iostream(&m_buffer)
            {
                open(db, table, column, row, mode, dbname);
            }
            blobstream(const blobstream &) = delete;
            blobstream(blobstream &&src) : std::iostream(&m_buffer), m_buffer(std::move(src.m_buffer)) {}
            blobstream &operator=(const blobstream &) = delete;
            blobstream &operator=(blobstream &&src)
            {
                m_buffer.operator=(std::move(src.m_buffer));
            }
            bool is_open() const { return m_buffer.is_open(); }
            void open(database &db, const char *table, const char *column, sqlite3_int64 row,
                      std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out, const char *dbname = "main")
            {
                if (m_buffer.open(db, table, column, row, mode | std::ios_base::in | std::ios_base::out, dbname) == nullptr)
                    this->setstate(std::ios_base::failbit);
                else
                    this->clear();
            }
            void open(database &db, const std::string &table, const std::string &column, sqlite3_int64 row,
                      std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out, const char *dbname = "main")
            {
                open(db, table.c_str(), column.c_str(), row, mode, dbname);
            }
            void close()
            {
                if (m_buffer.close() == nullptr)
                    this->setstate(std::ios_base::failbit);
            }
            blobbuf *rdbuf() const
            {
                return const_cast<blobbuf *>(&m_buffer);
            }
            std::streamoff blob_size() const { return m_buffer.size(); }
        private:
            blobbuf m_buffer;
        };
        typedef qtl::transaction<database> transaction;
        template <typename Record>
        using query_iterator = qtl::query_iterator<statement, Record>;
        template <typename Record>
        using query_result = qtl::query_result<statement, Record>;
        template <typename Params>
        inline statement &operator<<(statement &stmt, const Params &params)
        {
            stmt.reset();
            stmt.execute(params);
            return stmt;
        }
        if(first2<last1)
            return std::make_pair(first2, std::min(last1, last2));
        else
            return std::make_pair(0, 0);
        template <>
        inline const char *statement::get_text_value<char>(int col) const
        {
            return (const char *)sqlite3_column_text(m_stmt, col);
        }
        template <>
        inline const wchar_t *statement::get_text_value<wchar_t>(int col) const
        {
            return (const wchar_t *)sqlite3_column_text16(m_stmt, col);
        }
    }
    static std::pair<pos_type, pos_type> interval_intersection(pos_type first1, off_type count1, pos_type first2, off_type count2)
    {
        return interval_intersection(first1, first1+count1, first2, first2+count2);
    }
    void commit(off_type first, off_type last)
    {
        char_type* src= pbase()+(first-m_outpos);
        char_type* dest = eback()+(first-m_inpos);
        memmove(dest, src, last-first);
    }
    pos_type gabs() const // absolute offset of input pointer in blob field
    {
        return m_inpos+off_type(gptr()-eback());
    }
    pos_type pabs() const // absolute offset of output pointer in blob field
    {
        return m_outpos+off_type(pptr()-pbase());
    }
};
class iblobstream : public std::istream
{
public:
    iblobstream() : std::istream(&m_buffer) { }
    iblobstream(database& db, const char* table, const char* column, sqlite3_int64 row,
        std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
        : std::istream(&m_buffer)
    {
        open(db, table, column, row, mode, dbname);
    }
    iblobstream(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
        std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
        : std::istream(&m_buffer)
    {
        open(db, table, column, row, mode, dbname);
    }
    iblobstream(const iblobstream&) = delete;
    iblobstream(iblobstream&& src) : std::istream(&m_buffer), m_buffer(std::move(src.m_buffer)) { }
    iblobstream& operator=(const iblobstream&) = delete;
    iblobstream& operator=(iblobstream&& src)
    {
        m_buffer.operator =(std::move(src.m_buffer));
    }
    bool is_open() const { return m_buffer.is_open(); }
    void open(database& db, const char* table, const char* column, sqlite3_int64 row,
        std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
    {
        if(m_buffer.open(db, table, column, row, mode|std::ios_base::in, dbname)==nullptr)
            this->setstate(std::ios_base::failbit);
        else
            this->clear();
    }
    void open(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
        std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
    {
        open(db, table.c_str(), column.c_str(), row, mode, dbname);
    }
    void close()
    {
        if(m_buffer.close()==nullptr)
            this->setstate(std::ios_base::failbit);
    }
    blobbuf* rdbuf() const
    {
        return const_cast<blobbuf*>(&m_buffer);
    }
    std::streamoff blob_size() const { return m_buffer.size(); }
private:
    blobbuf m_buffer;
};
class oblobstream : public std::ostream
{
public:
    oblobstream() : std::ostream(&m_buffer) { }
    oblobstream(database& db, const char* table, const char* column, sqlite3_int64 row,
        std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
        : std::ostream(&m_buffer)
    {
        open(db, table, column, row, mode, dbname);
    }
    oblobstream(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
        std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
        : std::ostream(&m_buffer)
    {
        open(db, table, column, row, mode, dbname);
    }
    oblobstream(const oblobstream&) = delete;
    oblobstream(oblobstream&& src) : std::ostream(&m_buffer), m_buffer(std::move(src.m_buffer)) { }
    oblobstream& operator=(const oblobstream&) = delete;
    oblobstream& operator=(oblobstream&& src)
    {
        m_buffer.operator =(std::move(src.m_buffer));
    }
    bool is_open() const { return m_buffer.is_open(); }
    void open(database& db, const char* table, const char* column, sqlite3_int64 row,
        std::ios_base::openmode mode=std::ios_base::out, const char* dbname="main")
    {
        if(m_buffer.open(db, table, column, row, mode|std::ios_base::out, dbname)==nullptr)
            this->setstate(std::ios_base::failbit);
        else
            this->clear();
    }
    void open(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
        std::ios_base::openmode mode=std::ios_base::out, const char* dbname="main")
    {
        open(db, table.c_str(), column.c_str(), row, mode, dbname);
    }
    void close()
    {
        if(m_buffer.close()==nullptr)
            this->setstate(std::ios_base::failbit);
    }
    blobbuf* rdbuf() const
    {
        return const_cast<blobbuf*>(&m_buffer);
    }
    std::streamoff blob_size() const { return m_buffer.size(); }
private:
    blobbuf m_buffer;
};
class blobstream : public std::iostream
{
public:
    blobstream() : std::iostream(&m_buffer) { }
    blobstream(database& db, const char* table, const char* column, sqlite3_int64 row,
        std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
        : std::iostream(&m_buffer)
    {
        open(db, table, column, row, mode, dbname);
    }
    blobstream(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
        std::ios_base::openmode mode=std::ios_base::in, const char* dbname="main")
        : std::iostream(&m_buffer)
    {
        open(db, table, column, row, mode, dbname);
    }
    blobstream(const blobstream&) = delete;
    blobstream(blobstream&& src) : std::iostream(&m_buffer), m_buffer(std::move(src.m_buffer)) { }
    blobstream& operator=(const blobstream&) = delete;
    blobstream& operator=(blobstream&& src)
    {
        m_buffer.operator =(std::move(src.m_buffer));
    }
    bool is_open() const { return m_buffer.is_open(); }
    void open(database& db, const char* table, const char* column, sqlite3_int64 row,
        std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out, const char* dbname="main")
    {
        if(m_buffer.open(db, table, column, row, mode|std::ios_base::in|std::ios_base::out, dbname)==nullptr)
            this->setstate(std::ios_base::failbit);
        else
            this->clear();
    }
    void open(database& db, const std::string& table, const std::string& column, sqlite3_int64 row,
        std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out, const char* dbname="main")
    {
        open(db, table.c_str(), column.c_str(), row, mode, dbname);
    }
    void close()
    {
        if(m_buffer.close()==nullptr)
            this->setstate(std::ios_base::failbit);
    }
    blobbuf* rdbuf() const
    {
        return const_cast<blobbuf*>(&m_buffer);
    }
    std::streamoff blob_size() const { return m_buffer.size(); }
private:
    blobbuf m_buffer;
};
typedef qtl::transaction<database> transaction;
template<typename Record>
using query_iterator = qtl::query_iterator<statement, Record>;
template<typename Record>
using query_result = qtl::query_result<statement, Record>;
template<typename Params>
inline statement& operator<<(statement& stmt, const Params& params)
{
    stmt.reset();
    stmt.execute(params);
    return stmt;
}
template<>
inline const char* statement::get_text_value<char>(int col) const
{
    return (const char*)sqlite3_column_text(m_stmt, col);
}
template<>
inline const wchar_t* statement::get_text_value<wchar_t>(int col) const
{
    return (const wchar_t*)sqlite3_column_text16(m_stmt, col);
}
}
}
include/qtl_sqlite_pool.hpp
@@ -7,37 +7,36 @@
namespace qtl
{
namespace sqlite
{
class database_pool : public qtl::database_pool<database>
{
public:
    database_pool() : m_flags(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) { }
    virtual database* new_database() throw() override
    namespace sqlite
    {
        database* db=NULL;
        try
        class database_pool : public qtl::database_pool<database>
        {
            db=new database;
            db->open(m_filename.data(), m_flags);
        }
        catch (error& e)
        {
            delete db;
            db=NULL;
        }
        return db;
        public:
            database_pool() : m_flags(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) {}
            virtual database *new_database() throw() override
            {
                database *db = NULL;
                try
                {
                    db = new database;
                    db->open(m_filename.data(), m_flags);
                }
                catch (error &e)
                {
                    delete db;
                    db = NULL;
                }
                return db;
            }
        protected:
            std::string m_filename;
            int m_flags;
        };
    }
protected:
    std::string m_filename;
    int m_flags;
};
}
}
#endif //_QTL_SQLITE_POOL_H_