diff --git a/src/backends/db2/CMakeLists.txt b/src/backends/db2/CMakeLists.txt new file mode 100644 index 000000000..d1eab576b --- /dev/null +++ b/src/backends/db2/CMakeLists.txt @@ -0,0 +1,8 @@ +soci_backend(DB2 + DEPENDS DB2 + HEADERS soci-db2.h + DESCRIPTION "SOCI backend for DB2 Ubiversal Database" + AUTHORS "Denis Chapligin" + MAINTAINERS "Denis Chapligin") + +add_subdirectory(test) diff --git a/src/backends/db2/session.cpp b/src/backends/db2/session.cpp new file mode 100644 index 000000000..5ee1b9df0 --- /dev/null +++ b/src/backends/db2/session.cpp @@ -0,0 +1,104 @@ +// +// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton +// Copyright (C) 2011 Denis Chapligin +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#define SOCI_DB2_SOURCE +#include "soci-db2.h" + +#ifdef _MSC_VER +#pragma warning(disable:4355) +#endif + +using namespace soci; +using namespace soci::details; + + +db2_session_backend::db2_session_backend( + std::string const & connectString /* DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;AutoCommit=off */) +{ + SQLRETURN cliRC = SQL_SUCCESS; + + /* Prepare handles */ + cliRC = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&hEnv); + if (cliRC != SQL_SUCCESS) { + throw db2_soci_error("Error while allocating the enironment handle",cliRC); + } + + cliRC = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc); + if (cliRC != SQL_SUCCESS) { + SQLFreeHandle(SQL_HANDLE_ENV,hEnv); + throw db2_soci_error("Error while allocating the connection handle",cliRC); + } + + /* Set autocommit */ + cliRC = SQLSetConnectAttr(hDbc,SQL_ATTR_AUTOCOMMIT, this->autocommit ? (SQLPOINTER)SQL_AUTOCOMMIT_ON : (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_NTS); + if (cliRC != SQL_SUCCESS) { + SQLFreeHandle(SQL_HANDLE_DBC,hDbc); + SQLFreeHandle(SQL_HANDLE_ENV,hEnv); + throw db2_soci_error("Error while setting autocommit attribute",cliRC); + } + + /* Connect to database */ + cliRC = SQLConnect(hDbc,(SQLCHAR*)connectString.c_str(),SQL_NTS,(SQLCHAR*)username.c_str(),SQL_NTS,(SQLCHAR*)password.c_str(),SQL_NTS); + if (cliRC != SQL_SUCCESS) { + SQLFreeHandle(SQL_HANDLE_DBC,hDbc); + SQLFreeHandle(SQL_HANDLE_ENV,hEnv); + throw db2_soci_error("Error connecting to database",cliRC); + } +} + +db2_session_backend::~db2_session_backend() +{ + clean_up(); +} + +void db2_session_backend::begin() +{ + //Transations begin implicitly +} + +void db2_session_backend::commit() +{ + if (!autocommit) { + SQLRETURN cliRC = SQLEndTran(SQL_HANDLE_DBC,hDbc,SQL_COMMIT); + if (cliRC != SQL_SUCCESS) { + throw db2_soci_error("Commit failed",cliRC); + } + } +} + +void db2_session_backend::rollback() +{ + if (!autocommit) { + SQLRETURN cliRC = SQLEndTran(SQL_HANDLE_DBC,hDbc,SQL_ROLLBACK); + if (cliRC != SQL_SUCCESS) { + throw db2_soci_error("Rollback failed",cliRC); + } + } +} + +void db2_session_backend::clean_up() +{ + SQLDisconnect(hDbc); + SQLFreeHandle(SQL_HANDLE_DBC,hDbc); + SQLFreeHandle(SQL_HANDLE_ENV,hEnv); +} + +db2_statement_backend * db2_session_backend::make_statement_backend() +{ + return new db2_statement_backend(*this); +} + +db2_rowid_backend * db2_session_backend::make_rowid_backend() +{ + return new db2_rowid_backend(*this); +} + +db2_blob_backend * db2_session_backend::make_blob_backend() +{ + return new db2_blob_backend(*this); +} diff --git a/src/backends/db2/soci-db2.h b/src/backends/db2/soci-db2.h new file mode 100644 index 000000000..10e542202 --- /dev/null +++ b/src/backends/db2/soci-db2.h @@ -0,0 +1,211 @@ +// +// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton +// Copyright (C) 2011 Denis Chapligin +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef SOCI_DB2_H_INCLUDED +#define SOCI_DB2_H_INCLUDED + +#ifdef _WIN32 +# ifdef SOCI_DLL +# ifdef SOCI_DB2_SOURCE +# define SOCI_DB2_DECL __declspec(dllexport) +# else +# define SOCI_DB2_DECL __declspec(dllimport) +# endif // SOCI_DB2_SOURCE +# endif // SOCI_DLL +#endif // _WIN32 +// +// If SOCI_DB2_DECL isn't defined yet define it now +#ifndef SOCI_DB2_DECL +# define SOCI_DB2_DECL +#endif + +#include "soci-backend.h" + +#include +#include + +#include + +namespace soci +{ + +class db2_soci_error : public soci_error { +public: + db2_soci_error(std::string const & msg, SQLRETURN rc) : soci_error(msg),errorCode(rc) {}; + + SQLRETURN errorCode; +}; + +struct db2_statement_backend; + +struct SOCI_DB2_DECL db2_standard_into_type_backend : details::standard_into_type_backend +{ + db2_standard_into_type_backend(db2_statement_backend &st) + : statement_(st) + {} + + void define_by_pos(int& position, void* data, details::exchange_type type); + + void pre_fetch(); + void post_fetch(bool gotData, bool calledFromFetch, indicator* ind); + + void clean_up(); + + db2_statement_backend& statement_; +}; + +struct SOCI_DB2_DECL db2_vector_into_type_backend : details::vector_into_type_backend +{ + db2_vector_into_type_backend(db2_statement_backend &st) + : statement_(st) + {} + + void define_by_pos(int& position, void* data, details::exchange_type type); + + void pre_fetch(); + void post_fetch(bool gotData, indicator* ind); + + void resize(std::size_t sz); + std::size_t size(); + + void clean_up(); + + db2_statement_backend& statement_; +}; + +struct SOCI_DB2_DECL db2_standard_use_type_backend : details::standard_use_type_backend +{ + db2_standard_use_type_backend(db2_statement_backend &st) + : statement_(st) + {} + + void bind_by_pos(int& position, void* data, details::exchange_type type, bool readOnly); + void bind_by_name(std::string const& name, void* data, details::exchange_type type, bool readOnly); + + void pre_use(indicator const* ind); + void post_use(bool gotData, indicator* ind); + + void clean_up(); + + db2_statement_backend& statement_; +}; + +struct SOCI_DB2_DECL db2_vector_use_type_backend : details::vector_use_type_backend +{ + db2_vector_use_type_backend(db2_statement_backend &st) + : statement_(st) {} + + void bind_by_pos(int& position, void* data, details::exchange_type type); + void bind_by_name(std::string const& name, void* data, details::exchange_type type); + + void pre_use(indicator const* ind); + + std::size_t size(); + + void clean_up(); + + db2_statement_backend& statement_; +}; + +struct db2_session_backend; +struct SOCI_DB2_DECL db2_statement_backend : details::statement_backend +{ + db2_statement_backend(db2_session_backend &session); + + void alloc(); + void clean_up(); + void prepare(std::string const& query, details::statement_type eType); + + exec_fetch_result execute(int number); + exec_fetch_result fetch(int number); + + long long get_affected_rows(); + int get_number_of_rows(); + + std::string rewrite_for_procedure_call(std::string const& query); + + int prepare_for_describe(); + void describe_column(int colNum, data_type& dtype, std::string& columnName); + + db2_standard_into_type_backend* make_into_type_backend(); + db2_standard_use_type_backend* make_use_type_backend(); + db2_vector_into_type_backend* make_vector_into_type_backend(); + db2_vector_use_type_backend* make_vector_use_type_backend(); + + db2_session_backend& session_; +}; + +struct db2_rowid_backend : details::rowid_backend +{ + db2_rowid_backend(db2_session_backend &session); + + ~db2_rowid_backend(); +}; + +struct db2_blob_backend : details::blob_backend +{ + db2_blob_backend(db2_session_backend& session); + + ~db2_blob_backend(); + + std::size_t get_len(); + std::size_t read(std::size_t offset, char* buf, std::size_t toRead); + std::size_t write(std::size_t offset, char const* buf, std::size_t toWrite); + std::size_t append(char const* buf, std::size_t toWrite); + void trim(std::size_t newLen); + + db2_session_backend& session_; +}; + +struct db2_session_backend : details::session_backend +{ + db2_session_backend(std::string const& connectString); + + ~db2_session_backend(); + + void begin(); + void commit(); + void rollback(); + + std::string get_backend_name() const { return "DB2"; } + + void clean_up(); + + db2_statement_backend* make_statement_backend(); + db2_rowid_backend* make_rowid_backend(); + db2_blob_backend* make_blob_backend(); + + std::string dsn; + std::string username; + std::string password; + bool autocommit; + + SQLHANDLE hEnv; /* Environment handle */ + SQLHANDLE hDbc; /* Connection handle */ +}; + +struct SOCI_DB2_DECL db2_backend_factory : backend_factory +{ + db2_backend_factory() {} + db2_session_backend* make_session(std::string const& connectString) const; +}; + +extern SOCI_DB2_DECL db2_backend_factory const db2; + +extern "C" +{ + +// for dynamic backend loading +SOCI_DB2_DECL backend_factory const* factory_db2(); +SOCI_DB2_DECL void register_factory_db2(); + +} // extern "C" + +} // namespace soci + +#endif // SOCI_DB2_H_INCLUDED diff --git a/src/backends/db2/test/CMakeLists.txt b/src/backends/db2/test/CMakeLists.txt new file mode 100644 index 000000000..62d7aad4a --- /dev/null +++ b/src/backends/db2/test/CMakeLists.txt @@ -0,0 +1,4 @@ +soci_backend_test( + BACKEND DB2 + SOURCE test-db2.cpp + CONNSTR "dummy") \ No newline at end of file diff --git a/src/backends/db2/test/test-db2.cpp b/src/backends/db2/test/test-db2.cpp new file mode 100644 index 000000000..304153657 --- /dev/null +++ b/src/backends/db2/test/test-db2.cpp @@ -0,0 +1,170 @@ +// +// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#include "soci.h" +#include "soci-empty.h" +#include +#include +#include +#include +#include + +using namespace soci; + +std::string connectString; +backend_factory const &backEnd = *soci::factory_empty(); + + +// NOTE: +// This file is supposed to serve two purposes: +// 1. To be a starting point for implementing new tests (for new backends). +// 2. To exercise (at least some of) the syntax and try the SOCI library +// against different compilers, even in those environments where there +// is no database. SOCI uses advanced template techniques which are known +// to cause problems on different versions of popular compilers, and this +// test is handy to verify that the code is accepted by as many compilers +// as possible. +// +// Both of these purposes mean that the actual code here is meaningless +// from the database-development point of view. For new tests, you may wish +// to remove this code and keep only the general structure of this file. + +struct Person +{ + int id; + std::string firstName; + std::string lastName; +}; + +namespace soci +{ + template<> struct type_conversion + { + typedef values base_type; + static void from_base(values & /* r */, indicator /* ind */, + Person & /* p */) + { + } + }; +} + +void test1() +{ + { + session sql(backEnd, connectString); + + sql << "Do what I want."; + sql << "Do what I want " << 123 << " times."; + + std::string query = "some query"; + sql << query; + + int i = 7; + sql << "insert", use(i); + sql << "select", into(i); + +#if defined (__LP64__) || ( __WORDSIZE == 64 ) + long int li = 9; + sql << "insert", use(li); + sql << "select", into(li); +#endif + + long long ll = 11; + sql << "insert", use(ll); + sql << "select", into(ll); + + indicator ind = i_ok; + sql << "insert", use(i, ind); + sql << "select", into(i, ind); + + std::vector numbers(100); + sql << "insert", use(numbers); + sql << "select", into(numbers); + + std::vector inds(100); + sql << "insert", use(numbers, inds); + sql << "select", into(numbers, inds); + + { + statement st = (sql.prepare << "select", into(i)); + st.execute(); + st.fetch(); + } + { + statement st = (sql.prepare << "select", into(i, ind)); + } + { + statement st = (sql.prepare << "select", into(numbers)); + } + { + statement st = (sql.prepare << "select", into(numbers, inds)); + } + { + statement st = (sql.prepare << "insert", use(i)); + } + { + statement st = (sql.prepare << "insert", use(i, ind)); + } + { + statement st = (sql.prepare << "insert", use(numbers)); + } + { + statement st = (sql.prepare << "insert", use(numbers, inds)); + } + { + Person p; + sql << "select person", into(p); + } + + } + + std::cout << "test 1 passed" << std::endl; +} + + +int main(int argc, char** argv) +{ + +#ifdef _MSC_VER + // Redirect errors, unrecoverable problems, and assert() failures to STDERR, + // instead of debug message window. + // This hack is required to run asser()-driven tests by Buildbot. + // NOTE: Comment this 2 lines for debugging with Visual C++ debugger to catch assertions inside. + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); +#endif //_MSC_VER + + if (argc == 2) + { + connectString = argv[1]; + } + else + { + std::cout << "usage: " << argv[0] + << " connectstring\n" + << "example: " << argv[0] + << " \'connect_string_for_empty_backend\'\n"; + std::exit(1); + } + + try + { + test1(); + // test2(); + // ... + + std::cout << "\nOK, all tests passed.\n\n"; + + return EXIT_SUCCESS; + } + catch (std::exception const & e) + { + std::cout << e.what() << '\n'; + } + + return EXIT_FAILURE; +} diff --git a/src/cmake/SociDependencies.cmake b/src/cmake/SociDependencies.cmake index 060e446c6..3c12771a7 100644 --- a/src/cmake/SociDependencies.cmake +++ b/src/cmake/SociDependencies.cmake @@ -25,7 +25,8 @@ set(SOCI_BACKENDS_DB_DEPENDENCIES Oracle PostgreSQL SQLite3 - Firebird) + Firebird + DB2) set(SOCI_BACKENDS_ALL_DEPENDENCIES Boost diff --git a/src/cmake/dependencies/DB2.cmake b/src/cmake/dependencies/DB2.cmake new file mode 100644 index 000000000..063f1a17d --- /dev/null +++ b/src/cmake/dependencies/DB2.cmake @@ -0,0 +1,5 @@ +set(DB2_FIND_QUIETLY TRUE) + +find_package(DB2) + +boost_external_report(DB2 INCLUDE_DIR LIBRARIES) diff --git a/src/cmake/modules/FindDB2.cmake b/src/cmake/modules/FindDB2.cmake new file mode 100644 index 000000000..ac1815c52 --- /dev/null +++ b/src/cmake/modules/FindDB2.cmake @@ -0,0 +1,42 @@ +#TODO: Add detection on win32 + +find_path(DB2_INCLUDE_DIR sqlcli1.h + $ENV{DB2_INCLUDE_DIR} + $ENV{DB2_DIR}/include + /opt/ibm/db2/V9.7/include + /opt/ibm/db2/V9.5/include + /opt/ibm/db2/V9.1/include) + +find_library(DB2_LIBRARY NAMES db2 + PATHS + $ENV{DB2_LIB_DIR} + $ENV{DB2_DIR}/lib32 + $ENV{DB2_DIR}/lib64 + /opt/ibm/db2/V9.7/lib32 + /opt/ibm/db2/V9.5/lib64 + /opt/ibm/db2/V9.1/lib32 + /opt/ibm/db2/V9.1/lib64) + +if(DB2_LIBRARY) + get_filename_component(DB2_LIBRARY_DIR ${DB2_LIBRARY} PATH) +endif() + +if(DB2_INCLUDE_DIR AND DB2_LIBRARY_DIR) + set(DB2_FOUND TRUE) + + include_directories(${DB2_INCLUDE_DIR}) + link_directories(${DB2_LIBRARY_DIR}) + +endif() + +set(DB2_LIBRARIES ${DB2_LIBRARY}) + +# Handle the QUIETLY and REQUIRED arguments and set DB2_FOUND to TRUE +# if all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(DB2 + DEFAULT_MSG + DB2_INCLUDE_DIR + DB2_LIBRARIES) + +mark_as_advanced(DB2_INCLUDE_DIR DB2_LIBRARIES)