| | |
| | | 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_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> |
| | |
| | | #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); |
| | |
| | | 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)) |
| | |
| | | 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(); |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | 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) { |
| | |
| | | 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); |
| | |
| | | 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 ¶ms) |
| | | { |
| | | 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 { |
| | |
| | | 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 ¶ms) |
| | | { |
| | | 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 ¶ms) |
| | | { |
| | | 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 ¶ms) |
| | | { |
| | | 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 { |
| | |
| | | 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 ¶ms) |
| | | { |
| | | 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 ¶ms) |
| | | { |
| | | 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 ¶ms, 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); |
| | |
| | | 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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); |
| | |
| | | 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 ¶ms, 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 ¶ms, 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}; |
| | | }; |
| | | |
| | | } |
| | | |
| | |
| | | #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> |
| | |
| | | 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 ¶m) |
| | | { |
| | | 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 ¶m) |
| | | { |
| | | 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 ¶m) |
| | | { |
| | | 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> ¶m) |
| | | { |
| | | 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...> ¶ms) 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...> &¶ms) 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...> ¶ms) const |
| | | { |
| | | bind_param(m_command, 0, std::get<0>(params)); |
| | | } |
| | | void operator()(std::tuple<Types...> &¶ms) 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...> ¶ms) const |
| | | { |
| | | } |
| | | void operator()(std::tuple<Types...> &¶ms) 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 ¶m) 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...> ¶ms) 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 ¶m) |
| | | { |
| | | 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 ¶ms) |
| | | { |
| | | 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms) |
| | | { |
| | | 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 ¶ms) |
| | | { |
| | | return insert(query_text, strlen(query_text), params); |
| | | } |
| | | |
| | | template <typename Params> |
| | | uint64_t insert(const std::string &query_text, const Params ¶ms) |
| | | { |
| | | 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 ¶ms) |
| | | { |
| | | 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 ¶ms) |
| | | { |
| | | 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 ¶ms) |
| | | { |
| | | 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms, 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 ¶ms) |
| | | { |
| | | 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 ¶ms, 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...); |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | 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); |
| | |
| | | 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) |
| | | { |
| | | { |
| | |
| | | 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) |
| | | { |
| | |
| | | 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); |
| | |
| | | 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_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, ¶m](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, ¶m](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 ¶m) |
| | | { |
| | | m_binders[index].bind(nullptr, 0, MYSQL_TYPE_LONG_BLOB); |
| | | m_binderAddins[index].m_after_fetch = [this, index, ¶m](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 ¶m) |
| | | { |
| | | m_binders[index].bind(nullptr, 0, MYSQL_TYPE_LONG_BLOB); |
| | | m_binderAddins[index].m_after_fetch = [this, index, ¶m](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 ¶m) |
| | | { |
| | | 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([¶ms](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 ¶ms) |
| | | { |
| | | execute_custom([¶ms](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 ¶ms, 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); |
| | |
| | | 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 ¶ms) |
| | | { |
| | | 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; |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | 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; |
| | |
| | | { |
| | | 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 |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | |
| | | #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" |
| | |
| | | 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, ¶m](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 ¶m) |
| | | { |
| | | 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, ¶m](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 ¶m = 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 ¶m = 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 ¶m = 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 ¶ms) |
| | | { |
| | | 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 ¶meters); |
| | | std::string create_connection_text(const connection_parameters ¶meters); |
| | | }; |
| | | |
| | | 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 ¶ms, 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) |
| | | { |
| | |
| | | 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) |
| | |
| | | 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) |
| | | { |
| | |
| | | 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 ¶ms) |
| | | { |
| | | 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 ¶meters) |
| | | { |
| | | 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 ¶meters) |
| | | { |
| | | std::ostringstream oss; |
| | | for (auto ¶meter : 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 |
| | | |
| | |
| | | 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 |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | 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; |
| | | }; |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | 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 ¶ms) |
| | | { |
| | | 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 ¶ms) |
| | | { |
| | | 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); |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | 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_ |
| | | |