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.

---
 include/qtl_odbc.hpp | 1275 +++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 948 insertions(+), 327 deletions(-)

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, &param](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

--
Gitblit v1.9.3