From 14337cf5b302c5385f0ae1393caf6df7e83fc539 Mon Sep 17 00:00:00 2001
From: znone <glyc@sina.com.cn>
Date: Sat, 07 Dec 2019 06:52:19 +0000
Subject: [PATCH] 1. 允许绑定字段到std::optional和std::any 2. 增加函数bind_fields可以一次绑定到多个字段 3. 查询函数返回数据库对象自身,以支持链式调用
---
include/qtl_sqlite.hpp | 42 ++
/dev/null | 29 -
test/TestOdbc.cpp | 8
test/TestSqlite.cpp | 60 ++-
test/TestMysql.cpp | 38 ++
include/qtl_common.hpp | 239 ++++++++-------
include/qtl_odbc.hpp | 197 +++++++++++-
test/TestSqlite.h | 3
include/apply_tuple.h | 18
include/qtl_mysql.hpp | 237 +++++++++++----
test/TestMysql.h | 1
README.md | 15
12 files changed, 612 insertions(+), 275 deletions(-)
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 4581ef2..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,29 +0,0 @@
-# Compiled Object files
-*.slo
-*.lo
-*.o
-*.obj
-
-# Precompiled Headers
-*.gch
-*.pch
-
-# Compiled Dynamic libraries
-*.so
-*.dylib
-*.dll
-
-# Fortran module files
-*.mod
-*.smod
-
-# Compiled Static libraries
-*.lai
-*.la
-*.a
-*.lib
-
-# Executables
-*.exe
-*.out
-*.app
diff --git a/README.md b/README.md
index 55df680..1fe49a6 100644
--- a/README.md
+++ b/README.md
@@ -72,9 +72,7 @@
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);
}
}
@@ -109,8 +107,11 @@
- 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 实现一个专门化;
@@ -130,7 +131,7 @@
```
-#### 10. 在不同查询中复用同一数据结构
+#### 11. 在不同查询中复用同一数据结构
通常希望复用结构,将其绑定到多个不同的查询的结果集,这时候 qtl::bind_record就不够用了。需要利用 qtl::custom_bind 实现不同的绑定函数才能实现这一需求。有如下绑定函数:
```C++
@@ -151,7 +152,7 @@
```
qtl::bind_record不是唯一的方法。通过派生类也能实现类似的需求(qtl::record_with_tag)。
-#### 11.处理返回多个结果集的查询
+#### 12.处理返回多个结果集的查询
有些查询语句会返回多个结果集。使用函数query执行这些查询只能得到第一个结果集。要处理所有结果集需要使用query_multi或query_multi_with_params。query_multi不会为没有结果集的查询调用回调函数。例如:
```SQL
CREATE PROCEDURE test_proc()
@@ -172,7 +173,7 @@
```
-#### 12. 异步调用数据库
+#### 13. 异步调用数据库
通过类async_connection可以异步调用数据库。所有的异步函数都需要提供一个回调函数接受操作完成后的结果。如果异步调用中发生错误,错误做为回调函数的参数返回给调用者。
```
diff --git a/include/apply_tuple.h b/include/apply_tuple.h
index 2f47dbc..3e37cd9 100644
--- a/include/apply_tuple.h
+++ b/include/apply_tuple.h
@@ -6,7 +6,15 @@
#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
{
@@ -46,14 +54,6 @@
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
diff --git a/include/qtl_common.hpp b/include/qtl_common.hpp
index 3b2e8bb..aaaa0d6 100644
--- a/include/qtl_common.hpp
+++ b/include/qtl_common.hpp
@@ -1,11 +1,7 @@
#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
@@ -24,6 +20,12 @@
#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
{
@@ -145,6 +147,19 @@
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
@@ -208,22 +223,6 @@
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)
{
@@ -244,32 +243,14 @@
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);
}
@@ -278,8 +259,10 @@
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);
}
@@ -288,6 +271,47 @@
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
@@ -835,39 +859,40 @@
{
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>
@@ -947,7 +972,7 @@
}
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);
@@ -957,152 +982,154 @@
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:
diff --git a/include/qtl_mysql.hpp b/include/qtl_mysql.hpp
index 834e3c1..8174bca 100644
--- a/include/qtl_mysql.hpp
+++ b/include/qtl_mysql.hpp
@@ -10,6 +10,7 @@
#include <string>
#include <vector>
#include <array>
+#include <utility>
#include <functional>
#include <algorithm>
#include <system_error>
@@ -83,8 +84,9 @@
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)
{
@@ -275,6 +277,71 @@
}
};
+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:
@@ -409,7 +476,8 @@
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);
}
}
@@ -518,6 +586,104 @@
}
}
+#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)
@@ -571,7 +737,7 @@
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;
@@ -1000,71 +1166,6 @@
#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
diff --git a/include/qtl_odbc.hpp b/include/qtl_odbc.hpp
index 94ed746..c42d812 100644
--- a/include/qtl_odbc.hpp
+++ b/include/qtl_odbc.hpp
@@ -16,6 +16,11 @@
#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
@@ -34,7 +39,7 @@
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;
@@ -149,6 +154,13 @@
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>
@@ -445,7 +457,7 @@
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 {
@@ -488,7 +500,7 @@
};
}
- 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;
@@ -498,7 +510,7 @@
}
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];
@@ -521,6 +533,145 @@
};
}
+#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)
{
@@ -720,9 +871,9 @@
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)
{
@@ -732,21 +883,21 @@
{
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);
}
@@ -755,27 +906,27 @@
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)
@@ -863,7 +1014,7 @@
{
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)
diff --git a/include/qtl_sqlite.hpp b/include/qtl_sqlite.hpp
index 157c82f..b0e1807 100644
--- a/include/qtl_sqlite.hpp
+++ b/include/qtl_sqlite.hpp
@@ -224,6 +224,48 @@
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();
diff --git a/test/TestMysql.cpp b/test/TestMysql.cpp
index bfbcf49..6f9d922 100644
--- a/test/TestMysql.cpp
+++ b/test/TestMysql.cpp
@@ -2,6 +2,7 @@
#include "TestMysql.h"
#include <fstream>
#include <array>
+#include <iomanip>
#include "md5.h"
#include "../include/qtl_mysql.hpp"
@@ -30,9 +31,7 @@
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);
}
}
@@ -48,7 +47,8 @@
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)
}
@@ -331,6 +331,36 @@
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);
diff --git a/test/TestMysql.h b/test/TestMysql.h
index 3f0e1da..d8bd7ef 100644
--- a/test/TestMysql.h
+++ b/test/TestMysql.h
@@ -28,6 +28,7 @@
void test_select_blob();
void test_insert_stream();
void test_fetch_stream();
+ void test_any();
private:
uint32_t id;
diff --git a/test/TestOdbc.cpp b/test/TestOdbc.cpp
index 3230362..54bd33e 100644
--- a/test/TestOdbc.cpp
+++ b/test/TestOdbc.cpp
@@ -29,16 +29,13 @@
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;
@@ -308,7 +305,6 @@
cout<<hex;
for(size_t i=0; i!=n; i++)
cout<<(data[i]&0xFF);
- cout<<dec;
}
int main(int argc, char* argv[])
diff --git a/test/TestSqlite.cpp b/test/TestSqlite.cpp
index c349ad1..edf6c96 100644
--- a/test/TestSqlite.cpp
+++ b/test/TestSqlite.cpp
@@ -32,9 +32,7 @@
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);
}
}
@@ -50,10 +48,12 @@
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");
@@ -62,12 +62,12 @@
{
ASSERT_EXCEPTION(e);
}
+ return db;
}
void TestSqlite::test_dual()
{
- qtl::sqlite::database db;
- connect(db);
+ qtl::sqlite::database db = connect();
try
{
@@ -85,8 +85,7 @@
void TestSqlite::test_clear()
{
- qtl::sqlite::database db;
- connect(db);
+ qtl::sqlite::database db = connect();
try
{
@@ -100,8 +99,7 @@
void TestSqlite::test_insert()
{
- qtl::sqlite::database db;
- connect(db);
+ qtl::sqlite::database db = connect();
try
{
@@ -117,8 +115,7 @@
void TestSqlite::test_insert2()
{
- qtl::sqlite::database db;
- connect(db);
+ qtl::sqlite::database db = connect();
try
{
@@ -135,8 +132,7 @@
void TestSqlite::test_update()
{
- qtl::sqlite::database db;
- connect(db);
+ qtl::sqlite::database db = connect();
try
{
@@ -152,8 +148,7 @@
void TestSqlite::test_query()
{
- qtl::sqlite::database db;
- connect(db);
+ qtl::sqlite::database db = connect();
try
{
@@ -181,8 +176,7 @@
void TestSqlite::test_iterator()
{
- qtl::sqlite::database db;
- connect(db);
+ qtl::sqlite::database db = connect();
try
{
@@ -201,8 +195,7 @@
void TestSqlite::test_insert_blob()
{
- qtl::sqlite::database db;
- connect(db);
+ qtl::sqlite::database db = connect();
try
{
@@ -235,8 +228,7 @@
void TestSqlite::test_select_blob()
{
- qtl::sqlite::database db;
- connect(db);
+ qtl::sqlite::database db = connect();
try
{
@@ -260,6 +252,30 @@
}
}
+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;
diff --git a/test/TestSqlite.h b/test/TestSqlite.h
index b7a9350..1fa0e8e 100644
--- a/test/TestSqlite.h
+++ b/test/TestSqlite.h
@@ -26,10 +26,11 @@
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);
--
Gitblit v1.9.3