6 files added
9 files modified
| | |
| | | # QTL |
| | | QTL是一个访问SQL数据库的C++库,目前支持MySQL和SQLite。QTL是一个轻量级的库,只由头文件组成,不需要单独编译安装。QTL是对数据库原生客户端接口的薄封装,能提供友好使用方式的同时拥有接近于使用原生接口的性能。 |
| | | QTL是一个访问SQL数据库的C++库,目前支持MySQL、SQLite和ODBC。QTL是一个轻量级的库,只由头文件组成,不需要单独编译安装。QTL是对数据库原生客户端接口的薄封装,能提供友好使用方式的同时拥有接近于使用原生接口的性能。 |
| | | 使用QTL需要支持C++11的编译器。 |
| | | |
| | | ## 使用方式 |
| | |
| | | - qtl::sqlite::query_result |
| | | 表示一个SQLite的查询结果集,用于以迭代器方式遍历查询结果。 |
| | | |
| | | ## 有关ODBC的说明 |
| | | |
| | | 通过ODBC访问数据库时,包含头文件qtl_odbc.hpp。 |
| | | QTL不支持ODBC的输出参数。 |
| | | |
| | | ### ODBC的参数数据绑定 |
| | | |
| | | | 参数类型 | C++类型 | |
| | | | ------- | ------ | |
| | | | TINYINT | int8_t<br>uint8_t | |
| | | | SMALLINT | int16_t<br>uint16_t | |
| | | | INTEGER | int32_t<br>uint32_t | |
| | | | BIGINT | int64_t<br>uint64_t | |
| | | | FLOAT | float | |
| | | | DOUBLE | double | |
| | | | NUMERIC | SQL_NUMERIC_STRUCT | |
| | | | BIT | bool | |
| | | | CHAR<br>VARCHAR | const char*<br>std::string | |
| | | | WCHAR<br>WVARCHAR | const wchar_t*<br>std::wstring | |
| | | | BINARY | qtl::const_blob_data | |
| | | | LONGVARBINARY | std::istream | |
| | | | DATE | qtl::odbc::date | |
| | | | TIME<br>UTCTIME | qtl::odbc::time | |
| | | | TIMESTAMP<br>UTCDATETIME | qtl::odbc::datetime | |
| | | | GUID | SQLGUID | |
| | | |
| | | ### ODBC的字段数据绑定 |
| | | |
| | | | 字段类型 | C++类型 | |
| | | | ------- | ------ | |
| | | | TINYINT | int8_t<br>uint8_t | |
| | | | SMALLINT | int16_t<br>uint16_t | |
| | | | INTEGER | int32_t<br>uint32_t | |
| | | | BIGINT | int64_t<br>uint64_t | |
| | | | FLOAT | float | |
| | | | DOUBLE | double | |
| | | | NUMERIC | SQL_NUMERIC_STRUCT | |
| | | | BIT | bool | |
| | | | CHAR<br>VARCHAR | char[N]<br>std::array<char, N><br>std::string | |
| | | | WCHAR<br>WVARCHAR | wchar_t[N]<br>std::array<wchar_t, N><br>std::string | |
| | | | BINARY | qtl::blob_data | |
| | | | LONGVARBINARY | std::ostream | |
| | | | DATE | qtl::odbc::date | |
| | | | TIME<br>UTCTIME | qtl::odbc::time | |
| | | | TIMESTAMP<br>UTCDATETIME | qtl::odbc::datetime | |
| | | | GUID | SQLGUID | |
| | | |
| | | ### ODBC相关的C++类 |
| | | - qtl::odbc::database |
| | | 表示一个ODBC的数据库连接,程序主要通过这个类操纵数据库。 |
| | | - qtl::odbc::statement |
| | | 表示一个ODBC的查询语句,实现查询相关操作。 |
| | | - qtl::odbc::error |
| | | 表示一个ODBC的错误,当操作出错时,抛出该类型的异常,包含错误信息。 |
| | | - qtl::odbc::transaction |
| | | 表示一个ODBC的事务操作。 |
| | | - qtl::odbc::query_result |
| | | 表示一个ODBC的查询结果集,用于以迭代器方式遍历查询结果。 |
| | | |
| | | ## 关于测试 |
| | | |
| | | 编译测试用例的第三方库需要另外下载。除了数据库相关的库外,测试用例用到了测试框架[CppTest](https://sourceforge.net/projects/cpptest/ "CppTest")。 |
| | |
| | | binder(command, std::forward<T>(value)); |
| | | } |
| | | |
| | | template<typename Command, typename T> |
| | | inline void bind_record(Command& command, T& value) |
| | | { |
| | | bind_record(command, std::forward<T>(value)); |
| | | } |
| | | |
| | | template<typename Command, typename Record> |
| | | class query_iterator final : public std::iterator<std::forward_iterator_tag, Record> |
| | | { |
| | |
| | | std::thread m_background_thread; |
| | | bool m_stop_thread; |
| | | |
| | | virtual bool open_database(Database& db)=0; |
| | | virtual Database* new_database() throw()=0; |
| | | void recovery(Database* db) |
| | | { |
| | | if(db==NULL) return; |
| | |
| | | |
| | | Database* create_database() |
| | | { |
| | | Database* db=new Database; |
| | | if(open_database(*db)) |
| | | return db; |
| | | Database* db=new_database(); |
| | | if(db) return db; |
| | | |
| | | { |
| | | std::lock_guard<std::mutex> lock(m_pool_mutex); |
| | |
| | | m_binderAddins[index].m_after_fetch=[](const binder& bind) { |
| | | if(*bind.is_null) |
| | | memset(bind.buffer, 0, bind.buffer_length+1); |
| | | else |
| | | { |
| | | char* text=reinterpret_cast<char*>(bind.buffer); |
| | | text[*bind.length]='\0'; |
| | | } |
| | | }; |
| | | } |
| | | |
| | |
| | | public: |
| | | database_pool() : m_port(0) { } |
| | | virtual ~database_pool() { } |
| | | virtual bool open_database(database& db) override |
| | | virtual database* new_database() throw() override |
| | | { |
| | | db.charset_name("utf8"); |
| | | return db.open(m_host.data(), m_user.data(), m_password.data(), m_database.data(), 0, m_port); |
| | | database* db=new database; |
| | | db->charset_name("utf8"); |
| | | if(!db->open(m_host.data(), m_user.data(), m_password.data(), m_database.data(), 0, m_port)) |
| | | { |
| | | delete db; |
| | | db=NULL; |
| | | } |
| | | return db; |
| | | } |
| | | |
| | | protected: |
| New file |
| | |
| | | #ifndef _QTL_ODCB_H_ |
| | | #define _QTL_ODCB_H_ |
| | | |
| | | #include <sql.h> |
| | | #include <sqlext.h> |
| | | #include <sstream> |
| | | #include <vector> |
| | | #include <array> |
| | | #include <time.h> |
| | | #include <assert.h> |
| | | #include <malloc.h> |
| | | #include <limits.h> |
| | | |
| | | #if !defined(_WIN32) || defined(__MINGW32__) |
| | | #include <sys/time.h> |
| | | #endif //_WIN32 |
| | | |
| | | #include "qtl_common.hpp" |
| | | |
| | | namespace qtl |
| | | { |
| | | |
| | | namespace odbc |
| | | { |
| | | |
| | | template<SQLSMALLINT> class object; |
| | | class database; |
| | | |
| | | class error : public std::exception |
| | | { |
| | | public: |
| | | 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() { 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) |
| | | { |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | protected: |
| | | SQLHANDLE m_handle; |
| | | void verify_error(SQLINTEGER code) const |
| | | { |
| | | if(code<0) |
| | | throw odbc::error(*this, code); |
| | | } |
| | | }; |
| | | |
| | | class environment final : public object<SQL_HANDLE_ENV> |
| | | { |
| | | public: |
| | | environment() : object(SQL_NULL_HANDLE) |
| | | { |
| | | verify_error(SQLSetEnvAttr(m_handle, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0)); |
| | | } |
| | | environment(environment&& src) : object(std::forward<environment>(src)) { } |
| | | }; |
| | | |
| | | class statement final : public object<SQL_HANDLE_STMT> |
| | | { |
| | | public: |
| | | explicit statement(database& db); |
| | | statement(statement&& src) |
| | | : object(std::forward<statement>(src)), m_params(std::forward<std::vector<param_data>>(src.m_params)) |
| | | { |
| | | m_binded_cols=src.m_binded_cols; |
| | | src.m_binded_cols=false; |
| | | m_blob_buffer=src.m_blob_buffer; |
| | | src.m_blob_buffer=NULL; |
| | | } |
| | | ~statement() |
| | | { |
| | | if(m_blob_buffer) |
| | | free(m_blob_buffer); |
| | | } |
| | | statement& operator=(statement&& src) |
| | | { |
| | | if(this!=&src) |
| | | { |
| | | object::operator =(std::forward<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 open(const char* query_text, size_t text_length=SQL_NTS) |
| | | { |
| | | verify_error(SQLPrepare(m_handle, (SQLCHAR*)query_text, text_length)); |
| | | } |
| | | void open(const std::string& query_text) |
| | | { |
| | | open(query_text.data(), query_text.size()); |
| | | } |
| | | |
| | | void bind_param(SQLUSMALLINT index, const std::nullptr_t&) |
| | | { |
| | | m_params[index].m_indicator=SQL_NULL_DATA; |
| | | verify_error(SQLBindParameter(m_handle, index+1, |
| | | SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_DEFAULT, 0, 0, NULL, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const qtl::null&) |
| | | { |
| | | bind_param(index, nullptr); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const int8_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_STINYINT, SQL_TINYINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const uint8_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_UTINYINT, SQL_TINYINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const int16_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_SMALLINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const uint16_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_USHORT, SQL_SMALLINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const int32_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const uint32_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const int64_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const uint64_t& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const double& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const float& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const bool& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const DATE_STRUCT& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_DATE, SQL_DATE, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const TIME_STRUCT& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_TIME, SQL_TIME, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const TIMESTAMP_STRUCT& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const SQLGUID& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_GUID, SQL_GUID, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const SQL_NUMERIC_STRUCT& v) |
| | | { |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_NUMERIC, SQL_NUMERIC, |
| | | 0, 0, (SQLPOINTER)&v, 0, NULL)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const char* v, size_t n=SQL_NTS, SQLULEN size=0) |
| | | { |
| | | m_params[index].m_indicator=n; |
| | | if(size==0) size=strlen(v); |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, |
| | | size, 0, (SQLPOINTER)v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const wchar_t* v, size_t n=SQL_NTS, SQLULEN size=0) |
| | | { |
| | | m_params[index].m_indicator=n; |
| | | if(size==0) size=wcslen(v); |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR, |
| | | size, 0, (SQLPOINTER)v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const std::string& v) |
| | | { |
| | | bind_param(index, v.data(), v.size(), v.size()); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const std::wstring& v) |
| | | { |
| | | bind_param(index, v.data(), v.size(), v.size()); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, const const_blob_data& v) |
| | | { |
| | | m_params[index].m_indicator=v.size; |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, |
| | | v.size, 0, (SQLPOINTER)v.data, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_param(SQLUSMALLINT index, std::istream& s) |
| | | { |
| | | if(m_blob_buffer==NULL) |
| | | m_blob_buffer=malloc(blob_buffer_size); |
| | | m_params[index].m_data=m_blob_buffer; |
| | | m_params[index].m_size=blob_buffer_size; |
| | | m_params[index].m_indicator=SQL_LEN_DATA_AT_EXEC(m_params[index].m_size); |
| | | verify_error(SQLBindParameter(m_handle, index+1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, |
| | | 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_field(SQLUSMALLINT index, bool&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_BIT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, int8_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_STINYINT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, uint8_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_UTINYINT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, int16_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_SSHORT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, uint16_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_USHORT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, int32_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_SLONG, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, uint32_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_ULONG, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, int64_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_SBIGINT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, uint64_t&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_UBIGINT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, float&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_FLOAT, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, double&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_DOUBLE, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, DATE_STRUCT&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_TYPE_DATE, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, TIME_STRUCT&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_TYPE_TIME, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, TIMESTAMP_STRUCT&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_TYPE_TIMESTAMP, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, SQLGUID&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_GUID, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, SQL_NUMERIC_STRUCT&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_NUMERIC, &v, 0, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT 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, index+1, SQL_C_CHAR, v, n-1, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT 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, index+1, SQL_C_WCHAR, v, n-1, &m_params[index].m_indicator)); |
| | | } |
| | | template<typename CharType> |
| | | void bind_field(SQLUSMALLINT index, std::basic_string<CharType>&& v) |
| | | { |
| | | SQLLEN length=0; |
| | | SQLColAttribute(m_handle, index+1, SQL_DESC_LENGTH, NULL, 0, NULL, &length); |
| | | v.resize(length); |
| | | bind_field(index, const_cast<char*>(v.data()), v.size()+1); |
| | | m_params[index].m_after_fetch=[&v](const param_data& p) { |
| | | if(p.m_indicator==SQL_NULL_DATA) |
| | | v.clear(); |
| | | else |
| | | v.resize(p.m_indicator); |
| | | }; |
| | | } |
| | | template<size_t N> |
| | | void bind_field(SQLUSMALLINT index, std::array<char, N>&& value) |
| | | { |
| | | bind_field(index, value.data(), value.size()); |
| | | } |
| | | template<size_t N> |
| | | void bind_field(SQLUSMALLINT index, std::array<wchar_t, N>&& value) |
| | | { |
| | | bind_field(index, value.data(), value.size()); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, qtl::blob_data&& v) |
| | | { |
| | | verify_error(SQLBindCol(m_handle, index+1, SQL_C_BINARY, v.data, v.size, &m_params[index].m_indicator)); |
| | | } |
| | | void bind_field(SQLUSMALLINT index, std::ostream&& v) |
| | | { |
| | | if(m_blob_buffer==NULL) |
| | | m_blob_buffer=malloc(blob_buffer_size); |
| | | m_params[index].m_data=m_blob_buffer; |
| | | m_params[index].m_size=blob_buffer_size; |
| | | m_params[index].m_after_fetch=[this, index, &v](const param_data& p) { |
| | | SQLRETURN ret=SQLGetData(m_handle, index+1, SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN*>(&p.m_indicator)); |
| | | while(ret!=SQL_NO_DATA) |
| | | { |
| | | size_t n = (p.m_indicator > blob_buffer_size) || (p.m_indicator == SQL_NO_TOTAL) ? |
| | | blob_buffer_size : p.m_indicator; |
| | | verify_error(ret); |
| | | v.write((const char*)p.m_data, n); |
| | | ret=SQLGetData(m_handle, index+1, SQL_C_BINARY, p.m_data, p.m_size, const_cast<SQLLEN*>(&p.m_indicator)); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | 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) |
| | | { |
| | | 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); |
| | | }; |
| | | } |
| | | |
| | | template<typename Types> |
| | | void execute(const Types& params) |
| | | { |
| | | SQLSMALLINT count=0; |
| | | verify_error(SQLNumParams(m_handle, &count)); |
| | | if(count>0) |
| | | { |
| | | m_params.resize(count); |
| | | qtl::bind_params(*this, params); |
| | | } |
| | | |
| | | SQLRETURN ret=SQLExecute(m_handle); |
| | | verify_error(ret); |
| | | if(ret==SQL_NEED_DATA) |
| | | { |
| | | SQLPOINTER token; |
| | | size_t i=0; |
| | | ret=SQLParamData(m_handle, &token); |
| | | verify_error(ret); |
| | | while(ret==SQL_NEED_DATA) |
| | | { |
| | | while(i!=count) |
| | | { |
| | | if(&m_params[i]==token) |
| | | { |
| | | if(m_params[i].m_after_fetch) |
| | | m_params[i].m_after_fetch(m_params[i]); |
| | | break; |
| | | } |
| | | ++i; |
| | | } |
| | | ret=SQLParamData(m_handle, &token); |
| | | verify_error(ret); |
| | | } |
| | | } |
| | | } |
| | | |
| | | template<typename Types> |
| | | bool fetch(Types&& values) |
| | | { |
| | | if(!m_binded_cols) |
| | | { |
| | | SQLSMALLINT count=0; |
| | | verify_error(SQLNumResultCols(m_handle, &count)); |
| | | if(count>0) |
| | | { |
| | | m_params.resize(count); |
| | | qtl::bind_record(*this, std::forward<Types>(values)); |
| | | } |
| | | m_binded_cols=true; |
| | | } |
| | | return fetch(); |
| | | } |
| | | |
| | | bool fetch() |
| | | { |
| | | SQLRETURN ret=SQLFetch(m_handle); |
| | | if(ret==SQL_SUCCESS || ret==SQL_SUCCESS_WITH_INFO) |
| | | { |
| | | for(const param_data& data : m_params) |
| | | { |
| | | if(data.m_after_fetch) |
| | | data.m_after_fetch(data); |
| | | } |
| | | return true; |
| | | } |
| | | verify_error(ret); |
| | | return false; |
| | | } |
| | | |
| | | SQLLEN affetced_rows() |
| | | { |
| | | SQLLEN count=0; |
| | | verify_error(SQLRowCount(m_handle, &count)); |
| | | return count; |
| | | } |
| | | |
| | | /* |
| | | 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; |
| | | }*/ |
| | | |
| | | void reset() |
| | | { |
| | | verify_error(SQLFreeStmt(m_handle, SQL_RESET_PARAMS)); |
| | | } |
| | | |
| | | private: |
| | | 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; |
| | | }; |
| | | |
| | | 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; |
| | | |
| | | 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 database : public object<SQL_HANDLE_DBC>, public qtl::base_database<database, statement> |
| | | { |
| | | public: |
| | | explicit database(environment& env) : object(env.handle()), m_opened(false) |
| | | { |
| | | } |
| | | database(database&& src) |
| | | : object(std::forward<database>(src)), m_connection(std::forward<std::string>(src.m_connection)) |
| | | { |
| | | m_opened=src.m_opened; |
| | | src.m_opened=false; |
| | | } |
| | | ~database() |
| | | { |
| | | close(); |
| | | } |
| | | database& operator=(database&& src) |
| | | { |
| | | if(this!=&src) |
| | | { |
| | | object::operator =(std::forward<database>(src)); |
| | | m_opened=src.m_opened; |
| | | src.m_opened=false; |
| | | m_connection=std::forward<std::string>(src.m_connection); |
| | | } |
| | | return *this; |
| | | } |
| | | |
| | | void open(const char* server_name, size_t server_name_length, |
| | | const char* user_name, size_t user_name_length, const char* password, size_t password_length) |
| | | { |
| | | if(m_opened) close(); |
| | | verify_error(SQLConnectA(m_handle, (SQLCHAR*)server_name, server_name_length, (SQLCHAR*)user_name, user_name_length, (SQLCHAR*)password, password_length)); |
| | | m_opened=true; |
| | | } |
| | | void open(const char* server_name, const char* user_name, const char* password) |
| | | { |
| | | verify_error(SQLConnectA(m_handle, (SQLCHAR*)server_name, SQL_NTS, (SQLCHAR*)user_name, SQL_NTS, (SQLCHAR*)password, SQL_NTS)); |
| | | } |
| | | void open(const std::string& server_name, const std::string& user_name, const std::string& password) |
| | | { |
| | | open(server_name.data(), server_name.size(), user_name.data(), user_name.size(), password.data(), password.size()); |
| | | } |
| | | void open(const char* input_text, size_t text_length=SQL_NTS, SQLSMALLINT driver_completion=SQL_DRIVER_NOPROMPT, SQLHWND hwnd=NULL) |
| | | { |
| | | m_connection.resize(512); |
| | | SQLSMALLINT out_len; |
| | | if(m_opened) close(); |
| | | verify_error(SQLDriverConnectA(m_handle, hwnd, (SQLCHAR*)input_text, (SQLSMALLINT)text_length, |
| | | (SQLCHAR*)m_connection.data(), (SQLSMALLINT)m_connection.size(), &out_len, driver_completion)); |
| | | m_connection.resize(out_len); |
| | | m_opened=true; |
| | | } |
| | | void open(const std::string& input_text, SQLSMALLINT driver_completion=SQL_DRIVER_NOPROMPT, SQLHWND hwnd=NULL) |
| | | { |
| | | open(input_text.data(), input_text.size(), driver_completion, hwnd); |
| | | } |
| | | void open(SQLHWND hwnd, SQLSMALLINT driver_completion=SQL_DRIVER_COMPLETE) |
| | | { |
| | | open("", SQL_NTS, driver_completion, hwnd); |
| | | } |
| | | // InputPred like: |
| | | // bool input_parameters(connection_parameters& parameters); |
| | | template<typename InputPred> |
| | | void open(const char* connection_text, size_t text_length, InputPred&& pred) |
| | | { |
| | | SQLSMALLINT length=0; |
| | | SQLINTEGER ret=SQL_SUCCESS; |
| | | std::string input_text; |
| | | if(m_opened) close(); |
| | | if(text_length==SQL_NTS) |
| | | input_text=connection_text; |
| | | else |
| | | input_text.assign(connection_text, text_length); |
| | | m_connection.resize(1024); |
| | | while( (ret=SQLBrowseConnectA(m_handle, (SQLCHAR*)input_text.data(), SQL_NTS, |
| | | (SQLCHAR*)m_connection.data(), m_connection.size(), &length)) == SQL_NEED_DATA) |
| | | { |
| | | connection_parameters parameters; |
| | | parse_browse_string(m_connection.data(), length, parameters); |
| | | if(!pred(parameters)) |
| | | throw error(SQL_NEED_DATA, "User cancel operation."); |
| | | input_text=create_connection_text(parameters); |
| | | } |
| | | if(ret==SQL_ERROR || ret==SQL_SUCCESS_WITH_INFO) |
| | | verify_error(ret); |
| | | m_opened=true; |
| | | } |
| | | template<typename InputPred> |
| | | void open(const char* connection_text, InputPred&& pred) |
| | | { |
| | | open(connection_text, SQL_NTS, std::forward<InputPred>(pred)); |
| | | } |
| | | template<typename InputPred> |
| | | void open(const std::string& connection_text, InputPred&& pred) |
| | | { |
| | | open(connection_text.data(), connection_text.size(), std::forward<InputPred>(pred)); |
| | | } |
| | | void close() |
| | | { |
| | | if(m_opened) |
| | | { |
| | | verify_error(SQLDisconnect(m_handle)); |
| | | m_opened=false; |
| | | } |
| | | } |
| | | |
| | | void set_attribute(SQLINTEGER attr, SQLINTEGER value) |
| | | { |
| | | verify_error(SQLSetConnectAttr(m_handle, attr, &value, 0)); |
| | | } |
| | | void get_attribute(SQLINTEGER attr, SQLINTEGER& value) const |
| | | { |
| | | verify_error(SQLGetConnectAttr(m_handle, attr, &value, sizeof(SQLINTEGER), 0)); |
| | | } |
| | | void get_info(SQLSMALLINT info, std::string& value, SQLSMALLINT size=SQL_MAX_OPTION_STRING_LENGTH) const |
| | | { |
| | | value.resize(size); |
| | | verify_error(SQLGetInfo(m_handle, info, (SQLPOINTER)value.data(), size, &size)); |
| | | value.resize(size); |
| | | } |
| | | |
| | | void auto_commit(bool on) |
| | | { |
| | | set_attribute(SQL_ATTR_AUTOCOMMIT, on ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF); |
| | | } |
| | | void begin_transaction() |
| | | { |
| | | auto_commit(false); |
| | | } |
| | | void rollback() |
| | | { |
| | | verify_error(SQLEndTran(handler_type, m_handle, SQL_ROLLBACK)); |
| | | auto_commit(true); |
| | | } |
| | | void commit() |
| | | { |
| | | verify_error(SQLEndTran(handler_type, m_handle, SQL_COMMIT)); |
| | | auto_commit(true); |
| | | } |
| | | |
| | | std::string dbms_name() const |
| | | { |
| | | 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; |
| | | } |
| | | |
| | | bool is_alive() |
| | | { |
| | | SQLINTEGER value; |
| | | get_attribute(SQL_ATTR_CONNECTION_DEAD, value); |
| | | return value==SQL_CD_FALSE; |
| | | } |
| | | |
| | | 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 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()); |
| | | } |
| | | |
| | | private: |
| | | bool m_opened; |
| | | std::string m_connection; |
| | | |
| | | void parse_browse_string(const char* output_text, size_t text_length, connection_parameters& parameters); |
| | | std::string create_connection_text(const connection_parameters& parameters); |
| | | }; |
| | | |
| | | 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 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); |
| | | #elif defined(_POSIX_VERSION) |
| | | localtime_r(&value, &tm); |
| | | #else |
| | | 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; |
| | | } |
| | | |
| | | 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; |
| | | } |
| | | }; |
| | | |
| | | 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; |
| | | while(SQLGetDiagRecA(object<Type>::handler_type, h.handle(), ++i, state, &err, |
| | | message, SQL_MAX_MESSAGE_LENGTH, NULL)==SQL_SUCCESS) |
| | | { |
| | | oss<<"["<<state<<"] ("<<err<<") "<<message<<std::endl; |
| | | } |
| | | m_errmsg=oss.str(); |
| | | } |
| | | else if(code==SQL_INVALID_HANDLE) |
| | | { |
| | | m_errmsg="Invalid handle."; |
| | | } |
| | | } |
| | | |
| | | inline void 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; |
| | | } |
| | | ++sp; |
| | | } |
| | | if(!parameter.m_name.empty()) |
| | | parameters.emplace_back(parameter); |
| | | } |
| | | |
| | | inline std::string 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(); |
| | | } |
| | | |
| | | inline statement::statement(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::forward<database>(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()); |
| | | } |
| | | }; |
| | | |
| | | } //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) |
| | | { |
| | | 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 |
| | | |
| | | } |
| | | |
| | | #endif //_QTL_ODCB_H_ |
| New file |
| | |
| | | #ifndef _QTL_ODBC_POOL_HPP_ |
| | | #define _QTL_ODBC_POOL_HPP_ |
| | | |
| | | #include "qtl_database_pool.hpp" |
| | | #include "qtl_odbc.hpp" |
| | | |
| | | namespace qtl |
| | | { |
| | | |
| | | namespace odbc |
| | | { |
| | | |
| | | class database_pool : public qtl::database_pool<database> |
| | | { |
| | | public: |
| | | database_pool() { } |
| | | virtual ~database_pool() { } |
| | | virtual database* new_database() throw() override |
| | | { |
| | | database* db=NULL; |
| | | try |
| | | { |
| | | db=new database(m_env); |
| | | db->open(m_connection); |
| | | } |
| | | catch(error& e) |
| | | { |
| | | if(db) |
| | | { |
| | | delete db; |
| | | db=NULL; |
| | | } |
| | | } |
| | | return db; |
| | | } |
| | | |
| | | protected: |
| | | std::string m_connection; |
| | | environment m_env; |
| | | }; |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | | #endif //_QTL_ODBC_POOL_HPP_ |
| | |
| | | } |
| | | } |
| | | |
| | | void bind(int col, int value) |
| | | void bind_param(int index, int value) |
| | | { |
| | | verify_error(sqlite3_bind_int(m_stmt, col, value)); |
| | | verify_error(sqlite3_bind_int(m_stmt, index+1, value)); |
| | | } |
| | | void bind(int col, int64_t value) |
| | | void bind_param(int index, int64_t value) |
| | | { |
| | | verify_error(sqlite3_bind_int64(m_stmt, col, value)); |
| | | verify_error(sqlite3_bind_int64(m_stmt, index+1, value)); |
| | | } |
| | | void bind(int col, double value) |
| | | void bind_param(int index, double value) |
| | | { |
| | | verify_error(sqlite3_bind_double(m_stmt, col, value)); |
| | | verify_error(sqlite3_bind_double(m_stmt, index+1, value)); |
| | | } |
| | | void bind(int col, const char* value, size_t n=-1) |
| | | void bind_param(int index, const char* value, size_t n=-1) |
| | | { |
| | | if(value) |
| | | verify_error(sqlite3_bind_text(m_stmt, col, value, (int)n, NULL)); |
| | | verify_error(sqlite3_bind_text(m_stmt, index+1, value, (int)n, NULL)); |
| | | else |
| | | verify_error(sqlite3_bind_null(m_stmt, col)); |
| | | verify_error(sqlite3_bind_null(m_stmt, index+1)); |
| | | } |
| | | void bind(int col, const wchar_t* value, size_t n=-1) |
| | | void bind_param(int index, const wchar_t* value, size_t n=-1) |
| | | { |
| | | if(value) |
| | | verify_error(sqlite3_bind_text16(m_stmt, col, value, (int)n, NULL)); |
| | | verify_error(sqlite3_bind_text16(m_stmt, index+1, value, (int)n, NULL)); |
| | | else |
| | | verify_error(sqlite3_bind_null(m_stmt, col)); |
| | | verify_error(sqlite3_bind_null(m_stmt, index+1)); |
| | | } |
| | | void bind(int col, const std::string& value) |
| | | void bind_param(int index, const std::string& value) |
| | | { |
| | | bind(col, value.data(), value.size()); |
| | | bind_param(index, value.data(), value.size()); |
| | | } |
| | | void bind(int col, const std::wstring& value) |
| | | void bind_param(int index, const std::wstring& value) |
| | | { |
| | | bind(col, value.data(), value.size()); |
| | | bind_param(index, value.data(), value.size()); |
| | | } |
| | | void bind(int col, const const_blob_data& value) |
| | | void bind_param(int index, const const_blob_data& value) |
| | | { |
| | | if(value.size) |
| | | { |
| | | if(value.data) |
| | | verify_error(sqlite3_bind_blob(m_stmt, col, value.data, (int)value.size, NULL)); |
| | | verify_error(sqlite3_bind_blob(m_stmt, index+1, value.data, (int)value.size, NULL)); |
| | | else |
| | | verify_error(sqlite3_bind_zeroblob(m_stmt, col, (int)value.size)); |
| | | verify_error(sqlite3_bind_zeroblob(m_stmt, index+1, (int)value.size)); |
| | | } |
| | | else |
| | | verify_error(sqlite3_bind_null(m_stmt, col)); |
| | | verify_error(sqlite3_bind_null(m_stmt, index+1)); |
| | | } |
| | | void bind_zero_blob(int col, int n) |
| | | void bind_zero_blob(int index, int n) |
| | | { |
| | | verify_error(sqlite3_bind_zeroblob(m_stmt, col, (int)n)); |
| | | verify_error(sqlite3_bind_zeroblob(m_stmt, index+1, (int)n)); |
| | | } |
| | | void bind(int col, qtl::null value) |
| | | void bind_param(int index, qtl::null) |
| | | { |
| | | verify_error(sqlite3_bind_null(m_stmt, col)); |
| | | verify_error(sqlite3_bind_null(m_stmt, index+1)); |
| | | } |
| | | void bind(int col) |
| | | void bind_param(int index, std::nullptr_t) |
| | | { |
| | | verify_error(sqlite3_bind_null(m_stmt, col)); |
| | | verify_error(sqlite3_bind_null(m_stmt, index+1)); |
| | | } |
| | | int get_parameter_count() const |
| | | { |
| | |
| | | int get_parameter_index(const char* param_name) const |
| | | { |
| | | return sqlite3_bind_parameter_index(m_stmt, param_name); |
| | | } |
| | | |
| | | template<class Param> |
| | | void bind_param(size_t index, const Param& param) |
| | | { |
| | | bind((int)index+1, param); |
| | | } |
| | | |
| | | template<class Type> |
| | |
| | | { |
| | | size_t col_length=get_column_length((int)index); |
| | | if(col_length>0) |
| | | strncpy(value, (const char*)sqlite3_column_text(m_stmt, (int)index), std::min(length, col_length)); |
| | | strncpy(value, (const char*)sqlite3_column_text(m_stmt, (int)index), std::min(length, col_length+1)); |
| | | else |
| | | memset(value, 0, length*sizeof(char)); |
| | | } |
| | |
| | | { |
| | | size_t col_length=sqlite3_column_bytes16(m_stmt, (int)index); |
| | | if(col_length>0) |
| | | wcsncpy(value, (const wchar_t*)sqlite3_column_text16(m_stmt, (int)index), std::min(length, col_length)); |
| | | wcsncpy(value, (const wchar_t*)sqlite3_column_text16(m_stmt, (int)index), std::min(length, col_length+1)); |
| | | else |
| | | memset(value, 0, length*sizeof(wchar_t)); |
| | | } |
| | |
| | | { |
| | | public: |
| | | database_pool() : m_flags(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) { } |
| | | virtual bool open_database(database& db) override |
| | | virtual database* new_database() throw() override |
| | | { |
| | | database* db=NULL; |
| | | try |
| | | { |
| | | db.open(m_filename.data(), m_flags); |
| | | return true; |
| | | db=new database; |
| | | db->open(m_filename.data(), m_flags); |
| | | } |
| | | catch (error& e) |
| | | { |
| | | return false; |
| | | delete db; |
| | | db=NULL; |
| | | } |
| | | return db; |
| | | } |
| | | |
| | | protected: |
| New file |
| | |
| | | #include "stdafx.h" |
| | | #include "TestOdbc.h" |
| | | #include <fstream> |
| | | #include <array> |
| | | #include "md5.h" |
| | | |
| | | using namespace std; |
| | | |
| | | struct TestOdbcRecord |
| | | { |
| | | uint32_t id; |
| | | char name[33]; |
| | | qtl::odbc::timestamp create_time; |
| | | |
| | | TestOdbcRecord() |
| | | { |
| | | memset(this, 0, sizeof(TestOdbcRecord)); |
| | | } |
| | | |
| | | void print() const |
| | | { |
| | | printf("ID=\"%d\", Name=\"%s\"\n", |
| | | id, name); |
| | | } |
| | | }; |
| | | |
| | | namespace qtl |
| | | { |
| | | template<> |
| | | inline void bind_record<qtl::odbc::statement, TestOdbcRecord>(qtl::odbc::statement& command, TestOdbcRecord&& v) |
| | | { |
| | | qtl::bind_field(command, 0, v.id); |
| | | qtl::bind_field(command, 1, v.name); |
| | | qtl::bind_field(command, 2, v.create_time); |
| | | } |
| | | } |
| | | |
| | | TestOdbc::TestOdbc() : m_db(m_env) |
| | | { |
| | | m_db.open("DRIVER={SQL Server};SERVER=(local);UID=;PWD=;Trusted_Connection=yes;DATABASE=test"); |
| | | cout<<"DBMS: "<<m_db.dbms_name()<<endl; |
| | | cout<<"SERVER: "<<m_db.server_name()<<endl; |
| | | cout<<"USER: "<<m_db.user_name()<<endl; |
| | | cout<<"DATABASE: "<<m_db.db_name()<<endl; |
| | | |
| | | id=0; |
| | | TEST_ADD(TestOdbc::test_dual) |
| | | TEST_ADD(TestOdbc::test_clear) |
| | | TEST_ADD(TestOdbc::test_insert) |
| | | TEST_ADD(TestOdbc::test_select) |
| | | TEST_ADD(TestOdbc::test_update) |
| | | TEST_ADD(TestOdbc::test_insert2) |
| | | TEST_ADD(TestOdbc::test_iterator) |
| | | TEST_ADD(TestOdbc::test_insert_blob) |
| | | TEST_ADD(TestOdbc::test_select_blob) |
| | | } |
| | | |
| | | void TestOdbc::test_dual() |
| | | { |
| | | try |
| | | { |
| | | m_db.query("select 0, 'hello world';", |
| | | [](uint32_t i, const std::string& str) { |
| | | printf("0=\"%d\", 'hello world'=\"%s\"\n", |
| | | i, str.data()); |
| | | }); |
| | | } |
| | | catch(qtl::odbc::error& e) |
| | | { |
| | | if(!e) ASSERT_EXCEPTION(e); |
| | | } |
| | | } |
| | | |
| | | void TestOdbc::test_select() |
| | | { |
| | | try |
| | | { |
| | | m_db.query("select * from test where id=?", id, |
| | | [](const qtl::indicator<uint32_t>& id, const std::string& name, const qtl::odbc::timestamp& create_time) { |
| | | printf("ID=\"%d\", Name=\"%s\"\n", |
| | | id.data, name.data()); |
| | | }); |
| | | |
| | | m_db.query("select * from test where id=?", id, |
| | | [](const TestOdbcRecord& record) { |
| | | printf("ID=\"%d\", Name=\"%s\"\n", |
| | | record.id, record.name); |
| | | }); |
| | | |
| | | m_db.query("select * from test where id=?", id, |
| | | &TestOdbcRecord::print); |
| | | } |
| | | catch(qtl::odbc::error& e) |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | } |
| | | |
| | | void TestOdbc::test_insert() |
| | | { |
| | | try |
| | | { |
| | | m_db.execute("insert into test(Name, CreateTime) values(?, CURRENT_TIMESTAMP)", |
| | | "test_user"); |
| | | m_db.query_first("SELECT @@IDENTITY", id); |
| | | } |
| | | catch(qtl::odbc::error& e) |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | TEST_ASSERT_MSG(id>0, "insert failture."); |
| | | } |
| | | |
| | | void TestOdbc::test_insert2() |
| | | { |
| | | try |
| | | { |
| | | uint64_t affected=0; |
| | | qtl::odbc::statement stmt=m_db.open_command("insert into test(Name, CreateTime) values(?, CURRENT_TIMESTAMP)"); |
| | | qtl::execute(stmt, &affected, "second_user", "third_user"); |
| | | TEST_ASSERT_MSG(affected==2, "Cannot insert 2 records to table test."); |
| | | } |
| | | catch(qtl::odbc::error& e) |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | } |
| | | |
| | | void TestOdbc::test_update() |
| | | { |
| | | try |
| | | { |
| | | m_db.execute_direct("update test set Name=? WHERE ID=?", NULL, |
| | | "other_user", id); |
| | | } |
| | | catch(qtl::odbc::error& e) |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | TEST_ASSERT_MSG(id>0, "insert failture."); |
| | | } |
| | | |
| | | void TestOdbc::test_clear() |
| | | { |
| | | try |
| | | { |
| | | m_db.simple_execute("delete from test"); |
| | | } |
| | | catch(qtl::odbc::error& e) |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | } |
| | | |
| | | void TestOdbc::test_iterator() |
| | | { |
| | | try |
| | | { |
| | | cout<<"after insert all:"<<endl; |
| | | for(auto& record : m_db.result<TestOdbcRecord>("select ID, Name, CreateTime from test")) |
| | | { |
| | | printf("ID=\"%d\", Name=\"%s\"\n", |
| | | record.id, record.name); |
| | | } |
| | | } |
| | | catch(qtl::odbc::error& e) |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | } |
| | | |
| | | void TestOdbc::test_insert_blob() |
| | | { |
| | | try |
| | | { |
| | | #ifdef _WIN32 |
| | | const char filename[]="C:\\windows\\explorer.exe"; |
| | | #else |
| | | const char filename[]="/bin/sh"; |
| | | #endif //_WIN32 |
| | | fstream fs(filename, ios::binary|ios::in); |
| | | TEST_ASSERT_MSG(fs, "Cannot open test file."); |
| | | unsigned char md5[16]={0}; |
| | | char md5_hex[33]={0}; |
| | | get_md5(fs, md5); |
| | | cout<<"MD5 of file "<<filename<<": "; |
| | | print_hex(md5, sizeof(md5)); |
| | | cout<<endl; |
| | | |
| | | m_db.simple_execute("DELETE FROM test_blob"); |
| | | |
| | | fs.clear(); |
| | | fs.seekg(0, ios::beg); |
| | | m_db.execute("INSERT INTO test_blob (Filename, [Content], MD5) values(?, ?, ?)", |
| | | make_tuple(filename, ref((istream&)fs), qtl::const_blob_data(md5, sizeof(md5)))); |
| | | m_db.query_first("SELECT @@IDENTITY", id); |
| | | } |
| | | catch(qtl::odbc::error& e) |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | } |
| | | |
| | | void TestOdbc::test_select_blob() |
| | | { |
| | | try |
| | | { |
| | | const char dest_file[]="explorer.exe"; |
| | | fstream fs(dest_file, ios::binary|ios::in|ios::out|ios::trunc); |
| | | TEST_ASSERT_MSG(fs, "Cannot open test file."); |
| | | unsigned char md5[16]={0}; |
| | | char md5_hex[33]={0}; |
| | | |
| | | fs.seekg(ios::beg); |
| | | std::string source_file; |
| | | m_db.query_first("SELECT Filename, MD5 , [Content]FROM test_blob WHERE id=?", make_tuple(id), |
| | | make_tuple(ref(source_file), qtl::blob_data(md5, sizeof(md5)), ref((ostream&)fs))); |
| | | fs.flush(); |
| | | cout<<"MD5 of file "<<source_file<<" stored in database: "; |
| | | print_hex((const unsigned char*)md5, sizeof(md5)); |
| | | fs.clear(); |
| | | fs.seekg(0, ios::beg); |
| | | get_md5(fs, md5); |
| | | cout<<endl<<"MD5 of file "<<dest_file<<": "; |
| | | print_hex(md5, sizeof(md5)); |
| | | cout<<endl; |
| | | } |
| | | catch(qtl::odbc::error& e) |
| | | { |
| | | ASSERT_EXCEPTION(e); |
| | | } |
| | | } |
| | | |
| | | void TestOdbc::get_md5(std::istream& is, unsigned char* result) |
| | | { |
| | | std::array<char, 64*1024> buffer; |
| | | MD5_CTX context; |
| | | MD5Init(&context); |
| | | while(!is.eof()) |
| | | { |
| | | is.read(&buffer.front(), buffer.size()); |
| | | MD5Update(&context, (unsigned char*)buffer.data(), (unsigned int)is.gcount()); |
| | | } |
| | | MD5Final(result, &context); |
| | | } |
| | | |
| | | void TestOdbc::print_hex(const unsigned char* data, size_t n) |
| | | { |
| | | cout<<hex; |
| | | for(size_t i=0; i!=n; i++) |
| | | cout<<(data[i]&0xFF); |
| | | } |
| | | |
| | | int main(int argc, char* argv[]) |
| | | { |
| | | Test::TextOutput output(Test::TextOutput::Verbose); |
| | | |
| | | cout<<endl<<"Testing MSSQL(ODBC):"<<endl; |
| | | |
| | | try |
| | | { |
| | | TestOdbc test_odbc; |
| | | test_odbc.run(output); |
| | | } |
| | | catch(qtl::odbc::error& e) |
| | | { |
| | | cerr<<e.what()<<endl; |
| | | } |
| | | return 0; |
| | | } |
| | | |
| New file |
| | |
| | | #ifndef _TEST_ODBC_H_ |
| | | #define _TEST_ODBC_H_ |
| | | |
| | | #include "TestSuite.h" |
| | | #include "../include/qtl_odbc.hpp" |
| | | |
| | | namespace qtl |
| | | { |
| | | namespace odbc |
| | | { |
| | | class database; |
| | | } |
| | | } |
| | | |
| | | class TestOdbc : public TestSuite |
| | | { |
| | | public: |
| | | TestOdbc(); |
| | | |
| | | private: |
| | | void test_dual(); |
| | | void test_clear(); |
| | | void test_insert(); |
| | | void test_select(); |
| | | void test_update(); |
| | | void test_insert2(); |
| | | void test_iterator(); |
| | | void test_insert_blob(); |
| | | void test_select_blob(); |
| | | |
| | | private: |
| | | uint64_t id; |
| | | qtl::odbc::environment m_env; |
| | | qtl::odbc::database m_db; |
| | | void get_md5(std::istream& is, unsigned char* result); |
| | | static void print_hex(const unsigned char* data, size_t n); |
| | | }; |
| | | |
| | | #endif //_TEST_ODBC_H_ |
| | |
| | | #define NOMINMAX 1 |
| | | #endif //NOMINMAX |
| | | |
| | | #ifndef _WIN32_WINNT // ÔÊÐíʹÓÃÌØ¶¨ÓÚ Windows XP »ò¸ü¸ß°æ±¾µÄ¹¦ÄÜ¡£ |
| | | #define _WIN32_WINNT 0x0501 // ½«´ËÖµ¸ü¸ÄΪÏàÓ¦µÄÖµ£¬ÒÔÊÊÓÃÓÚ Windows µÄÆäËû°æ±¾¡£ |
| | | #ifndef _WIN32_WINNT |
| | | #define _WIN32_WINNT 0x0501 |
| | | #endif |
| | | |
| | | #include <WinSock2.h> |
| New file |
| | |
| | | TARGET=test_odbc |
| | | CC=g++ |
| | | PCH_HEADER=stdafx.h |
| | | PCH=stdafx.h.gch |
| | | OBJ=TestOdbc.o md5.o |
| | | CFLAGS=-g -D_DEBUG -O2 -I/usr/local/include |
| | | CXXFLAGS=-I../include -std=c++11 |
| | | LDFLAGS= -L/usr/local/lib -lcpptest -lodbc |
| | | |
| | | all : $(TARGET) |
| | | |
| | | $(PCH) : $(PCH_HEADER) |
| | | $(CC) $(CFLAGS) $(CXXFLAGS) -x c++-header -o $@ $< |
| | | |
| | | TestOdbc.o : TestOdbc.cpp TestOdbc.h $(PCH) |
| | | $(CC) -c $(CFLAGS) $(CXXFLAGS) -o $@ $< |
| | | |
| | | md5.o : md5.c md5.h |
| | | gcc -c $(CFLAGS) -o $@ $< |
| | | |
| | | $(TARGET) : $(OBJ) |
| | | libtool --tag=CXX --mode=link $(CC) $(LDFLAGS) -o $@ $^ |
| | | |
| | | clean: |
| | | rm $(TARGET) $(PCH) $(OBJ) -f |
| | |
| | | EndProject |
| | | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_sqlite", "test_sqlite\test_sqlite.vcproj", "{8C793DE3-283E-460C-A876-29455F68CB45}" |
| | | EndProject |
| | | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_odbc", "test_odbc\test_odbc.vcproj", "{86C60EAD-F5B4-482B-9CC2-92A77323E759}" |
| | | EndProject |
| | | Global |
| | | GlobalSection(SolutionConfigurationPlatforms) = preSolution |
| | | Debug|Win32 = Debug|Win32 |
| | |
| | | {8C793DE3-283E-460C-A876-29455F68CB45}.Debug|Win32.Build.0 = Debug|Win32 |
| | | {8C793DE3-283E-460C-A876-29455F68CB45}.Release|Win32.ActiveCfg = Release|Win32 |
| | | {8C793DE3-283E-460C-A876-29455F68CB45}.Release|Win32.Build.0 = Release|Win32 |
| | | {86C60EAD-F5B4-482B-9CC2-92A77323E759}.Debug|Win32.ActiveCfg = Debug|Win32 |
| | | {86C60EAD-F5B4-482B-9CC2-92A77323E759}.Debug|Win32.Build.0 = Debug|Win32 |
| | | {86C60EAD-F5B4-482B-9CC2-92A77323E759}.Release|Win32.ActiveCfg = Release|Win32 |
| | | {86C60EAD-F5B4-482B-9CC2-92A77323E759}.Release|Win32.Build.0 = Release|Win32 |
| | | EndGlobalSection |
| | | GlobalSection(SolutionProperties) = preSolution |
| | | HideSolutionNode = FALSE |
| New file |
| | |
| | | <?xml version="1.0" encoding="gb2312"?> |
| | | <VisualStudioProject |
| | | ProjectType="Visual C++" |
| | | Version="8.00" |
| | | Name="test_odbc" |
| | | ProjectGUID="{86C60EAD-F5B4-482B-9CC2-92A77323E759}" |
| | | RootNamespace="test_odbc" |
| | | Keyword="Win32Proj" |
| | | > |
| | | <Platforms> |
| | | <Platform |
| | | Name="Win32" |
| | | /> |
| | | </Platforms> |
| | | <ToolFiles> |
| | | </ToolFiles> |
| | | <Configurations> |
| | | <Configuration |
| | | Name="Debug|Win32" |
| | | OutputDirectory="$(SolutionDir)$(ConfigurationName)" |
| | | IntermediateDirectory="$(ConfigurationName)" |
| | | ConfigurationType="1" |
| | | CharacterSet="0" |
| | | > |
| | | <Tool |
| | | Name="VCPreBuildEventTool" |
| | | /> |
| | | <Tool |
| | | Name="VCCustomBuildTool" |
| | | /> |
| | | <Tool |
| | | Name="VCXMLDataGeneratorTool" |
| | | /> |
| | | <Tool |
| | | Name="VCWebServiceProxyGeneratorTool" |
| | | /> |
| | | <Tool |
| | | Name="VCMIDLTool" |
| | | /> |
| | | <Tool |
| | | Name="VCCLCompilerTool" |
| | | Optimization="0" |
| | | PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" |
| | | MinimalRebuild="true" |
| | | BasicRuntimeChecks="3" |
| | | RuntimeLibrary="3" |
| | | UsePrecompiledHeader="2" |
| | | WarningLevel="3" |
| | | Detect64BitPortabilityProblems="false" |
| | | DebugInformationFormat="4" |
| | | /> |
| | | <Tool |
| | | Name="VCManagedResourceCompilerTool" |
| | | /> |
| | | <Tool |
| | | Name="VCResourceCompilerTool" |
| | | /> |
| | | <Tool |
| | | Name="VCPreLinkEventTool" |
| | | /> |
| | | <Tool |
| | | Name="VCLinkerTool" |
| | | AdditionalDependencies=" cpptest_debug.lib" |
| | | LinkIncremental="2" |
| | | GenerateDebugInformation="true" |
| | | SubSystem="1" |
| | | TargetMachine="1" |
| | | /> |
| | | <Tool |
| | | Name="VCALinkTool" |
| | | /> |
| | | <Tool |
| | | Name="VCManifestTool" |
| | | /> |
| | | <Tool |
| | | Name="VCXDCMakeTool" |
| | | /> |
| | | <Tool |
| | | Name="VCBscMakeTool" |
| | | /> |
| | | <Tool |
| | | Name="VCFxCopTool" |
| | | /> |
| | | <Tool |
| | | Name="VCAppVerifierTool" |
| | | /> |
| | | <Tool |
| | | Name="VCWebDeploymentTool" |
| | | /> |
| | | <Tool |
| | | Name="VCPostBuildEventTool" |
| | | /> |
| | | </Configuration> |
| | | <Configuration |
| | | Name="Release|Win32" |
| | | OutputDirectory="$(SolutionDir)$(ConfigurationName)" |
| | | IntermediateDirectory="$(ConfigurationName)" |
| | | ConfigurationType="1" |
| | | CharacterSet="0" |
| | | WholeProgramOptimization="1" |
| | | > |
| | | <Tool |
| | | Name="VCPreBuildEventTool" |
| | | /> |
| | | <Tool |
| | | Name="VCCustomBuildTool" |
| | | /> |
| | | <Tool |
| | | Name="VCXMLDataGeneratorTool" |
| | | /> |
| | | <Tool |
| | | Name="VCWebServiceProxyGeneratorTool" |
| | | /> |
| | | <Tool |
| | | Name="VCMIDLTool" |
| | | /> |
| | | <Tool |
| | | Name="VCCLCompilerTool" |
| | | PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" |
| | | RuntimeLibrary="2" |
| | | UsePrecompiledHeader="2" |
| | | WarningLevel="3" |
| | | Detect64BitPortabilityProblems="false" |
| | | DebugInformationFormat="3" |
| | | /> |
| | | <Tool |
| | | Name="VCManagedResourceCompilerTool" |
| | | /> |
| | | <Tool |
| | | Name="VCResourceCompilerTool" |
| | | /> |
| | | <Tool |
| | | Name="VCPreLinkEventTool" |
| | | /> |
| | | <Tool |
| | | Name="VCLinkerTool" |
| | | AdditionalDependencies=" cpptest.lib" |
| | | LinkIncremental="1" |
| | | GenerateDebugInformation="true" |
| | | SubSystem="1" |
| | | OptimizeReferences="2" |
| | | EnableCOMDATFolding="2" |
| | | TargetMachine="1" |
| | | /> |
| | | <Tool |
| | | Name="VCALinkTool" |
| | | /> |
| | | <Tool |
| | | Name="VCManifestTool" |
| | | /> |
| | | <Tool |
| | | Name="VCXDCMakeTool" |
| | | /> |
| | | <Tool |
| | | Name="VCBscMakeTool" |
| | | /> |
| | | <Tool |
| | | Name="VCFxCopTool" |
| | | /> |
| | | <Tool |
| | | Name="VCAppVerifierTool" |
| | | /> |
| | | <Tool |
| | | Name="VCWebDeploymentTool" |
| | | /> |
| | | <Tool |
| | | Name="VCPostBuildEventTool" |
| | | /> |
| | | </Configuration> |
| | | </Configurations> |
| | | <References> |
| | | </References> |
| | | <Files> |
| | | <Filter |
| | | Name="Ô´Îļþ" |
| | | Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" |
| | | UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" |
| | | > |
| | | <File |
| | | RelativePath="..\..\md5.c" |
| | | > |
| | | <FileConfiguration |
| | | Name="Debug|Win32" |
| | | > |
| | | <Tool |
| | | Name="VCCLCompilerTool" |
| | | UsePrecompiledHeader="0" |
| | | /> |
| | | </FileConfiguration> |
| | | <FileConfiguration |
| | | Name="Release|Win32" |
| | | > |
| | | <Tool |
| | | Name="VCCLCompilerTool" |
| | | UsePrecompiledHeader="0" |
| | | /> |
| | | </FileConfiguration> |
| | | </File> |
| | | <File |
| | | RelativePath="..\..\stdafx.cpp" |
| | | > |
| | | <FileConfiguration |
| | | Name="Debug|Win32" |
| | | > |
| | | <Tool |
| | | Name="VCCLCompilerTool" |
| | | UsePrecompiledHeader="1" |
| | | /> |
| | | </FileConfiguration> |
| | | <FileConfiguration |
| | | Name="Release|Win32" |
| | | > |
| | | <Tool |
| | | Name="VCCLCompilerTool" |
| | | UsePrecompiledHeader="1" |
| | | /> |
| | | </FileConfiguration> |
| | | </File> |
| | | <File |
| | | RelativePath="..\..\TestOdbc.cpp" |
| | | > |
| | | </File> |
| | | </Filter> |
| | | <Filter |
| | | Name="Í·Îļþ" |
| | | Filter="h;hpp;hxx;hm;inl;inc;xsd" |
| | | UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" |
| | | > |
| | | <File |
| | | RelativePath="..\..\md5.h" |
| | | > |
| | | </File> |
| | | <File |
| | | RelativePath="..\..\stdafx.h" |
| | | > |
| | | </File> |
| | | <File |
| | | RelativePath="..\..\TestOdbc.h" |
| | | > |
| | | </File> |
| | | <File |
| | | RelativePath="..\..\TestSuite.h" |
| | | > |
| | | </File> |
| | | </Filter> |
| | | <Filter |
| | | Name="×ÊÔ´Îļþ" |
| | | Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" |
| | | UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" |
| | | > |
| | | </Filter> |
| | | <File |
| | | RelativePath=".\ReadMe.txt" |
| | | > |
| | | </File> |
| | | </Files> |
| | | <Globals> |
| | | </Globals> |
| | | </VisualStudioProject> |