From 50457f28fa7567b81ccf6a423e09312d5587cfbc Mon Sep 17 00:00:00 2001
From: znone <glyc@sina.com.cn>
Date: Fri, 04 Dec 2020 14:40:30 +0000
Subject: [PATCH] ODBC database can be accessed asynchronously.
---
test/vs/AsyncMssql/AsyncMssql.vcxproj.filters | 30
test/vs/AsyncMssql/AsyncMssql.sln | 31 +
include/qtl_odbc.hpp | 1275 ++++++++++++++++++++++++++++++----------
test/vs/AsyncMssql/AsyncMssql.vcxproj | 170 +++++
include/qtl_async.hpp | 34
test/vs/AsyncMssql/AsyncMssql.cpp | 154 ++++
test/TestMariaDB.cpp | 23
test/vs/test_sqlite/test_sqlite.vcproj | 28
include/qtl_asio.hpp | 26
include/qtl_odbc_pool.hpp | 31 +
10 files changed, 1,416 insertions(+), 386 deletions(-)
diff --git a/include/qtl_asio.hpp b/include/qtl_asio.hpp
index cbf51c9..ee8fae2 100644
--- a/include/qtl_asio.hpp
+++ b/include/qtl_asio.hpp
@@ -35,7 +35,7 @@
typedef NS_ASIO::io_context service_type;
#endif // ASIO_VERSION
- service() { }
+ service() NOEXCEPT { }
explicit service(int concurrency_hint) : _service(concurrency_hint) { }
void reset()
@@ -53,15 +53,15 @@
_service.stop();
}
- service_type& context() { return _service; }
+ service_type& context() NOEXCEPT { return _service; }
private:
class event_item : public qtl::event
{
public:
- event_item(service_type& service, qtl::socket_type fd)
- : _strand(service), _socket(service, NS_ASIO::ip::tcp::v4(), fd), _timer(service), _busying(false)
+ event_item(service& service, qtl::socket_type fd)
+ : _service(service), _strand(service.context()), _socket(service.context(), NS_ASIO::ip::tcp::v4(), fd), _timer(service.context()), _busying(false)
{
}
@@ -122,9 +122,11 @@
virtual void remove() override
{
-#if ASIO_VERSION < 101200 && (!defined(_WIN32) && _WIN32_WINNT >= 0x0603 )
+ if (_busying) return;
+#if ASIO_VERSION >= 101200 && (!defined(_WIN32) || _WIN32_WINNT >= 0x0603 )
_socket.release();
#endif //Windows 8.1
+ _service.remove(this);
}
virtual bool is_busying() override
{
@@ -132,6 +134,7 @@
}
private:
+ service& _service;
service_type::strand _strand;
NS_ASIO::ip::tcp::socket _socket;
NS_ASIO::steady_timer _timer;
@@ -143,14 +146,25 @@
template<typename Connection>
event_item* add(Connection* connection)
{
- event_item* item = new event_item(_service, connection->socket());
+ event_item* item = new event_item(*this, connection->socket());
+ std::lock_guard<std::mutex> lock(_mutex);
_events.push_back(std::unique_ptr<event_item>(item));
return item;
}
private:
service_type _service;
+ std::mutex _mutex;
std::vector<std::unique_ptr<event_item>> _events;
+
+ void remove(event_item* item)
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ auto it = std::find_if(_events.begin(), _events.end(), [item](std::unique_ptr<event_item>& v) {
+ return item==v.get();
+ });
+ if (it != _events.end()) _events.erase(it);
+ }
};
#if ASIO_VERSION < 101200
diff --git a/include/qtl_async.hpp b/include/qtl_async.hpp
index 7505425..ec877ea 100644
--- a/include/qtl_async.hpp
+++ b/include/qtl_async.hpp
@@ -157,7 +157,7 @@
Note: parameter affected must has default value.
*/
template<typename Params, typename ResultHandler>
- void execute(ResultHandler&& handler, const char* query_text, size_t text_length, const Params& params)
+ void execute(ResultHandler handler, const char* query_text, size_t text_length, const Params& params)
{
T* pThis = static_cast<T*>(this);
pThis->open_command(query_text, text_length, [handler, params](const typename T::exception_type& e, std::shared_ptr<Command>& command) mutable {
@@ -186,30 +186,30 @@
});
}
template<typename Params, typename ResultHandler>
- void execute(ResultHandler&& handler, const char* query_text, const Params& params, uint64_t* affected = NULL)
+ void execute(ResultHandler handler, const char* query_text, const Params& params)
{
- return execute(handler, query_text, strlen(query_text), params, affected);
+ return execute(std::forward<ResultHandler>(handler), query_text, strlen(query_text), params);
}
template<typename Params, typename ResultHandler>
- void execute(ResultHandler&& handler, const std::string& query_text, const Params& params, uint64_t* affected = NULL)
+ void execute(ResultHandler handler, const std::string& query_text, const Params& params)
{
- return execute(handler, query_text.data(), query_text.length(), params, affected);
+ return execute(std::forward<ResultHandler>(handler), query_text.data(), query_text.length(), params);
}
template<typename... Params, typename ResultHandler>
- void execute_direct(ResultHandler&& handler, const char* query_text, size_t text_length, uint64_t* affected, const Params&... params)
+ void execute_direct(ResultHandler handler, const char* query_text, size_t text_length, const Params&... params)
{
- execute(handler, query_text, text_length, std::forward_as_tuple(params...), affected);
+ execute(std::forward<ResultHandler>(handler), query_text, text_length, std::forward_as_tuple(params...));
}
template<typename... Params, typename ResultHandler>
- void execute_direct(ResultHandler&& handler, const char* query_text, uint64_t* affected, const Params&... params)
+ void execute_direct(ResultHandler handler, const char* query_text, const Params&... params)
{
- execute(handler, query_text, std::forward_as_tuple(params...), affected);
+ execute(std::forward<ResultHandler>(handler), query_text, std::forward_as_tuple(params...));
}
template<typename... Params, typename ResultHandler>
- void execute_direct(ResultHandler&& handler, const std::string& query_text, uint64_t* affected, const Params&... params)
+ void execute_direct(ResultHandler handler, const std::string& query_text, const Params&... params)
{
- execute(handler, query_text, query_text, std::forward_as_tuple(params...), affected);
+ execute(std::forward<ResultHandler>(handler), query_text, query_text, std::forward_as_tuple(params...));
}
/*
@@ -221,7 +221,7 @@
Note: parameter insert_id must has default value.
*/
template<typename Params, typename ResultHandler>
- void insert(ResultHandler&& handler, const char* query_text, size_t text_length, const Params& params)
+ void insert(ResultHandler handler, const char* query_text, size_t text_length, const Params& params)
{
T* pThis = static_cast<T*>(this);
pThis->open_command(query_text, text_length, [handler, params](const typename T::exception_type& e, std::shared_ptr<Command>& command) {
@@ -255,31 +255,31 @@
template<typename Params, typename ResultHandler>
void insert(ResultHandler&& handler, const char* query_text, const Params& params)
{
- insert(handler, query_text, strlen(query_text), params);
+ insert(std::forward<ResultHandler>(handler), query_text, strlen(query_text), params);
}
template<typename Params, typename ResultHandler>
void insert(ResultHandler&& handler, const std::string& query_text, const Params& params)
{
- insert(handler, query_text.data(), query_text.length(), params);
+ insert(std::forward<ResultHandler>(handler), query_text.data(), query_text.length(), params);
}
template<typename... Params, typename ResultHandler>
void insert_direct(ResultHandler&& handler, const char* query_text, size_t text_length, const Params&... params)
{
- insert(handler, query_text, text_length, std::forward_as_tuple(params...));
+ insert(std::forward<ResultHandler>(handler), query_text, text_length, std::forward_as_tuple(params...));
}
template<typename... Params, typename ResultHandler>
void insert_direct(ResultHandler&& handler, const char* query_text, const Params&... params)
{
- insert(handler, query_text, strlen(query_text), std::forward_as_tuple(params...));
+ insert(std::forward<ResultHandler>(handler), query_text, strlen(query_text), std::forward_as_tuple(params...));
}
template<typename... Params, typename ResultHandler>
void insert_direct(ResultHandler&& handler, const std::string& query_text, const Params&... params)
{
- insert(handler, query_text.data(), query_text.length(), std::forward_as_tuple(params...));
+ insert(std::forward<ResultHandler>(handler), query_text.data(), query_text.length(), std::forward_as_tuple(params...));
}
/*
diff --git a/include/qtl_odbc.hpp b/include/qtl_odbc.hpp
index 0250f84..3233ead 100644
--- a/include/qtl_odbc.hpp
+++ b/include/qtl_odbc.hpp
@@ -16,12 +16,13 @@
#include <sys/time.h>
#endif //_WIN32
-#if (ODBCVER >= 0x0380) && (WIN32_WINNT >= 0x0602)
+#if (ODBCVER >= 0x0380) && (_WIN32_WINNT >= 0x0602)
#define QTL_ODBC_ENABLE_ASYNC_MODE 1
#endif //ODBC 3.80 && Windows
#include "qtl_common.hpp"
+#include "qtl_async.hpp"
namespace qtl
{
@@ -30,16 +31,17 @@
{
template<SQLSMALLINT> class object;
-class database;
+class base_database;
class error : public std::exception
{
public:
+ error() : m_errno(SQL_SUCCESS) { }
template<SQLSMALLINT Type>
error(const object<Type>& h, SQLINTEGER code);
error(SQLINTEGER code, const char* msg) : m_errno(code), m_errmsg(msg) { }
SQLINTEGER code() const { return m_errno; }
- operator bool() const { return m_errno!=SQL_SUCCESS || m_errno!=SQL_SUCCESS_WITH_INFO; }
+ 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;
@@ -129,7 +131,7 @@
virtual bool read_blob(char* buffer, off_type& count, pos_type position) override
{
SQLLEN indicator=0;
- SQLRETURN ret = SQLGetData(m_stmt->handle(), m_field + 1, SQL_C_BINARY, buffer, count, const_cast<SQLLEN*>(&indicator));
+ SQLRETURN ret = SQLGetData(m_stmt->handle(), m_field + 1, SQL_C_BINARY, buffer, static_cast<SQLINTEGER>(count), const_cast<SQLLEN*>(&indicator));
if (ret != SQL_NO_DATA)
{
count = (indicator > count) || (indicator == SQL_NO_TOTAL) ?
@@ -151,7 +153,12 @@
public:
environment() : object(SQL_NULL_HANDLE)
{
- verify_error(SQLSetEnvAttr(m_handle, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0));
+#if ODBCVER >= 0x0380
+ const SQLPOINTER version = (SQLPOINTER)SQL_OV_ODBC3_80;
+#else
+ const SQLPOINTER version = (SQLPOINTER)SQL_OV_ODBC3;
+#endif
+ verify_error(SQLSetEnvAttr(m_handle, SQL_ATTR_ODBC_VERSION, version, SQL_IS_INTEGER));
}
environment(environment&& src) : object(std::forward<environment>(src)) { }
@@ -163,28 +170,28 @@
}
};
-class statement final : public object<SQL_HANDLE_STMT>
+class base_statement : public object<SQL_HANDLE_STMT>
{
public:
- explicit statement(database& db);
- statement(statement&& src)
- : object(std::forward<statement>(src)), m_params(std::forward<std::vector<param_data>>(src.m_params))
+ explicit base_statement(base_database& db);
+ base_statement(base_statement&& src)
+ : object(std::forward<base_statement>(src)), m_params(std::forward<std::vector<param_data>>(src.m_params))
{
m_binded_cols=src.m_binded_cols;
src.m_binded_cols=false;
m_blob_buffer=src.m_blob_buffer;
src.m_blob_buffer=NULL;
}
- ~statement()
+ ~base_statement()
{
if(m_blob_buffer)
free(m_blob_buffer);
}
- statement& operator=(statement&& src)
+ base_statement& operator=(base_statement&& src)
{
if(this!=&src)
{
- object::operator =(std::forward<statement>(src));
+ object::operator =(std::forward<base_statement>(src));
m_params=std::forward<std::vector<param_data>>(src.m_params);
m_binded_cols=src.m_binded_cols;
src.m_binded_cols=false;
@@ -194,141 +201,132 @@
return *this;
}
- void open(const char* query_text, size_t text_length=SQL_NTS)
- {
- verify_error(SQLPrepare(m_handle, (SQLCHAR*)query_text, text_length));
- }
- void open(const std::string& query_text)
- {
- open(query_text.data(), query_text.size());
- }
-
- void bind_param(SQLUSMALLINT index, const std::nullptr_t&)
+ void bind_param(size_t index, const std::nullptr_t&)
{
m_params[index].m_indicator=SQL_NULL_DATA;
- verify_error(SQLBindParameter(m_handle, index+1,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1),
SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_DEFAULT, 0, 0, NULL, 0, &m_params[index].m_indicator));
}
- void bind_param(SQLUSMALLINT index, const qtl::null&)
+ void bind_param(size_t index, const qtl::null&)
{
bind_param(index, nullptr);
}
- void bind_param(SQLUSMALLINT index, const int8_t& v)
+ void bind_param(size_t index, const int8_t& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_STINYINT, SQL_TINYINT,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_STINYINT, SQL_TINYINT,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const uint8_t& v)
+ void bind_param(size_t index, const uint8_t& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_UTINYINT, SQL_TINYINT,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_UTINYINT, SQL_TINYINT,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const int16_t& v)
+ void bind_param(size_t index, const int16_t& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_SMALLINT,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_SMALLINT,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const uint16_t& v)
+ void bind_param(size_t index, const uint16_t& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_USHORT, SQL_SMALLINT,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_USHORT, SQL_SMALLINT,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const int32_t& v)
+ void bind_param(size_t index, const int32_t& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const uint32_t& v)
+ void bind_param(size_t index, const uint32_t& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const int64_t& v)
+ void bind_param(size_t index, const int64_t& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const uint64_t& v)
+ void bind_param(size_t index, const uint64_t& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const double& v)
+ void bind_param(size_t index, const double& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const float& v)
+ void bind_param(size_t index, const float& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const bool& v)
+ void bind_param(size_t index, const bool& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const DATE_STRUCT& v)
+ void bind_param(size_t index, const DATE_STRUCT& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_DATE, SQL_DATE,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_DATE, SQL_DATE,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const TIME_STRUCT& v)
+ void bind_param(size_t index, const TIME_STRUCT& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_TIME, SQL_TIME,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_TIME, SQL_TIME,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const TIMESTAMP_STRUCT& v)
+ void bind_param(size_t index, const TIMESTAMP_STRUCT& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const SQLGUID& v)
+ void bind_param(size_t index, const SQLGUID& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_GUID, SQL_GUID,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_GUID, SQL_GUID,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const SQL_NUMERIC_STRUCT& v)
+ void bind_param(size_t index, const SQL_NUMERIC_STRUCT& v)
{
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_NUMERIC, SQL_NUMERIC,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_NUMERIC, SQL_NUMERIC,
0, 0, (SQLPOINTER)&v, 0, NULL));
}
- void bind_param(SQLUSMALLINT index, const char* v, size_t n=SQL_NTS, SQLULEN size=0)
+ void bind_param(size_t index, const char* v, size_t n=SQL_NTS, SQLULEN size=0)
{
m_params[index].m_indicator=n;
if(size==0) size=strlen(v);
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
size, 0, (SQLPOINTER)v, 0, &m_params[index].m_indicator));
}
- void bind_param(SQLUSMALLINT index, const wchar_t* v, size_t n=SQL_NTS, SQLULEN size=0)
+ void bind_param(size_t index, const wchar_t* v, size_t n=SQL_NTS, SQLULEN size=0)
{
m_params[index].m_indicator=n;
if(size==0) size=wcslen(v);
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR,
size, 0, (SQLPOINTER)v, 0, &m_params[index].m_indicator));
}
- void bind_param(SQLUSMALLINT index, const std::string& v)
+ void bind_param(size_t index, const std::string& v)
{
bind_param(index, v.data(), v.size(), v.size());
}
- void bind_param(SQLUSMALLINT index, const std::wstring& v)
+ void bind_param(size_t index, const std::wstring& v)
{
bind_param(index, v.data(), v.size(), v.size());
}
- void bind_param(SQLUSMALLINT index, const const_blob_data& v)
+ void bind_param(size_t index, const const_blob_data& v)
{
m_params[index].m_indicator=v.size;
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY,
v.size, 0, (SQLPOINTER)v.data, 0, &m_params[index].m_indicator));
}
- void bind_param(SQLUSMALLINT index, std::istream& s)
+ void bind_param(size_t index, std::istream& s)
{
if(m_blob_buffer==NULL)
m_blob_buffer=malloc(blob_buffer_size);
m_params[index].m_data=m_blob_buffer;
m_params[index].m_size=blob_buffer_size;
m_params[index].m_indicator=SQL_LEN_DATA_AT_EXEC(m_params[index].m_size);
- verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
INT_MAX, 0, &m_params[index], 0, &m_params[index].m_indicator));
m_params[index].m_after_fetch=[this, &s](const param_data& p) {
SQLLEN readed=SQL_NULL_DATA;
@@ -344,86 +342,86 @@
};
}
- void bind_param(SQLUSMALLINT index, const blob_writer& param)
+ void bind_param(size_t index, const blob_writer& param)
{
m_params[index].m_data = nullptr;
m_params[index].m_size = blob_buffer_size;
m_params[index].m_indicator = SQL_LEN_DATA_AT_EXEC(m_params[index].m_size);
- verify_error(SQLBindParameter(m_handle, index + 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
+ verify_error(SQLBindParameter(m_handle, static_cast<SQLSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
INT_MAX, 0, &m_params[index], 0, &m_params[index].m_indicator));
m_params[index].m_after_fetch = [this, index, ¶m](const param_data& b) {
blobbuf buf;
- buf.open(this, index, std::ios::out);
+ buf.open(this, static_cast<SQLSMALLINT>(index), std::ios::out);
std::ostream s(&buf);
param(s);
};
}
- void bind_field(SQLUSMALLINT index, bool&& v)
+ void bind_field(size_t index, bool&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_BIT, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_BIT, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, int8_t&& v)
+ void bind_field(size_t index, int8_t&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_STINYINT, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_STINYINT, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, uint8_t&& v)
+ void bind_field(size_t index, uint8_t&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_UTINYINT, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_UTINYINT, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, int16_t&& v)
+ void bind_field(size_t index, int16_t&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_SSHORT, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_SSHORT, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, uint16_t&& v)
+ void bind_field(size_t index, uint16_t&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_USHORT, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_USHORT, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, int32_t&& v)
+ void bind_field(size_t index, int32_t&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_SLONG, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_SLONG, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, uint32_t&& v)
+ void bind_field(size_t index, uint32_t&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_ULONG, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_ULONG, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, int64_t&& v)
+ void bind_field(size_t index, int64_t&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_SBIGINT, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_SBIGINT, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, uint64_t&& v)
+ void bind_field(size_t index, uint64_t&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_UBIGINT, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_UBIGINT, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, float&& v)
+ void bind_field(size_t index, float&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_FLOAT, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_FLOAT, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, double&& v)
+ void bind_field(size_t index, double&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_DOUBLE, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_DOUBLE, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, DATE_STRUCT&& v)
+ void bind_field(size_t index, DATE_STRUCT&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_TYPE_DATE, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_TYPE_DATE, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, TIME_STRUCT&& v)
+ void bind_field(size_t index, TIME_STRUCT&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_TYPE_TIME, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_TYPE_TIME, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, TIMESTAMP_STRUCT&& v)
+ void bind_field(size_t index, TIMESTAMP_STRUCT&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_TYPE_TIMESTAMP, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_TYPE_TIMESTAMP, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, SQLGUID&& v)
+ void bind_field(size_t index, SQLGUID&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_GUID, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_GUID, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, SQL_NUMERIC_STRUCT&& v)
+ void bind_field(size_t index, SQL_NUMERIC_STRUCT&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_NUMERIC, &v, 0, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_NUMERIC, &v, 0, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, char* v, size_t n)
+ void bind_field(size_t index, char* v, size_t n)
{
m_params[index].m_data=v;
m_params[index].m_size=n;
@@ -436,9 +434,9 @@
text[p.m_indicator]='\0';
}
};
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_CHAR, v, n, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_CHAR, v, n, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, wchar_t* v, size_t n)
+ void bind_field(size_t index, wchar_t* v, size_t n)
{
m_params[index].m_data=v;
m_params[index].m_size=n;
@@ -451,13 +449,13 @@
text[p.m_indicator]='\0';
}
};
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_WCHAR, v, n, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_WCHAR, v, n, &m_params[index].m_indicator));
}
template<typename T>
- void bind_field(SQLUSMALLINT index, qtl::bind_string_helper<T>&& v)
+ void bind_field(size_t index, qtl::bind_string_helper<T>&& v)
{
SQLLEN length=0;
- verify_error(SQLColAttribute(m_handle, index+1, SQL_DESC_LENGTH, NULL, 0, NULL, &length));
+ verify_error(SQLColAttribute(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_DESC_LENGTH, NULL, 0, NULL, &length));
typename qtl::bind_string_helper<T>::char_type* data=v.alloc(length);
bind_field(index, data, length+1);
m_params[index].m_after_fetch=[v](const param_data& p) mutable {
@@ -468,49 +466,49 @@
};
}
template<size_t N>
- void bind_field(SQLUSMALLINT index, std::array<char, N>&& value)
+ void bind_field(size_t index, std::array<char, N>&& value)
{
bind_field(index, value.data(), value.size());
}
template<size_t N>
- void bind_field(SQLUSMALLINT index, std::array<wchar_t, N>&& value)
+ void bind_field(size_t index, std::array<wchar_t, N>&& value)
{
bind_field(index, value.data(), value.size());
}
- void bind_field(SQLUSMALLINT index, qtl::blob_data&& v)
+ void bind_field(size_t index, qtl::blob_data&& v)
{
- verify_error(SQLBindCol(m_handle, index+1, SQL_C_BINARY, v.data, v.size, &m_params[index].m_indicator));
+ verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_BINARY, v.data, v.size, &m_params[index].m_indicator));
}
- void bind_field(SQLUSMALLINT index, std::ostream&& v)
+ void bind_field(size_t index, std::ostream&& v)
{
if(m_blob_buffer==NULL)
m_blob_buffer=malloc(blob_buffer_size);
m_params[index].m_data=m_blob_buffer;
m_params[index].m_size=blob_buffer_size;
m_params[index].m_after_fetch=[this, index, &v](const param_data& p) {
- SQLRETURN ret=SQLGetData(m_handle, index+1, SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN*>(&p.m_indicator));
+ SQLRETURN ret=SQLGetData(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN*>(&p.m_indicator));
while(ret!=SQL_NO_DATA)
{
size_t n = (p.m_indicator > blob_buffer_size) || (p.m_indicator == SQL_NO_TOTAL) ?
blob_buffer_size : p.m_indicator;
verify_error(ret);
v.write((const char*)p.m_data, n);
- ret=SQLGetData(m_handle, index+1, SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN*>(&p.m_indicator));
+ ret=SQLGetData(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN*>(&p.m_indicator));
}
};
}
- void bind_field(SQLUSMALLINT index, blobbuf&& value)
+ void bind_field(size_t index, blobbuf&& value)
{
m_params[index].m_data = nullptr;
m_params[index].m_size = 0;
m_params[index].m_after_fetch = [this, index, &value](const param_data& p) {
- value.open(this, index, std::ios::in);
+ value.open(this, static_cast<SQLSMALLINT>(index), std::ios::in);
};
}
template<typename Type>
- void bind_field(SQLUSMALLINT index, indicator<Type>&& value)
+ void bind_field(size_t index, indicator<Type>&& value)
{
qtl::bind_field(*this, index, value.data);
param_data& param=m_params[index];
@@ -536,7 +534,7 @@
#ifdef _QTL_ENABLE_CPP17
template<typename Type>
- void bind_field(SQLUSMALLINT index, std::optional<Type>&& value)
+ void bind_field(size_t index, std::optional<Type>&& value)
{
qtl::bind_field(*this, index, *value);
param_data& param = m_params[index];
@@ -548,7 +546,7 @@
};
}
- void bind_field(SQLUSMALLINT index, std::any&& value)
+ void bind_field(size_t index, std::any&& value)
{
SQLLEN type = 0, isUnsigned=SQL_FALSE;
verify_error(SQLColAttribute(m_handle, index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &type));
@@ -672,90 +670,6 @@
}
#endif // C++17
- template<typename Types>
- void execute(const Types& params)
- {
- SQLSMALLINT count=0;
- verify_error(SQLNumParams(m_handle, &count));
- if(count>0)
- {
- m_params.resize(count);
- qtl::bind_params(*this, params);
- }
-
- SQLRETURN ret=SQLExecute(m_handle);
- verify_error(ret);
- if(ret==SQL_NEED_DATA)
- {
- SQLPOINTER token;
- size_t i=0;
- ret=SQLParamData(m_handle, &token);
- verify_error(ret);
- while(ret==SQL_NEED_DATA)
- {
- while(i!=count)
- {
- if(&m_params[i]==token)
- {
- if(m_params[i].m_after_fetch)
- m_params[i].m_after_fetch(m_params[i]);
- break;
- }
- ++i;
- }
- ret=SQLParamData(m_handle, &token);
- verify_error(ret);
- }
- }
- }
-
- template<typename Types>
- bool fetch(Types&& values)
- {
- if(!m_binded_cols)
- {
- SQLSMALLINT count=0;
- verify_error(SQLNumResultCols(m_handle, &count));
- if(count>0)
- {
- m_params.resize(count);
- qtl::bind_record(*this, std::forward<Types>(values));
- }
- m_binded_cols=true;
- }
- return fetch();
- }
-
- bool fetch()
- {
- SQLRETURN ret=SQLFetch(m_handle);
- if(ret==SQL_SUCCESS || ret==SQL_SUCCESS_WITH_INFO)
- {
- for(const param_data& data : m_params)
- {
- if(data.m_after_fetch)
- data.m_after_fetch(data);
- }
- return true;
- }
- verify_error(ret);
- return false;
- }
-
- bool next_result()
- {
- SQLRETURN ret;
- SQLSMALLINT count=0;
- m_binded_cols=false;
- do
- {
- ret=SQLMoreResults(m_handle);
- if(ret==SQL_ERROR || ret==SQL_INVALID_HANDLE)
- verify_error(ret);
- verify_error(SQLNumResultCols(m_handle, &count));
- }while(count==0);
- return ret==SQL_SUCCESS || ret==SQL_SUCCESS_WITH_INFO;
- }
SQLLEN affetced_rows()
{
@@ -784,6 +698,11 @@
return -1;
}
+ void reset()
+ {
+ verify_error(SQLFreeStmt(m_handle, SQL_RESET_PARAMS));
+ }
+
/*
ODBC do not support this function, but you can use query to instead it:
For MS SQL Server: SELECT @@IDENTITY;
@@ -796,12 +715,7 @@
return 0;
}*/
- void reset()
- {
- verify_error(SQLFreeStmt(m_handle, SQL_RESET_PARAMS));
- }
-
-private:
+protected:
struct param_data
{
SQLPOINTER m_data;
@@ -814,6 +728,119 @@
SQLPOINTER m_blob_buffer;
std::vector<param_data> m_params;
bool m_binded_cols;
+};
+
+class statement : public base_statement
+{
+public:
+ statement() = default;
+ explicit statement(base_database& db) : base_statement(db) { }
+ statement(statement&& src) : base_statement(std::move(src)) { }
+ statement& operator=(statement&& src)
+ {
+ base_statement::operator =(std::move(src));
+ return *this;
+ }
+ ~statement()
+ {
+ close();
+ }
+
+ void open(const char* query_text, size_t text_length = SQL_NTS)
+ {
+ reset();
+ verify_error(SQLPrepareA(m_handle, (SQLCHAR*)query_text, text_length));
+ }
+ void open(const std::string& query_text)
+ {
+ open(query_text.data(), query_text.size());
+ }
+
+
+ template<typename Types>
+ void execute(const Types& params)
+ {
+ SQLSMALLINT count = 0;
+ verify_error(SQLNumParams(m_handle, &count));
+ if (count > 0)
+ {
+ m_params.resize(count);
+ qtl::bind_params(*this, params);
+ }
+
+ SQLRETURN ret = SQLExecute(m_handle);
+ verify_error(ret);
+ if (ret == SQL_NEED_DATA)
+ {
+ SQLPOINTER token;
+ size_t i = 0;
+ ret = SQLParamData(m_handle, &token);
+ verify_error(ret);
+ while (ret == SQL_NEED_DATA)
+ {
+ while (i != count)
+ {
+ if (&m_params[i] == token)
+ {
+ if (m_params[i].m_after_fetch)
+ m_params[i].m_after_fetch(m_params[i]);
+ break;
+ }
+ ++i;
+ }
+ ret = SQLParamData(m_handle, &token);
+ verify_error(ret);
+ }
+ }
+ }
+
+ template<typename Types>
+ bool fetch(Types&& values)
+ {
+ if (!m_binded_cols)
+ {
+ SQLSMALLINT count = 0;
+ verify_error(SQLNumResultCols(m_handle, &count));
+ if (count > 0)
+ {
+ m_params.resize(count);
+ qtl::bind_record(*this, std::forward<Types>(values));
+ }
+ m_binded_cols = true;
+ }
+ return fetch();
+ }
+
+ bool fetch()
+ {
+ SQLRETURN ret = SQLFetch(m_handle);
+ if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
+ {
+ for (const param_data& data : m_params)
+ {
+ if (data.m_after_fetch)
+ data.m_after_fetch(data);
+ }
+ return true;
+ }
+ verify_error(ret);
+ return false;
+ }
+
+ bool next_result()
+ {
+ SQLRETURN ret;
+ SQLSMALLINT count = 0;
+ m_binded_cols = false;
+ do
+ {
+ ret = SQLMoreResults(m_handle);
+ if (ret == SQL_ERROR || ret == SQL_INVALID_HANDLE)
+ verify_error(ret);
+ verify_error(SQLNumResultCols(m_handle, &count));
+ } while (count == 0);
+ return ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO;
+ }
};
struct connection_parameter
@@ -838,29 +865,30 @@
};
typedef std::vector<connection_parameter> connection_parameters;
-class database : public object<SQL_HANDLE_DBC>, public qtl::base_database<database, statement>
+class base_database : public object<SQL_HANDLE_DBC>
{
public:
typedef odbc::error exception_type;
- explicit database(environment& env) : object(env.handle()), m_opened(false)
+ explicit base_database(environment& env) : object(env.handle()), m_opened(false)
{
}
- database(database&& src)
- : object(std::forward<database>(src)), m_connection(std::forward<std::string>(src.m_connection))
+ base_database(const base_database&) = delete;
+ base_database(base_database&& src)
+ : object(std::forward<base_database>(src)), m_connection(std::forward<std::string>(src.m_connection))
{
m_opened=src.m_opened;
src.m_opened=false;
}
- ~database()
+ ~base_database()
{
close();
}
- database& operator=(database&& src)
+ base_database& operator=(base_database&& src)
{
if(this!=&src)
{
- object::operator =(std::forward<database>(src));
+ object::operator =(std::forward<base_database>(src));
m_opened=src.m_opened;
src.m_opened=false;
m_connection=std::forward<std::string>(src.m_connection);
@@ -868,76 +896,6 @@
return *this;
}
- void open(const char* server_name, size_t server_name_length,
- const char* user_name, size_t user_name_length, const char* password, size_t password_length)
- {
- if (m_opened) close();
- verify_error(SQLConnectA(m_handle, (SQLCHAR*)server_name, server_name_length, (SQLCHAR*)user_name, user_name_length, (SQLCHAR*)password, password_length));
- m_opened = true;
- }
- void open(const char* server_name, const char* user_name, const char* password)
- {
- verify_error(SQLConnectA(m_handle, (SQLCHAR*)server_name, SQL_NTS, (SQLCHAR*)user_name, SQL_NTS, (SQLCHAR*)password, SQL_NTS));
- }
- void open(const std::string& server_name, const std::string& user_name, const std::string& password)
- {
- open(server_name.data(), server_name.size(), user_name.data(), user_name.size(), password.data(), password.size());
- }
- void open(const char* input_text, size_t text_length = SQL_NTS, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
- {
- m_connection.resize(512);
- SQLSMALLINT out_len;
- if (m_opened) close();
- verify_error(SQLDriverConnectA(m_handle, hwnd, (SQLCHAR*)input_text, (SQLSMALLINT)text_length,
- (SQLCHAR*)m_connection.data(), (SQLSMALLINT)m_connection.size(), &out_len, driver_completion));
- m_connection.resize(out_len);
- m_opened = true;
- }
- void open(const std::string& input_text, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
- {
- open(input_text.data(), input_text.size(), driver_completion, hwnd);
- }
- void open(SQLHWND hwnd, SQLSMALLINT driver_completion = SQL_DRIVER_COMPLETE)
- {
- open("", SQL_NTS, driver_completion, hwnd);
- }
- // InputPred like:
- // bool input_parameters(connection_parameters& parameters);
- template<typename InputPred>
- void open(const char* connection_text, size_t text_length, InputPred&& pred)
- {
- SQLSMALLINT length = 0;
- SQLINTEGER ret = SQL_SUCCESS;
- std::string input_text;
- if (m_opened) close();
- if (text_length == SQL_NTS)
- input_text = connection_text;
- else
- input_text.assign(connection_text, text_length);
- m_connection.resize(1024);
- while ((ret = SQLBrowseConnectA(m_handle, (SQLCHAR*)input_text.data(), SQL_NTS,
- (SQLCHAR*)m_connection.data(), m_connection.size(), &length)) == SQL_NEED_DATA)
- {
- connection_parameters parameters;
- parse_browse_string(m_connection.data(), length, parameters);
- if (!pred(parameters))
- throw error(SQL_NEED_DATA, "User cancel operation.");
- input_text = create_connection_text(parameters);
- }
- if (ret == SQL_ERROR || ret == SQL_SUCCESS_WITH_INFO)
- verify_error(ret);
- m_opened = true;
- }
- template<typename InputPred>
- void open(const char* connection_text, InputPred&& pred)
- {
- open(connection_text, SQL_NTS, std::forward<InputPred>(pred));
- }
- template<typename InputPred>
- void open(const std::string& connection_text, InputPred&& pred)
- {
- open(connection_text.data(), connection_text.size(), std::forward<InputPred>(pred));
- }
void close()
{
if(m_opened)
@@ -947,46 +905,83 @@
}
}
+ void set_attribute(SQLINTEGER attr, SQLPOINTER value)
+ {
+ verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_POINTER));
+ }
void set_attribute(SQLINTEGER attr, SQLINTEGER value)
{
- verify_error(SQLSetConnectAttr(m_handle, attr, (SQLPOINTER)value, 0));
+ verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_INTEGER));
+ }
+ void set_attribute(SQLINTEGER attr, SQLUINTEGER value)
+ {
+ verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_UINTEGER));
+ }
+ void set_attribute(SQLINTEGER attr, SQLSMALLINT value)
+ {
+ verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_SMALLINT));
+ }
+ void set_attribute(SQLINTEGER attr, SQLUSMALLINT value)
+ {
+ verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_USMALLINT));
}
void set_attribute(SQLINTEGER attr, const char* value)
{
- verify_error(SQLSetConnectAttr(m_handle, attr, (SQLPOINTER)value, SQL_NTS));
+ verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_NTS));
}
void set_attribute(SQLINTEGER attr, const std::string& value)
{
- verify_error(SQLSetConnectAttr(m_handle, attr, (SQLPOINTER)value.data(), value.size()));
+ verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value.data(), value.size()));
+ }
+ void set_attribute(SQLINTEGER attr, const void* value, SQLINTEGER length)
+ {
+ verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_LEN_BINARY_ATTR(length)));
+ }
+ void get_attribute(SQLINTEGER attr, SQLPOINTER& value) const
+ {
+ verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_POINTER, 0));
}
void get_attribute(SQLINTEGER attr, SQLINTEGER& value) const
{
- verify_error(SQLGetConnectAttr(m_handle, attr, &value, sizeof(SQLINTEGER), 0));
+ value = 0;
+ verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_INTEGER, 0));
+ }
+ void get_attribute(SQLINTEGER attr, SQLUINTEGER& value) const
+ {
+ value = 0;
+ verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_UINTEGER, 0));
+ }
+ void get_attribute(SQLINTEGER attr, SQLSMALLINT& value) const
+ {
+ value = 0;
+ verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_SMALLINT, 0));
+ }
+ void get_attribute(SQLINTEGER attr, SQLUSMALLINT& value) const
+ {
+ value = 0;
+ verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_USMALLINT, 0));
+ }
+ void get_attribute(SQLINTEGER attr, void* buffer, SQLINTEGER length) const
+ {
+ verify_error(SQLGetConnectAttrA(m_handle, attr, buffer, SQL_LEN_BINARY_ATTR(length), 0));
+ }
+ void get_attribute(SQLINTEGER attr, char* buffer, size_t length) const
+ {
+ verify_error(SQLGetConnectAttrA(m_handle, attr, buffer, length, 0));
+ }
+ void get_attribute(SQLINTEGER attr, std::string& value) const
+ {
+ SQLINTEGER length = 0;
+ verify_error(SQLGetConnectAttrA(m_handle, attr, NULL, 0, &length));
+ value.resize(length);
+ if(length>0)
+ verify_error(SQLGetConnectAttrA(m_handle, attr, (SQLPOINTER)value.data(), length, 0));
}
void get_info(SQLSMALLINT info, std::string& value, SQLSMALLINT size=SQL_MAX_OPTION_STRING_LENGTH) const
{
value.resize(size);
verify_error(SQLGetInfo(m_handle, info, (SQLPOINTER)value.data(), size, &size));
value.resize(size);
- }
-
- void auto_commit(bool on)
- {
- set_attribute(SQL_ATTR_AUTOCOMMIT, on ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
- }
- void begin_transaction()
- {
- auto_commit(false);
- }
- void rollback()
- {
- verify_error(SQLEndTran(handler_type, m_handle, SQL_ROLLBACK));
- auto_commit(true);
- }
- void commit()
- {
- verify_error(SQLEndTran(handler_type, m_handle, SQL_COMMIT));
- auto_commit(true);
}
std::string dbms_name() const
@@ -1018,11 +1013,95 @@
return m_connection;
}
- bool is_alive()
+protected:
+ bool m_opened;
+ std::string m_connection;
+
+ void parse_browse_string(const char* output_text, size_t text_length, connection_parameters& parameters);
+ std::string create_connection_text(const connection_parameters& parameters);
+};
+
+class database : public base_database, public qtl::base_database<database, statement>
+{
+public:
+ database() = default;
+ explicit database(environment& env) : odbc::base_database(env)
{
- SQLINTEGER value;
- get_attribute(SQL_ATTR_CONNECTION_DEAD, value);
- return value == SQL_CD_FALSE;
+ }
+ database(database&& src) : odbc::base_database(std::move(src))
+ {
+ }
+
+ void open(const char* server_name, size_t server_name_length,
+ const char* user_name, size_t user_name_length, const char* password, size_t password_length)
+ {
+ if (m_opened) close();
+ verify_error(SQLConnectA(m_handle, (SQLCHAR*)server_name, static_cast<SQLSMALLINT>(server_name_length),
+ (SQLCHAR*)user_name, static_cast<SQLSMALLINT>(user_name_length), (SQLCHAR*)password, static_cast<SQLSMALLINT>(password_length)));
+ m_opened = true;
+ }
+ void open(const char* server_name, const char* user_name, const char* password)
+ {
+ verify_error(SQLConnectA(m_handle, (SQLCHAR*)server_name, SQL_NTS, (SQLCHAR*)user_name, SQL_NTS, (SQLCHAR*)password, SQL_NTS));
+ }
+ void open(const std::string& server_name, const std::string& user_name, const std::string& password)
+ {
+ open(server_name.data(), server_name.size(), user_name.data(), user_name.size(), password.data(), password.size());
+ }
+ void open(const char* input_text, size_t text_length = SQL_NTS, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
+ {
+ m_connection.resize(512);
+ SQLSMALLINT out_len=0;
+ if (m_opened) close();
+ verify_error(SQLDriverConnectA(m_handle, hwnd, (SQLCHAR*)input_text, (SQLSMALLINT)text_length,
+ (SQLCHAR*)m_connection.data(), (SQLSMALLINT)m_connection.size(), &out_len, driver_completion));
+ m_connection.resize(out_len);
+ m_opened = true;
+ }
+ void open(const std::string& input_text, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
+ {
+ open(input_text.data(), input_text.size(), driver_completion, hwnd);
+ }
+ void open(SQLHWND hwnd, SQLSMALLINT driver_completion = SQL_DRIVER_COMPLETE)
+ {
+ open("", SQL_NTS, driver_completion, hwnd);
+ }
+ // InputPred like:
+ // bool input_parameters(connection_parameters& parameters);
+ template<typename InputPred>
+ void open(const char* connection_text, size_t text_length, InputPred&& pred)
+ {
+ SQLSMALLINT length = 0;
+ SQLRETURN ret = SQL_SUCCESS;
+ std::string input_text;
+ if (m_opened) close();
+ if (text_length == SQL_NTS)
+ input_text = connection_text;
+ else
+ input_text.assign(connection_text, text_length);
+ m_connection.resize(1024);
+ while ((ret = SQLBrowseConnectA(m_handle, (SQLCHAR*)input_text.data(), SQL_NTS,
+ (SQLCHAR*)m_connection.data(), m_connection.size(), &length)) == SQL_NEED_DATA)
+ {
+ connection_parameters parameters;
+ parse_browse_string(m_connection.data(), length, parameters);
+ if (!pred(parameters))
+ throw error(SQL_NEED_DATA, "User cancel operation.");
+ input_text = create_connection_text(parameters);
+ }
+ if (ret == SQL_ERROR || ret == SQL_SUCCESS_WITH_INFO)
+ verify_error(ret);
+ m_opened = true;
+ }
+ template<typename InputPred>
+ void open(const char* connection_text, InputPred&& pred)
+ {
+ open(connection_text, SQL_NTS, std::forward<InputPred>(pred));
+ }
+ template<typename InputPred>
+ void open(const std::string& connection_text, InputPred&& pred)
+ {
+ open(connection_text.data(), connection_text.size(), std::forward<InputPred>(pred));
}
statement open_command(const char* query_text, size_t text_length)
@@ -1040,11 +1119,11 @@
return open_command(query_text.data(), query_text.length());
}
- void simple_execute(const char* query_text, size_t text_length=SQL_NTS)
+ void simple_execute(const char* query_text, size_t text_length = SQL_NTS)
{
statement command(*this);
- SQLRETURN ret=SQLExecDirectA(command.handle(), (SQLCHAR*)query_text, text_length);
- if(ret!=SQL_SUCCESS && ret!=SQL_NO_DATA)
+ SQLRETURN ret = SQLExecDirectA(command.handle(), (SQLCHAR*)query_text, text_length);
+ if (ret != SQL_SUCCESS && ret != SQL_NO_DATA)
verify_error(ret);
}
void simple_execute(const std::string& query_text)
@@ -1052,12 +1131,38 @@
simple_execute(query_text.data(), query_text.size());
}
-private:
- bool m_opened;
- std::string m_connection;
+ void auto_commit(bool on)
+ {
+ set_attribute(SQL_ATTR_AUTOCOMMIT, on ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
+ }
+ void begin_transaction()
+ {
+ auto_commit(false);
+ }
+ void rollback()
+ {
+ verify_error(SQLEndTran(handler_type, m_handle, SQL_ROLLBACK));
+ auto_commit(true);
+ }
+ void commit()
+ {
+ verify_error(SQLEndTran(handler_type, m_handle, SQL_COMMIT));
+ auto_commit(true);
+ }
- void parse_browse_string(const char* output_text, size_t text_length, connection_parameters& parameters);
- std::string create_connection_text(const connection_parameters& parameters);
+ bool is_alive()
+ {
+ SQLINTEGER value;
+ get_attribute(SQL_ATTR_CONNECTION_DEAD, value);
+ return value == SQL_CD_FALSE;
+ }
+
+#ifdef QTL_ODBC_ENABLE_ASYNC_MODE
+
+ //async_connection async_mode();
+
+#endif //ODBC 3.80
+
};
struct date : public SQL_DATE_STRUCT
@@ -1145,6 +1250,490 @@
}
};
+#ifdef QTL_ODBC_ENABLE_ASYNC_MODE
+
+class async_connection;
+
+inline bool is_still_executing(SQLINTEGER code)
+{
+ return code == SQL_STILL_EXECUTING;
+}
+
+class async_statement : public base_statement
+{
+public:
+ explicit async_statement(async_connection& db);
+ async_statement(async_statement&& src)
+ : base_statement(std::move(src))
+ {
+ m_hCompleteEvent = src.m_hCompleteEvent;
+ m_event=src.m_event;
+ m_nQueryTimeout = src.m_nQueryTimeout;
+ src.m_hCompleteEvent = nullptr;
+ src.m_event = nullptr;
+ }
+ async_statement& operator=(async_statement&& src)
+ {
+ if (this != &src)
+ {
+ base_statement::operator =(std::move(src));
+ m_hCompleteEvent = src.m_hCompleteEvent;
+ m_event = src.m_event;
+ m_nQueryTimeout = src.m_nQueryTimeout;
+ src.m_hCompleteEvent = nullptr;
+ src.m_event = nullptr;
+ }
+ return *this;
+ }
+ ~async_statement()
+ {
+ close();
+ }
+
+ /*
+ Handler defiens as:
+ void handler(const qtl::odbc::error& e);
+ */
+ template<typename Handler>
+ void open(Handler&& handler, const char *query_text, size_t text_length = 0)
+ {
+ if (text_length == 0) text_length = strlen(query_text);
+ reset();
+ SQLRETURN ret = SQLPrepareA(m_handle, (SQLCHAR*)query_text, text_length);
+ async_wait(ret, std::forward<Handler>(handler));
+ }
+
+ /*
+ ExecuteHandler defiens as:
+ void handler(const qtl::odbc::error& e, uint64_t affected);
+ */
+ template<typename Types, typename Handler>
+ void execute(const Types& params, Handler&& handler)
+ {
+ SQLSMALLINT count = 0;
+ SQLRETURN ret = SQLNumParams(m_handle, &count);
+ if (!SQL_SUCCEEDED(ret))
+ {
+ handler(error(*this, ret), 0);
+ return;
+ }
+ if (count > 0)
+ {
+ m_params.resize(count);
+ qtl::bind_params(*this, params);
+ }
+
+ if (m_nQueryTimeout == 0)
+ m_nQueryTimeout = query_timeout();
+ ret = SQLExecute(m_handle);
+ async_wait(ret, [this, count, handler](const error& e) mutable {
+ SQLINTEGER ret = e.code();
+ if (ret == SQL_NEED_DATA)
+ async_param_data(0, count, std::forward<Handler>(handler));
+ else if(ret>=0)
+ handler(error(*this, ret), affetced_rows());
+ else
+ handler(error(*this, ret), 0);
+ });
+ }
+
+ template<typename Types, typename RowHandler, typename FinishHandler>
+ void fetch(Types&& values, RowHandler&& row_handler, FinishHandler&& finish_handler)
+ {
+ if (!m_binded_cols)
+ {
+ SQLSMALLINT count = 0;
+ SQLRETURN ret = SQLNumResultCols(m_handle, &count);
+ if(!SQL_SUCCEEDED(ret))
+ {
+ finish_handler(error(*this, ret));
+ return;
+ }
+ if (count > 0)
+ {
+ m_params.resize(count);
+ qtl::bind_record(*this, std::forward<Types>(values));
+ }
+ m_binded_cols = true;
+ }
+ return fetch(std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler));
+ }
+
+ template<typename RowHandler, typename FinishHandler>
+ void fetch(RowHandler&& row_handler, FinishHandler&& finish_handler)
+ {
+ SQLRETURN ret = SQLFetch(m_handle);
+ async_wait(ret, [this, row_handler, finish_handler](const error& e) mutable {
+ SQLINTEGER ret = e.code();
+ if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
+ {
+ for (const param_data& data : m_params)
+ {
+ if (data.m_after_fetch)
+ data.m_after_fetch(data);
+ }
+ if (row_handler())
+ fetch(row_handler, finish_handler);
+ else
+ finish_handler(error());
+ }
+ else
+ {
+ if (e.code() == SQL_NO_DATA)
+ finish_handler(error());
+ else
+ finish_handler(e);
+ }
+ });
+ }
+
+ template<typename Handler>
+ void next_result(Handler handler)
+ {
+ SQLRETURN ret;
+ m_binded_cols = false;
+ ret = SQLMoreResults(m_handle);
+ async_wait(ret, [this, handler](const error& e) mutable {
+ SQLINTEGER ret=e.code();
+ SQLSMALLINT count = 0;
+ if (ret == SQL_ERROR || ret == SQL_INVALID_HANDLE)
+ {
+ reset();
+ handler(error(*this, ret));
+ return;
+ }
+ ret = SQLNumResultCols(m_handle, &count);
+ if (ret == SQL_ERROR || ret == SQL_INVALID_HANDLE)
+ {
+ reset();
+ handler(error(*this, ret));
+ return;
+ }
+ if (count > 0)
+ handler(error());
+ else
+ next_result(handler);
+ });
+ }
+
+ HANDLE event_handle() const { return m_hCompleteEvent; }
+
+ void close()
+ {
+ close_event();
+ base_statement::close();
+ }
+
+ template<typename CloseHandler>
+ void close(CloseHandler&& handler)
+ {
+ if (m_handle)
+ {
+ close_event();
+ SQLRETURN ret = SQLFreeHandle(handler_type, m_handle);
+ if(SQL_SUCCEEDED(ret))
+ m_handle = SQL_NULL_HANDLE;
+ handler(error(*this, ret));
+ }
+ else
+ {
+ handler(error());
+ }
+ }
+
+private:
+
+ void close_event()
+ {
+ if (m_hCompleteEvent)
+ {
+ if (m_event)
+ m_event->remove();
+ verify_error(SQLCancelHandle(handler_type, m_handle));
+ verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_STMT_EVENT, NULL, SQL_IS_POINTER));
+ verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_OFF, SQL_IS_INTEGER));
+ CloseHandle(m_hCompleteEvent);
+ m_hCompleteEvent = NULL;
+ }
+ }
+
+ template<typename Handler>
+ void async_wait(SQLINTEGER ret, Handler&& handler) NOEXCEPT
+ {
+ if(is_still_executing(ret))
+ {
+ m_event->set_io_handler(0, m_nQueryTimeout,
+ [this, handler](int flags) mutable {
+ RETCODE code;
+ SQLCompleteAsync(SQL_HANDLE_STMT, m_handle, &code);
+ if (SQL_SUCCEEDED(code))
+ {
+ handler(odbc::error());
+ }
+ else
+ {
+ SetEvent(m_hCompleteEvent);
+ handler(odbc::error(*this, code));
+ }
+ });
+ }
+ else
+ {
+ handler(error(*this, ret));
+ }
+ }
+
+ template<typename Handler>
+ void async_param_data(SQLSMALLINT index, SQLSMALLINT count, Handler&& handler) NOEXCEPT
+ {
+ SQLPOINTER token;
+ SQLRETURN ret = SQLParamData(m_handle, &token);
+ async_wait(ret, [this, index, count, token, handler](const error& e) mutable {
+ SQLINTEGER ret = e.code();
+ if (ret == SQL_NEED_DATA)
+ {
+ while (index != count)
+ {
+ if (&m_params[index] == token)
+ {
+ if (m_params[index].m_after_fetch)
+ m_params[index].m_after_fetch(m_params[index]);
+ break;
+ }
+ ++index;
+ }
+ async_param_data(index, count, handler);
+ }
+ else
+ {
+ handler(error(*this, ret), affetced_rows());
+ }
+ });
+ }
+
+ int query_timeout() const
+ {
+ SQLULEN timeout = 0;
+ verify_error(SQLGetStmtAttr(m_handle, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)&timeout, NULL, NULL));
+ return timeout;
+ }
+
+private:
+ HANDLE m_hCompleteEvent;
+ qtl::event* m_event;
+ SQLULEN m_nQueryTimeout;
+};
+
+class async_connection : public base_database, public qtl::async_connection<async_connection, async_statement>
+{
+public:
+ async_connection(environment& env) : base_database(env)
+ {
+ set_attribute(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, SQL_ASYNC_DBC_ENABLE_ON);
+ m_hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (m_hCompleteEvent == NULL)
+ {
+ throw std::system_error(std::error_code(GetLastError(), std::system_category()));
+ }
+ set_attribute(SQL_ATTR_ASYNC_DBC_EVENT, m_hCompleteEvent);
+ }
+ async_connection(async_connection&& src) :
+ base_database(std::move(src)),
+ qtl::async_connection<async_connection, async_statement>(std::move(src)),
+ m_BindFunc(std::move(src.m_BindFunc))
+ {
+ m_hCompleteEvent = src.m_hCompleteEvent;
+ src.m_hCompleteEvent = nullptr;
+ }
+ ~async_connection()
+ {
+ if (m_hCompleteEvent)
+ {
+ verify_error(SQLCancelHandle(handler_type, m_handle));
+ set_attribute(SQL_ATTR_ASYNC_DBC_EVENT, (SQLPOINTER)NULL);
+ }
+ if (m_opened)
+ {
+ set_attribute(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, SQL_ASYNC_DBC_ENABLE_OFF);
+ verify_error(SQLDisconnect(m_handle));
+ m_opened = false;
+ }
+ if (m_hCompleteEvent)
+ {
+ CloseHandle(m_hCompleteEvent);
+ }
+ }
+
+ /*
+ OpenHandler defines as:
+ void handler(const qtl::odbc::error& e) NOEXCEPT;
+ */
+ template<class EventLoop, typename OpenHandler>
+ void open(EventLoop& ev, OpenHandler&& handler, const char* server_name, size_t server_name_length,
+ const char* user_name, size_t user_name_length, const char* password, size_t password_length)
+ {
+ if (m_opened) close();
+ SQLRETURN err = SQLConnectA(m_handle, (SQLCHAR*)server_name, static_cast<SQLSMALLINT>(server_name_length),
+ (SQLCHAR*)user_name, static_cast<SQLSMALLINT>(user_name_length), (SQLCHAR*)password, static_cast<SQLSMALLINT>(password_length));
+ async_wait_connect(err, ev, std::forward<OpenHandler>(handler));
+ }
+ template<class EventLoop, typename OpenHandler>
+ void open(EventLoop& ev, OpenHandler&& handler, const char* server_name, const char* user_name, const char* password)
+ {
+ if (m_opened) close();
+ SQLRETURN err = SQLConnectA(m_handle, (SQLCHAR*)server_name, SQL_NTS, (SQLCHAR*)user_name, SQL_NTS, (SQLCHAR*)password, SQL_NTS);
+ async_wait_connect(err, ev, std::forward<OpenHandler>(handler));
+ }
+ template<class EventLoop, typename OpenHandler>
+ void open(EventLoop& ev, OpenHandler&& handler, const std::string& server_name, const std::string& user_name, const std::string& password)
+ {
+ open(ev, std::forward<OpenHandler>(handler), server_name.data(), server_name.size(), user_name.data(), user_name.size(), password.data(), password.size());
+ }
+ template<class EventLoop, typename OpenHandler>
+ void open(EventLoop& ev, OpenHandler&& handler, const char* input_text, size_t text_length = SQL_NTS, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
+ {
+ m_connection.resize(512);
+ SQLSMALLINT out_len=0;
+ SQLRETURN err = SQLDriverConnectA(m_handle, hwnd, (SQLCHAR*)input_text, (SQLSMALLINT)text_length,
+ (SQLCHAR*)m_connection.data(), (SQLSMALLINT)m_connection.size(), &out_len, driver_completion);
+ m_connection.resize(out_len);
+ async_wait_connect(err, ev, std::forward<OpenHandler>(handler));
+ }
+ template<class EventLoop, typename OpenHandler>
+ void open(EventLoop& ev, OpenHandler&& handler, const std::string& input_text, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL)
+ {
+ open(ev, std::forward<OpenHandler>(handler), input_text.data(), input_text.size(), driver_completion, hwnd);
+ }
+ template<class EventLoop, typename OpenHandler>
+ void open(EventLoop& ev, OpenHandler&& handler, SQLHWND hwnd, SQLSMALLINT driver_completion = SQL_DRIVER_COMPLETE)
+ {
+ open(ev, std::forward<OpenHandler>(handler), "", SQL_NTS, driver_completion, hwnd);
+ }
+
+ /*
+ CloseHandler defines as:
+ void handler(const qtl::odbc::error& e) NOEXCEPT;
+ */
+ template<typename CloseHandler >
+ void close(CloseHandler&& handler) NOEXCEPT
+ {
+ SQLRETURN ret = SQLDisconnect(m_handle);
+ m_opened = false;
+ async_wait(ret, [this, handler](const error& e) {
+ if (!e) m_opened = false;
+ handler(e);
+ });
+ }
+
+ /*
+ ExecuteHandler defines as:
+ void handler(const qtl::odbc::error& e) NOEXCEPT;
+ */
+ template<typename ExecuteHandler>
+ void simple_execute(ExecuteHandler&& handler, const char* query_text, size_t text_length = SQL_NTS) NOEXCEPT
+ {
+ statement command(*this);
+ SQLRETURN ret = SQLExecDirectA(command.handle(), (SQLCHAR*)query_text, static_cast<SQLINTEGER>(text_length));
+ async_wait(ret, std::forward<ExecuteHandler>(handler));
+ }
+ template<typename ExecuteHandler>
+ void simple_execute(ExecuteHandler&& handler, const std::string& query_text)
+ {
+ simple_execute(std::forward<ExecuteHandler>(handler), query_text.data(), query_text.size());
+ }
+
+ template<typename Handler>
+ void open_command(const char* query_text, size_t text_length, Handler&& handler)
+ {
+ std::shared_ptr<async_statement> stmt = std::make_shared<async_statement>(*this);
+ stmt->open([stmt, handler](const odbc::error& e) mutable {
+ handler(e, stmt);
+ }, query_text, text_length);
+ }
+
+ HANDLE event_handle() const { return m_hCompleteEvent; }
+
+ qtl::event* rebind(HANDLE hEvent)
+ {
+ return m_BindFunc(hEvent);
+ }
+
+private:
+
+ template<typename Handler>
+ void async_wait(SQLRETURN ret, Handler&& handler) NOEXCEPT
+ {
+ if (is_still_executing(ret))
+ {
+ m_event_handler->set_io_handler(0, connect_timeout(),
+ [this, handler](int flags) mutable {
+ RETCODE code;
+ SQLCompleteAsync(SQL_HANDLE_DBC, m_handle, &code);
+ if (SQL_SUCCEEDED(code))
+ {
+ handler(odbc::error());
+ }
+ else
+ {
+ SetEvent(m_hCompleteEvent);
+ handler(odbc::error(*this, code));
+ }
+ });
+ }
+ else
+ {
+ handler(odbc::error(*this, ret));
+ }
+ }
+
+ template<typename EventLoop, typename Handler>
+ void async_wait_connect(SQLRETURN err, EventLoop& ev, Handler&& handler)
+ {
+ bind(ev);
+ m_BindFunc = [&ev](HANDLE hEvent) {
+ return ev.add(hEvent);
+ };
+ if(is_still_executing(err))
+ {
+ async_wait(err, [this, handler](const error& e) mutable {
+ if (!e) m_opened = true;
+ handler(e);
+ });
+ }
+ else
+ {
+ handler(odbc::error(*this, err));
+ }
+ }
+
+ int connect_timeout() const
+ {
+ int timeout=0;
+ verify_error(SQLGetConnectAttr(m_handle, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)&timeout, 0, NULL));
+ return timeout;
+ }
+
+private:
+ HANDLE m_hCompleteEvent;
+ std::function<qtl::event*(HANDLE)> m_BindFunc;
+};
+
+inline async_statement::async_statement(async_connection& db)
+ : base_statement(static_cast<base_database&>(db))
+{
+ verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER));
+ m_hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (m_hCompleteEvent == NULL)
+ {
+ throw std::system_error(std::error_code(GetLastError(), std::system_category()));
+ }
+ verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_STMT_EVENT, m_hCompleteEvent, SQL_IS_POINTER));
+ m_event = db.rebind(this->m_hCompleteEvent);
+ m_nQueryTimeout = query_timeout();
+}
+
+#endif //ODBC 3.80
+
typedef qtl::transaction<database> transaction;
template<typename Record>
@@ -1172,10 +1761,13 @@
SQLCHAR message[SQL_MAX_MESSAGE_LENGTH];
SQLCHAR state[SQL_SQLSTATE_SIZE+1];
std::ostringstream oss;
- while(SQLGetDiagRecA(object<Type>::handler_type, h.handle(), ++i, state, &err,
- message, SQL_MAX_MESSAGE_LENGTH, NULL)==SQL_SUCCESS)
+ SQLRETURN ret = SQLGetDiagRecA(object<Type>::handler_type, h.handle(), ++i, state, &err,
+ message, SQL_MAX_MESSAGE_LENGTH, NULL);
+ while(ret==SQL_SUCCESS)
{
oss<<"["<<state<<"] ("<<err<<") "<<message<<std::endl;
+ ret = SQLGetDiagRecA(object<Type>::handler_type, h.handle(), ++i, state, &err,
+ message, SQL_MAX_MESSAGE_LENGTH, NULL);
}
m_errmsg=oss.str();
}
@@ -1185,7 +1777,7 @@
}
}
-inline void database::parse_browse_string(const char* output_text, size_t text_length, connection_parameters& parameters)
+inline void base_database::parse_browse_string(const char* output_text, size_t text_length, connection_parameters& parameters)
{
enum { part_name, part_prompt, part_list, part_value };
const char* sp=output_text;
@@ -1242,7 +1834,7 @@
parameters.emplace_back(parameter);
}
-inline std::string database::create_connection_text(const connection_parameters& parameters)
+inline std::string base_database::create_connection_text(const connection_parameters& parameters)
{
std::ostringstream oss;
for(auto& parameter : parameters)
@@ -1253,7 +1845,7 @@
return oss.str();
}
-inline statement::statement(database& db)
+inline base_statement::base_statement(base_database& db)
: object(db.handle()), m_blob_buffer(NULL), m_binded_cols(false)
{
}
@@ -1269,7 +1861,7 @@
{
public:
explicit database(odbc::environment& env) : odbc::database(env) { }
- database(database&& src) : odbc::database(std::forward<database>(src)) { }
+ database(database&& src) : odbc::database(std::move(src)) { }
void open(const char* server, const char* db=NULL, const char* user=NULL, const char* password=NULL)
{
@@ -1288,6 +1880,35 @@
}
};
+#ifdef QTL_ODBC_ENABLE_ASYNC_MODE
+
+class async_connection : public odbc::async_connection
+{
+public:
+ explicit async_connection(odbc::environment& env) : odbc::async_connection(env) { }
+ async_connection(async_connection&& src) : odbc::async_connection(std::move(src)) { }
+
+ template<typename EventLoop, typename OpenHandler>
+ void open(EventLoop& ev, const OpenHandler& handler, const char* server, const char* db = NULL, const char* user = NULL, const char* password = NULL)
+ {
+ std::ostringstream oss;
+ oss << "DRIVER={ODBC Driver 11 for SQL Server};SERVER=" << server << ";";
+ if (user == NULL)
+ oss << "UID=;PWD=;Trusted_Connection=yes;";
+ else
+ {
+ oss << "UID=" << user << ";PWD=";
+ if (password) oss << password;
+ oss << ";Trusted_Connection=no;";
+ }
+ oss << "DATABASE=" << db;
+ odbc::async_connection::open(ev, handler, oss.str());
+ }
+
+};
+
+#endif
+
} //mssql
namespace msaccess
diff --git a/include/qtl_odbc_pool.hpp b/include/qtl_odbc_pool.hpp
index 4014cb1..eb5eb5e 100644
--- a/include/qtl_odbc_pool.hpp
+++ b/include/qtl_odbc_pool.hpp
@@ -39,6 +39,37 @@
environment m_env;
};
+#ifdef QTL_ODBC_ENABLE_ASYNC_MODE
+
+template<typename EventLoop>
+class async_pool : public qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection>
+{
+ typedef qtl::async_pool<async_pool<EventLoop>, EventLoop, async_connection> base_class;
+public:
+ async_pool(EventLoop& ev) : base_class(ev) { }
+ virtual ~async_pool() { }
+
+ template<typename Handler>
+ void new_connection(EventLoop& ev, Handler&& handler) throw()
+ {
+ async_connection* db = new async_connection(env);
+ db->open(ev, [this, handler, db](const mysql::error& e) mutable {
+ if (e)
+ {
+ delete db;
+ db = nullptr;
+ }
+ handler(e, db);
+ }, m_connection);
+ }
+
+protected:
+ std::string m_connection;
+ environment m_env;
+};
+
+#endif //QTL_ODBC_ENABLE_ASYNC_MODE
+
}
}
diff --git a/test/TestMariaDB.cpp b/test/TestMariaDB.cpp
index c78eda6..cf3a4f6 100644
--- a/test/TestMariaDB.cpp
+++ b/test/TestMariaDB.cpp
@@ -82,6 +82,7 @@
if (e)
{
LogError(e);
+ Close();
}
else
{
@@ -93,20 +94,18 @@
void MariaDBTest::Execute()
{
- _connection.simple_query("select * from test", 0, [](MYSQL_ROW row, int field_count) {
- for (int i = 0; i != field_count; i++)
- printf("%s\t", row[i]);
- printf("\n");
- return true;
- }, [this](const error& e, size_t row_count) {
+ _connection.execute([this](const error& e, uint64_t affected) mutable {
if (e)
+ {
LogError(e);
+ Close();
+ }
else
{
- printf("Total %lu rows.\n", row_count);
+ printf("Insert %llu records ok.\n", affected);
Query();
}
- });
+ }, "insert into test(name, createtime, company) values(?, now(), ?)", 0, std::make_tuple("test name", "test company"));
}
void MariaDBTest::Query()
@@ -124,9 +123,14 @@
}, [this](const error& e) {
printf("query has completed.\n");
if (e)
+ {
LogError(e);
+ Close();
+ }
else
+ {
MultiQuery();
+ }
});
}
@@ -136,8 +140,7 @@
[this](const error& e) {
if (e)
LogError(e);
- else
- Close();
+ Close();
}, [](uint32_t i, const std::string& str) {
printf("0=\"%d\", 'hello world'=\"%s\"\n", i, str.data());
diff --git a/test/vs/AsyncMssql/AsyncMssql.cpp b/test/vs/AsyncMssql/AsyncMssql.cpp
new file mode 100644
index 0000000..4c5979b
--- /dev/null
+++ b/test/vs/AsyncMssql/AsyncMssql.cpp
@@ -0,0 +1,154 @@
+#include "pch.h"
+#include <iostream>
+#include <array>
+#include "../../../include/qtl_odbc.hpp"
+
+class SimpleEventLoop
+{
+public:
+ SimpleEventLoop()
+ {
+ m_objs.reserve(MAXIMUM_WAIT_OBJECTS);
+ m_nExpired = INFINITE;
+ m_hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ m_objs.push_back(m_hStopEvent);
+ }
+
+ qtl::event* add(HANDLE hEvent)
+ {
+ if (m_objs.size() < m_objs.capacity())
+ {
+ m_objs.push_back(hEvent);
+
+ std::unique_ptr<event_item> item(new event_item(*this, hEvent));
+ event_item* result = item.get();
+ m_items.push_back(std::move(item));
+ return result;
+ }
+ return nullptr;
+ }
+
+ void run()
+ {
+ while (m_objs.size()>1)
+ {
+ DWORD dwSize = m_objs.size();
+ DWORD dwTimeout = m_nExpired - GetTickCount();
+ DWORD dwObject = WaitForMultipleObjects(dwSize, m_objs.data(), FALSE, dwTimeout);
+ if (dwObject >= WAIT_OBJECT_0 && WAIT_OBJECT_0 + dwSize)
+ {
+ DWORD index = dwObject - WAIT_OBJECT_0;
+ HANDLE hEvent = m_objs[index];
+ if (hEvent == m_hStopEvent)
+ break;
+
+ m_objs.erase(m_objs.begin() + index);
+ fire(hEvent, qtl::event::ef_read | qtl::event::ef_write);
+ }
+ else if (dwObject == WAIT_TIMEOUT)
+ {
+ fire_timeout();
+ }
+ }
+ }
+
+ void stop()
+ {
+ SetEvent(m_hStopEvent);
+ }
+
+private:
+ class event_item : public qtl::event
+ {
+ public:
+ event_item(SimpleEventLoop& ev, HANDLE hEvent) : m_ev(&ev), m_hEvent(hEvent), m_busying(false)
+ {
+ }
+ virtual void set_io_handler(int flags, long timeout, std::function<void(int)>&& handler) override
+ {
+ m_handler = handler;
+ m_busying = true;
+ m_nExpired = GetTickCount() + timeout*1000;
+ m_ev->m_nExpired = std::min<DWORD>(m_ev->m_nExpired, m_nExpired);
+ }
+ virtual void remove() override
+ {
+ if(!m_busying)
+ m_ev->remove(this);
+ }
+ virtual bool is_busying() override
+ {
+ return m_busying;
+ }
+
+ HANDLE m_hEvent;
+ std::function<void(int)> m_handler;
+ SimpleEventLoop* m_ev;
+ DWORD m_nExpired;
+ bool m_busying;
+ };
+
+ HANDLE m_hStopEvent;
+ std::vector<HANDLE> m_objs;
+ std::vector<std::unique_ptr<event_item>> m_items;
+ DWORD m_nExpired;
+
+ void remove_object(event_item* item)
+ {
+ auto it = std::find_if(m_objs.begin(), m_objs.end(), [item](HANDLE hEvent) {
+ return item->m_hEvent == hEvent;
+ });
+ if (it != m_objs.end())
+ {
+ m_objs.erase(it);
+ }
+ }
+
+ void remove(event_item* item)
+ {
+ remove_object(item);
+ {
+ auto it = std::find_if(m_items.begin(), m_items.end(), [item](const std::unique_ptr<event_item>& v) {
+ return item == v.get();
+ });
+ if (it != m_items.end())
+ {
+ m_items.erase(it);
+ }
+ }
+ }
+
+ void fire(HANDLE hEvent, int flags)
+ {
+ auto it = std::find_if(m_items.begin(), m_items.end(), [hEvent](const std::unique_ptr<event_item>& v) {
+ return hEvent == v->m_hEvent;
+ });
+ if (it != m_items.end())
+ {
+ (*it)->m_handler(flags);
+ }
+ }
+
+ void fire_timeout()
+ {
+ for (auto& item : m_items)
+ {
+ if (item->m_nExpired < m_nExpired)
+ {
+ item->m_handler(qtl::event::ef_timeout);
+ remove_object(item.get());
+ }
+ else
+ {
+ m_nExpired = std::min<DWORD>(item->m_nExpired, m_nExpired);
+ }
+ }
+ }
+};
+
+int main()
+{
+ SimpleEventLoop ev;
+ ev.run();
+ return 0;
+}
diff --git a/test/vs/AsyncMssql/AsyncMssql.sln b/test/vs/AsyncMssql/AsyncMssql.sln
new file mode 100644
index 0000000..71ce299
--- /dev/null
+++ b/test/vs/AsyncMssql/AsyncMssql.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.902
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AsyncMssql", "AsyncMssql.vcxproj", "{50BABD35-ACC8-41BB-A13C-A9F433B495A7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {50BABD35-ACC8-41BB-A13C-A9F433B495A7}.Debug|x64.ActiveCfg = Debug|x64
+ {50BABD35-ACC8-41BB-A13C-A9F433B495A7}.Debug|x64.Build.0 = Debug|x64
+ {50BABD35-ACC8-41BB-A13C-A9F433B495A7}.Debug|x86.ActiveCfg = Debug|Win32
+ {50BABD35-ACC8-41BB-A13C-A9F433B495A7}.Debug|x86.Build.0 = Debug|Win32
+ {50BABD35-ACC8-41BB-A13C-A9F433B495A7}.Release|x64.ActiveCfg = Release|x64
+ {50BABD35-ACC8-41BB-A13C-A9F433B495A7}.Release|x64.Build.0 = Release|x64
+ {50BABD35-ACC8-41BB-A13C-A9F433B495A7}.Release|x86.ActiveCfg = Release|Win32
+ {50BABD35-ACC8-41BB-A13C-A9F433B495A7}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E5EB88D9-CF54-4729-A4F0-2A7BB7711845}
+ EndGlobalSection
+EndGlobal
diff --git a/test/vs/AsyncMssql/AsyncMssql.vcxproj b/test/vs/AsyncMssql/AsyncMssql.vcxproj
new file mode 100644
index 0000000..6cbf303
--- /dev/null
+++ b/test/vs/AsyncMssql/AsyncMssql.vcxproj
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{50BABD35-ACC8-41BB-A13C-A9F433B495A7}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>AsyncMssql</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <MinimumRequiredVersion>6.2</MinimumRequiredVersion>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="pch.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\AsyncMssql.cpp" />
+ <ClCompile Include="pch.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/test/vs/AsyncMssql/AsyncMssql.vcxproj.filters b/test/vs/AsyncMssql/AsyncMssql.vcxproj.filters
new file mode 100644
index 0000000..737f300
--- /dev/null
+++ b/test/vs/AsyncMssql/AsyncMssql.vcxproj.filters
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="源文件">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="头文件">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="资源文件">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="pch.h">
+ <Filter>头文件</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="pch.cpp">
+ <Filter>源文件</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\AsyncMssql.cpp">
+ <Filter>源文件</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/test/vs/test_sqlite/test_sqlite.vcproj b/test/vs/test_sqlite/test_sqlite.vcproj
index 52f0fde..784ca35 100644
--- a/test/vs/test_sqlite/test_sqlite.vcproj
+++ b/test/vs/test_sqlite/test_sqlite.vcproj
@@ -61,7 +61,7 @@
/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="cpptest.lib"
+ AdditionalDependencies="sqlite.lib cpptest.lib"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
@@ -136,7 +136,7 @@
/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="cpptest.lib"
+ AdditionalDependencies="sqlite.lib cpptest.lib"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
@@ -199,26 +199,6 @@
</FileConfiguration>
</File>
<File
- RelativePath="..\..\..\sqlite3.c"
- >
- <FileConfiguration
- Name="Debug|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- UsePrecompiledHeader="0"
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- UsePrecompiledHeader="0"
- />
- </FileConfiguration>
- </File>
- <File
RelativePath="..\..\stdafx.cpp"
>
<FileConfiguration
@@ -248,10 +228,6 @@
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
- <File
- RelativePath="..\..\md5.h"
- >
- </File>
<File
RelativePath="..\..\stdafx.h"
>
--
Gitblit v1.9.3