| | |
| | | |
| | | #if (ODBCVER >= 0x0380) && (_WIN32_WINNT >= 0x0602) |
| | | #define QTL_ODBC_ENABLE_ASYNC_MODE 1 |
| | | #endif //ODBC 3.80 && Windows |
| | | |
| | | #endif // ODBC 3.80 && Windows |
| | | |
| | | #include "qtl_common.hpp" |
| | | #include "qtl_async.hpp" |
| | |
| | | namespace qtl |
| | | { |
| | | |
| | | namespace odbc |
| | | { |
| | | namespace odbc |
| | | { |
| | | |
| | | template<SQLSMALLINT> class object; |
| | | class base_database; |
| | | template <SQLSMALLINT> |
| | | class object; |
| | | class base_database; |
| | | |
| | | class error : public std::exception |
| | | { |
| | | public: |
| | | error() : m_errno(SQL_SUCCESS) { } |
| | | template<SQLSMALLINT Type> |
| | | error(const object<Type>& h, SQLINTEGER code); |
| | | error(SQLINTEGER code, const char* msg) : m_errno(code), m_errmsg(msg) { } |
| | | SQLINTEGER code() const { return m_errno; } |
| | | operator bool() const { return m_errno!=SQL_SUCCESS && m_errno!=SQL_SUCCESS_WITH_INFO; } |
| | | virtual const char* what() const throw() override { return m_errmsg.data(); } |
| | | private: |
| | | SQLINTEGER m_errno; |
| | | std::string m_errmsg; |
| | | }; |
| | | |
| | | template<SQLSMALLINT Type> |
| | | class object |
| | | { |
| | | public: |
| | | enum { handler_type=Type }; |
| | | object() : m_handle(SQL_NULL_HANDLE) { }; |
| | | object(const object&) = delete; |
| | | object(object&& src) : m_handle(src.m_handle) |
| | | { |
| | | src.m_handle=SQL_NULL_HANDLE; |
| | | } |
| | | explicit object(SQLHANDLE parent) |
| | | { |
| | | verify_error(SQLAllocHandle(handler_type, parent, &m_handle)); |
| | | } |
| | | ~object() |
| | | { |
| | | close(); |
| | | } |
| | | object& operator=(const object&) = delete; |
| | | object& operator=(object&& src) |
| | | { |
| | | if(this!=&src) |
| | | class error : public std::exception |
| | | { |
| | | close(); |
| | | m_handle=src.m_handle; |
| | | src.m_handle=NULL; |
| | | } |
| | | return *this; |
| | | } |
| | | SQLHANDLE handle() const { return m_handle; } |
| | | public: |
| | | error() : m_errno(SQL_SUCCESS) {} |
| | | template <SQLSMALLINT Type> |
| | | error(const object<Type> &h, SQLINTEGER code); |
| | | error(SQLINTEGER code, const char *msg) : m_errno(code), m_errmsg(msg) {} |
| | | SQLINTEGER code() const { return m_errno; } |
| | | operator bool() const { return m_errno != SQL_SUCCESS && m_errno != SQL_SUCCESS_WITH_INFO; } |
| | | virtual const char *what() const throw() override { return m_errmsg.data(); } |
| | | |
| | | void close() |
| | | { |
| | | if(m_handle) |
| | | private: |
| | | SQLINTEGER m_errno; |
| | | std::string m_errmsg; |
| | | }; |
| | | |
| | | template <SQLSMALLINT Type> |
| | | class object |
| | | { |
| | | verify_error(SQLFreeHandle(handler_type, m_handle)); |
| | | m_handle=SQL_NULL_HANDLE; |
| | | } |
| | | } |
| | | |
| | | void verify_error(SQLINTEGER code) const |
| | | { |
| | | if (code < 0) |
| | | throw odbc::error(*this, code); |
| | | } |
| | | |
| | | protected: |
| | | SQLHANDLE m_handle; |
| | | }; |
| | | |
| | | class blobbuf : public qtl::blobbuf |
| | | { |
| | | public: |
| | | blobbuf() : m_stmt(nullptr), m_field(0) |
| | | { |
| | | } |
| | | blobbuf(const blobbuf&) = default; |
| | | blobbuf& operator=(const blobbuf&) = default; |
| | | virtual ~blobbuf() { overflow(); } |
| | | |
| | | void open(object<SQL_HANDLE_STMT>* stmt, SQLSMALLINT field, std::ios_base::openmode mode) |
| | | { |
| | | if (m_stmt && m_field) |
| | | { |
| | | overflow(); |
| | | } |
| | | |
| | | assert(stmt != SQL_NULL_HANDLE); |
| | | m_stmt = stmt; |
| | | m_field = field; |
| | | m_size = INTMAX_MAX; |
| | | init_buffer(mode); |
| | | } |
| | | |
| | | private: |
| | | object<SQL_HANDLE_STMT>* m_stmt; |
| | | SQLSMALLINT m_field; |
| | | |
| | | protected: |
| | | virtual bool read_blob(char* buffer, off_type& count, pos_type position) override |
| | | { |
| | | SQLLEN indicator=0; |
| | | SQLRETURN ret = SQLGetData(m_stmt->handle(), m_field + 1, SQL_C_BINARY, buffer, static_cast<SQLINTEGER>(count), const_cast<SQLLEN*>(&indicator)); |
| | | if (ret != SQL_NO_DATA) |
| | | { |
| | | count = (indicator > count) || (indicator == SQL_NO_TOTAL) ? |
| | | count : indicator; |
| | | m_stmt->verify_error(ret); |
| | | return true; |
| | | } |
| | | else return false; |
| | | } |
| | | |
| | | virtual void write_blob(const char* buffer, size_t count) override |
| | | { |
| | | m_stmt->verify_error(SQLPutData(m_stmt->handle(), (SQLPOINTER)buffer, count)); |
| | | } |
| | | }; |
| | | |
| | | class environment final : public object<SQL_HANDLE_ENV> |
| | | { |
| | | public: |
| | | environment() : object(SQL_NULL_HANDLE) |
| | | { |
| | | #if ODBCVER >= 0x0380 |
| | | const SQLPOINTER version = (SQLPOINTER)SQL_OV_ODBC3_80; |
| | | #else |
| | | const SQLPOINTER version = (SQLPOINTER)SQL_OV_ODBC3; |
| | | #endif |
| | | verify_error(SQLSetEnvAttr(m_handle, SQL_ATTR_ODBC_VERSION, version, SQL_IS_INTEGER)); |
| | | } |
| | | environment(environment&& src) : object(std::forward<environment>(src)) { } |
| | | |
| | | int32_t version() const |
| | | { |
| | | int32_t ver = 0; |
| | | verify_error(SQLGetEnvAttr(m_handle, SQL_ATTR_ODBC_VERSION, &ver, sizeof(DWORD), NULL)); |
| | | return ver; |
| | | } |
| | | }; |
| | | |
| | | class base_statement : public object<SQL_HANDLE_STMT> |
| | | { |
| | | public: |
| | | explicit base_statement(base_database& db); |
| | | base_statement(base_statement&& src) |
| | | : object(std::forward<base_statement>(src)), m_params(std::forward<std::vector<param_data>>(src.m_params)) |
| | | { |
| | | m_binded_cols=src.m_binded_cols; |
| | | src.m_binded_cols=false; |
| | | m_blob_buffer=src.m_blob_buffer; |
| | | src.m_blob_buffer=NULL; |
| | | } |
| | | ~base_statement() |
| | | { |
| | | if(m_blob_buffer) |
| | | free(m_blob_buffer); |
| | | } |
| | | base_statement& operator=(base_statement&& src) |
| | | { |
| | | if(this!=&src) |
| | | { |
| | | object::operator =(std::forward<base_statement>(src)); |
| | | m_params=std::forward<std::vector<param_data>>(src.m_params); |
| | | m_binded_cols=src.m_binded_cols; |
| | | src.m_binded_cols=false; |
| | | m_blob_buffer=src.m_blob_buffer; |
| | | src.m_blob_buffer=NULL; |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | void bind_param(size_t index, const std::nullptr_t&) |
| | | { |
| | | m_params[index].m_indicator=SQL_NULL_DATA; |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), |
| | | SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_DEFAULT, 0, 0, NULL, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_param(size_t index, const qtl::null&) |
| | | { |
| | | bind_param(index, nullptr); |
| | | } |
| | | void bind_param(size_t index, const int8_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_STINYINT, SQL_TINYINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const uint8_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_UTINYINT, SQL_TINYINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const int16_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_SMALLINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const uint16_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_USHORT, SQL_SMALLINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const int32_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const uint32_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const int64_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const uint64_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const double& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const float& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const bool& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const DATE_STRUCT& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_DATE, SQL_DATE, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const TIME_STRUCT& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_TIME, SQL_TIME, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const TIMESTAMP_STRUCT& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const SQLGUID& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_GUID, SQL_GUID, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const SQL_NUMERIC_STRUCT& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_NUMERIC, SQL_NUMERIC, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const char* v, size_t n=SQL_NTS, SQLULEN size=0) |
| | | { |
| | | m_params[index].m_indicator=n; |
| | | if(size==0) size=strlen(v); |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, |
| | | size, 0, (SQLPOINTER)v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_param(size_t index, const wchar_t* v, size_t n=SQL_NTS, SQLULEN size=0) |
| | | { |
| | | m_params[index].m_indicator=n; |
| | | if(size==0) size=wcslen(v); |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR, |
| | | size, 0, (SQLPOINTER)v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_param(size_t index, const std::string& v) |
| | | { |
| | | bind_param(index, v.data(), v.size(), v.size()); |
| | | } |
| | | void bind_param(size_t index, const std::wstring& v) |
| | | { |
| | | bind_param(index, v.data(), v.size(), v.size()); |
| | | } |
| | | void bind_param(size_t index, const const_blob_data& v) |
| | | { |
| | | m_params[index].m_indicator=v.size; |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, |
| | | v.size, 0, (SQLPOINTER)v.data, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_param(size_t index, std::istream& s) |
| | | { |
| | | if(m_blob_buffer==NULL) |
| | | m_blob_buffer=malloc(blob_buffer_size); |
| | | m_params[index].m_data=m_blob_buffer; |
| | | m_params[index].m_size=blob_buffer_size; |
| | | m_params[index].m_indicator=SQL_LEN_DATA_AT_EXEC(m_params[index].m_size); |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, |
| | | INT_MAX, 0, &m_params[index], 0, &m_params[index].m_indicator)); |
| | | m_params[index].m_after_fetch=[this, &s](const param_data& p) { |
| | | SQLLEN readed=SQL_NULL_DATA; |
| | | while(!s.eof() && !s.fail()) |
| | | public: |
| | | enum |
| | | { |
| | | s.read((char*)p.m_data, p.m_size); |
| | | readed=(unsigned long)s.gcount(); |
| | | if(readed>0) |
| | | handler_type = Type |
| | | }; |
| | | object() : m_handle(SQL_NULL_HANDLE){}; |
| | | object(const object &) = delete; |
| | | object(object &&src) : m_handle(src.m_handle) |
| | | { |
| | | src.m_handle = SQL_NULL_HANDLE; |
| | | } |
| | | explicit object(SQLHANDLE parent) |
| | | { |
| | | verify_error(SQLAllocHandle(handler_type, parent, &m_handle)); |
| | | } |
| | | ~object() |
| | | { |
| | | close(); |
| | | } |
| | | object &operator=(const object &) = delete; |
| | | object &operator=(object &&src) |
| | | { |
| | | if (this != &src) |
| | | { |
| | | verify_error(SQLPutData(m_handle, p.m_data, readed)); |
| | | close(); |
| | | m_handle = src.m_handle; |
| | | src.m_handle = NULL; |
| | | } |
| | | return *this; |
| | | } |
| | | SQLHANDLE handle() const { return m_handle; } |
| | | |
| | | void close() |
| | | { |
| | | if (m_handle) |
| | | { |
| | | verify_error(SQLFreeHandle(handler_type, m_handle)); |
| | | m_handle = SQL_NULL_HANDLE; |
| | | } |
| | | } |
| | | }; |
| | | } |
| | | |
| | | void bind_param(size_t index, const blob_writer& param) |
| | | { |
| | | m_params[index].m_data = nullptr; |
| | | m_params[index].m_size = blob_buffer_size; |
| | | m_params[index].m_indicator = SQL_LEN_DATA_AT_EXEC(m_params[index].m_size); |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, |
| | | INT_MAX, 0, &m_params[index], 0, &m_params[index].m_indicator)); |
| | | m_params[index].m_after_fetch = [this, index, ¶m](const param_data& b) { |
| | | blobbuf buf; |
| | | buf.open(this, static_cast<SQLSMALLINT>(index), std::ios::out); |
| | | std::ostream s(&buf); |
| | | param(s); |
| | | }; |
| | | } |
| | | void verify_error(SQLINTEGER code) const |
| | | { |
| | | if (code < 0) |
| | | throw odbc::error(*this, code); |
| | | } |
| | | |
| | | void bind_field(size_t index, bool&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_BIT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, int8_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_STINYINT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, uint8_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_UTINYINT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, int16_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_SSHORT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, uint16_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_USHORT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, int32_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_SLONG, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, uint32_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_ULONG, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, int64_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_SBIGINT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, uint64_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_UBIGINT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, float&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_FLOAT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, double&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_DOUBLE, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, DATE_STRUCT&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_TYPE_DATE, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, TIME_STRUCT&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_TYPE_TIME, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, TIMESTAMP_STRUCT&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_TYPE_TIMESTAMP, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, SQLGUID&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_GUID, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, SQL_NUMERIC_STRUCT&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_NUMERIC, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, char* v, size_t n) |
| | | { |
| | | m_params[index].m_data=v; |
| | | m_params[index].m_size=n; |
| | | m_params[index].m_after_fetch=[](const param_data& p) { |
| | | if(p.m_indicator==SQL_NULL_DATA) |
| | | memset(p.m_data, 0, p.m_size*sizeof(char)); |
| | | else |
| | | { |
| | | char* text=reinterpret_cast<char*>(p.m_data); |
| | | text[p.m_indicator]='\0'; |
| | | } |
| | | protected: |
| | | SQLHANDLE m_handle; |
| | | }; |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_CHAR, v, n, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, wchar_t* v, size_t n) |
| | | { |
| | | m_params[index].m_data=v; |
| | | m_params[index].m_size=n; |
| | | m_params[index].m_after_fetch=[](const param_data& p) { |
| | | if(p.m_indicator==SQL_NULL_DATA) |
| | | memset(p.m_data, 0, p.m_size*sizeof(wchar_t)); |
| | | else |
| | | { |
| | | wchar_t* text=reinterpret_cast<wchar_t*>(p.m_data); |
| | | text[p.m_indicator]='\0'; |
| | | } |
| | | }; |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_WCHAR, v, n, &m_params[index].m_indicator)); |
| | | } |
| | | template<typename T> |
| | | void bind_field(size_t index, qtl::bind_string_helper<T>&& v) |
| | | { |
| | | SQLLEN length=0; |
| | | verify_error(SQLColAttribute(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_DESC_LENGTH, NULL, 0, NULL, &length)); |
| | | typename qtl::bind_string_helper<T>::char_type* data=v.alloc(length); |
| | | bind_field(index, data, length+1); |
| | | m_params[index].m_after_fetch=[v](const param_data& p) mutable { |
| | | if(p.m_indicator==SQL_NULL_DATA) |
| | | v.clear(); |
| | | else |
| | | v.truncate(p.m_indicator); |
| | | }; |
| | | } |
| | | template<size_t N> |
| | | void bind_field(size_t index, std::array<char, N>&& value) |
| | | { |
| | | bind_field(index, value.data(), value.size()); |
| | | } |
| | | template<size_t N> |
| | | void bind_field(size_t index, std::array<wchar_t, N>&& value) |
| | | { |
| | | bind_field(index, value.data(), value.size()); |
| | | } |
| | | void bind_field(size_t index, qtl::blob_data&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_BINARY, v.data, v.size, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, std::ostream&& v) |
| | | { |
| | | if(m_blob_buffer==NULL) |
| | | m_blob_buffer=malloc(blob_buffer_size); |
| | | m_params[index].m_data=m_blob_buffer; |
| | | m_params[index].m_size=blob_buffer_size; |
| | | m_params[index].m_after_fetch=[this, index, &v](const param_data& p) { |
| | | SQLRETURN ret=SQLGetData(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN*>(&p.m_indicator)); |
| | | while(ret!=SQL_NO_DATA) |
| | | { |
| | | size_t n = (p.m_indicator > blob_buffer_size) || (p.m_indicator == SQL_NO_TOTAL) ? |
| | | blob_buffer_size : p.m_indicator; |
| | | verify_error(ret); |
| | | v.write((const char*)p.m_data, n); |
| | | ret=SQLGetData(m_handle, static_cast<SQLUSMALLINT>(index+1), SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN*>(&p.m_indicator)); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | void bind_field(size_t index, blobbuf&& value) |
| | | { |
| | | m_params[index].m_data = nullptr; |
| | | m_params[index].m_size = 0; |
| | | m_params[index].m_after_fetch = [this, index, &value](const param_data& p) { |
| | | value.open(this, static_cast<SQLSMALLINT>(index), std::ios::in); |
| | | }; |
| | | } |
| | | class blobbuf : public qtl::blobbuf |
| | | { |
| | | public: |
| | | blobbuf() : m_stmt(nullptr), m_field(0) |
| | | { |
| | | } |
| | | blobbuf(const blobbuf &) = default; |
| | | blobbuf &operator=(const blobbuf &) = default; |
| | | virtual ~blobbuf() { overflow(); } |
| | | |
| | | template<typename Type> |
| | | void bind_field(size_t index, indicator<Type>&& value) |
| | | { |
| | | qtl::bind_field(*this, index, value.data); |
| | | param_data& param=m_params[index]; |
| | | auto fetch_fun=param.m_after_fetch; |
| | | param.m_after_fetch=[fetch_fun, &value](const param_data& p) { |
| | | value.is_truncated=false; |
| | | if(p.m_indicator==SQL_NULL_DATA) |
| | | void open(object<SQL_HANDLE_STMT> *stmt, SQLSMALLINT field, std::ios_base::openmode mode) |
| | | { |
| | | value.is_null=true; |
| | | value.length=0; |
| | | if (m_stmt && m_field) |
| | | { |
| | | overflow(); |
| | | } |
| | | |
| | | assert(stmt != SQL_NULL_HANDLE); |
| | | m_stmt = stmt; |
| | | m_field = field; |
| | | m_size = INTMAX_MAX; |
| | | init_buffer(mode); |
| | | } |
| | | else if(p.m_indicator>=0) |
| | | |
| | | private: |
| | | object<SQL_HANDLE_STMT> *m_stmt; |
| | | SQLSMALLINT m_field; |
| | | |
| | | protected: |
| | | virtual bool read_blob(char *buffer, off_type &count, pos_type position) override |
| | | { |
| | | value.is_null=false; |
| | | value.length=p.m_indicator; |
| | | if(p.m_size>0 && p.m_indicator>=p.m_size) |
| | | value.is_truncated=true; |
| | | SQLLEN indicator = 0; |
| | | SQLRETURN ret = SQLGetData(m_stmt->handle(), m_field + 1, SQL_C_BINARY, buffer, static_cast<SQLINTEGER>(count), const_cast<SQLLEN *>(&indicator)); |
| | | if (ret != SQL_NO_DATA) |
| | | { |
| | | count = (indicator > count) || (indicator == SQL_NO_TOTAL) ? count : indicator; |
| | | m_stmt->verify_error(ret); |
| | | return true; |
| | | } |
| | | else |
| | | return false; |
| | | } |
| | | if(fetch_fun) fetch_fun(p); |
| | | |
| | | virtual void write_blob(const char *buffer, size_t count) override |
| | | { |
| | | m_stmt->verify_error(SQLPutData(m_stmt->handle(), (SQLPOINTER)buffer, count)); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | class environment final : public object<SQL_HANDLE_ENV> |
| | | { |
| | | public: |
| | | environment() : object(SQL_NULL_HANDLE) |
| | | { |
| | | #if ODBCVER >= 0x0380 |
| | | const SQLPOINTER version = (SQLPOINTER)SQL_OV_ODBC3_80; |
| | | #else |
| | | const SQLPOINTER version = (SQLPOINTER)SQL_OV_ODBC3; |
| | | #endif |
| | | verify_error(SQLSetEnvAttr(m_handle, SQL_ATTR_ODBC_VERSION, version, SQL_IS_INTEGER)); |
| | | } |
| | | environment(environment &&src) : object(std::forward<environment>(src)) {} |
| | | |
| | | int32_t version() const |
| | | { |
| | | int32_t ver = 0; |
| | | verify_error(SQLGetEnvAttr(m_handle, SQL_ATTR_ODBC_VERSION, &ver, sizeof(DWORD), NULL)); |
| | | return ver; |
| | | } |
| | | }; |
| | | |
| | | class base_statement : public object<SQL_HANDLE_STMT> |
| | | { |
| | | public: |
| | | explicit base_statement(base_database &db); |
| | | base_statement(base_statement &&src) |
| | | : object(std::forward<base_statement>(src)), m_params(std::forward<std::vector<param_data>>(src.m_params)) |
| | | { |
| | | m_binded_cols = src.m_binded_cols; |
| | | src.m_binded_cols = false; |
| | | m_blob_buffer = src.m_blob_buffer; |
| | | src.m_blob_buffer = NULL; |
| | | } |
| | | ~base_statement() |
| | | { |
| | | if (m_blob_buffer) |
| | | free(m_blob_buffer); |
| | | } |
| | | base_statement &operator=(base_statement &&src) |
| | | { |
| | | if (this != &src) |
| | | { |
| | | object::operator=(std::forward<base_statement>(src)); |
| | | m_params = std::forward<std::vector<param_data>>(src.m_params); |
| | | m_binded_cols = src.m_binded_cols; |
| | | src.m_binded_cols = false; |
| | | m_blob_buffer = src.m_blob_buffer; |
| | | src.m_blob_buffer = NULL; |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | void bind_param(size_t index, const std::nullptr_t &) |
| | | { |
| | | m_params[index].m_indicator = SQL_NULL_DATA; |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), |
| | | SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_DEFAULT, 0, 0, NULL, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_param(size_t index, const qtl::null &) |
| | | { |
| | | bind_param(index, nullptr); |
| | | } |
| | | void bind_param(size_t index, const int8_t &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_STINYINT, SQL_TINYINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const uint8_t &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_UTINYINT, SQL_TINYINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const int16_t &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_SMALLINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const uint16_t &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_USHORT, SQL_SMALLINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const int32_t &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const uint32_t &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const int64_t &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const uint64_t &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const double &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const float &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const bool &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const DATE_STRUCT &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_DATE, SQL_DATE, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const TIME_STRUCT &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_TIME, SQL_TIME, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const TIMESTAMP_STRUCT &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const SQLGUID &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_GUID, SQL_GUID, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const SQL_NUMERIC_STRUCT &v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_NUMERIC, SQL_NUMERIC, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(size_t index, const char *v, size_t n = SQL_NTS, SQLULEN size = 0) |
| | | { |
| | | m_params[index].m_indicator = n; |
| | | if (size == 0) |
| | | size = strlen(v); |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, |
| | | size, 0, (SQLPOINTER)v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_param(size_t index, const wchar_t *v, size_t n = SQL_NTS, SQLULEN size = 0) |
| | | { |
| | | m_params[index].m_indicator = n; |
| | | if (size == 0) |
| | | size = wcslen(v); |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR, |
| | | size, 0, (SQLPOINTER)v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_param(size_t index, const std::string &v) |
| | | { |
| | | bind_param(index, v.data(), v.size(), v.size()); |
| | | } |
| | | void bind_param(size_t index, const std::wstring &v) |
| | | { |
| | | bind_param(index, v.data(), v.size(), v.size()); |
| | | } |
| | | void bind_param(size_t index, const const_blob_data &v) |
| | | { |
| | | m_params[index].m_indicator = v.size; |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, |
| | | v.size, 0, (SQLPOINTER)v.data, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_param(size_t index, std::istream &s) |
| | | { |
| | | if (m_blob_buffer == NULL) |
| | | m_blob_buffer = malloc(blob_buffer_size); |
| | | m_params[index].m_data = m_blob_buffer; |
| | | m_params[index].m_size = blob_buffer_size; |
| | | m_params[index].m_indicator = SQL_LEN_DATA_AT_EXEC(m_params[index].m_size); |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, |
| | | INT_MAX, 0, &m_params[index], 0, &m_params[index].m_indicator)); |
| | | m_params[index].m_after_fetch = [this, &s](const param_data &p) |
| | | { |
| | | SQLLEN readed = SQL_NULL_DATA; |
| | | while (!s.eof() && !s.fail()) |
| | | { |
| | | s.read((char *)p.m_data, p.m_size); |
| | | readed = (unsigned long)s.gcount(); |
| | | if (readed > 0) |
| | | { |
| | | verify_error(SQLPutData(m_handle, p.m_data, readed)); |
| | | } |
| | | } |
| | | }; |
| | | } |
| | | |
| | | void bind_param(size_t index, const blob_writer ¶m) |
| | | { |
| | | m_params[index].m_data = nullptr; |
| | | m_params[index].m_size = blob_buffer_size; |
| | | m_params[index].m_indicator = SQL_LEN_DATA_AT_EXEC(m_params[index].m_size); |
| | | verify_error(SQLBindParameter(m_handle, static_cast<SQLSMALLINT>(index + 1), SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, |
| | | INT_MAX, 0, &m_params[index], 0, &m_params[index].m_indicator)); |
| | | m_params[index].m_after_fetch = [this, index, ¶m](const param_data &b) |
| | | { |
| | | blobbuf buf; |
| | | buf.open(this, static_cast<SQLSMALLINT>(index), std::ios::out); |
| | | std::ostream s(&buf); |
| | | param(s); |
| | | }; |
| | | } |
| | | |
| | | void bind_field(size_t index, bool &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_BIT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, int8_t &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_STINYINT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, uint8_t &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_UTINYINT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, int16_t &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_SSHORT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, uint16_t &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_USHORT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, int32_t &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_SLONG, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, uint32_t &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_ULONG, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, int64_t &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_SBIGINT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, uint64_t &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_UBIGINT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, float &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_FLOAT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, double &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_DOUBLE, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, DATE_STRUCT &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_TYPE_DATE, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, TIME_STRUCT &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_TYPE_TIME, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, TIMESTAMP_STRUCT &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_TYPE_TIMESTAMP, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, SQLGUID &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_GUID, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, SQL_NUMERIC_STRUCT &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_NUMERIC, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, char *v, size_t n) |
| | | { |
| | | m_params[index].m_data = v; |
| | | m_params[index].m_size = n; |
| | | m_params[index].m_after_fetch = [](const param_data &p) |
| | | { |
| | | if (p.m_indicator == SQL_NULL_DATA) |
| | | memset(p.m_data, 0, p.m_size * sizeof(char)); |
| | | else |
| | | { |
| | | char *text = reinterpret_cast<char *>(p.m_data); |
| | | text[p.m_indicator] = '\0'; |
| | | } |
| | | }; |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_CHAR, v, n, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, wchar_t *v, size_t n) |
| | | { |
| | | m_params[index].m_data = v; |
| | | m_params[index].m_size = n; |
| | | m_params[index].m_after_fetch = [](const param_data &p) |
| | | { |
| | | if (p.m_indicator == SQL_NULL_DATA) |
| | | memset(p.m_data, 0, p.m_size * sizeof(wchar_t)); |
| | | else |
| | | { |
| | | wchar_t *text = reinterpret_cast<wchar_t *>(p.m_data); |
| | | text[p.m_indicator] = '\0'; |
| | | } |
| | | }; |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_WCHAR, v, n, &m_params[index].m_indicator)); |
| | | } |
| | | template <typename T> |
| | | void bind_field(size_t index, qtl::bind_string_helper<T> &&v) |
| | | { |
| | | SQLLEN length = 0; |
| | | verify_error(SQLColAttribute(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_DESC_LENGTH, NULL, 0, NULL, &length)); |
| | | typename qtl::bind_string_helper<T>::char_type *data = v.alloc(length); |
| | | bind_field(index, data, length + 1); |
| | | m_params[index].m_after_fetch = [v](const param_data &p) mutable |
| | | { |
| | | if (p.m_indicator == SQL_NULL_DATA) |
| | | v.clear(); |
| | | else |
| | | v.truncate(p.m_indicator); |
| | | }; |
| | | } |
| | | template <size_t N> |
| | | void bind_field(size_t index, std::array<char, N> &&value) |
| | | { |
| | | bind_field(index, value.data(), value.size()); |
| | | } |
| | | template <size_t N> |
| | | void bind_field(size_t index, std::array<wchar_t, N> &&value) |
| | | { |
| | | bind_field(index, value.data(), value.size()); |
| | | } |
| | | void bind_field(size_t index, qtl::blob_data &&v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_BINARY, v.data, v.size, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(size_t index, std::ostream &&v) |
| | | { |
| | | if (m_blob_buffer == NULL) |
| | | m_blob_buffer = malloc(blob_buffer_size); |
| | | m_params[index].m_data = m_blob_buffer; |
| | | m_params[index].m_size = blob_buffer_size; |
| | | m_params[index].m_after_fetch = [this, index, &v](const param_data &p) |
| | | { |
| | | SQLRETURN ret = SQLGetData(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN *>(&p.m_indicator)); |
| | | while (ret != SQL_NO_DATA) |
| | | { |
| | | size_t n = (p.m_indicator > blob_buffer_size) || (p.m_indicator == SQL_NO_TOTAL) ? blob_buffer_size : p.m_indicator; |
| | | verify_error(ret); |
| | | v.write((const char *)p.m_data, n); |
| | | ret = SQLGetData(m_handle, static_cast<SQLUSMALLINT>(index + 1), SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN *>(&p.m_indicator)); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | void bind_field(size_t index, blobbuf &&value) |
| | | { |
| | | m_params[index].m_data = nullptr; |
| | | m_params[index].m_size = 0; |
| | | m_params[index].m_after_fetch = [this, index, &value](const param_data &p) |
| | | { |
| | | value.open(this, static_cast<SQLSMALLINT>(index), std::ios::in); |
| | | }; |
| | | } |
| | | |
| | | template <typename Type> |
| | | void bind_field(size_t index, indicator<Type> &&value) |
| | | { |
| | | qtl::bind_field(*this, index, value.data); |
| | | param_data ¶m = m_params[index]; |
| | | auto fetch_fun = param.m_after_fetch; |
| | | param.m_after_fetch = [fetch_fun, &value](const param_data &p) |
| | | { |
| | | value.is_truncated = false; |
| | | if (p.m_indicator == SQL_NULL_DATA) |
| | | { |
| | | value.is_null = true; |
| | | value.length = 0; |
| | | } |
| | | else if (p.m_indicator >= 0) |
| | | { |
| | | value.is_null = false; |
| | | value.length = p.m_indicator; |
| | | if (p.m_size > 0 && p.m_indicator >= p.m_size) |
| | | value.is_truncated = true; |
| | | } |
| | | if (fetch_fun) |
| | | fetch_fun(p); |
| | | }; |
| | | } |
| | | |
| | | #ifdef _QTL_ENABLE_CPP17 |
| | | |
| | | template<typename Type> |
| | | void bind_field(size_t index, std::optional<Type>&& value) |
| | | { |
| | | qtl::bind_field(*this, index, *value); |
| | | param_data& param = m_params[index]; |
| | | auto fetch_fun = param.m_after_fetch; |
| | | param.m_after_fetch = [fetch_fun, &value](const param_data& p) { |
| | | if (fetch_fun) fetch_fun(p); |
| | | if (p.m_indicator == SQL_NULL_DATA) |
| | | value.reset(); |
| | | }; |
| | | } |
| | | template <typename Type> |
| | | void bind_field(size_t index, std::optional<Type> &&value) |
| | | { |
| | | qtl::bind_field(*this, index, *value); |
| | | param_data ¶m = m_params[index]; |
| | | auto fetch_fun = param.m_after_fetch; |
| | | param.m_after_fetch = [fetch_fun, &value](const param_data &p) |
| | | { |
| | | if (fetch_fun) |
| | | fetch_fun(p); |
| | | if (p.m_indicator == SQL_NULL_DATA) |
| | | value.reset(); |
| | | }; |
| | | } |
| | | |
| | | void bind_field(size_t index, std::any&& value) |
| | | { |
| | | SQLLEN type = 0, isUnsigned=SQL_FALSE; |
| | | verify_error(SQLColAttribute(m_handle, index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &type)); |
| | | verify_error(SQLColAttribute(m_handle, index + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL, &isUnsigned)); |
| | | switch (type) |
| | | { |
| | | case SQL_BIT: |
| | | value.emplace<bool>(); |
| | | bind_field(index, std::forward<bool>(std::any_cast<bool&>(value))); |
| | | break; |
| | | case SQL_TINYINT: |
| | | if (isUnsigned) |
| | | void bind_field(size_t index, std::any &&value) |
| | | { |
| | | value.emplace<uint8_t>(); |
| | | bind_field(index, std::forward<uint8_t>(std::any_cast<uint8_t&>(value))); |
| | | SQLLEN type = 0, isUnsigned = SQL_FALSE; |
| | | verify_error(SQLColAttribute(m_handle, index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &type)); |
| | | verify_error(SQLColAttribute(m_handle, index + 1, SQL_DESC_UNSIGNED, NULL, 0, NULL, &isUnsigned)); |
| | | switch (type) |
| | | { |
| | | case SQL_BIT: |
| | | value.emplace<bool>(); |
| | | bind_field(index, std::forward<bool>(std::any_cast<bool &>(value))); |
| | | break; |
| | | case SQL_TINYINT: |
| | | if (isUnsigned) |
| | | { |
| | | value.emplace<uint8_t>(); |
| | | bind_field(index, std::forward<uint8_t>(std::any_cast<uint8_t &>(value))); |
| | | } |
| | | else |
| | | { |
| | | value.emplace<int8_t>(); |
| | | bind_field(index, std::forward<int8_t>(std::any_cast<int8_t &>(value))); |
| | | } |
| | | break; |
| | | case SQL_SMALLINT: |
| | | if (isUnsigned) |
| | | { |
| | | value.emplace<uint16_t>(); |
| | | bind_field(index, std::forward<uint16_t>(std::any_cast<uint16_t &>(value))); |
| | | } |
| | | else |
| | | { |
| | | value.emplace<int16_t>(); |
| | | bind_field(index, std::forward<int16_t>(std::any_cast<int16_t &>(value))); |
| | | } |
| | | break; |
| | | case SQL_INTEGER: |
| | | if (isUnsigned) |
| | | { |
| | | value.emplace<uint32_t>(); |
| | | bind_field(index, std::forward<uint32_t>(std::any_cast<uint32_t &>(value))); |
| | | } |
| | | else |
| | | { |
| | | value.emplace<int32_t>(); |
| | | bind_field(index, std::forward<int32_t>(std::any_cast<int32_t &>(value))); |
| | | } |
| | | break; |
| | | case SQL_BIGINT: |
| | | if (isUnsigned) |
| | | { |
| | | value.emplace<uint64_t>(); |
| | | bind_field(index, std::forward<uint64_t>(std::any_cast<uint64_t &>(value))); |
| | | } |
| | | else |
| | | { |
| | | value.emplace<int64_t>(); |
| | | bind_field(index, std::forward<int64_t>(std::any_cast<int64_t &>(value))); |
| | | } |
| | | break; |
| | | case SQL_FLOAT: |
| | | value.emplace<float>(); |
| | | bind_field(index, std::forward<float>(std::any_cast<float &>(value))); |
| | | break; |
| | | case SQL_DOUBLE: |
| | | value.emplace<double>(); |
| | | bind_field(index, std::forward<double>(std::any_cast<double &>(value))); |
| | | break; |
| | | case SQL_NUMERIC: |
| | | value.emplace<SQL_NUMERIC_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_NUMERIC_STRUCT>(std::any_cast<SQL_NUMERIC_STRUCT &>(value))); |
| | | break; |
| | | case SQL_TIME: |
| | | value.emplace<SQL_TIME_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_TIME_STRUCT>(std::any_cast<SQL_TIME_STRUCT &>(value))); |
| | | break; |
| | | case SQL_DATE: |
| | | value.emplace<SQL_DATE_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_DATE_STRUCT>(std::any_cast<SQL_DATE_STRUCT &>(value))); |
| | | break; |
| | | case SQL_TIMESTAMP: |
| | | value.emplace<SQL_TIMESTAMP_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_TIMESTAMP_STRUCT>(std::any_cast<SQL_TIMESTAMP_STRUCT &>(value))); |
| | | break; |
| | | case SQL_INTERVAL_MONTH: |
| | | case SQL_INTERVAL_YEAR: |
| | | case SQL_INTERVAL_YEAR_TO_MONTH: |
| | | case SQL_INTERVAL_DAY: |
| | | case SQL_INTERVAL_HOUR: |
| | | case SQL_INTERVAL_MINUTE: |
| | | case SQL_INTERVAL_SECOND: |
| | | case SQL_INTERVAL_DAY_TO_HOUR: |
| | | case SQL_INTERVAL_DAY_TO_MINUTE: |
| | | case SQL_INTERVAL_DAY_TO_SECOND: |
| | | case SQL_INTERVAL_HOUR_TO_MINUTE: |
| | | case SQL_INTERVAL_HOUR_TO_SECOND: |
| | | case SQL_INTERVAL_MINUTE_TO_SECOND: |
| | | value.emplace<SQL_INTERVAL_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_INTERVAL_STRUCT>(std::any_cast<SQL_INTERVAL_STRUCT &>(value))); |
| | | break; |
| | | case SQL_CHAR: |
| | | value.emplace<std::string>(); |
| | | bind_field(index, qtl::bind_string(std::any_cast<std::string &>(value))); |
| | | break; |
| | | case SQL_GUID: |
| | | value.emplace<SQLGUID>(); |
| | | bind_field(index, std::forward<SQLGUID>(std::any_cast<SQLGUID &>(value))); |
| | | break; |
| | | case SQL_BINARY: |
| | | value.emplace<blobbuf>(); |
| | | bind_field(index, std::forward<blobbuf>(std::any_cast<blobbuf &>(value))); |
| | | break; |
| | | default: |
| | | throw odbc::error(*this, SQL_ERROR); |
| | | } |
| | | param_data ¶m = m_params[index]; |
| | | auto fetch_fun = param.m_after_fetch; |
| | | param.m_after_fetch = [fetch_fun, &value](const param_data &p) |
| | | { |
| | | if (fetch_fun) |
| | | fetch_fun(p); |
| | | if (p.m_indicator == SQL_NULL_DATA) |
| | | value.reset(); |
| | | }; |
| | | } |
| | | else |
| | | { |
| | | value.emplace<int8_t>(); |
| | | bind_field(index, std::forward<int8_t>(std::any_cast<int8_t&>(value))); |
| | | } |
| | | break; |
| | | case SQL_SMALLINT: |
| | | if (isUnsigned) |
| | | { |
| | | value.emplace<uint16_t>(); |
| | | bind_field(index, std::forward<uint16_t>(std::any_cast<uint16_t&>(value))); |
| | | } |
| | | else |
| | | { |
| | | value.emplace<int16_t>(); |
| | | bind_field(index, std::forward<int16_t>(std::any_cast<int16_t&>(value))); |
| | | } |
| | | break; |
| | | case SQL_INTEGER: |
| | | if (isUnsigned) |
| | | { |
| | | value.emplace<uint32_t>(); |
| | | bind_field(index, std::forward<uint32_t>(std::any_cast<uint32_t&>(value))); |
| | | } |
| | | else |
| | | { |
| | | value.emplace<int32_t>(); |
| | | bind_field(index, std::forward<int32_t>(std::any_cast<int32_t&>(value))); |
| | | } |
| | | break; |
| | | case SQL_BIGINT: |
| | | if (isUnsigned) |
| | | { |
| | | value.emplace<uint64_t>(); |
| | | bind_field(index, std::forward<uint64_t>(std::any_cast<uint64_t&>(value))); |
| | | } |
| | | else |
| | | { |
| | | value.emplace<int64_t>(); |
| | | bind_field(index, std::forward<int64_t>(std::any_cast<int64_t&>(value))); |
| | | } |
| | | break; |
| | | case SQL_FLOAT: |
| | | value.emplace<float>(); |
| | | bind_field(index, std::forward<float>(std::any_cast<float&>(value))); |
| | | break; |
| | | case SQL_DOUBLE: |
| | | value.emplace<double>(); |
| | | bind_field(index, std::forward<double>(std::any_cast<double&>(value))); |
| | | break; |
| | | case SQL_NUMERIC: |
| | | value.emplace<SQL_NUMERIC_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_NUMERIC_STRUCT>(std::any_cast<SQL_NUMERIC_STRUCT&>(value))); |
| | | break; |
| | | case SQL_TIME: |
| | | value.emplace<SQL_TIME_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_TIME_STRUCT>(std::any_cast<SQL_TIME_STRUCT&>(value))); |
| | | break; |
| | | case SQL_DATE: |
| | | value.emplace<SQL_DATE_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_DATE_STRUCT>(std::any_cast<SQL_DATE_STRUCT&>(value))); |
| | | break; |
| | | case SQL_TIMESTAMP: |
| | | value.emplace<SQL_TIMESTAMP_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_TIMESTAMP_STRUCT>(std::any_cast<SQL_TIMESTAMP_STRUCT&>(value))); |
| | | break; |
| | | case SQL_INTERVAL_MONTH: |
| | | case SQL_INTERVAL_YEAR: |
| | | case SQL_INTERVAL_YEAR_TO_MONTH: |
| | | case SQL_INTERVAL_DAY: |
| | | case SQL_INTERVAL_HOUR: |
| | | case SQL_INTERVAL_MINUTE: |
| | | case SQL_INTERVAL_SECOND: |
| | | case SQL_INTERVAL_DAY_TO_HOUR: |
| | | case SQL_INTERVAL_DAY_TO_MINUTE: |
| | | case SQL_INTERVAL_DAY_TO_SECOND: |
| | | case SQL_INTERVAL_HOUR_TO_MINUTE: |
| | | case SQL_INTERVAL_HOUR_TO_SECOND: |
| | | case SQL_INTERVAL_MINUTE_TO_SECOND: |
| | | value.emplace<SQL_INTERVAL_STRUCT>(); |
| | | bind_field(index, std::forward<SQL_INTERVAL_STRUCT>(std::any_cast<SQL_INTERVAL_STRUCT&>(value))); |
| | | break; |
| | | case SQL_CHAR: |
| | | value.emplace<std::string>(); |
| | | bind_field(index, qtl::bind_string(std::any_cast<std::string&>(value))); |
| | | break; |
| | | case SQL_GUID: |
| | | value.emplace<SQLGUID>(); |
| | | bind_field(index, std::forward<SQLGUID>(std::any_cast<SQLGUID&>(value))); |
| | | break; |
| | | case SQL_BINARY: |
| | | value.emplace<blobbuf>(); |
| | | bind_field(index, std::forward<blobbuf>(std::any_cast<blobbuf&>(value))); |
| | | break; |
| | | default: |
| | | throw odbc::error(*this, SQL_ERROR); |
| | | } |
| | | param_data& param = m_params[index]; |
| | | auto fetch_fun = param.m_after_fetch; |
| | | param.m_after_fetch = [fetch_fun, &value](const param_data& p) { |
| | | if (fetch_fun) fetch_fun(p); |
| | | if (p.m_indicator == SQL_NULL_DATA) |
| | | value.reset(); |
| | | }; |
| | | } |
| | | |
| | | #endif // C++17 |
| | | |
| | | SQLLEN affetced_rows() |
| | | { |
| | | SQLLEN count=0; |
| | | verify_error(SQLRowCount(m_handle, &count)); |
| | | return count; |
| | | } |
| | | |
| | | size_t find_field(const char* name) const |
| | | { |
| | | SQLSMALLINT count=0; |
| | | verify_error(SQLNumResultCols(m_handle, &count)); |
| | | for(SQLSMALLINT i=0; i!=count; i++) |
| | | { |
| | | SQLCHAR field_name[256]={0}; |
| | | SQLSMALLINT name_length=0; |
| | | SQLSMALLINT data_type; |
| | | SQLULEN column_size; |
| | | SQLSMALLINT digits; |
| | | SQLSMALLINT nullable; |
| | | verify_error(SQLDescribeColA(m_handle, i, field_name, sizeof(field_name), &name_length, |
| | | &data_type, &column_size, &digits, &nullable)); |
| | | if(strncmp((char*)field_name, name, name_length)==0) |
| | | return i; |
| | | } |
| | | return -1; |
| | | } |
| | | |
| | | void reset() |
| | | { |
| | | verify_error(SQLFreeStmt(m_handle, SQL_RESET_PARAMS)); |
| | | } |
| | | |
| | | /* |
| | | ODBC do not support this function, but you can use query to instead it: |
| | | For MS SQL Server: SELECT @@IDENTITY; |
| | | For MySQL: SELECT LAST_INSERT_ID(); |
| | | For SQLite: SELECT last_insert_rowid(); |
| | | */ |
| | | /*uint64_t insert_id() |
| | | { |
| | | assert(false); |
| | | return 0; |
| | | }*/ |
| | | |
| | | protected: |
| | | struct param_data |
| | | { |
| | | SQLPOINTER m_data; |
| | | SQLLEN m_size; |
| | | SQLLEN m_indicator; |
| | | std::function<void(const param_data&)> m_after_fetch; |
| | | |
| | | param_data() : m_data(NULL), m_size(0), m_indicator(0) { } |
| | | }; |
| | | SQLPOINTER m_blob_buffer; |
| | | std::vector<param_data> m_params; |
| | | bool m_binded_cols; |
| | | }; |
| | | |
| | | class statement : public base_statement |
| | | { |
| | | public: |
| | | statement() = default; |
| | | explicit statement(base_database& db) : base_statement(db) { } |
| | | statement(statement&& src) : base_statement(std::move(src)) { } |
| | | statement& operator=(statement&& src) |
| | | { |
| | | base_statement::operator =(std::move(src)); |
| | | return *this; |
| | | } |
| | | ~statement() |
| | | { |
| | | close(); |
| | | } |
| | | |
| | | void open(const char* query_text, size_t text_length = SQL_NTS) |
| | | { |
| | | reset(); |
| | | verify_error(SQLPrepareA(m_handle, (SQLCHAR*)query_text, text_length)); |
| | | } |
| | | void open(const std::string& query_text) |
| | | { |
| | | open(query_text.data(), query_text.size()); |
| | | } |
| | | |
| | | |
| | | template<typename Types> |
| | | void execute(const Types& params) |
| | | { |
| | | SQLSMALLINT count = 0; |
| | | verify_error(SQLNumParams(m_handle, &count)); |
| | | if (count > 0) |
| | | { |
| | | m_params.resize(count); |
| | | qtl::bind_params(*this, params); |
| | | } |
| | | |
| | | SQLRETURN ret = SQLExecute(m_handle); |
| | | verify_error(ret); |
| | | if (ret == SQL_NEED_DATA) |
| | | { |
| | | SQLPOINTER token; |
| | | size_t i = 0; |
| | | ret = SQLParamData(m_handle, &token); |
| | | verify_error(ret); |
| | | while (ret == SQL_NEED_DATA) |
| | | SQLLEN affetced_rows() |
| | | { |
| | | while (i != count) |
| | | SQLLEN count = 0; |
| | | verify_error(SQLRowCount(m_handle, &count)); |
| | | return count; |
| | | } |
| | | |
| | | size_t find_field(const char *name) const |
| | | { |
| | | SQLSMALLINT count = 0; |
| | | verify_error(SQLNumResultCols(m_handle, &count)); |
| | | for (SQLSMALLINT i = 0; i != count; i++) |
| | | { |
| | | if (&m_params[i] == token) |
| | | { |
| | | if (m_params[i].m_after_fetch) |
| | | m_params[i].m_after_fetch(m_params[i]); |
| | | break; |
| | | } |
| | | ++i; |
| | | SQLCHAR field_name[256] = {0}; |
| | | SQLSMALLINT name_length = 0; |
| | | SQLSMALLINT data_type; |
| | | SQLULEN column_size; |
| | | SQLSMALLINT digits; |
| | | SQLSMALLINT nullable; |
| | | verify_error(SQLDescribeColA(m_handle, i, field_name, sizeof(field_name), &name_length, |
| | | &data_type, &column_size, &digits, &nullable)); |
| | | if (strncmp((char *)field_name, name, name_length) == 0) |
| | | return i; |
| | | } |
| | | ret = SQLParamData(m_handle, &token); |
| | | verify_error(ret); |
| | | return -1; |
| | | } |
| | | } |
| | | } |
| | | |
| | | template<typename Types> |
| | | bool fetch(Types&& values) |
| | | { |
| | | if (!m_binded_cols) |
| | | { |
| | | SQLSMALLINT count = 0; |
| | | verify_error(SQLNumResultCols(m_handle, &count)); |
| | | if (count > 0) |
| | | void reset() |
| | | { |
| | | m_params.resize(count); |
| | | qtl::bind_record(*this, std::forward<Types>(values)); |
| | | verify_error(SQLFreeStmt(m_handle, SQL_RESET_PARAMS)); |
| | | } |
| | | m_binded_cols = true; |
| | | } |
| | | return fetch(); |
| | | } |
| | | |
| | | bool fetch() |
| | | { |
| | | SQLRETURN ret = SQLFetch(m_handle); |
| | | if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) |
| | | { |
| | | for (const param_data& data : m_params) |
| | | /* |
| | | ODBC do not support this function, but you can use query to instead it: |
| | | For MS SQL Server: SELECT @@IDENTITY; |
| | | For MySQL: SELECT LAST_INSERT_ID(); |
| | | For SQLite: SELECT last_insert_rowid(); |
| | | */ |
| | | /*uint64_t insert_id() |
| | | { |
| | | if (data.m_after_fetch) |
| | | data.m_after_fetch(data); |
| | | assert(false); |
| | | return 0; |
| | | }*/ |
| | | |
| | | protected: |
| | | struct param_data |
| | | { |
| | | SQLPOINTER m_data; |
| | | SQLLEN m_size; |
| | | SQLLEN m_indicator; |
| | | std::function<void(const param_data &)> m_after_fetch; |
| | | |
| | | param_data() : m_data(NULL), m_size(0), m_indicator(0) {} |
| | | }; |
| | | SQLPOINTER m_blob_buffer; |
| | | std::vector<param_data> m_params; |
| | | bool m_binded_cols; |
| | | }; |
| | | |
| | | class statement : public base_statement |
| | | { |
| | | public: |
| | | statement() = default; |
| | | explicit statement(base_database &db) : base_statement(db) {} |
| | | statement(statement &&src) : base_statement(std::move(src)) {} |
| | | statement &operator=(statement &&src) |
| | | { |
| | | base_statement::operator=(std::move(src)); |
| | | return *this; |
| | | } |
| | | return true; |
| | | } |
| | | verify_error(ret); |
| | | return false; |
| | | } |
| | | ~statement() |
| | | { |
| | | close(); |
| | | } |
| | | |
| | | bool next_result() |
| | | { |
| | | SQLRETURN ret; |
| | | SQLSMALLINT count = 0; |
| | | m_binded_cols = false; |
| | | do |
| | | { |
| | | ret = SQLMoreResults(m_handle); |
| | | if (ret == SQL_ERROR || ret == SQL_INVALID_HANDLE) |
| | | void open(const char *query_text, size_t text_length = SQL_NTS) |
| | | { |
| | | reset(); |
| | | verify_error(SQLPrepareA(m_handle, (SQLCHAR *)query_text, text_length)); |
| | | } |
| | | void open(const std::string &query_text) |
| | | { |
| | | open(query_text.data(), query_text.size()); |
| | | } |
| | | |
| | | template <typename Types> |
| | | void execute(const Types ¶ms) |
| | | { |
| | | SQLSMALLINT count = 0; |
| | | verify_error(SQLNumParams(m_handle, &count)); |
| | | if (count > 0) |
| | | { |
| | | m_params.resize(count); |
| | | qtl::bind_params(*this, params); |
| | | } |
| | | |
| | | SQLRETURN ret = SQLExecute(m_handle); |
| | | verify_error(ret); |
| | | verify_error(SQLNumResultCols(m_handle, &count)); |
| | | } while (count == 0); |
| | | return ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO; |
| | | } |
| | | }; |
| | | if (ret == SQL_NEED_DATA) |
| | | { |
| | | SQLPOINTER token; |
| | | size_t i = 0; |
| | | ret = SQLParamData(m_handle, &token); |
| | | verify_error(ret); |
| | | while (ret == SQL_NEED_DATA) |
| | | { |
| | | while (i != count) |
| | | { |
| | | if (&m_params[i] == token) |
| | | { |
| | | if (m_params[i].m_after_fetch) |
| | | m_params[i].m_after_fetch(m_params[i]); |
| | | break; |
| | | } |
| | | ++i; |
| | | } |
| | | ret = SQLParamData(m_handle, &token); |
| | | verify_error(ret); |
| | | } |
| | | } |
| | | } |
| | | |
| | | struct connection_parameter |
| | | { |
| | | std::string m_name; |
| | | std::string m_prompt; |
| | | std::string m_value; |
| | | std::vector<std::string> m_value_list; |
| | | bool m_optinal; |
| | | bool m_assigned; |
| | | template <typename Types> |
| | | bool fetch(Types &&values) |
| | | { |
| | | if (!m_binded_cols) |
| | | { |
| | | SQLSMALLINT count = 0; |
| | | verify_error(SQLNumResultCols(m_handle, &count)); |
| | | if (count > 0) |
| | | { |
| | | m_params.resize(count); |
| | | qtl::bind_record(*this, std::forward<Types>(values)); |
| | | } |
| | | m_binded_cols = true; |
| | | } |
| | | return fetch(); |
| | | } |
| | | |
| | | connection_parameter() : m_optinal(false), m_assigned(false) { } |
| | | void reset() |
| | | { |
| | | m_name.clear(); |
| | | m_prompt.clear(); |
| | | m_value.clear(); |
| | | m_value_list.clear(); |
| | | m_optinal=false; |
| | | m_assigned=false; |
| | | } |
| | | }; |
| | | typedef std::vector<connection_parameter> connection_parameters; |
| | | bool fetch() |
| | | { |
| | | SQLRETURN ret = SQLFetch(m_handle); |
| | | if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) |
| | | { |
| | | for (const param_data &data : m_params) |
| | | { |
| | | if (data.m_after_fetch) |
| | | data.m_after_fetch(data); |
| | | } |
| | | return true; |
| | | } |
| | | verify_error(ret); |
| | | return false; |
| | | } |
| | | |
| | | class base_database : public object<SQL_HANDLE_DBC> |
| | | { |
| | | public: |
| | | typedef odbc::error exception_type; |
| | | bool next_result() |
| | | { |
| | | SQLRETURN ret; |
| | | SQLSMALLINT count = 0; |
| | | m_binded_cols = false; |
| | | do |
| | | { |
| | | ret = SQLMoreResults(m_handle); |
| | | if (ret == SQL_ERROR || ret == SQL_INVALID_HANDLE) |
| | | verify_error(ret); |
| | | verify_error(SQLNumResultCols(m_handle, &count)); |
| | | } while (count == 0); |
| | | return ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO; |
| | | } |
| | | }; |
| | | |
| | | explicit base_database(environment& env) : object(env.handle()), m_opened(false) |
| | | { |
| | | } |
| | | base_database(const base_database&) = delete; |
| | | base_database(base_database&& src) |
| | | : object(std::forward<base_database>(src)), m_connection(std::forward<std::string>(src.m_connection)) |
| | | { |
| | | m_opened=src.m_opened; |
| | | src.m_opened=false; |
| | | } |
| | | ~base_database() |
| | | { |
| | | close(); |
| | | } |
| | | base_database& operator=(base_database&& src) |
| | | { |
| | | if(this!=&src) |
| | | struct connection_parameter |
| | | { |
| | | object::operator =(std::forward<base_database>(src)); |
| | | m_opened=src.m_opened; |
| | | src.m_opened=false; |
| | | m_connection=std::forward<std::string>(src.m_connection); |
| | | } |
| | | return *this; |
| | | } |
| | | std::string m_name; |
| | | std::string m_prompt; |
| | | std::string m_value; |
| | | std::vector<std::string> m_value_list; |
| | | bool m_optinal; |
| | | bool m_assigned; |
| | | |
| | | void close() |
| | | { |
| | | if(m_opened) |
| | | connection_parameter() : m_optinal(false), m_assigned(false) {} |
| | | void reset() |
| | | { |
| | | m_name.clear(); |
| | | m_prompt.clear(); |
| | | m_value.clear(); |
| | | m_value_list.clear(); |
| | | m_optinal = false; |
| | | m_assigned = false; |
| | | } |
| | | }; |
| | | typedef std::vector<connection_parameter> connection_parameters; |
| | | |
| | | class base_database : public object<SQL_HANDLE_DBC> |
| | | { |
| | | verify_error(SQLDisconnect(m_handle)); |
| | | m_opened=false; |
| | | } |
| | | } |
| | | public: |
| | | typedef odbc::error exception_type; |
| | | |
| | | void set_attribute(SQLINTEGER attr, SQLPOINTER value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_POINTER)); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, SQLINTEGER value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_INTEGER)); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, SQLUINTEGER value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_UINTEGER)); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, SQLSMALLINT value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_SMALLINT)); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, SQLUSMALLINT value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_USMALLINT)); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, const char* value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_NTS)); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, const std::string& value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value.data(), value.size())); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, const void* value, SQLINTEGER length) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_LEN_BINARY_ATTR(length))); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, SQLPOINTER& value) const |
| | | { |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_POINTER, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, SQLINTEGER& value) const |
| | | { |
| | | value = 0; |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_INTEGER, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, SQLUINTEGER& value) const |
| | | { |
| | | value = 0; |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_UINTEGER, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, SQLSMALLINT& value) const |
| | | { |
| | | value = 0; |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_SMALLINT, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, SQLUSMALLINT& value) const |
| | | { |
| | | value = 0; |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_USMALLINT, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, void* buffer, SQLINTEGER length) const |
| | | { |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, buffer, SQL_LEN_BINARY_ATTR(length), 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, char* buffer, size_t length) const |
| | | { |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, buffer, length, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, std::string& value) const |
| | | { |
| | | SQLINTEGER length = 0; |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, NULL, 0, &length)); |
| | | value.resize(length); |
| | | if(length>0) |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, (SQLPOINTER)value.data(), length, 0)); |
| | | } |
| | | void get_info(SQLSMALLINT info, std::string& value, SQLSMALLINT size=SQL_MAX_OPTION_STRING_LENGTH) const |
| | | { |
| | | value.resize(size); |
| | | verify_error(SQLGetInfo(m_handle, info, (SQLPOINTER)value.data(), size, &size)); |
| | | value.resize(size); |
| | | } |
| | | explicit base_database(environment &env) : object(env.handle()), m_opened(false) |
| | | { |
| | | } |
| | | base_database(const base_database &) = delete; |
| | | base_database(base_database &&src) |
| | | : object(std::forward<base_database>(src)), m_connection(std::forward<std::string>(src.m_connection)) |
| | | { |
| | | m_opened = src.m_opened; |
| | | src.m_opened = false; |
| | | } |
| | | ~base_database() |
| | | { |
| | | close(); |
| | | } |
| | | base_database &operator=(base_database &&src) |
| | | { |
| | | if (this != &src) |
| | | { |
| | | object::operator=(std::forward<base_database>(src)); |
| | | m_opened = src.m_opened; |
| | | src.m_opened = false; |
| | | m_connection = std::forward<std::string>(src.m_connection); |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | std::string dbms_name() const |
| | | { |
| | | std::string name; |
| | | get_info(SQL_DBMS_NAME, name); |
| | | return name; |
| | | } |
| | | std::string server_name() const |
| | | { |
| | | std::string name; |
| | | get_info(SQL_SERVER_NAME, name); |
| | | return name; |
| | | } |
| | | std::string user_name() const |
| | | { |
| | | std::string name; |
| | | get_info(SQL_USER_NAME, name); |
| | | return name; |
| | | } |
| | | std::string db_name() const |
| | | { |
| | | std::string name; |
| | | get_info(SQL_DATABASE_NAME, name); |
| | | return name; |
| | | } |
| | | const std::string& connection_text() const |
| | | { |
| | | return m_connection; |
| | | } |
| | | void close() |
| | | { |
| | | if (m_opened) |
| | | { |
| | | verify_error(SQLDisconnect(m_handle)); |
| | | m_opened = false; |
| | | } |
| | | } |
| | | |
| | | protected: |
| | | bool m_opened; |
| | | std::string m_connection; |
| | | void set_attribute(SQLINTEGER attr, SQLPOINTER value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_POINTER)); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, SQLINTEGER value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_INTEGER)); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, SQLUINTEGER value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_UINTEGER)); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, SQLSMALLINT value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_SMALLINT)); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, SQLUSMALLINT value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_IS_USMALLINT)); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, const char *value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_NTS)); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, const std::string &value) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value.data(), value.size())); |
| | | } |
| | | void set_attribute(SQLINTEGER attr, const void *value, SQLINTEGER length) |
| | | { |
| | | verify_error(SQLSetConnectAttrA(m_handle, attr, (SQLPOINTER)value, SQL_LEN_BINARY_ATTR(length))); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, SQLPOINTER &value) const |
| | | { |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_POINTER, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, SQLINTEGER &value) const |
| | | { |
| | | value = 0; |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_INTEGER, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, SQLUINTEGER &value) const |
| | | { |
| | | value = 0; |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_UINTEGER, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, SQLSMALLINT &value) const |
| | | { |
| | | value = 0; |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_SMALLINT, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, SQLUSMALLINT &value) const |
| | | { |
| | | value = 0; |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, &value, SQL_IS_USMALLINT, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, void *buffer, SQLINTEGER length) const |
| | | { |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, buffer, SQL_LEN_BINARY_ATTR(length), 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, char *buffer, size_t length) const |
| | | { |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, buffer, length, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, std::string &value) const |
| | | { |
| | | SQLINTEGER length = 0; |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, NULL, 0, &length)); |
| | | value.resize(length); |
| | | if (length > 0) |
| | | verify_error(SQLGetConnectAttrA(m_handle, attr, (SQLPOINTER)value.data(), length, 0)); |
| | | } |
| | | void get_info(SQLSMALLINT info, std::string &value, SQLSMALLINT size = SQL_MAX_OPTION_STRING_LENGTH) const |
| | | { |
| | | value.resize(size); |
| | | verify_error(SQLGetInfo(m_handle, info, (SQLPOINTER)value.data(), size, &size)); |
| | | value.resize(size); |
| | | } |
| | | |
| | | void parse_browse_string(const char* output_text, size_t text_length, connection_parameters& parameters); |
| | | std::string create_connection_text(const connection_parameters& parameters); |
| | | }; |
| | | std::string dbms_name() const |
| | | { |
| | | std::string name; |
| | | get_info(SQL_DBMS_NAME, name); |
| | | return name; |
| | | } |
| | | std::string server_name() const |
| | | { |
| | | std::string name; |
| | | get_info(SQL_SERVER_NAME, name); |
| | | return name; |
| | | } |
| | | std::string user_name() const |
| | | { |
| | | std::string name; |
| | | get_info(SQL_USER_NAME, name); |
| | | return name; |
| | | } |
| | | std::string db_name() const |
| | | { |
| | | std::string name; |
| | | get_info(SQL_DATABASE_NAME, name); |
| | | return name; |
| | | } |
| | | const std::string &connection_text() const |
| | | { |
| | | return m_connection; |
| | | } |
| | | |
| | | class database : public base_database, public qtl::base_database<database, statement> |
| | | { |
| | | public: |
| | | database() = default; |
| | | explicit database(environment& env) : odbc::base_database(env) |
| | | { |
| | | } |
| | | database(database&& src) : odbc::base_database(std::move(src)) |
| | | { |
| | | } |
| | | protected: |
| | | bool m_opened; |
| | | std::string m_connection; |
| | | |
| | | void open(const char* server_name, size_t server_name_length, |
| | | const char* user_name, size_t user_name_length, const char* password, size_t password_length) |
| | | { |
| | | if (m_opened) close(); |
| | | verify_error(SQLConnectA(m_handle, (SQLCHAR*)server_name, static_cast<SQLSMALLINT>(server_name_length), |
| | | (SQLCHAR*)user_name, static_cast<SQLSMALLINT>(user_name_length), (SQLCHAR*)password, static_cast<SQLSMALLINT>(password_length))); |
| | | m_opened = true; |
| | | } |
| | | void open(const char* server_name, const char* user_name, const char* password) |
| | | { |
| | | verify_error(SQLConnectA(m_handle, (SQLCHAR*)server_name, SQL_NTS, (SQLCHAR*)user_name, SQL_NTS, (SQLCHAR*)password, SQL_NTS)); |
| | | } |
| | | void open(const std::string& server_name, const std::string& user_name, const std::string& password) |
| | | { |
| | | open(server_name.data(), server_name.size(), user_name.data(), user_name.size(), password.data(), password.size()); |
| | | } |
| | | void open(const char* input_text, size_t text_length = SQL_NTS, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL) |
| | | { |
| | | m_connection.resize(512); |
| | | SQLSMALLINT out_len=0; |
| | | if (m_opened) close(); |
| | | verify_error(SQLDriverConnectA(m_handle, hwnd, (SQLCHAR*)input_text, (SQLSMALLINT)text_length, |
| | | (SQLCHAR*)m_connection.data(), (SQLSMALLINT)m_connection.size(), &out_len, driver_completion)); |
| | | m_connection.resize(out_len); |
| | | m_opened = true; |
| | | } |
| | | void open(const std::string& input_text, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL) |
| | | { |
| | | open(input_text.data(), input_text.size(), driver_completion, hwnd); |
| | | } |
| | | void open(SQLHWND hwnd, SQLSMALLINT driver_completion = SQL_DRIVER_COMPLETE) |
| | | { |
| | | open("", SQL_NTS, driver_completion, hwnd); |
| | | } |
| | | // InputPred like: |
| | | // bool input_parameters(connection_parameters& parameters); |
| | | template<typename InputPred> |
| | | void open(const char* connection_text, size_t text_length, InputPred&& pred) |
| | | { |
| | | SQLSMALLINT length = 0; |
| | | SQLRETURN ret = SQL_SUCCESS; |
| | | std::string input_text; |
| | | if (m_opened) close(); |
| | | if (text_length == SQL_NTS) |
| | | input_text = connection_text; |
| | | else |
| | | input_text.assign(connection_text, text_length); |
| | | m_connection.resize(1024); |
| | | while ((ret = SQLBrowseConnectA(m_handle, (SQLCHAR*)input_text.data(), SQL_NTS, |
| | | (SQLCHAR*)m_connection.data(), m_connection.size(), &length)) == SQL_NEED_DATA) |
| | | void parse_browse_string(const char *output_text, size_t text_length, connection_parameters ¶meters); |
| | | std::string create_connection_text(const connection_parameters ¶meters); |
| | | }; |
| | | |
| | | class database : public base_database, public qtl::base_database<database, statement> |
| | | { |
| | | connection_parameters parameters; |
| | | parse_browse_string(m_connection.data(), length, parameters); |
| | | if (!pred(parameters)) |
| | | throw error(SQL_NEED_DATA, "User cancel operation."); |
| | | input_text = create_connection_text(parameters); |
| | | } |
| | | if (ret == SQL_ERROR || ret == SQL_SUCCESS_WITH_INFO) |
| | | verify_error(ret); |
| | | m_opened = true; |
| | | } |
| | | template<typename InputPred> |
| | | void open(const char* connection_text, InputPred&& pred) |
| | | { |
| | | open(connection_text, SQL_NTS, std::forward<InputPred>(pred)); |
| | | } |
| | | template<typename InputPred> |
| | | void open(const std::string& connection_text, InputPred&& pred) |
| | | { |
| | | open(connection_text.data(), connection_text.size(), std::forward<InputPred>(pred)); |
| | | } |
| | | public: |
| | | database() = default; |
| | | explicit database(environment &env) : odbc::base_database(env) |
| | | { |
| | | } |
| | | database(database &&src) : odbc::base_database(std::move(src)) |
| | | { |
| | | } |
| | | |
| | | statement open_command(const char* query_text, size_t text_length) |
| | | { |
| | | statement stmt(*this); |
| | | stmt.open(query_text, text_length); |
| | | return stmt; |
| | | } |
| | | statement open_command(const char* query_text) |
| | | { |
| | | return open_command(query_text, strlen(query_text)); |
| | | } |
| | | statement open_command(const std::string& query_text) |
| | | { |
| | | return open_command(query_text.data(), query_text.length()); |
| | | } |
| | | void open(const char *server_name, size_t server_name_length, |
| | | const char *user_name, size_t user_name_length, const char *password, size_t password_length) |
| | | { |
| | | if (m_opened) |
| | | close(); |
| | | verify_error(SQLConnectA(m_handle, (SQLCHAR *)server_name, static_cast<SQLSMALLINT>(server_name_length), |
| | | (SQLCHAR *)user_name, static_cast<SQLSMALLINT>(user_name_length), (SQLCHAR *)password, static_cast<SQLSMALLINT>(password_length))); |
| | | m_opened = true; |
| | | } |
| | | void open(const char *server_name, const char *user_name, const char *password) |
| | | { |
| | | verify_error(SQLConnectA(m_handle, (SQLCHAR *)server_name, SQL_NTS, (SQLCHAR *)user_name, SQL_NTS, (SQLCHAR *)password, SQL_NTS)); |
| | | } |
| | | void open(const std::string &server_name, const std::string &user_name, const std::string &password) |
| | | { |
| | | open(server_name.data(), server_name.size(), user_name.data(), user_name.size(), password.data(), password.size()); |
| | | } |
| | | void open(const char *input_text, size_t text_length = SQL_NTS, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL) |
| | | { |
| | | m_connection.resize(512); |
| | | SQLSMALLINT out_len = 0; |
| | | if (m_opened) |
| | | close(); |
| | | verify_error(SQLDriverConnectA(m_handle, hwnd, (SQLCHAR *)input_text, (SQLSMALLINT)text_length, |
| | | (SQLCHAR *)m_connection.data(), (SQLSMALLINT)m_connection.size(), &out_len, driver_completion)); |
| | | m_connection.resize(out_len); |
| | | m_opened = true; |
| | | } |
| | | void open(const std::string &input_text, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL) |
| | | { |
| | | open(input_text.data(), input_text.size(), driver_completion, hwnd); |
| | | } |
| | | void open(SQLHWND hwnd, SQLSMALLINT driver_completion = SQL_DRIVER_COMPLETE) |
| | | { |
| | | open("", SQL_NTS, driver_completion, hwnd); |
| | | } |
| | | // InputPred like: |
| | | // bool input_parameters(connection_parameters& parameters); |
| | | template <typename InputPred> |
| | | void open(const char *connection_text, size_t text_length, InputPred &&pred) |
| | | { |
| | | SQLSMALLINT length = 0; |
| | | SQLRETURN ret = SQL_SUCCESS; |
| | | std::string input_text; |
| | | if (m_opened) |
| | | close(); |
| | | if (text_length == SQL_NTS) |
| | | input_text = connection_text; |
| | | else |
| | | input_text.assign(connection_text, text_length); |
| | | m_connection.resize(1024); |
| | | while ((ret = SQLBrowseConnectA(m_handle, (SQLCHAR *)input_text.data(), SQL_NTS, |
| | | (SQLCHAR *)m_connection.data(), m_connection.size(), &length)) == SQL_NEED_DATA) |
| | | { |
| | | connection_parameters parameters; |
| | | parse_browse_string(m_connection.data(), length, parameters); |
| | | if (!pred(parameters)) |
| | | throw error(SQL_NEED_DATA, "User cancel operation."); |
| | | input_text = create_connection_text(parameters); |
| | | } |
| | | if (ret == SQL_ERROR || ret == SQL_SUCCESS_WITH_INFO) |
| | | verify_error(ret); |
| | | m_opened = true; |
| | | } |
| | | template <typename InputPred> |
| | | void open(const char *connection_text, InputPred &&pred) |
| | | { |
| | | open(connection_text, SQL_NTS, std::forward<InputPred>(pred)); |
| | | } |
| | | template <typename InputPred> |
| | | void open(const std::string &connection_text, InputPred &&pred) |
| | | { |
| | | open(connection_text.data(), connection_text.size(), std::forward<InputPred>(pred)); |
| | | } |
| | | |
| | | void simple_execute(const char* query_text, size_t text_length = SQL_NTS) |
| | | { |
| | | statement command(*this); |
| | | SQLRETURN ret = SQLExecDirectA(command.handle(), (SQLCHAR*)query_text, text_length); |
| | | if (ret != SQL_SUCCESS && ret != SQL_NO_DATA) |
| | | verify_error(ret); |
| | | } |
| | | void simple_execute(const std::string& query_text) |
| | | { |
| | | simple_execute(query_text.data(), query_text.size()); |
| | | } |
| | | statement open_command(const char *query_text, size_t text_length) |
| | | { |
| | | statement stmt(*this); |
| | | stmt.open(query_text, text_length); |
| | | return stmt; |
| | | } |
| | | statement open_command(const char *query_text) |
| | | { |
| | | return open_command(query_text, strlen(query_text)); |
| | | } |
| | | statement open_command(const std::string &query_text) |
| | | { |
| | | return open_command(query_text.data(), query_text.length()); |
| | | } |
| | | |
| | | void auto_commit(bool on) |
| | | { |
| | | set_attribute(SQL_ATTR_AUTOCOMMIT, on ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF); |
| | | } |
| | | void begin_transaction() |
| | | { |
| | | auto_commit(false); |
| | | } |
| | | void rollback() |
| | | { |
| | | verify_error(SQLEndTran(handler_type, m_handle, SQL_ROLLBACK)); |
| | | auto_commit(true); |
| | | } |
| | | void commit() |
| | | { |
| | | verify_error(SQLEndTran(handler_type, m_handle, SQL_COMMIT)); |
| | | auto_commit(true); |
| | | } |
| | | void simple_execute(const char *query_text, size_t text_length = SQL_NTS) |
| | | { |
| | | statement command(*this); |
| | | SQLRETURN ret = SQLExecDirectA(command.handle(), (SQLCHAR *)query_text, text_length); |
| | | if (ret != SQL_SUCCESS && ret != SQL_NO_DATA) |
| | | verify_error(ret); |
| | | } |
| | | void simple_execute(const std::string &query_text) |
| | | { |
| | | simple_execute(query_text.data(), query_text.size()); |
| | | } |
| | | |
| | | bool is_alive() |
| | | { |
| | | SQLINTEGER value; |
| | | get_attribute(SQL_ATTR_CONNECTION_DEAD, value); |
| | | return value == SQL_CD_FALSE; |
| | | } |
| | | void auto_commit(bool on) |
| | | { |
| | | set_attribute(SQL_ATTR_AUTOCOMMIT, on ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF); |
| | | } |
| | | void begin_transaction() |
| | | { |
| | | auto_commit(false); |
| | | } |
| | | void rollback() |
| | | { |
| | | verify_error(SQLEndTran(handler_type, m_handle, SQL_ROLLBACK)); |
| | | auto_commit(true); |
| | | } |
| | | void commit() |
| | | { |
| | | verify_error(SQLEndTran(handler_type, m_handle, SQL_COMMIT)); |
| | | auto_commit(true); |
| | | } |
| | | |
| | | bool is_alive() |
| | | { |
| | | SQLINTEGER value; |
| | | get_attribute(SQL_ATTR_CONNECTION_DEAD, value); |
| | | return value == SQL_CD_FALSE; |
| | | } |
| | | |
| | | #ifdef QTL_ODBC_ENABLE_ASYNC_MODE |
| | | |
| | | //async_connection async_mode(); |
| | | // async_connection async_mode(); |
| | | |
| | | #endif //ODBC 3.80 |
| | | #endif // ODBC 3.80 |
| | | }; |
| | | |
| | | }; |
| | | struct date : public SQL_DATE_STRUCT |
| | | { |
| | | date() |
| | | { |
| | | memset(this, 0, sizeof(SQL_DATE_STRUCT)); |
| | | } |
| | | }; |
| | | |
| | | struct date : public SQL_DATE_STRUCT |
| | | { |
| | | date() |
| | | { |
| | | memset(this, 0, sizeof(SQL_DATE_STRUCT)); |
| | | } |
| | | }; |
| | | struct time : public SQL_TIME_STRUCT |
| | | { |
| | | time() |
| | | { |
| | | memset(this, 0, sizeof(SQL_TIME_STRUCT)); |
| | | } |
| | | }; |
| | | |
| | | struct time : public SQL_TIME_STRUCT |
| | | { |
| | | time() |
| | | { |
| | | memset(this, 0, sizeof(SQL_TIME_STRUCT)); |
| | | } |
| | | }; |
| | | |
| | | struct timestamp : public SQL_TIMESTAMP_STRUCT |
| | | { |
| | | timestamp() |
| | | { |
| | | memset(this, 0, sizeof(SQL_TIMESTAMP_STRUCT)); |
| | | } |
| | | timestamp(struct tm& tm) |
| | | { |
| | | year=tm.tm_year+1900; |
| | | month=tm.tm_mon+1; |
| | | day=tm.tm_mday; |
| | | hour=tm.tm_hour; |
| | | minute=tm.tm_min; |
| | | second=tm.tm_sec; |
| | | } |
| | | timestamp(time_t value) |
| | | { |
| | | struct tm tm; |
| | | struct timestamp : public SQL_TIMESTAMP_STRUCT |
| | | { |
| | | timestamp() |
| | | { |
| | | memset(this, 0, sizeof(SQL_TIMESTAMP_STRUCT)); |
| | | } |
| | | timestamp(struct tm &tm) |
| | | { |
| | | year = tm.tm_year + 1900; |
| | | month = tm.tm_mon + 1; |
| | | day = tm.tm_mday; |
| | | hour = tm.tm_hour; |
| | | minute = tm.tm_min; |
| | | second = tm.tm_sec; |
| | | } |
| | | timestamp(time_t value) |
| | | { |
| | | struct tm tm; |
| | | #if defined(_MSC_VER) |
| | | localtime_s(&tm, &value); |
| | | localtime_s(&tm, &value); |
| | | #elif defined(_POSIX_VERSION) |
| | | localtime_r(&value, &tm); |
| | | localtime_r(&value, &tm); |
| | | #else |
| | | tm=*localtime(&value); |
| | | tm = *localtime(&value); |
| | | #endif |
| | | new(this)timestamp(tm); |
| | | } |
| | | timestamp(const timestamp& src) |
| | | { |
| | | memcpy(this, &src, sizeof(SQL_TIMESTAMP_STRUCT)); |
| | | } |
| | | timestamp& operator=(const timestamp& src) |
| | | { |
| | | if(this!=&src) |
| | | memcpy(this, &src, sizeof(SQL_TIMESTAMP_STRUCT)); |
| | | return *this; |
| | | } |
| | | new (this) timestamp(tm); |
| | | } |
| | | timestamp(const timestamp &src) |
| | | { |
| | | memcpy(this, &src, sizeof(SQL_TIMESTAMP_STRUCT)); |
| | | } |
| | | timestamp &operator=(const timestamp &src) |
| | | { |
| | | if (this != &src) |
| | | memcpy(this, &src, sizeof(SQL_TIMESTAMP_STRUCT)); |
| | | return *this; |
| | | } |
| | | |
| | | static timestamp now() |
| | | { |
| | | time_t value; |
| | | ::time(&value); |
| | | return timestamp(value); |
| | | } |
| | | static timestamp now() |
| | | { |
| | | time_t value; |
| | | ::time(&value); |
| | | return timestamp(value); |
| | | } |
| | | |
| | | time_t as_tm(struct tm& tm) const |
| | | { |
| | | tm.tm_year=year-1900; |
| | | tm.tm_mon=month-1; |
| | | tm.tm_mday=day; |
| | | tm.tm_hour=hour; |
| | | tm.tm_min=minute; |
| | | tm.tm_sec=second; |
| | | return mktime(&tm); |
| | | } |
| | | time_t get_time() const |
| | | { |
| | | struct tm tm; |
| | | return as_tm(tm); |
| | | } |
| | | timeval get_timeval() const |
| | | { |
| | | timeval tv; |
| | | struct tm tm; |
| | | tv.tv_sec=as_tm(tm); |
| | | tv.tv_usec=fraction/1000; |
| | | } |
| | | }; |
| | | time_t as_tm(struct tm &tm) const |
| | | { |
| | | tm.tm_year = year - 1900; |
| | | tm.tm_mon = month - 1; |
| | | tm.tm_mday = day; |
| | | tm.tm_hour = hour; |
| | | tm.tm_min = minute; |
| | | tm.tm_sec = second; |
| | | return mktime(&tm); |
| | | } |
| | | time_t get_time() const |
| | | { |
| | | struct tm tm; |
| | | return as_tm(tm); |
| | | } |
| | | timeval get_timeval() const |
| | | { |
| | | timeval tv; |
| | | struct tm tm; |
| | | tv.tv_sec = as_tm(tm); |
| | | tv.tv_usec = fraction / 1000; |
| | | } |
| | | }; |
| | | |
| | | #ifdef QTL_ODBC_ENABLE_ASYNC_MODE |
| | | |
| | | class async_connection; |
| | | class async_connection; |
| | | |
| | | inline bool is_still_executing(SQLINTEGER code) |
| | | { |
| | | return code == SQL_STILL_EXECUTING; |
| | | } |
| | | |
| | | class async_statement : public base_statement |
| | | { |
| | | public: |
| | | explicit async_statement(async_connection& db); |
| | | async_statement(async_statement&& src) |
| | | : base_statement(std::move(src)) |
| | | { |
| | | m_hCompleteEvent = src.m_hCompleteEvent; |
| | | m_event=src.m_event; |
| | | m_nQueryTimeout = src.m_nQueryTimeout; |
| | | src.m_hCompleteEvent = nullptr; |
| | | src.m_event = nullptr; |
| | | } |
| | | async_statement& operator=(async_statement&& src) |
| | | { |
| | | if (this != &src) |
| | | inline bool is_still_executing(SQLINTEGER code) |
| | | { |
| | | base_statement::operator =(std::move(src)); |
| | | m_hCompleteEvent = src.m_hCompleteEvent; |
| | | m_event = src.m_event; |
| | | m_nQueryTimeout = src.m_nQueryTimeout; |
| | | src.m_hCompleteEvent = nullptr; |
| | | src.m_event = nullptr; |
| | | } |
| | | return *this; |
| | | } |
| | | ~async_statement() |
| | | { |
| | | close(); |
| | | } |
| | | |
| | | /* |
| | | Handler defiens as: |
| | | void handler(const qtl::odbc::error& e); |
| | | */ |
| | | template<typename Handler> |
| | | void open(Handler&& handler, const char *query_text, size_t text_length = 0) |
| | | { |
| | | if (text_length == 0) text_length = strlen(query_text); |
| | | reset(); |
| | | SQLRETURN ret = SQLPrepareA(m_handle, (SQLCHAR*)query_text, text_length); |
| | | async_wait(ret, std::forward<Handler>(handler)); |
| | | } |
| | | |
| | | /* |
| | | ExecuteHandler defiens as: |
| | | void handler(const qtl::odbc::error& e, uint64_t affected); |
| | | */ |
| | | template<typename Types, typename Handler> |
| | | void execute(const Types& params, Handler&& handler) |
| | | { |
| | | SQLSMALLINT count = 0; |
| | | SQLRETURN ret = SQLNumParams(m_handle, &count); |
| | | if (!SQL_SUCCEEDED(ret)) |
| | | { |
| | | handler(error(*this, ret), 0); |
| | | return; |
| | | } |
| | | if (count > 0) |
| | | { |
| | | m_params.resize(count); |
| | | qtl::bind_params(*this, params); |
| | | return code == SQL_STILL_EXECUTING; |
| | | } |
| | | |
| | | if (m_nQueryTimeout == 0) |
| | | m_nQueryTimeout = query_timeout(); |
| | | ret = SQLExecute(m_handle); |
| | | async_wait(ret, [this, count, handler](const error& e) mutable { |
| | | class async_statement : public base_statement |
| | | { |
| | | public: |
| | | explicit async_statement(async_connection &db); |
| | | async_statement(async_statement &&src) |
| | | : base_statement(std::move(src)) |
| | | { |
| | | m_hCompleteEvent = src.m_hCompleteEvent; |
| | | m_event = src.m_event; |
| | | m_nQueryTimeout = src.m_nQueryTimeout; |
| | | src.m_hCompleteEvent = nullptr; |
| | | src.m_event = nullptr; |
| | | } |
| | | async_statement &operator=(async_statement &&src) |
| | | { |
| | | if (this != &src) |
| | | { |
| | | base_statement::operator=(std::move(src)); |
| | | m_hCompleteEvent = src.m_hCompleteEvent; |
| | | m_event = src.m_event; |
| | | m_nQueryTimeout = src.m_nQueryTimeout; |
| | | src.m_hCompleteEvent = nullptr; |
| | | src.m_event = nullptr; |
| | | } |
| | | return *this; |
| | | } |
| | | ~async_statement() |
| | | { |
| | | close(); |
| | | } |
| | | |
| | | /* |
| | | Handler defiens as: |
| | | void handler(const qtl::odbc::error& e); |
| | | */ |
| | | template <typename Handler> |
| | | void open(Handler &&handler, const char *query_text, size_t text_length = 0) |
| | | { |
| | | if (text_length == 0) |
| | | text_length = strlen(query_text); |
| | | reset(); |
| | | SQLRETURN ret = SQLPrepareA(m_handle, (SQLCHAR *)query_text, text_length); |
| | | async_wait(ret, std::forward<Handler>(handler)); |
| | | } |
| | | |
| | | /* |
| | | ExecuteHandler defiens as: |
| | | void handler(const qtl::odbc::error& e, uint64_t affected); |
| | | */ |
| | | template <typename Types, typename Handler> |
| | | void execute(const Types ¶ms, Handler &&handler) |
| | | { |
| | | SQLSMALLINT count = 0; |
| | | SQLRETURN ret = SQLNumParams(m_handle, &count); |
| | | if (!SQL_SUCCEEDED(ret)) |
| | | { |
| | | handler(error(*this, ret), 0); |
| | | return; |
| | | } |
| | | if (count > 0) |
| | | { |
| | | m_params.resize(count); |
| | | qtl::bind_params(*this, params); |
| | | } |
| | | |
| | | if (m_nQueryTimeout == 0) |
| | | m_nQueryTimeout = query_timeout(); |
| | | ret = SQLExecute(m_handle); |
| | | async_wait(ret, [this, count, handler](const error &e) mutable |
| | | { |
| | | SQLINTEGER ret = e.code(); |
| | | if (ret == SQL_NEED_DATA) |
| | | async_param_data(0, count, std::forward<Handler>(handler)); |
| | | else if(ret>=0) |
| | | handler(error(*this, ret), affetced_rows()); |
| | | else |
| | | handler(error(*this, ret), 0); |
| | | }); |
| | | } |
| | | |
| | | template<typename Types, typename RowHandler, typename FinishHandler> |
| | | void fetch(Types&& values, RowHandler&& row_handler, FinishHandler&& finish_handler) |
| | | { |
| | | if (!m_binded_cols) |
| | | { |
| | | SQLSMALLINT count = 0; |
| | | SQLRETURN ret = SQLNumResultCols(m_handle, &count); |
| | | if(!SQL_SUCCEEDED(ret)) |
| | | { |
| | | finish_handler(error(*this, ret)); |
| | | return; |
| | | handler(error(*this, ret), 0); }); |
| | | } |
| | | if (count > 0) |
| | | |
| | | template <typename Types, typename RowHandler, typename FinishHandler> |
| | | void fetch(Types &&values, RowHandler &&row_handler, FinishHandler &&finish_handler) |
| | | { |
| | | m_params.resize(count); |
| | | qtl::bind_record(*this, std::forward<Types>(values)); |
| | | if (!m_binded_cols) |
| | | { |
| | | SQLSMALLINT count = 0; |
| | | SQLRETURN ret = SQLNumResultCols(m_handle, &count); |
| | | if (!SQL_SUCCEEDED(ret)) |
| | | { |
| | | finish_handler(error(*this, ret)); |
| | | return; |
| | | } |
| | | if (count > 0) |
| | | { |
| | | m_params.resize(count); |
| | | qtl::bind_record(*this, std::forward<Types>(values)); |
| | | } |
| | | m_binded_cols = true; |
| | | } |
| | | return fetch(std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler)); |
| | | } |
| | | m_binded_cols = true; |
| | | } |
| | | return fetch(std::forward<RowHandler>(row_handler), std::forward<FinishHandler>(finish_handler)); |
| | | } |
| | | |
| | | template<typename RowHandler, typename FinishHandler> |
| | | void fetch(RowHandler&& row_handler, FinishHandler&& finish_handler) |
| | | { |
| | | SQLRETURN ret = SQLFetch(m_handle); |
| | | async_wait(ret, [this, row_handler, finish_handler](const error& e) mutable { |
| | | template <typename RowHandler, typename FinishHandler> |
| | | void fetch(RowHandler &&row_handler, FinishHandler &&finish_handler) |
| | | { |
| | | SQLRETURN ret = SQLFetch(m_handle); |
| | | async_wait(ret, [this, row_handler, finish_handler](const error &e) mutable |
| | | { |
| | | SQLINTEGER ret = e.code(); |
| | | if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) |
| | | { |
| | |
| | | finish_handler(error()); |
| | | else |
| | | finish_handler(e); |
| | | } }); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void next_result(Handler handler) |
| | | { |
| | | SQLRETURN ret; |
| | | m_binded_cols = false; |
| | | ret = SQLMoreResults(m_handle); |
| | | async_wait(ret, [this, handler](const error& e) mutable { |
| | | template <typename Handler> |
| | | void next_result(Handler handler) |
| | | { |
| | | SQLRETURN ret; |
| | | m_binded_cols = false; |
| | | ret = SQLMoreResults(m_handle); |
| | | async_wait(ret, [this, handler](const error &e) mutable |
| | | { |
| | | SQLINTEGER ret=e.code(); |
| | | SQLSMALLINT count = 0; |
| | | if (ret == SQL_ERROR || ret == SQL_INVALID_HANDLE) |
| | |
| | | if (count > 0) |
| | | handler(error()); |
| | | else |
| | | next_result(handler); |
| | | }); |
| | | } |
| | | next_result(handler); }); |
| | | } |
| | | |
| | | HANDLE event_handle() const { return m_hCompleteEvent; } |
| | | HANDLE event_handle() const { return m_hCompleteEvent; } |
| | | |
| | | void close() |
| | | { |
| | | close_event(); |
| | | base_statement::close(); |
| | | } |
| | | void close() |
| | | { |
| | | close_event(); |
| | | base_statement::close(); |
| | | } |
| | | |
| | | template<typename CloseHandler> |
| | | void close(CloseHandler&& handler) |
| | | { |
| | | if (m_handle) |
| | | { |
| | | close_event(); |
| | | SQLRETURN ret = SQLFreeHandle(handler_type, m_handle); |
| | | if(SQL_SUCCEEDED(ret)) |
| | | m_handle = SQL_NULL_HANDLE; |
| | | handler(error(*this, ret)); |
| | | } |
| | | else |
| | | { |
| | | handler(error()); |
| | | } |
| | | } |
| | | template <typename CloseHandler> |
| | | void close(CloseHandler &&handler) |
| | | { |
| | | if (m_handle) |
| | | { |
| | | close_event(); |
| | | SQLRETURN ret = SQLFreeHandle(handler_type, m_handle); |
| | | if (SQL_SUCCEEDED(ret)) |
| | | m_handle = SQL_NULL_HANDLE; |
| | | handler(error(*this, ret)); |
| | | } |
| | | else |
| | | { |
| | | handler(error()); |
| | | } |
| | | } |
| | | |
| | | private: |
| | | private: |
| | | void close_event() |
| | | { |
| | | if (m_hCompleteEvent) |
| | | { |
| | | if (m_event) |
| | | m_event->remove(); |
| | | verify_error(SQLCancelHandle(handler_type, m_handle)); |
| | | verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_STMT_EVENT, NULL, SQL_IS_POINTER)); |
| | | verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_OFF, SQL_IS_INTEGER)); |
| | | CloseHandle(m_hCompleteEvent); |
| | | m_hCompleteEvent = NULL; |
| | | } |
| | | } |
| | | |
| | | void close_event() |
| | | { |
| | | if (m_hCompleteEvent) |
| | | { |
| | | if (m_event) |
| | | m_event->remove(); |
| | | verify_error(SQLCancelHandle(handler_type, m_handle)); |
| | | verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_STMT_EVENT, NULL, SQL_IS_POINTER)); |
| | | verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_OFF, SQL_IS_INTEGER)); |
| | | CloseHandle(m_hCompleteEvent); |
| | | m_hCompleteEvent = NULL; |
| | | } |
| | | } |
| | | template <typename Handler> |
| | | void async_wait(SQLINTEGER ret, Handler &&handler) NOEXCEPT |
| | | { |
| | | if (is_still_executing(ret)) |
| | | { |
| | | m_event->set_io_handler(0, m_nQueryTimeout, |
| | | [this, handler](int flags) mutable |
| | | { |
| | | RETCODE code; |
| | | SQLCompleteAsync(SQL_HANDLE_STMT, m_handle, &code); |
| | | if (SQL_SUCCEEDED(code)) |
| | | { |
| | | handler(odbc::error()); |
| | | } |
| | | else |
| | | { |
| | | SetEvent(m_hCompleteEvent); |
| | | handler(odbc::error(*this, code)); |
| | | } |
| | | }); |
| | | } |
| | | else |
| | | { |
| | | handler(error(*this, ret)); |
| | | } |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void async_wait(SQLINTEGER ret, Handler&& handler) NOEXCEPT |
| | | { |
| | | if(is_still_executing(ret)) |
| | | { |
| | | m_event->set_io_handler(0, m_nQueryTimeout, |
| | | [this, handler](int flags) mutable { |
| | | RETCODE code; |
| | | SQLCompleteAsync(SQL_HANDLE_STMT, m_handle, &code); |
| | | if (SQL_SUCCEEDED(code)) |
| | | { |
| | | handler(odbc::error()); |
| | | } |
| | | else |
| | | { |
| | | SetEvent(m_hCompleteEvent); |
| | | handler(odbc::error(*this, code)); |
| | | } |
| | | }); |
| | | } |
| | | else |
| | | { |
| | | handler(error(*this, ret)); |
| | | } |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void async_param_data(SQLSMALLINT index, SQLSMALLINT count, Handler&& handler) NOEXCEPT |
| | | { |
| | | SQLPOINTER token; |
| | | SQLRETURN ret = SQLParamData(m_handle, &token); |
| | | async_wait(ret, [this, index, count, token, handler](const error& e) mutable { |
| | | template <typename Handler> |
| | | void async_param_data(SQLSMALLINT index, SQLSMALLINT count, Handler &&handler) NOEXCEPT |
| | | { |
| | | SQLPOINTER token; |
| | | SQLRETURN ret = SQLParamData(m_handle, &token); |
| | | async_wait(ret, [this, index, count, token, handler](const error &e) mutable |
| | | { |
| | | SQLINTEGER ret = e.code(); |
| | | if (ret == SQL_NEED_DATA) |
| | | { |
| | |
| | | else |
| | | { |
| | | handler(error(*this, ret), affetced_rows()); |
| | | } }); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | int query_timeout() const |
| | | { |
| | | SQLULEN timeout = 0; |
| | | verify_error(SQLGetStmtAttr(m_handle, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)&timeout, NULL, NULL)); |
| | | return timeout; |
| | | } |
| | | int query_timeout() const |
| | | { |
| | | SQLULEN timeout = 0; |
| | | verify_error(SQLGetStmtAttr(m_handle, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)&timeout, NULL, NULL)); |
| | | return timeout; |
| | | } |
| | | |
| | | private: |
| | | HANDLE m_hCompleteEvent; |
| | | qtl::event* m_event; |
| | | SQLULEN m_nQueryTimeout; |
| | | }; |
| | | private: |
| | | HANDLE m_hCompleteEvent; |
| | | qtl::event *m_event; |
| | | SQLULEN m_nQueryTimeout; |
| | | }; |
| | | |
| | | class async_connection : public base_database, public qtl::async_connection<async_connection, async_statement> |
| | | { |
| | | public: |
| | | async_connection(environment& env) : base_database(env) |
| | | { |
| | | set_attribute(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, SQL_ASYNC_DBC_ENABLE_ON); |
| | | m_hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
| | | if (m_hCompleteEvent == NULL) |
| | | class async_connection : public base_database, public qtl::async_connection<async_connection, async_statement> |
| | | { |
| | | throw std::system_error(std::error_code(GetLastError(), std::system_category())); |
| | | } |
| | | set_attribute(SQL_ATTR_ASYNC_DBC_EVENT, m_hCompleteEvent); |
| | | } |
| | | async_connection(async_connection&& src) : |
| | | base_database(std::move(src)), |
| | | qtl::async_connection<async_connection, async_statement>(std::move(src)), |
| | | m_BindFunc(std::move(src.m_BindFunc)) |
| | | { |
| | | m_hCompleteEvent = src.m_hCompleteEvent; |
| | | src.m_hCompleteEvent = nullptr; |
| | | } |
| | | ~async_connection() |
| | | { |
| | | if (m_hCompleteEvent) |
| | | { |
| | | verify_error(SQLCancelHandle(handler_type, m_handle)); |
| | | set_attribute(SQL_ATTR_ASYNC_DBC_EVENT, (SQLPOINTER)NULL); |
| | | } |
| | | if (m_opened) |
| | | { |
| | | set_attribute(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, SQL_ASYNC_DBC_ENABLE_OFF); |
| | | verify_error(SQLDisconnect(m_handle)); |
| | | m_opened = false; |
| | | } |
| | | if (m_hCompleteEvent) |
| | | { |
| | | CloseHandle(m_hCompleteEvent); |
| | | } |
| | | } |
| | | |
| | | /* |
| | | OpenHandler defines as: |
| | | void handler(const qtl::odbc::error& e) NOEXCEPT; |
| | | */ |
| | | template<class EventLoop, typename OpenHandler> |
| | | void open(EventLoop& ev, OpenHandler&& handler, const char* server_name, size_t server_name_length, |
| | | const char* user_name, size_t user_name_length, const char* password, size_t password_length) |
| | | { |
| | | if (m_opened) close(); |
| | | SQLRETURN err = SQLConnectA(m_handle, (SQLCHAR*)server_name, static_cast<SQLSMALLINT>(server_name_length), |
| | | (SQLCHAR*)user_name, static_cast<SQLSMALLINT>(user_name_length), (SQLCHAR*)password, static_cast<SQLSMALLINT>(password_length)); |
| | | async_wait_connect(err, ev, std::forward<OpenHandler>(handler)); |
| | | } |
| | | template<class EventLoop, typename OpenHandler> |
| | | void open(EventLoop& ev, OpenHandler&& handler, const char* server_name, const char* user_name, const char* password) |
| | | { |
| | | if (m_opened) close(); |
| | | SQLRETURN err = SQLConnectA(m_handle, (SQLCHAR*)server_name, SQL_NTS, (SQLCHAR*)user_name, SQL_NTS, (SQLCHAR*)password, SQL_NTS); |
| | | async_wait_connect(err, ev, std::forward<OpenHandler>(handler)); |
| | | } |
| | | template<class EventLoop, typename OpenHandler> |
| | | void open(EventLoop& ev, OpenHandler&& handler, const std::string& server_name, const std::string& user_name, const std::string& password) |
| | | { |
| | | open(ev, std::forward<OpenHandler>(handler), server_name.data(), server_name.size(), user_name.data(), user_name.size(), password.data(), password.size()); |
| | | } |
| | | template<class EventLoop, typename OpenHandler> |
| | | void open(EventLoop& ev, OpenHandler&& handler, const char* input_text, size_t text_length = SQL_NTS, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL) |
| | | { |
| | | m_connection.resize(512); |
| | | SQLSMALLINT out_len=0; |
| | | SQLRETURN err = SQLDriverConnectA(m_handle, hwnd, (SQLCHAR*)input_text, (SQLSMALLINT)text_length, |
| | | (SQLCHAR*)m_connection.data(), (SQLSMALLINT)m_connection.size(), &out_len, driver_completion); |
| | | m_connection.resize(out_len); |
| | | async_wait_connect(err, ev, std::forward<OpenHandler>(handler)); |
| | | } |
| | | template<class EventLoop, typename OpenHandler> |
| | | void open(EventLoop& ev, OpenHandler&& handler, const std::string& input_text, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL) |
| | | { |
| | | open(ev, std::forward<OpenHandler>(handler), input_text.data(), input_text.size(), driver_completion, hwnd); |
| | | } |
| | | template<class EventLoop, typename OpenHandler> |
| | | void open(EventLoop& ev, OpenHandler&& handler, SQLHWND hwnd, SQLSMALLINT driver_completion = SQL_DRIVER_COMPLETE) |
| | | { |
| | | open(ev, std::forward<OpenHandler>(handler), "", SQL_NTS, driver_completion, hwnd); |
| | | } |
| | | |
| | | /* |
| | | CloseHandler defines as: |
| | | void handler(const qtl::odbc::error& e) NOEXCEPT; |
| | | */ |
| | | template<typename CloseHandler > |
| | | void close(CloseHandler&& handler) NOEXCEPT |
| | | { |
| | | SQLRETURN ret = SQLDisconnect(m_handle); |
| | | m_opened = false; |
| | | async_wait(ret, [this, handler](const error& e) { |
| | | if (!e) m_opened = false; |
| | | handler(e); |
| | | }); |
| | | } |
| | | |
| | | /* |
| | | ExecuteHandler defines as: |
| | | void handler(const qtl::odbc::error& e) NOEXCEPT; |
| | | */ |
| | | template<typename ExecuteHandler> |
| | | void simple_execute(ExecuteHandler&& handler, const char* query_text, size_t text_length = SQL_NTS) NOEXCEPT |
| | | { |
| | | statement command(*this); |
| | | SQLRETURN ret = SQLExecDirectA(command.handle(), (SQLCHAR*)query_text, static_cast<SQLINTEGER>(text_length)); |
| | | async_wait(ret, std::forward<ExecuteHandler>(handler)); |
| | | } |
| | | template<typename ExecuteHandler> |
| | | void simple_execute(ExecuteHandler&& handler, const std::string& query_text) |
| | | { |
| | | simple_execute(std::forward<ExecuteHandler>(handler), query_text.data(), query_text.size()); |
| | | } |
| | | |
| | | template<typename Handler> |
| | | void open_command(const char* query_text, size_t text_length, Handler&& handler) |
| | | { |
| | | std::shared_ptr<async_statement> stmt = std::make_shared<async_statement>(*this); |
| | | stmt->open([stmt, handler](const odbc::error& e) mutable { |
| | | handler(e, stmt); |
| | | }, query_text, text_length); |
| | | } |
| | | |
| | | HANDLE event_handle() const { return m_hCompleteEvent; } |
| | | |
| | | qtl::event* rebind(HANDLE hEvent) |
| | | { |
| | | return m_BindFunc(hEvent); |
| | | } |
| | | |
| | | private: |
| | | |
| | | template<typename Handler> |
| | | void async_wait(SQLRETURN ret, Handler&& handler) NOEXCEPT |
| | | { |
| | | if (is_still_executing(ret)) |
| | | { |
| | | m_event_handler->set_io_handler(0, connect_timeout(), |
| | | [this, handler](int flags) mutable { |
| | | RETCODE code; |
| | | SQLCompleteAsync(SQL_HANDLE_DBC, m_handle, &code); |
| | | if (SQL_SUCCEEDED(code)) |
| | | public: |
| | | async_connection(environment &env) : base_database(env) |
| | | { |
| | | set_attribute(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, SQL_ASYNC_DBC_ENABLE_ON); |
| | | m_hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
| | | if (m_hCompleteEvent == NULL) |
| | | { |
| | | handler(odbc::error()); |
| | | throw std::system_error(std::error_code(GetLastError(), std::system_category())); |
| | | } |
| | | set_attribute(SQL_ATTR_ASYNC_DBC_EVENT, m_hCompleteEvent); |
| | | } |
| | | async_connection(async_connection &&src) : base_database(std::move(src)), |
| | | qtl::async_connection<async_connection, async_statement>(std::move(src)), |
| | | m_BindFunc(std::move(src.m_BindFunc)) |
| | | { |
| | | m_hCompleteEvent = src.m_hCompleteEvent; |
| | | src.m_hCompleteEvent = nullptr; |
| | | } |
| | | ~async_connection() |
| | | { |
| | | if (m_hCompleteEvent) |
| | | { |
| | | verify_error(SQLCancelHandle(handler_type, m_handle)); |
| | | set_attribute(SQL_ATTR_ASYNC_DBC_EVENT, (SQLPOINTER)NULL); |
| | | } |
| | | if (m_opened) |
| | | { |
| | | set_attribute(SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, SQL_ASYNC_DBC_ENABLE_OFF); |
| | | verify_error(SQLDisconnect(m_handle)); |
| | | m_opened = false; |
| | | } |
| | | if (m_hCompleteEvent) |
| | | { |
| | | CloseHandle(m_hCompleteEvent); |
| | | } |
| | | } |
| | | |
| | | /* |
| | | OpenHandler defines as: |
| | | void handler(const qtl::odbc::error& e) NOEXCEPT; |
| | | */ |
| | | template <class EventLoop, typename OpenHandler> |
| | | void open(EventLoop &ev, OpenHandler &&handler, const char *server_name, size_t server_name_length, |
| | | const char *user_name, size_t user_name_length, const char *password, size_t password_length) |
| | | { |
| | | if (m_opened) |
| | | close(); |
| | | SQLRETURN err = SQLConnectA(m_handle, (SQLCHAR *)server_name, static_cast<SQLSMALLINT>(server_name_length), |
| | | (SQLCHAR *)user_name, static_cast<SQLSMALLINT>(user_name_length), (SQLCHAR *)password, static_cast<SQLSMALLINT>(password_length)); |
| | | async_wait_connect(err, ev, std::forward<OpenHandler>(handler)); |
| | | } |
| | | template <class EventLoop, typename OpenHandler> |
| | | void open(EventLoop &ev, OpenHandler &&handler, const char *server_name, const char *user_name, const char *password) |
| | | { |
| | | if (m_opened) |
| | | close(); |
| | | SQLRETURN err = SQLConnectA(m_handle, (SQLCHAR *)server_name, SQL_NTS, (SQLCHAR *)user_name, SQL_NTS, (SQLCHAR *)password, SQL_NTS); |
| | | async_wait_connect(err, ev, std::forward<OpenHandler>(handler)); |
| | | } |
| | | template <class EventLoop, typename OpenHandler> |
| | | void open(EventLoop &ev, OpenHandler &&handler, const std::string &server_name, const std::string &user_name, const std::string &password) |
| | | { |
| | | open(ev, std::forward<OpenHandler>(handler), server_name.data(), server_name.size(), user_name.data(), user_name.size(), password.data(), password.size()); |
| | | } |
| | | template <class EventLoop, typename OpenHandler> |
| | | void open(EventLoop &ev, OpenHandler &&handler, const char *input_text, size_t text_length = SQL_NTS, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL) |
| | | { |
| | | m_connection.resize(512); |
| | | SQLSMALLINT out_len = 0; |
| | | SQLRETURN err = SQLDriverConnectA(m_handle, hwnd, (SQLCHAR *)input_text, (SQLSMALLINT)text_length, |
| | | (SQLCHAR *)m_connection.data(), (SQLSMALLINT)m_connection.size(), &out_len, driver_completion); |
| | | m_connection.resize(out_len); |
| | | async_wait_connect(err, ev, std::forward<OpenHandler>(handler)); |
| | | } |
| | | template <class EventLoop, typename OpenHandler> |
| | | void open(EventLoop &ev, OpenHandler &&handler, const std::string &input_text, SQLSMALLINT driver_completion = SQL_DRIVER_NOPROMPT, SQLHWND hwnd = NULL) |
| | | { |
| | | open(ev, std::forward<OpenHandler>(handler), input_text.data(), input_text.size(), driver_completion, hwnd); |
| | | } |
| | | template <class EventLoop, typename OpenHandler> |
| | | void open(EventLoop &ev, OpenHandler &&handler, SQLHWND hwnd, SQLSMALLINT driver_completion = SQL_DRIVER_COMPLETE) |
| | | { |
| | | open(ev, std::forward<OpenHandler>(handler), "", SQL_NTS, driver_completion, hwnd); |
| | | } |
| | | |
| | | /* |
| | | CloseHandler defines as: |
| | | void handler(const qtl::odbc::error& e) NOEXCEPT; |
| | | */ |
| | | template <typename CloseHandler> |
| | | void close(CloseHandler &&handler) NOEXCEPT |
| | | { |
| | | SQLRETURN ret = SQLDisconnect(m_handle); |
| | | m_opened = false; |
| | | async_wait(ret, [this, handler](const error &e) |
| | | { |
| | | if (!e) m_opened = false; |
| | | handler(e); }); |
| | | } |
| | | |
| | | /* |
| | | ExecuteHandler defines as: |
| | | void handler(const qtl::odbc::error& e) NOEXCEPT; |
| | | */ |
| | | template <typename ExecuteHandler> |
| | | void simple_execute(ExecuteHandler &&handler, const char *query_text, size_t text_length = SQL_NTS) NOEXCEPT |
| | | { |
| | | statement command(*this); |
| | | SQLRETURN ret = SQLExecDirectA(command.handle(), (SQLCHAR *)query_text, static_cast<SQLINTEGER>(text_length)); |
| | | async_wait(ret, std::forward<ExecuteHandler>(handler)); |
| | | } |
| | | template <typename ExecuteHandler> |
| | | void simple_execute(ExecuteHandler &&handler, const std::string &query_text) |
| | | { |
| | | simple_execute(std::forward<ExecuteHandler>(handler), query_text.data(), query_text.size()); |
| | | } |
| | | |
| | | template <typename Handler> |
| | | void open_command(const char *query_text, size_t text_length, Handler &&handler) |
| | | { |
| | | std::shared_ptr<async_statement> stmt = std::make_shared<async_statement>(*this); |
| | | stmt->open([stmt, handler](const odbc::error &e) mutable |
| | | { handler(e, stmt); }, query_text, text_length); |
| | | } |
| | | |
| | | HANDLE event_handle() const { return m_hCompleteEvent; } |
| | | |
| | | qtl::event *rebind(HANDLE hEvent) |
| | | { |
| | | return m_BindFunc(hEvent); |
| | | } |
| | | |
| | | private: |
| | | template <typename Handler> |
| | | void async_wait(SQLRETURN ret, Handler &&handler) NOEXCEPT |
| | | { |
| | | if (is_still_executing(ret)) |
| | | { |
| | | m_event_handler->set_io_handler(0, connect_timeout(), |
| | | [this, handler](int flags) mutable |
| | | { |
| | | RETCODE code; |
| | | SQLCompleteAsync(SQL_HANDLE_DBC, m_handle, &code); |
| | | if (SQL_SUCCEEDED(code)) |
| | | { |
| | | handler(odbc::error()); |
| | | } |
| | | else |
| | | { |
| | | SetEvent(m_hCompleteEvent); |
| | | handler(odbc::error(*this, code)); |
| | | } |
| | | }); |
| | | } |
| | | else |
| | | { |
| | | SetEvent(m_hCompleteEvent); |
| | | handler(odbc::error(*this, code)); |
| | | handler(odbc::error(*this, ret)); |
| | | } |
| | | }); |
| | | } |
| | | else |
| | | { |
| | | handler(odbc::error(*this, ret)); |
| | | } |
| | | } |
| | | |
| | | template<typename EventLoop, typename Handler> |
| | | void async_wait_connect(SQLRETURN err, EventLoop& ev, Handler&& handler) |
| | | { |
| | | bind(ev); |
| | | m_BindFunc = [&ev](HANDLE hEvent) { |
| | | return ev.add(hEvent); |
| | | }; |
| | | if(is_still_executing(err)) |
| | | { |
| | | async_wait(err, [this, handler](const error& e) mutable { |
| | | if (!e) m_opened = true; |
| | | handler(e); |
| | | }); |
| | | } |
| | | else |
| | | { |
| | | handler(odbc::error(*this, err)); |
| | | } |
| | | } |
| | | |
| | | int connect_timeout() const |
| | | { |
| | | int timeout=0; |
| | | verify_error(SQLGetConnectAttr(m_handle, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)&timeout, 0, NULL)); |
| | | return timeout; |
| | | } |
| | | |
| | | private: |
| | | HANDLE m_hCompleteEvent; |
| | | std::function<qtl::event*(HANDLE)> m_BindFunc; |
| | | }; |
| | | |
| | | inline async_statement::async_statement(async_connection& db) |
| | | : base_statement(static_cast<base_database&>(db)) |
| | | { |
| | | verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER)); |
| | | m_hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
| | | if (m_hCompleteEvent == NULL) |
| | | { |
| | | throw std::system_error(std::error_code(GetLastError(), std::system_category())); |
| | | } |
| | | verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_STMT_EVENT, m_hCompleteEvent, SQL_IS_POINTER)); |
| | | m_event = db.rebind(this->m_hCompleteEvent); |
| | | m_nQueryTimeout = query_timeout(); |
| | | } |
| | | |
| | | #endif //ODBC 3.80 |
| | | |
| | | typedef qtl::transaction<database> transaction; |
| | | |
| | | template<typename Record> |
| | | using query_iterator = qtl::query_iterator<statement, Record>; |
| | | |
| | | template<typename Record> |
| | | using query_result = qtl::query_result<statement, Record>; |
| | | |
| | | template<typename Params> |
| | | inline statement& operator<<(statement& stmt, const Params& params) |
| | | { |
| | | stmt.reset(); |
| | | stmt.execute(params); |
| | | return stmt; |
| | | } |
| | | |
| | | template<SQLSMALLINT Type> |
| | | inline error::error(const object<Type>& h, SQLINTEGER code) |
| | | { |
| | | m_errno=code; |
| | | if(code==SQL_ERROR || code==SQL_SUCCESS_WITH_INFO) |
| | | { |
| | | SQLSMALLINT i=0; |
| | | SQLINTEGER err=SQL_SUCCESS; |
| | | SQLCHAR message[SQL_MAX_MESSAGE_LENGTH]; |
| | | SQLCHAR state[SQL_SQLSTATE_SIZE+1]; |
| | | std::ostringstream oss; |
| | | SQLRETURN ret = SQLGetDiagRecA(object<Type>::handler_type, h.handle(), ++i, state, &err, |
| | | message, SQL_MAX_MESSAGE_LENGTH, NULL); |
| | | while(ret==SQL_SUCCESS) |
| | | { |
| | | oss<<"["<<state<<"] ("<<err<<") "<<message<<std::endl; |
| | | ret = SQLGetDiagRecA(object<Type>::handler_type, h.handle(), ++i, state, &err, |
| | | message, SQL_MAX_MESSAGE_LENGTH, NULL); |
| | | } |
| | | m_errmsg=oss.str(); |
| | | } |
| | | else if(code==SQL_INVALID_HANDLE) |
| | | { |
| | | m_errmsg="Invalid handle."; |
| | | } |
| | | } |
| | | |
| | | inline void base_database::parse_browse_string(const char* output_text, size_t text_length, connection_parameters& parameters) |
| | | { |
| | | enum { part_name, part_prompt, part_list, part_value }; |
| | | const char* sp=output_text; |
| | | const char* token=sp; |
| | | connection_parameter parameter; |
| | | int part_type=part_name; |
| | | while(sp!=output_text+text_length) |
| | | { |
| | | switch(*sp) |
| | | { |
| | | case ';': |
| | | parameters.emplace_back(parameter); |
| | | parameter.reset(); |
| | | part_type=part_name; |
| | | token=sp+1; |
| | | break; |
| | | case '=': |
| | | if(part_type==part_prompt) |
| | | parameter.m_prompt.assign(token, sp-token); |
| | | part_type=part_value; |
| | | token=sp+1; |
| | | break; |
| | | case ':': |
| | | if(part_type==part_name) |
| | | parameter.m_name.assign(token, sp-token); |
| | | part_type=part_prompt; |
| | | token=sp+1; |
| | | break; |
| | | case '{': |
| | | part_type=part_list; |
| | | parameter.m_value_list.clear(); |
| | | token=sp+1; |
| | | break;; |
| | | case '}': |
| | | case ',': |
| | | if(part_type==part_list) |
| | | parameter.m_value_list.emplace_back(token, sp-token); |
| | | token=sp+1; |
| | | break; |
| | | case '*': |
| | | if(part_type==part_name && token==sp) |
| | | { |
| | | parameter.m_optinal=true; |
| | | token=sp+1; |
| | | } |
| | | break; |
| | | case '?': |
| | | token=sp+1; |
| | | break; |
| | | |
| | | template <typename EventLoop, typename Handler> |
| | | void async_wait_connect(SQLRETURN err, EventLoop &ev, Handler &&handler) |
| | | { |
| | | bind(ev); |
| | | m_BindFunc = [&ev](HANDLE hEvent) |
| | | { |
| | | return ev.add(hEvent); |
| | | }; |
| | | if (is_still_executing(err)) |
| | | { |
| | | async_wait(err, [this, handler](const error &e) mutable |
| | | { |
| | | if (!e) m_opened = true; |
| | | handler(e); }); |
| | | } |
| | | else |
| | | { |
| | | handler(odbc::error(*this, err)); |
| | | } |
| | | } |
| | | |
| | | int connect_timeout() const |
| | | { |
| | | int timeout = 0; |
| | | verify_error(SQLGetConnectAttr(m_handle, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)&timeout, 0, NULL)); |
| | | return timeout; |
| | | } |
| | | |
| | | private: |
| | | HANDLE m_hCompleteEvent; |
| | | std::function<qtl::event *(HANDLE)> m_BindFunc; |
| | | }; |
| | | |
| | | inline async_statement::async_statement(async_connection &db) |
| | | : base_statement(static_cast<base_database &>(db)) |
| | | { |
| | | verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER)); |
| | | m_hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
| | | if (m_hCompleteEvent == NULL) |
| | | { |
| | | throw std::system_error(std::error_code(GetLastError(), std::system_category())); |
| | | } |
| | | verify_error(SQLSetStmtAttr(m_handle, SQL_ATTR_ASYNC_STMT_EVENT, m_hCompleteEvent, SQL_IS_POINTER)); |
| | | m_event = db.rebind(this->m_hCompleteEvent); |
| | | m_nQueryTimeout = query_timeout(); |
| | | } |
| | | ++sp; |
| | | } |
| | | if(!parameter.m_name.empty()) |
| | | parameters.emplace_back(parameter); |
| | | } |
| | | |
| | | inline std::string base_database::create_connection_text(const connection_parameters& parameters) |
| | | { |
| | | std::ostringstream oss; |
| | | for(auto& parameter : parameters) |
| | | { |
| | | if(parameter.m_assigned) |
| | | oss<<parameter.m_name<<'='<<parameter.m_value<<';'; |
| | | } |
| | | return oss.str(); |
| | | } |
| | | #endif // ODBC 3.80 |
| | | |
| | | inline base_statement::base_statement(base_database& db) |
| | | : object(db.handle()), m_blob_buffer(NULL), m_binded_cols(false) |
| | | { |
| | | } |
| | | typedef qtl::transaction<database> transaction; |
| | | |
| | | } //odbc |
| | | template <typename Record> |
| | | using query_iterator = qtl::query_iterator<statement, Record>; |
| | | |
| | | template <typename Record> |
| | | using query_result = qtl::query_result<statement, Record>; |
| | | |
| | | template <typename Params> |
| | | inline statement &operator<<(statement &stmt, const Params ¶ms) |
| | | { |
| | | stmt.reset(); |
| | | stmt.execute(params); |
| | | return stmt; |
| | | } |
| | | |
| | | template <SQLSMALLINT Type> |
| | | inline error::error(const object<Type> &h, SQLINTEGER code) |
| | | { |
| | | m_errno = code; |
| | | if (code == SQL_ERROR || code == SQL_SUCCESS_WITH_INFO) |
| | | { |
| | | SQLSMALLINT i = 0; |
| | | SQLINTEGER err = SQL_SUCCESS; |
| | | SQLCHAR message[SQL_MAX_MESSAGE_LENGTH]; |
| | | SQLCHAR state[SQL_SQLSTATE_SIZE + 1]; |
| | | std::ostringstream oss; |
| | | SQLRETURN ret = SQLGetDiagRecA(object<Type>::handler_type, h.handle(), ++i, state, &err, |
| | | message, SQL_MAX_MESSAGE_LENGTH, NULL); |
| | | while (ret == SQL_SUCCESS) |
| | | { |
| | | oss << "[" << state << "] (" << err << ") " << message << std::endl; |
| | | ret = SQLGetDiagRecA(object<Type>::handler_type, h.handle(), ++i, state, &err, |
| | | message, SQL_MAX_MESSAGE_LENGTH, NULL); |
| | | } |
| | | m_errmsg = oss.str(); |
| | | } |
| | | else if (code == SQL_INVALID_HANDLE) |
| | | { |
| | | m_errmsg = "Invalid handle."; |
| | | } |
| | | } |
| | | |
| | | inline void base_database::parse_browse_string(const char *output_text, size_t text_length, connection_parameters ¶meters) |
| | | { |
| | | enum |
| | | { |
| | | part_name, |
| | | part_prompt, |
| | | part_list, |
| | | part_value |
| | | }; |
| | | const char *sp = output_text; |
| | | const char *token = sp; |
| | | connection_parameter parameter; |
| | | int part_type = part_name; |
| | | while (sp != output_text + text_length) |
| | | { |
| | | switch (*sp) |
| | | { |
| | | case ';': |
| | | parameters.emplace_back(parameter); |
| | | parameter.reset(); |
| | | part_type = part_name; |
| | | token = sp + 1; |
| | | break; |
| | | case '=': |
| | | if (part_type == part_prompt) |
| | | parameter.m_prompt.assign(token, sp - token); |
| | | part_type = part_value; |
| | | token = sp + 1; |
| | | break; |
| | | case ':': |
| | | if (part_type == part_name) |
| | | parameter.m_name.assign(token, sp - token); |
| | | part_type = part_prompt; |
| | | token = sp + 1; |
| | | break; |
| | | case '{': |
| | | part_type = part_list; |
| | | parameter.m_value_list.clear(); |
| | | token = sp + 1; |
| | | break; |
| | | ; |
| | | case '}': |
| | | case ',': |
| | | if (part_type == part_list) |
| | | parameter.m_value_list.emplace_back(token, sp - token); |
| | | token = sp + 1; |
| | | break; |
| | | case '*': |
| | | if (part_type == part_name && token == sp) |
| | | { |
| | | parameter.m_optinal = true; |
| | | token = sp + 1; |
| | | } |
| | | break; |
| | | case '?': |
| | | token = sp + 1; |
| | | break; |
| | | } |
| | | ++sp; |
| | | } |
| | | if (!parameter.m_name.empty()) |
| | | parameters.emplace_back(parameter); |
| | | } |
| | | |
| | | inline std::string base_database::create_connection_text(const connection_parameters ¶meters) |
| | | { |
| | | std::ostringstream oss; |
| | | for (auto ¶meter : parameters) |
| | | { |
| | | if (parameter.m_assigned) |
| | | oss << parameter.m_name << '=' << parameter.m_value << ';'; |
| | | } |
| | | return oss.str(); |
| | | } |
| | | |
| | | inline base_statement::base_statement(base_database &db) |
| | | : object(db.handle()), m_blob_buffer(NULL), m_binded_cols(false) |
| | | { |
| | | } |
| | | |
| | | } // odbc |
| | | |
| | | #ifdef _WIN32 |
| | | |
| | | namespace mssql |
| | | { |
| | | |
| | | class database : public odbc::database |
| | | { |
| | | public: |
| | | explicit database(odbc::environment& env) : odbc::database(env) { } |
| | | database(database&& src) : odbc::database(std::move(src)) { } |
| | | |
| | | void open(const char* server, const char* db=NULL, const char* user=NULL, const char* password=NULL) |
| | | namespace mssql |
| | | { |
| | | std::ostringstream oss; |
| | | oss<<"DRIVER={SQL Server};SERVER="<<server<<";"; |
| | | if(user==NULL) |
| | | oss<<"UID=;PWD=;Trusted_Connection=yes;"; |
| | | else |
| | | |
| | | class database : public odbc::database |
| | | { |
| | | oss<<"UID="<<user<<";PWD="; |
| | | if(password) oss<<password; |
| | | oss<<";Trusted_Connection=no;"; |
| | | } |
| | | oss<<"DATABASE="<<db; |
| | | odbc::database::open(oss.str()); |
| | | } |
| | | }; |
| | | public: |
| | | explicit database(odbc::environment &env) : odbc::database(env) {} |
| | | database(database &&src) : odbc::database(std::move(src)) {} |
| | | |
| | | void open(const char *server, const char *db = NULL, const char *user = NULL, const char *password = NULL) |
| | | { |
| | | std::ostringstream oss; |
| | | oss << "DRIVER={SQL Server};SERVER=" << server << ";"; |
| | | if (user == NULL) |
| | | oss << "UID=;PWD=;Trusted_Connection=yes;"; |
| | | else |
| | | { |
| | | oss << "UID=" << user << ";PWD="; |
| | | if (password) |
| | | oss << password; |
| | | oss << ";Trusted_Connection=no;"; |
| | | } |
| | | oss << "DATABASE=" << db; |
| | | odbc::database::open(oss.str()); |
| | | } |
| | | }; |
| | | |
| | | #ifdef QTL_ODBC_ENABLE_ASYNC_MODE |
| | | |
| | | class async_connection : public odbc::async_connection |
| | | { |
| | | public: |
| | | explicit async_connection(odbc::environment& env) : odbc::async_connection(env) { } |
| | | async_connection(async_connection&& src) : odbc::async_connection(std::move(src)) { } |
| | | |
| | | template<typename EventLoop, typename OpenHandler> |
| | | void open(EventLoop& ev, const OpenHandler& handler, const char* server, const char* db = NULL, const char* user = NULL, const char* password = NULL) |
| | | { |
| | | std::ostringstream oss; |
| | | oss << "DRIVER={ODBC Driver 11 for SQL Server};SERVER=" << server << ";"; |
| | | if (user == NULL) |
| | | oss << "UID=;PWD=;Trusted_Connection=yes;"; |
| | | else |
| | | class async_connection : public odbc::async_connection |
| | | { |
| | | oss << "UID=" << user << ";PWD="; |
| | | if (password) oss << password; |
| | | oss << ";Trusted_Connection=no;"; |
| | | } |
| | | oss << "DATABASE=" << db; |
| | | odbc::async_connection::open(ev, handler, oss.str()); |
| | | } |
| | | public: |
| | | explicit async_connection(odbc::environment &env) : odbc::async_connection(env) {} |
| | | async_connection(async_connection &&src) : odbc::async_connection(std::move(src)) {} |
| | | |
| | | }; |
| | | template <typename EventLoop, typename OpenHandler> |
| | | void open(EventLoop &ev, const OpenHandler &handler, const char *server, const char *db = NULL, const char *user = NULL, const char *password = NULL) |
| | | { |
| | | std::ostringstream oss; |
| | | oss << "DRIVER={ODBC Driver 11 for SQL Server};SERVER=" << server << ";"; |
| | | if (user == NULL) |
| | | oss << "UID=;PWD=;Trusted_Connection=yes;"; |
| | | else |
| | | { |
| | | oss << "UID=" << user << ";PWD="; |
| | | if (password) |
| | | oss << password; |
| | | oss << ";Trusted_Connection=no;"; |
| | | } |
| | | oss << "DATABASE=" << db; |
| | | odbc::async_connection::open(ev, handler, oss.str()); |
| | | } |
| | | }; |
| | | |
| | | #endif |
| | | |
| | | } //mssql |
| | | } // mssql |
| | | |
| | | namespace msaccess |
| | | { |
| | | |
| | | class database : public odbc::database |
| | | { |
| | | public: |
| | | explicit database(odbc::environment& env) : odbc::database(env) { } |
| | | database(database&& src) : odbc::database(std::forward<database>(src)) { } |
| | | |
| | | void open(const char* filename, const char* user=NULL, const char* password=NULL) |
| | | namespace msaccess |
| | | { |
| | | std::ostringstream oss; |
| | | oss<<"DRIVER={Microsoft Access Driver};DBQ="<<filename; |
| | | if(user) oss<<";UID:"<<user; |
| | | if(password) oss<<";PWD="<<password; |
| | | odbc::database::open(oss.str()); |
| | | } |
| | | }; |
| | | |
| | | } //msaccess |
| | | class database : public odbc::database |
| | | { |
| | | public: |
| | | explicit database(odbc::environment &env) : odbc::database(env) {} |
| | | database(database &&src) : odbc::database(std::forward<database>(src)) {} |
| | | |
| | | void open(const char *filename, const char *user = NULL, const char *password = NULL) |
| | | { |
| | | std::ostringstream oss; |
| | | oss << "DRIVER={Microsoft Access Driver};DBQ=" << filename; |
| | | if (user) |
| | | oss << ";UID:" << user; |
| | | if (password) |
| | | oss << ";PWD=" << password; |
| | | odbc::database::open(oss.str()); |
| | | } |
| | | }; |
| | | |
| | | } // msaccess |
| | | |
| | | #endif //_WIN32 |
| | | |