1. 允许绑定字段到std::optional和std::any
2. 增加函数bind_fields可以一次绑定到多个字段
3. 查询函数返回数据库对象自身,以支持链式调用
11 files modified
1 files deleted
| | |
| | | template<> |
| | | inline void bind_record<qtl::mysql::statement, TestMysqlRecord>(qtl::mysql::statement& command, TestMysqlRecord&& v) |
| | | { |
| | | qtl::bind_field(command, 0, v.id); |
| | | qtl::bind_field(command, 1, v.name); |
| | | qtl::bind_field(command, 2, v.create_time); |
| | | qtl::bind_fields(command, v.id, v.name, v.create_time); |
| | | } |
| | | } |
| | | |
| | |
| | | - is_null 字段是否为空 |
| | | - length 数据的实际长度 |
| | | - is_truncated 数据是否被截断 |
| | | |
| | | #### 9. std::optional和std::any |
| | | 可以绑定字段到 C++17 中的 std::optional 和 std::any。当字段为null时,它们不包含任何内容,否则他们包含字段的值。 |
| | | |
| | | #### 9. 支持标准库以外的字符串类型 |
| | | #### 10. 支持标准库以外的字符串类型 |
| | | 除了标准库提供的std::string,另外其他库也提供了自己的字符串类,比如QT的QString,MFC/ATL的CString等。qtl也可以将字符字段绑定到这些类型上。扩展方法是: |
| | | 1. 为你的字符串类型,对 qtl::bind_string_helper 实现一个专门化。如果该字符串类型有符合标准库字符串语义的以下成员函数,可以跳过这一步:assign,clear,resize,data,size; |
| | | 2. 为你的字符串类型,对 qtl::bind_field 实现一个专门化; |
| | |
| | | |
| | | ``` |
| | | |
| | | #### 10. 在不同查询中复用同一数据结构 |
| | | #### 11. 在不同查询中复用同一数据结构 |
| | | 通常希望复用结构,将其绑定到多个不同的查询的结果集,这时候 qtl::bind_record就不够用了。需要利用 qtl::custom_bind 实现不同的绑定函数才能实现这一需求。有如下绑定函数: |
| | | |
| | | ```C++ |
| | |
| | | ``` |
| | | qtl::bind_record不是唯一的方法。通过派生类也能实现类似的需求(qtl::record_with_tag)。 |
| | | |
| | | #### 11.处理返回多个结果集的查询 |
| | | #### 12.处理返回多个结果集的查询 |
| | | 有些查询语句会返回多个结果集。使用函数query执行这些查询只能得到第一个结果集。要处理所有结果集需要使用query_multi或query_multi_with_params。query_multi不会为没有结果集的查询调用回调函数。例如: |
| | | ```SQL |
| | | CREATE PROCEDURE test_proc() |
| | |
| | | |
| | | ``` |
| | | |
| | | #### 12. 异步调用数据库 |
| | | #### 13. 异步调用数据库 |
| | | |
| | | 通过类async_connection可以异步调用数据库。所有的异步函数都需要提供一个回调函数接受操作完成后的结果。如果异步调用中发生错误,错误做为回调函数的参数返回给调用者。 |
| | | ``` |
| | |
| | | #include <type_traits> |
| | | #include <utility> |
| | | |
| | | #if __cplusplus < 201703L |
| | | #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)); |
| | | } |
| | | |
| | | #else |
| | | |
| | | namespace detail |
| | | { |
| | |
| | | return detail::apply< std::tuple_size< |
| | | typename std::decay<T>::type |
| | | >::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t)); |
| | | } |
| | | |
| | | #else |
| | | |
| | | 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)); |
| | | } |
| | | |
| | | #endif // C++17 |
| | |
| | | #ifndef _QTL_COMMON_H_ |
| | | #define _QTL_COMMON_H_ |
| | | |
| | | #if defined(_MSC_VER) |
| | | #if _MSC_VER<1800 |
| | | #error QTL need C++11 compiler |
| | | #endif //MSC |
| | | #elif __cplusplus<201103L |
| | | #if __cplusplus<201103L && _MSC_VER<1800 |
| | | #error QTL need C++11 compiler |
| | | #endif //C++11 |
| | | |
| | |
| | | #include <vector> |
| | | #include <functional> |
| | | #include "apply_tuple.h" |
| | | |
| | | #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) |
| | | #define _QTL_ENABLE_CPP17 |
| | | #include <optional> |
| | | #include <any> |
| | | #endif // C++17 |
| | | |
| | | namespace qtl |
| | | { |
| | |
| | | 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); |
| | | } |
| | | |
| | | #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 |
| | | |
| | |
| | | bind_field(command, index, std::forward<T>(value)); |
| | | } |
| | | |
| | | template<typename FieldType, typename Command, typename BindType> |
| | | inline void bind_field(Command& command, size_t index, BindType& value) |
| | | { |
| | | FieldType temp=FieldType(); |
| | | bind_field(command, index, temp); |
| | | value=static_cast<BindType>(temp); |
| | | } |
| | | |
| | | template<typename FieldType, typename Command, typename BindType, typename CastFun> |
| | | inline void bind_field(Command& command, size_t index, BindType& value, CastFun&& fun) |
| | | { |
| | | FieldType temp=FieldType(); |
| | | bind_field(command, index, temp); |
| | | fun(value, temp); |
| | | } |
| | | |
| | | 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) |
| | | { |
| | |
| | | bind_field(command, index, std::forward<T>(value)); |
| | | } |
| | | |
| | | template<typename FieldType, typename Command, typename BindType> |
| | | inline void bind_field(Command& command, const char* name, BindType& value) |
| | | { |
| | | size_t index=command.find_field(name); |
| | | if(index==-1) |
| | | value=BindType(); |
| | | else |
| | | bind_field<FieldType>(command, index, value); |
| | | } |
| | | |
| | | template<typename FieldType, typename Command, typename BindType, typename CastFun> |
| | | inline void bind_field(Command& command, const char* name, BindType& value, CastFun&& fun) |
| | | { |
| | | size_t index=command.find_field(name); |
| | | if(index==-1) |
| | | value=BindType(); |
| | | else |
| | | bind_field<FieldType>(command, index, value, fun); |
| | | } |
| | | |
| | | 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) |
| | | value[0]='\0'; |
| | | if (index == -1) |
| | | { |
| | | if (length > 0) value[0] = '\0'; |
| | | } |
| | | else |
| | | command.bind_field(index, value, length); |
| | | } |
| | |
| | | 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) |
| | | value[0]='\0'; |
| | | if (index == -1) |
| | | { |
| | | if (length > 0) value[0] = '\0'; |
| | | } |
| | | else |
| | | command.bind_field(index, value, length); |
| | | } |
| | |
| | | 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, 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... 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 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)...); |
| | | } |
| | | |
| | | namespace detail |
| | |
| | | { |
| | | public: |
| | | template<typename Params> |
| | | void execute(const char* query_text, size_t text_length, const Params& params, uint64_t* affected=NULL) |
| | | 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> |
| | | void execute(const char* query_text, const Params& params, uint64_t* affected=NULL) |
| | | 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> |
| | | void execute(const std::string& query_text, const Params& params, uint64_t* affected=NULL) |
| | | 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> |
| | | void execute_direct(const char* query_text, size_t text_length, uint64_t* affected, const Params&... params) |
| | | base_database& execute_direct(const char* query_text, size_t text_length, uint64_t* affected, const Params&... params) |
| | | { |
| | | execute(query_text, text_length, std::forward_as_tuple(params...), affected); |
| | | return execute(query_text, text_length, std::forward_as_tuple(params...), affected); |
| | | } |
| | | template<typename... Params> |
| | | void execute_direct(const char* query_text, uint64_t* affected, const Params&... params) |
| | | base_database& execute_direct(const char* query_text, uint64_t* affected, const Params&... params) |
| | | { |
| | | execute(query_text, std::forward_as_tuple(params...), affected); |
| | | return execute(query_text, std::forward_as_tuple(params...), affected); |
| | | } |
| | | template<typename... Params> |
| | | void execute_direct(const std::string& query_text, uint64_t* affected, const Params&... params) |
| | | base_database& execute_direct(const std::string& query_text, uint64_t* affected, const Params&... params) |
| | | { |
| | | execute(query_text, std::forward_as_tuple(params...), affected); |
| | | return execute(query_text, std::forward_as_tuple(params...), affected); |
| | | } |
| | | |
| | | template<typename Params> |
| | |
| | | } |
| | | |
| | | template<typename Params, typename Values, typename ValueProc> |
| | | void query_explicit(const char* query_text, size_t text_length, const Params& params, Values&& values, ValueProc&& proc) |
| | | 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); |
| | |
| | | if(!detail::apply(std::forward<ValueProc>(proc), std::forward<Values>(values))) break; |
| | | } |
| | | command.close(); |
| | | return *this; |
| | | } |
| | | |
| | | template<typename Params, typename Values, typename ValueProc> |
| | | void query_explicit(const char* query_text, const Params& params, Values&& values, ValueProc&& proc) |
| | | base_database& query_explicit(const char* query_text, const Params& params, Values&& values, ValueProc&& proc) |
| | | { |
| | | query_explicit(query_text, strlen(query_text), params, std::forward<Values>(values), std::forward<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> |
| | | void query_explicit(const std::string& query_text, const Params& params, Values&& values, ValueProc&& proc) |
| | | base_database& query_explicit(const std::string& query_text, const Params& params, Values&& values, ValueProc&& proc) |
| | | { |
| | | query_explicit(query_text.data(), query_text.size(), params, std::forward<Values>(values), std::forward<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> |
| | | void query_explicit(const char* query_text, size_t text_length, Values&& values, ValueProc&& proc) |
| | | base_database& query_explicit(const char* query_text, size_t text_length, Values&& values, ValueProc&& proc) |
| | | { |
| | | query_explicit(query_text, text_length, std::make_tuple(), std::forward<Values>(values), std::forward<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> |
| | | void query_explicit(const char* query_text, Values&& values, ValueProc&& proc) |
| | | base_database& query_explicit(const char* query_text, Values&& values, ValueProc&& proc) |
| | | { |
| | | query_explicit(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), std::forward<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> |
| | | void query_explicit(const std::string& query_text, Values&& values, ValueProc&& proc) |
| | | base_database& query_explicit(const std::string& query_text, Values&& values, ValueProc&& proc) |
| | | { |
| | | query_explicit(query_text, std::make_tuple(), std::forward<Values>(values), std::forward<ValueProc>(proc)); |
| | | return query_explicit(query_text, std::make_tuple(), std::forward<Values>(values), std::forward<ValueProc>(proc)); |
| | | } |
| | | |
| | | template<typename Params, typename ValueProc> |
| | | void query(const char* query_text, size_t text_length, const Params& params, ValueProc&& proc) |
| | | base_database& query(const char* query_text, size_t text_length, const Params& params, ValueProc&& proc) |
| | | { |
| | | query_explicit(query_text, text_length, params, detail::make_values(proc), std::forward<ValueProc>(proc)); |
| | | return query_explicit(query_text, text_length, params, detail::make_values(proc), std::forward<ValueProc>(proc)); |
| | | } |
| | | template<typename Params, typename ValueProc> |
| | | void query(const char* query_text, const Params& params, ValueProc&& proc) |
| | | base_database& query(const char* query_text, const Params& params, ValueProc&& proc) |
| | | { |
| | | query_explicit(query_text, params, detail::make_values(proc), std::forward<ValueProc>(proc)); |
| | | return query_explicit(query_text, params, detail::make_values(proc), std::forward<ValueProc>(proc)); |
| | | } |
| | | template<typename Params, typename ValueProc> |
| | | void query(const std::string& query_text, const Params& params, ValueProc&& proc) |
| | | base_database& query(const std::string& query_text, const Params& params, ValueProc&& proc) |
| | | { |
| | | query_explicit(query_text, params, detail::make_values(proc), std::forward<ValueProc>(proc)); |
| | | return query_explicit(query_text, params, detail::make_values(proc), std::forward<ValueProc>(proc)); |
| | | } |
| | | template<typename ValueProc> |
| | | void query(const char* query_text, size_t text_length, ValueProc&& proc) |
| | | base_database& query(const char* query_text, size_t text_length, ValueProc&& proc) |
| | | { |
| | | query_explicit(query_text, text_length, detail::make_values(proc), std::forward<ValueProc>(proc)); |
| | | return query_explicit(query_text, text_length, detail::make_values(proc), std::forward<ValueProc>(proc)); |
| | | } |
| | | template<typename ValueProc> |
| | | void query(const char* query_text, ValueProc&& proc) |
| | | base_database& query(const char* query_text, ValueProc&& proc) |
| | | { |
| | | query_explicit(query_text, detail::make_values(proc), std::forward<ValueProc>(proc)); |
| | | return query_explicit(query_text, detail::make_values(proc), std::forward<ValueProc>(proc)); |
| | | } |
| | | template<typename ValueProc> |
| | | void query(const std::string& query_text, ValueProc&& proc) |
| | | base_database& query(const std::string& query_text, ValueProc&& proc) |
| | | { |
| | | query_explicit(query_text, detail::make_values(proc), std::forward<ValueProc>(proc)); |
| | | return query_explicit(query_text, detail::make_values(proc), std::forward<ValueProc>(proc)); |
| | | } |
| | | |
| | | template<typename Params, typename... ValueProc> |
| | | void query_multi_with_params(const char* query_text, size_t text_length, const Params& params, ValueProc&&... proc) |
| | | 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> |
| | | void query_multi_with_params(const char* query_text, const Params& params, ValueProc&&... proc) |
| | | base_database& query_multi_with_params(const char* query_text, const Params& params, ValueProc&&... proc) |
| | | { |
| | | query_multi_with_params(query_text, strlen(query_text), params, std::forward<ValueProc>(proc)...); |
| | | return query_multi_with_params(query_text, strlen(query_text), params, std::forward<ValueProc>(proc)...); |
| | | } |
| | | template<typename Params, typename... ValueProc> |
| | | void query_multi_with_params(const std::string& query_text, const Params& params, ValueProc&&... proc) |
| | | base_database& query_multi_with_params(const std::string& query_text, const Params& params, ValueProc&&... proc) |
| | | { |
| | | query_multi_with_params(query_text.data(), query_text.size(), params, std::forward<ValueProc>(proc)...); |
| | | return query_multi_with_params(query_text.data(), query_text.size(), params, std::forward<ValueProc>(proc)...); |
| | | } |
| | | template<typename... ValueProc> |
| | | void query_multi(const char* query_text, size_t text_length, ValueProc&&... proc) |
| | | base_database& query_multi(const char* query_text, size_t text_length, ValueProc&&... proc) |
| | | { |
| | | query_multi_with_params<std::tuple<>, ValueProc...>(query_text, text_length, std::make_tuple(), std::forward<ValueProc>(proc)...); |
| | | return query_multi_with_params<std::tuple<>, ValueProc...>(query_text, text_length, std::make_tuple(), std::forward<ValueProc>(proc)...); |
| | | } |
| | | template<typename... ValueProc> |
| | | void query_multi(const char* query_text, ValueProc&&... proc) |
| | | base_database& query_multi(const char* query_text, ValueProc&&... proc) |
| | | { |
| | | query_multi_with_params<std::tuple<>, ValueProc...>(query_text, strlen(query_text), std::make_tuple(), std::forward<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> |
| | | void query_multi(const std::string& query_text, ValueProc&&... proc) |
| | | base_database& query_multi(const std::string& query_text, ValueProc&&... proc) |
| | | { |
| | | query_multi_with_params<std::tuple<>, ValueProc...>(query_text.data(), query_text.size(), std::make_tuple(), std::forward<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> |
| | | void query_first(const char* query_text, size_t text_length, const Params& params, Values&& values) |
| | | base_database& query_first(const char* query_text, size_t text_length, const Params& params, Values&& values) |
| | | { |
| | | query_explicit(query_text, text_length, params, std::forward<Values>(values), first_record()); |
| | | return query_explicit(query_text, text_length, params, std::forward<Values>(values), first_record()); |
| | | } |
| | | |
| | | template<typename Params, typename Values> |
| | | void query_first(const char* query_text, const Params& params, Values&& values) |
| | | base_database& query_first(const char* query_text, const Params& params, Values&& values) |
| | | { |
| | | query_explicit(query_text, strlen(query_text), params, std::forward<Values>(values), first_record()); |
| | | return query_explicit(query_text, strlen(query_text), params, std::forward<Values>(values), first_record()); |
| | | } |
| | | |
| | | template<typename Params, typename Values> |
| | | void query_first(const std::string& query_text, const Params& params, Values&& values) |
| | | base_database& query_first(const std::string& query_text, const Params& params, Values&& values) |
| | | { |
| | | query_explicit(query_text, params, values, first_record()); |
| | | return query_explicit(query_text, params, values, first_record()); |
| | | } |
| | | |
| | | template<typename Values> |
| | | void query_first(const char* query_text, size_t text_length, Values&& values) |
| | | base_database& query_first(const char* query_text, size_t text_length, Values&& values) |
| | | { |
| | | query_explicit(query_text, text_length, std::make_tuple(), std::forward<Values>(values), first_record()); |
| | | return query_explicit(query_text, text_length, std::make_tuple(), std::forward<Values>(values), first_record()); |
| | | } |
| | | |
| | | template<typename Values> |
| | | void query_first(const char* query_text, Values&& values) |
| | | base_database& query_first(const char* query_text, Values&& values) |
| | | { |
| | | query_explicit(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), first_record()); |
| | | return query_explicit(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), first_record()); |
| | | } |
| | | |
| | | template<typename Values> |
| | | void query_first(const std::string& query_text, Values&& values) |
| | | base_database& query_first(const std::string& query_text, Values&& values) |
| | | { |
| | | query_explicit(query_text, std::make_tuple(), std::forward<Values>(values), first_record()); |
| | | return query_explicit(query_text, std::make_tuple(), std::forward<Values>(values), first_record()); |
| | | } |
| | | |
| | | template<typename... Values> |
| | | void query_first_direct(const char* query_text, size_t text_length, Values&... values) |
| | | base_database& query_first_direct(const char* query_text, size_t text_length, Values&... values) |
| | | { |
| | | query_first(query_text, text_length, std::tie(values...)); |
| | | return query_first(query_text, text_length, std::tie(values...)); |
| | | } |
| | | |
| | | template<typename... Values> |
| | | void query_first_direct(const char* query_text, Values&... values) |
| | | base_database& query_first_direct(const char* query_text, Values&... values) |
| | | { |
| | | query_first(query_text, std::tie(values...)); |
| | | return query_first(query_text, std::tie(values...)); |
| | | } |
| | | |
| | | template<typename... Values> |
| | | void query_first_direct(const std::string& query_text, Values&... values) |
| | | base_database& query_first_direct(const std::string& query_text, Values&... values) |
| | | { |
| | | query_first(query_text, std::tie(values...)); |
| | | return query_first(query_text, std::tie(values...)); |
| | | } |
| | | |
| | | protected: |
| | |
| | | #include <string> |
| | | #include <vector> |
| | | #include <array> |
| | | #include <utility> |
| | | #include <functional> |
| | | #include <algorithm> |
| | | #include <system_error> |
| | |
| | | void bind(bool& v) |
| | | { |
| | | init(); |
| | | buffer_type = MYSQL_TYPE_TINY; |
| | | buffer_type = MYSQL_TYPE_BIT; |
| | | buffer = &v; |
| | | buffer_length = 1; |
| | | } |
| | | void bind(int8_t& v) |
| | | { |
| | |
| | | } |
| | | }; |
| | | |
| | | 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: |
| | |
| | | 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); |
| | | m_binderAddins[index].m_after_fetch = |
| | | if_null<typename std::remove_reference<Type>::type>(value); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | #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) |
| | | { |
| | | 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: |
| | | case MYSQL_TYPE_JSON: |
| | | 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) |
| | |
| | | template<typename Value> |
| | | struct if_null |
| | | { |
| | | if_null(Value& value, Value&& def=Value()) : m_value(value), m_def(std::move(def)) { } |
| | | 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; |
| | |
| | | |
| | | #endif //MariaDB |
| | | |
| | | }; |
| | | |
| | | 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); |
| | | } |
| | | }; |
| | | |
| | | #if MARIADB_VERSION_ID >= 100000 |
| | |
| | | #include <sys/time.h> |
| | | #endif //_WIN32 |
| | | |
| | | #if (ODBCVER >= 0x0380) && (WIN32_WINNT >= 0x0602) |
| | | #define QTL_ODBC_ENABLE_ASYNC_MODE 1 |
| | | #endif //ODBC 3.80 && Windows |
| | | |
| | | |
| | | #include "qtl_common.hpp" |
| | | |
| | | namespace qtl |
| | |
| | | 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() { return m_errno!=SQL_SUCCESS || m_errno!=SQL_SUCCESS_WITH_INFO; } |
| | | 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; |
| | |
| | | verify_error(SQLSetEnvAttr(m_handle, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0)); |
| | | } |
| | | 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 statement final : public object<SQL_HANDLE_STMT> |
| | |
| | | void bind_field(SQLUSMALLINT index, qtl::bind_string_helper<T>&& v) |
| | | { |
| | | SQLLEN length=0; |
| | | SQLColAttribute(m_handle, index+1, SQL_DESC_LENGTH, NULL, 0, NULL, &length); |
| | | verify_error(SQLColAttribute(m_handle, 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 { |
| | |
| | | }; |
| | | } |
| | | |
| | | void bind_field(size_t index, blobbuf&& value) |
| | | void bind_field(SQLUSMALLINT index, blobbuf&& value) |
| | | { |
| | | m_params[index].m_data = nullptr; |
| | | m_params[index].m_size = 0; |
| | |
| | | } |
| | | |
| | | template<typename Type> |
| | | void bind_field(size_t index, indicator<Type>&& value) |
| | | void bind_field(SQLUSMALLINT index, indicator<Type>&& value) |
| | | { |
| | | qtl::bind_field(*this, index, value.data); |
| | | param_data& param=m_params[index]; |
| | |
| | | }; |
| | | } |
| | | |
| | | #ifdef _QTL_ENABLE_CPP17 |
| | | |
| | | template<typename Type> |
| | | void bind_field(SQLUSMALLINT index, std::optional<Type>&& value) |
| | | { |
| | | qtl::bind_field(*this, index, *value); |
| | | param_data& param = m_params[index]; |
| | | auto fetch_fun = param.m_after_fetch; |
| | | param.m_after_fetch = [fetch_fun, &value](const param_data& p) { |
| | | if (fetch_fun) fetch_fun(p); |
| | | if (p.m_indicator == SQL_NULL_DATA) |
| | | value.reset(); |
| | | }; |
| | | } |
| | | |
| | | void bind_field(SQLUSMALLINT 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) |
| | | { |
| | | value.emplace<uint8_t>(); |
| | | bind_field(index, std::forward<uint8_t>(std::any_cast<uint8_t&>(value))); |
| | | } |
| | | else |
| | | { |
| | | value.emplace<int8_t>(); |
| | | bind_field(index, std::forward<int8_t>(std::any_cast<int8_t&>(value))); |
| | | } |
| | | break; |
| | | case SQL_SMALLINT: |
| | | if (isUnsigned) |
| | | { |
| | | value.emplace<uint16_t>(); |
| | | bind_field(index, std::forward<uint16_t>(std::any_cast<uint16_t&>(value))); |
| | | } |
| | | else |
| | | { |
| | | value.emplace<int16_t>(); |
| | | bind_field(index, std::forward<int16_t>(std::any_cast<int16_t&>(value))); |
| | | } |
| | | break; |
| | | case SQL_INTEGER: |
| | | if (isUnsigned) |
| | | { |
| | | value.emplace<uint32_t>(); |
| | | bind_field(index, std::forward<uint32_t>(std::any_cast<uint32_t&>(value))); |
| | | } |
| | | else |
| | | { |
| | | value.emplace<int32_t>(); |
| | | bind_field(index, std::forward<int32_t>(std::any_cast<int32_t&>(value))); |
| | | } |
| | | break; |
| | | case SQL_BIGINT: |
| | | if (isUnsigned) |
| | | { |
| | | value.emplace<uint64_t>(); |
| | | bind_field(index, std::forward<uint64_t>(std::any_cast<uint64_t&>(value))); |
| | | } |
| | | else |
| | | { |
| | | value.emplace<int64_t>(); |
| | | bind_field(index, std::forward<int64_t>(std::any_cast<int64_t&>(value))); |
| | | } |
| | | break; |
| | | case SQL_FLOAT: |
| | | value.emplace<float>(); |
| | | bind_field(index, std::forward<float>(std::any_cast<float&>(value))); |
| | | break; |
| | | case SQL_DOUBLE: |
| | | value.emplace<double>(); |
| | | bind_field(index, std::forward<double>(std::any_cast<double&>(value))); |
| | | break; |
| | | case SQL_NUMERIC: |
| | | value.emplace<SQL_NUMERIC_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_NUMERIC_STRUCT>(std::any_cast<SQL_NUMERIC_STRUCT&>(value))); |
| | | break; |
| | | case SQL_TIME: |
| | | value.emplace<SQL_TIME_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_TIME_STRUCT>(std::any_cast<SQL_TIME_STRUCT&>(value))); |
| | | break; |
| | | case SQL_DATE: |
| | | value.emplace<SQL_DATE_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_DATE_STRUCT>(std::any_cast<SQL_DATE_STRUCT&>(value))); |
| | | break; |
| | | case SQL_TIMESTAMP: |
| | | value.emplace<SQL_TIMESTAMP_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_TIMESTAMP_STRUCT>(std::any_cast<SQL_TIMESTAMP_STRUCT&>(value))); |
| | | break; |
| | | case SQL_INTERVAL_MONTH: |
| | | case SQL_INTERVAL_YEAR: |
| | | case SQL_INTERVAL_YEAR_TO_MONTH: |
| | | case SQL_INTERVAL_DAY: |
| | | case SQL_INTERVAL_HOUR: |
| | | case SQL_INTERVAL_MINUTE: |
| | | case SQL_INTERVAL_SECOND: |
| | | case SQL_INTERVAL_DAY_TO_HOUR: |
| | | case SQL_INTERVAL_DAY_TO_MINUTE: |
| | | case SQL_INTERVAL_DAY_TO_SECOND: |
| | | case SQL_INTERVAL_HOUR_TO_MINUTE: |
| | | case SQL_INTERVAL_HOUR_TO_SECOND: |
| | | case SQL_INTERVAL_MINUTE_TO_SECOND: |
| | | value.emplace<SQL_INTERVAL_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_INTERVAL_STRUCT>(std::any_cast<SQL_INTERVAL_STRUCT&>(value))); |
| | | break; |
| | | case SQL_CHAR: |
| | | value.emplace<std::string>(); |
| | | bind_field(index, qtl::bind_string(std::any_cast<std::string&>(value))); |
| | | break; |
| | | case SQL_GUID: |
| | | value.emplace<SQLGUID>(); |
| | | bind_field(index, std::forward<SQLGUID>(std::any_cast<SQLGUID&>(value))); |
| | | break; |
| | | case SQL_BINARY: |
| | | value.emplace<blobbuf>(); |
| | | bind_field(index, std::forward<blobbuf>(std::any_cast<blobbuf&>(value))); |
| | | break; |
| | | default: |
| | | throw odbc::error(*this, SQL_ERROR); |
| | | } |
| | | param_data& param = m_params[index]; |
| | | auto fetch_fun = param.m_after_fetch; |
| | | param.m_after_fetch = [fetch_fun, &value](const param_data& p) { |
| | | if (fetch_fun) fetch_fun(p); |
| | | if (p.m_indicator == SQL_NULL_DATA) |
| | | value.reset(); |
| | | }; |
| | | } |
| | | |
| | | #endif // C++17 |
| | | template<typename Types> |
| | | void execute(const Types& params) |
| | | { |
| | |
| | | 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(); |
| | | if (m_opened) close(); |
| | | verify_error(SQLConnectA(m_handle, (SQLCHAR*)server_name, server_name_length, (SQLCHAR*)user_name, user_name_length, (SQLCHAR*)password, password_length)); |
| | | m_opened=true; |
| | | m_opened = true; |
| | | } |
| | | void open(const char* server_name, const char* user_name, const char* 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) |
| | | 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; |
| | | if(m_opened) close(); |
| | | verify_error(SQLDriverConnectA(m_handle, hwnd, (SQLCHAR*)input_text, (SQLSMALLINT)text_length, |
| | | 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; |
| | | m_opened = true; |
| | | } |
| | | void open(const std::string& input_text, SQLSMALLINT driver_completion=SQL_DRIVER_NOPROMPT, SQLHWND hwnd=NULL) |
| | | 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) |
| | | void open(SQLHWND hwnd, SQLSMALLINT driver_completion = SQL_DRIVER_COMPLETE) |
| | | { |
| | | open("", SQL_NTS, driver_completion, hwnd); |
| | | } |
| | |
| | | template<typename InputPred> |
| | | void open(const char* connection_text, size_t text_length, InputPred&& pred) |
| | | { |
| | | SQLSMALLINT length=0; |
| | | SQLINTEGER ret=SQL_SUCCESS; |
| | | SQLSMALLINT length = 0; |
| | | SQLINTEGER ret = SQL_SUCCESS; |
| | | std::string input_text; |
| | | if(m_opened) close(); |
| | | if(text_length==SQL_NTS) |
| | | input_text=connection_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, |
| | | 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)) |
| | | if (!pred(parameters)) |
| | | throw error(SQL_NEED_DATA, "User cancel operation."); |
| | | input_text=create_connection_text(parameters); |
| | | input_text = create_connection_text(parameters); |
| | | } |
| | | if(ret==SQL_ERROR || ret==SQL_SUCCESS_WITH_INFO) |
| | | if (ret == SQL_ERROR || ret == SQL_SUCCESS_WITH_INFO) |
| | | verify_error(ret); |
| | | m_opened=true; |
| | | m_opened = true; |
| | | } |
| | | template<typename InputPred> |
| | | void open(const char* connection_text, InputPred&& pred) |
| | |
| | | { |
| | | SQLINTEGER value; |
| | | get_attribute(SQL_ATTR_CONNECTION_DEAD, value); |
| | | return value==SQL_CD_FALSE; |
| | | return value == SQL_CD_FALSE; |
| | | } |
| | | |
| | | statement open_command(const char* query_text, size_t text_length) |
| | |
| | | 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); |
| | | } |
| | | } |
| | | |
| | | 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(); |
| | |
| | | #include "TestMysql.h" |
| | | #include <fstream> |
| | | #include <array> |
| | | #include <iomanip> |
| | | #include "md5.h" |
| | | #include "../include/qtl_mysql.hpp" |
| | | |
| | |
| | | template<> |
| | | inline void bind_record<qtl::mysql::statement, TestMysqlRecord>(qtl::mysql::statement& command, TestMysqlRecord&& v) |
| | | { |
| | | qtl::bind_field(command, static_cast<size_t>(0), v.id); |
| | | qtl::bind_field(command, 1, v.name); |
| | | qtl::bind_field(command, 2, v.create_time); |
| | | qtl::bind_fields(command, v.id, v.name, v.create_time); |
| | | } |
| | | } |
| | | |
| | |
| | | TEST_ADD(TestMysql::test_iterator) |
| | | TEST_ADD(TestMysql::test_insert_blob) |
| | | TEST_ADD(TestMysql::test_select_blob) |
| | | //TEST_ADD(TestMysql::test_insert_stream) |
| | | TEST_ADD(TestMysql::test_any) |
| | | //TEST_ADD(TestMysql::test_insert_stream) |
| | | //TEST_ADD(TestMysql::test_fetch_stream) |
| | | } |
| | | |
| | |
| | | MD5Final(result, &context); |
| | | } |
| | | |
| | | void TestMysql::test_any() |
| | | { |
| | | #ifdef _QTL_ENABLE_CPP17 |
| | | |
| | | qtl::mysql::database db; |
| | | connect(db); |
| | | |
| | | try |
| | | { |
| | | db.query("select 0, 'hello world', now() from dual", |
| | | [](const std::any& i, const std::any& str, const std::any& now) { |
| | | const qtl::mysql::time& time = std::any_cast<const qtl::mysql::time&>(now); |
| | | struct tm tm; |
| | | time.as_tm(tm); |
| | | cout << "0=\"" << std::any_cast<int32_t>(i) << "\", 'hello world'=\"" << |
| | | std::any_cast<const std::string&>(str) << "\", now=\"" << |
| | | std::put_time(&tm, "%c") << "\" \n"; |
| | | }); |
| | | } |
| | | catch (qtl::mysql::error& e) |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | catch (std::bad_cast& e) |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | #endif |
| | | } |
| | | |
| | | int main(int argc, char* argv[]) |
| | | { |
| | | Test::TextOutput output(Test::TextOutput::Verbose); |
| | |
| | | void test_select_blob(); |
| | | void test_insert_stream(); |
| | | void test_fetch_stream(); |
| | | void test_any(); |
| | | |
| | | private: |
| | | uint32_t id; |
| | |
| | | template<> |
| | | inline void bind_record<qtl::odbc::statement, TestOdbcRecord>(qtl::odbc::statement& command, TestOdbcRecord&& v) |
| | | { |
| | | qtl::bind_field(command, (size_t)0, v.id); |
| | | qtl::bind_field(command, 1, v.name); |
| | | qtl::bind_field(command, 2, v.create_time); |
| | | qtl::bind_fields(command, v.id, v.name, v.create_time); |
| | | } |
| | | } |
| | | |
| | | TestOdbc::TestOdbc() : m_db(m_env) |
| | | { |
| | | m_db.open("DRIVER={SQL Server};SERVER=(local);UID=;PWD=;Trusted_Connection=yes;DATABASE=test"); |
| | | //m_db.open("DRIVER={SQL Server};SERVER=(local);UID=;PWD=;Trusted_Connection=no;DATABASE=test;UID=sa;PWD=111111;"); |
| | | m_db.open("DRIVER={SQL Server};SERVER=(local);UID=;PWD=;Trusted_Connection=no;DATABASE=test;UID=sa;PWD=111111;"); |
| | | cout<<"DBMS: "<<m_db.dbms_name()<<endl; |
| | | cout<<"SERVER: "<<m_db.server_name()<<endl; |
| | | cout<<"USER: "<<m_db.user_name()<<endl; |
| | |
| | | cout<<hex; |
| | | for(size_t i=0; i!=n; i++) |
| | | cout<<(data[i]&0xFF); |
| | | cout<<dec; |
| | | } |
| | | |
| | | int main(int argc, char* argv[]) |
| | |
| | | template<> |
| | | inline void bind_record<qtl::sqlite::statement, TestSqliteRecord>(qtl::sqlite::statement& command, TestSqliteRecord&& v) |
| | | { |
| | | qtl::bind_field(command, 0, v.id); |
| | | qtl::bind_field(command, 1, v.name); |
| | | qtl::bind_field(command, 2, v.create_time); |
| | | qtl::bind_fields(command, v.id, v.name, v.create_time); |
| | | } |
| | | } |
| | | |
| | |
| | | TEST_ADD(TestSqlite::test_iterator) |
| | | TEST_ADD(TestSqlite::test_insert_blob) |
| | | TEST_ADD(TestSqlite::test_select_blob) |
| | | TEST_ADD(TestSqlite::test_any) |
| | | } |
| | | |
| | | inline void TestSqlite::connect(qtl::sqlite::database& db) |
| | | inline qtl::sqlite::database TestSqlite::connect() |
| | | { |
| | | qtl::sqlite::database db; |
| | | try |
| | | { |
| | | db.open("test.db"); |
| | |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | return db; |
| | | } |
| | | |
| | | void TestSqlite::test_dual() |
| | | { |
| | | qtl::sqlite::database db; |
| | | connect(db); |
| | | qtl::sqlite::database db = connect(); |
| | | |
| | | try |
| | | { |
| | |
| | | |
| | | void TestSqlite::test_clear() |
| | | { |
| | | qtl::sqlite::database db; |
| | | connect(db); |
| | | qtl::sqlite::database db = connect(); |
| | | |
| | | try |
| | | { |
| | |
| | | |
| | | void TestSqlite::test_insert() |
| | | { |
| | | qtl::sqlite::database db; |
| | | connect(db); |
| | | qtl::sqlite::database db = connect(); |
| | | |
| | | try |
| | | { |
| | |
| | | |
| | | void TestSqlite::test_insert2() |
| | | { |
| | | qtl::sqlite::database db; |
| | | connect(db); |
| | | qtl::sqlite::database db = connect(); |
| | | |
| | | try |
| | | { |
| | |
| | | |
| | | void TestSqlite::test_update() |
| | | { |
| | | qtl::sqlite::database db; |
| | | connect(db); |
| | | qtl::sqlite::database db = connect(); |
| | | |
| | | try |
| | | { |
| | |
| | | |
| | | void TestSqlite::test_query() |
| | | { |
| | | qtl::sqlite::database db; |
| | | connect(db); |
| | | qtl::sqlite::database db = connect(); |
| | | |
| | | try |
| | | { |
| | |
| | | |
| | | void TestSqlite::test_iterator() |
| | | { |
| | | qtl::sqlite::database db; |
| | | connect(db); |
| | | qtl::sqlite::database db = connect(); |
| | | |
| | | try |
| | | { |
| | |
| | | |
| | | void TestSqlite::test_insert_blob() |
| | | { |
| | | qtl::sqlite::database db; |
| | | connect(db); |
| | | qtl::sqlite::database db = connect(); |
| | | |
| | | try |
| | | { |
| | |
| | | |
| | | void TestSqlite::test_select_blob() |
| | | { |
| | | qtl::sqlite::database db; |
| | | connect(db); |
| | | qtl::sqlite::database db = connect(); |
| | | |
| | | try |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | void TestSqlite::test_any() |
| | | { |
| | | #ifdef _QTL_ENABLE_CPP17 |
| | | qtl::sqlite::database db = connect(); |
| | | |
| | | try |
| | | { |
| | | db.query("select 0, 'hello world'", |
| | | [](const std::any& i, const std::any& str) { |
| | | cout << "0=\"" << std::any_cast<int64_t>(i) << "\", 'hello world'=\"" << |
| | | std::any_cast<const std::string_view&>(str).data() << "\" \n"; |
| | | }); |
| | | } |
| | | catch (qtl::sqlite::error& e) |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | catch (std::bad_cast& e) |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | #endif // C++17 |
| | | } |
| | | |
| | | void TestSqlite::get_md5(std::string& str, unsigned char* result) |
| | | { |
| | | MD5_CTX context; |
| | |
| | | void test_iterator(); |
| | | void test_insert_blob(); |
| | | void test_select_blob(); |
| | | void test_any(); |
| | | |
| | | private: |
| | | int64_t id; |
| | | void connect(qtl::sqlite::database& db); |
| | | qtl::sqlite::database connect(); |
| | | void get_md5(std::string& str, unsigned char* result); |
| | | void copy_stream(std::istream& is, std::ostream& os); |
| | | static void print_hex(const unsigned char* data, size_t n); |