diff --git a/src/backends/db2/session.cpp b/src/backends/db2/session.cpp index 43285e915..25538b918 100644 --- a/src/backends/db2/session.cpp +++ b/src/backends/db2/session.cpp @@ -17,7 +17,7 @@ using namespace soci; using namespace soci::details; const std::string db2_soci_error::sqlState(std::string const & msg,const SQLSMALLINT htype,const SQLHANDLE hndl) { - std::stringstream ss(msg); + std::ostringstream ss(msg, std::ostringstream::app); SQLCHAR message[SQL_MAX_MESSAGE_LENGTH + 1]; @@ -76,7 +76,8 @@ void db2_session_backend::parseConnectString(std::string const & connectString) } db2_session_backend::db2_session_backend( - std::string const & connectString /* DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;AutoCommit=off */) + std::string const & connectString /* DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;AutoCommit=off */) : + in_transaction(false) { parseConnectString(connectString); SQLRETURN cliRC = SQL_SUCCESS; @@ -108,7 +109,9 @@ db2_session_backend::db2_session_backend( } /* Connect to database */ - cliRC = SQLConnect(hDbc,(SQLCHAR*)dsn.c_str(),SQL_NTS,(SQLCHAR*)username.c_str(),SQL_NTS,(SQLCHAR*)password.c_str(),SQL_NTS); + cliRC = SQLConnect(hDbc, const_cast((const SQLCHAR *) dsn.c_str()), SQL_NTS, + const_cast((const SQLCHAR *) username.c_str()), SQL_NTS, + const_cast((const SQLCHAR *) password.c_str()), SQL_NTS); if (cliRC != SQL_SUCCESS) { std::string msg=db2_soci_error::sqlState("Error connecting to database",SQL_HANDLE_DBC,hDbc); SQLFreeHandle(SQL_HANDLE_DBC,hDbc); @@ -124,14 +127,40 @@ db2_session_backend::~db2_session_backend() void db2_session_backend::begin() { - //Transations begin implicitly + // In DB2, transations begin implicitly; however, autocommit must be disabled for the duration of the transaction + if(autocommit) + { + SQLRETURN cliRC = SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_OFF, SQL_NTS); + if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO) + { + std::string msg=db2_soci_error::sqlState("Clearing the autocommit attribute failed", SQL_HANDLE_DBC, hDbc); + SQLFreeHandle(SQL_HANDLE_DBC,hDbc); + SQLFreeHandle(SQL_HANDLE_ENV,hEnv); + throw db2_soci_error(msg,cliRC); + } + } + + in_transaction = true; } void db2_session_backend::commit() { - if (!autocommit) { + if (!autocommit || in_transaction) { + in_transaction = false; SQLRETURN cliRC = SQLEndTran(SQL_HANDLE_DBC,hDbc,SQL_COMMIT); - if (cliRC != SQL_SUCCESS) { + if(autocommit) + { + SQLRETURN cliRC2 = SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_NTS); + if ((cliRC == SQL_SUCCESS || cliRC == SQL_SUCCESS_WITH_INFO) && + cliRC2 != SQL_SUCCESS && cliRC2 != SQL_SUCCESS_WITH_INFO) + { + std::string msg=db2_soci_error::sqlState("Setting the autocommit attribute failed", SQL_HANDLE_DBC, hDbc); + SQLFreeHandle(SQL_HANDLE_DBC,hDbc); + SQLFreeHandle(SQL_HANDLE_ENV,hEnv); + throw db2_soci_error(msg,cliRC); + } + } + if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO) { throw db2_soci_error("Commit failed",cliRC); } } @@ -139,9 +168,22 @@ void db2_session_backend::commit() void db2_session_backend::rollback() { - if (!autocommit) { + if (!autocommit || in_transaction) { + in_transaction = false; SQLRETURN cliRC = SQLEndTran(SQL_HANDLE_DBC,hDbc,SQL_ROLLBACK); - if (cliRC != SQL_SUCCESS) { + if(autocommit) + { + SQLRETURN cliRC2 = SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_NTS); + if ((cliRC == SQL_SUCCESS || cliRC == SQL_SUCCESS_WITH_INFO) && + cliRC2 != SQL_SUCCESS && cliRC2 != SQL_SUCCESS_WITH_INFO) + { + std::string msg=db2_soci_error::sqlState("Setting the autocommit attribute failed", SQL_HANDLE_DBC, hDbc); + SQLFreeHandle(SQL_HANDLE_DBC,hDbc); + SQLFreeHandle(SQL_HANDLE_ENV,hEnv); + throw db2_soci_error(msg,cliRC); + } + } + if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO) { throw db2_soci_error("Rollback failed",cliRC); } } @@ -149,6 +191,9 @@ void db2_session_backend::rollback() void db2_session_backend::clean_up() { + // if a transaction is in progress, it will automatically be rolled back upon when the connection is disconnected/freed + in_transaction = false; + SQLDisconnect(hDbc); SQLFreeHandle(SQL_HANDLE_DBC,hDbc); SQLFreeHandle(SQL_HANDLE_ENV,hEnv); diff --git a/src/backends/db2/soci-db2.h b/src/backends/db2/soci-db2.h index 287a85b6d..4bcc682b4 100644 --- a/src/backends/db2/soci-db2.h +++ b/src/backends/db2/soci-db2.h @@ -38,6 +38,17 @@ namespace soci { + namespace details { namespace db2 + { + enum binding_method + { + BOUND_BY_NONE, + BOUND_BY_NAME, + BOUND_BY_POSITION + }; + }} + + static const std::size_t maxBuffer = 1024 * 1024 * 1024; //CLI limit is about 3 GB, but 1GB should be enough class db2_soci_error : public soci_error { public: @@ -107,7 +118,7 @@ struct SOCI_DB2_DECL db2_vector_into_type_backend : details::vector_into_type_ba 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),buf(NULL),indptr(NULL) + : statement_(st),buf(NULL),ind(0) {} void bind_by_pos(int& position, void* data, details::exchange_type type, bool readOnly); @@ -128,7 +139,7 @@ struct SOCI_DB2_DECL db2_standard_use_type_backend : details::standard_use_type_ int position; std::string name; char* buf; - SQLLEN indptr; + SQLLEN ind; }; struct SOCI_DB2_DECL db2_vector_use_type_backend : details::vector_use_type_backend @@ -192,6 +203,7 @@ struct SOCI_DB2_DECL db2_statement_backend : details::statement_backend std::vector names; bool hasVectorUseElements; SQLUINTEGER numRowsFetched; + details::db2::binding_method use_binding_method_; }; struct db2_rowid_backend : details::rowid_backend @@ -241,6 +253,7 @@ struct db2_session_backend : details::session_backend std::string username; std::string password; bool autocommit; + bool in_transaction; SQLHANDLE hEnv; /* Environment handle */ SQLHANDLE hDbc; /* Connection handle */ diff --git a/src/backends/db2/standard-into-type.cpp b/src/backends/db2/standard-into-type.cpp index d368f9330..f6f3dd174 100644 --- a/src/backends/db2/standard-into-type.cpp +++ b/src/backends/db2/standard-into-type.cpp @@ -49,7 +49,7 @@ void db2_standard_into_type_backend::define_by_pos( break; case x_integer: cType = SQL_C_SLONG; - size = sizeof(long); + size = sizeof(SQLINTEGER); break; case x_long_long: cType = SQL_C_SBIGINT; diff --git a/src/backends/db2/standard-use-type.cpp b/src/backends/db2/standard-use-type.cpp index ce4e7ae75..581ac9b17 100644 --- a/src/backends/db2/standard-use-type.cpp +++ b/src/backends/db2/standard-use-type.cpp @@ -55,7 +55,7 @@ void db2_standard_use_type_backend::prepare_for_bind( size = sizeof(char) + 1; buf = new char[size]; data = buf; - indptr = SQL_NTS; + ind = SQL_NTS; break; case x_stdstring: { @@ -67,7 +67,7 @@ void db2_standard_use_type_backend::prepare_for_bind( size = s->size() + 1; buf = new char[size]; data = buf; - indptr = SQL_NTS; + ind = SQL_NTS; } break; case x_stdtm: @@ -102,7 +102,7 @@ void db2_standard_use_type_backend::bind_helper(int &position, void *data, detai SQLRETURN cliRC = SQLBindParameter(statement_.hStmt, static_cast(position++), SQL_PARAM_INPUT, - cType, sqlType, size, 0, data, size, &indptr); + cType, sqlType, size, 0, data, size, &ind); if (cliRC != SQL_SUCCESS) { @@ -113,13 +113,24 @@ void db2_standard_use_type_backend::bind_helper(int &position, void *data, detai void db2_standard_use_type_backend::bind_by_pos( int &position, void *data, exchange_type type, bool /* readOnly */) { - bind_helper(position, data, type); + if (statement_.use_binding_method_ == details::db2::BOUND_BY_NAME) + { + throw soci_error("Binding for use elements must be either by position or by name."); + } + statement_.use_binding_method_ = details::db2::BOUND_BY_POSITION; + bind_helper(position, data, type); } void db2_standard_use_type_backend::bind_by_name( std::string const &name, void *data, exchange_type type, bool /* readOnly */) { + if (statement_.use_binding_method_ == details::db2::BOUND_BY_POSITION) + { + throw soci_error("Binding for use elements must be either by position or by name."); + } + statement_.use_binding_method_ = details::db2::BOUND_BY_NAME; + int position = -1; int count = 1; @@ -146,7 +157,7 @@ void db2_standard_use_type_backend::bind_by_name( } } -void db2_standard_use_type_backend::pre_use(indicator const *ind) +void db2_standard_use_type_backend::pre_use(indicator const *ind_ptr) { // first deal with data if (type == x_char) @@ -180,9 +191,9 @@ void db2_standard_use_type_backend::pre_use(indicator const *ind) } // then handle indicators - if (ind != NULL && *ind == i_null) + if (ind_ptr != NULL && *ind_ptr == i_null) { - indptr = SQL_NULL_DATA; // null + ind = SQL_NULL_DATA; // null } } diff --git a/src/backends/db2/statement.cpp b/src/backends/db2/statement.cpp index 3b29b8343..5f57decdf 100644 --- a/src/backends/db2/statement.cpp +++ b/src/backends/db2/statement.cpp @@ -19,7 +19,7 @@ using namespace soci::details; db2_statement_backend::db2_statement_backend(db2_session_backend &session) - : session_(session),hasVectorUseElements(false) + : session_(session),hasVectorUseElements(false),use_binding_method_(details::db2::BOUND_BY_NONE) { } @@ -37,14 +37,6 @@ void db2_statement_backend::clean_up() { SQLRETURN cliRC = SQL_SUCCESS; - cliRC=SQLFreeStmt(hStmt,SQL_CLOSE); - if (cliRC != SQL_SUCCESS) { - throw db2_soci_error(db2_soci_error::sqlState("Statement handle close error",SQL_HANDLE_STMT,hStmt),cliRC); - } - cliRC=SQLFreeStmt(hStmt,SQL_UNBIND); - if (cliRC != SQL_SUCCESS) { - throw db2_soci_error(db2_soci_error::sqlState("Statement handle unbind error",SQL_HANDLE_STMT,hStmt),cliRC); - } cliRC=SQLFreeHandle(SQL_HANDLE_STMT,hStmt); if (cliRC != SQL_SUCCESS) { throw db2_soci_error(db2_soci_error::sqlState("Statement handle clean-up error",SQL_HANDLE_STMT,hStmt),cliRC); @@ -131,7 +123,7 @@ void db2_statement_backend::prepare(std::string const & query , query_ += ss.str(); } - SQLRETURN cliRC = SQLPrepare(hStmt,(SQLCHAR*)query_.c_str(),SQL_NTS); + SQLRETURN cliRC = SQLPrepare(hStmt, const_cast((const SQLCHAR *) query_.c_str()), SQL_NTS); if (cliRC!=SQL_SUCCESS) { throw db2_soci_error("Error while preparing query",cliRC); } @@ -140,7 +132,8 @@ void db2_statement_backend::prepare(std::string const & query , statement_backend::exec_fetch_result db2_statement_backend::execute(int number ) { - SQLULEN rows_processed = 0; + SQLUINTEGER rows_processed = 0; + SQLRETURN cliRC; if (hasVectorUseElements) { @@ -149,13 +142,18 @@ db2_statement_backend::execute(int number ) // if we are called twice for the same statement we need to close the open // cursor or an "invalid cursor state" error will occur on execute - - SQLRETURN cliRC = SQLExecute(hStmt); + cliRC = SQLFreeStmt(hStmt,SQL_CLOSE); if (cliRC != SQL_SUCCESS) { throw db2_soci_error(db2_soci_error::sqlState("Statement execution error",SQL_HANDLE_STMT,hStmt),cliRC); } + cliRC = SQLExecute(hStmt); + if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO) + { + throw db2_soci_error(db2_soci_error::sqlState("Statement execution error",SQL_HANDLE_STMT,hStmt),cliRC); + } + SQLSMALLINT colCount; SQLNumResultCols(hStmt, &colCount); @@ -164,8 +162,6 @@ db2_statement_backend::execute(int number ) return fetch(number); } - SQLFreeStmt(hStmt,SQL_RESET_PARAMS); //Prepare for next call - return ef_success; } @@ -185,9 +181,9 @@ db2_statement_backend::fetch(int number ) return ef_no_data; } - if (cliRC != SQL_SUCCESS) + if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO) { - throw db2_soci_error("Error while fetching data",cliRC); + throw db2_soci_error(db2_soci_error::sqlState("Error while fetching data", SQL_HANDLE_STMT, hStmt), cliRC); } return ef_success; @@ -195,8 +191,19 @@ db2_statement_backend::fetch(int number ) long long db2_statement_backend::get_affected_rows() { - // ... - return -1; + SQLLEN rows; + + SQLRETURN cliRC = SQLRowCount(hStmt, &rows); + if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO) + { + throw db2_soci_error(db2_soci_error::sqlState("Error while getting affected row count", SQL_HANDLE_STMT, hStmt), cliRC); + } + else if (rows == -1) + { + throw soci_error("Error getting affected row count: statement did not perform an update, insert, delete, or merge"); + } + + return rows; } int db2_statement_backend::get_number_of_rows() diff --git a/src/backends/db2/test/test-db2.cpp b/src/backends/db2/test/test-db2.cpp index 45b68bf87..471e336da 100644 --- a/src/backends/db2/test/test-db2.cpp +++ b/src/backends/db2/test/test-db2.cpp @@ -8,6 +8,7 @@ #include "soci.h" #include "soci-db2.h" +#include "common-tests.h" #include #include #include @@ -15,24 +16,92 @@ #include using namespace soci; +using namespace soci::tests; std::string connectString; backend_factory const &backEnd = *soci::factory_db2(); -// 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. +// Support for soci Common Tests +// + +struct table_creator_one : public table_creator_base +{ + table_creator_one(session & sql) + : table_creator_base(sql) + { + sql << "CREATE TABLE SOCI_TEST(ID INTEGER, VAL SMALLINT, C CHAR, STR VARCHAR(20), SH SMALLINT, UL NUMERIC(20), D DOUBLE, " + "TM TIMESTAMP, I1 INTEGER, I2 INTEGER, I3 INTEGER, NAME VARCHAR(20))"; + } +}; + +struct table_creator_two : public table_creator_base +{ + table_creator_two(session & sql) + : table_creator_base(sql) + { + sql << "CREATE TABLE SOCI_TEST(NUM_FLOAT DOUBLE, NUM_INT INTEGER, NAME VARCHAR(20), SOMETIME TIMESTAMP, CHR CHAR)"; + } +}; + +struct table_creator_three : public table_creator_base +{ + table_creator_three(session & sql) + : table_creator_base(sql) + { + sql << "CREATE TABLE SOCI_TEST(NAME VARCHAR(100) NOT NULL, PHONE VARCHAR(15))"; + } +}; + +struct table_creator_for_get_affected_rows : table_creator_base +{ + table_creator_for_get_affected_rows(session & sql) + : table_creator_base(sql) + { + sql << "CREATE TABLE SOCI_TEST(VAL INTEGER)"; + } +}; + +class test_context :public test_context_base +{ +public: + test_context(backend_factory const & pi_back_end, std::string const & pi_connect_string) + : test_context_base(pi_back_end, pi_connect_string) {} + + table_creator_base* table_creator_1(session & pr_s) const + { + pr_s << "SET CURRENT SCHEMA = 'DB2INST1'"; + return new table_creator_one(pr_s); + } + + table_creator_base* table_creator_2(session & pr_s) const + { + pr_s << "SET CURRENT SCHEMA = 'DB2INST1'"; + return new table_creator_two(pr_s); + } + + table_creator_base* table_creator_3(session & pr_s) const + { + pr_s << "SET CURRENT SCHEMA = 'DB2INST1'"; + return new table_creator_three(pr_s); + } + + table_creator_base* table_creator_4(session& s) const + { + return new table_creator_for_get_affected_rows(s); + } + + std::string to_date_time(std::string const & pi_datdt_string) const + { + return "to_date('" + pi_datdt_string + "', 'YYYY-MM-DD HH24:MI:SS')"; + } +}; + + +// +// Additional tests to exercise the DB2 backend +// void test1() { @@ -42,64 +111,178 @@ void test1() sql << "SELECT CURRENT TIMESTAMP FROM SYSIBM.SYSDUMMY1"; sql << "SELECT " << 123 << " FROM SYSIBM.SYSDUMMY1"; - std::string query = "CREATE TABLE DB2INST1.TEST (ID BIGINT,DATA VARCHAR(8))"; + std::string query = "CREATE TABLE DB2INST1.SOCI_TEST (ID BIGINT,DATA VARCHAR(8))"; sql << query; - int i = 7; - sql << "insert into db2inst1.TEST (id) values (:id)", use(i,"id"); - sql << "select id from db2inst1.TEST where id=7", into(i); + { + const int i = 7; + sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(i,"id"); + int j = 0; + sql << "select id from db2inst1.SOCI_TEST where id=7", into(j); + assert(j == i); + } - -#if defined (__LP64__) || ( __WORDSIZE == 64 ) - long int li = 9; - sql << "insert into db2inst1.TEST (id) values (:id)", use(li,"id"); - sql << "select id from db2inst1.TEST where id=9", into(li); -#endif + { + const long int li = 9; + sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(li,"id"); + long int lj = 0;; + sql << "select id from db2inst1.SOCI_TEST where id=9", into(lj); + assert(lj == li); + } - long long ll = 11; - sql << "insert into db2inst1.TEST (id) values (:id)", use(ll,"id"); - sql << "select id from db2inst1.TEST where id=11", into(ll); + { + const long long ll = 11; + sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(ll,"id"); + long long lj = 0; + sql << "select id from db2inst1.SOCI_TEST where id=11", into(lj); + assert(lj == ll); + } - indicator ind = i_ok; - sql << "insert into db2inst1.TEST (id) values (:id)", use(i,ind,"id"); - sql << "select id from db2inst1.TEST where id=7", into(i,ind); + { + const int i = 13; + indicator i_ind = i_ok; + sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(i,i_ind,"id"); + int j = 0; + indicator j_ind = i_null; + sql << "select id from db2inst1.SOCI_TEST where id=13", into(j,j_ind); + assert(j == i); + assert(j_ind == i_ok); + } - std::vector numbers(100); - sql << "insert into db2inst1.TEST (id) values (:id)", use(numbers,"id"); - sql << "select id from db2inst1.TEST", into(numbers); + { + std::vector numbers(100); + for (int i = 0 ; i < 100 ; i++) + { + numbers[i] = i + 1000; + } + sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(numbers,"id"); + sql << "select id from db2inst1.SOCI_TEST where id >= 1000 and id < 2000 order by id", into(numbers); + for (int i = 0 ; i < 100 ; i++) + { + assert(numbers[i] == i + 1000); + } + } - std::vector inds(100); - sql << "insert into db2inst1.TEST (id) values (:id)", use(numbers,inds,"id"); - sql << "select id from db2inst1.TEST", into(numbers,inds); + { + std::vector numbers(100); + std::vector inds(100); + for (int i = 0 ; i < 100 ; i++) + { + numbers[i] = i + 2000; + inds[i] = i_ok; + } + sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(numbers,inds,"id"); + for (int i = 0 ; i < 100 ; i++) + { + numbers[i] = 0; + inds[i] = i_null; + } + sql << "select id from db2inst1.SOCI_TEST where id >= 2000 and id < 3000 order by id", into(numbers,inds); + for (int i = 0 ; i < 100 ; i++) + { + assert(numbers[i] == i + 2000); + assert(inds[i] == i_ok); + } + } { - statement st = (sql.prepare << "select id from db2inst1.TEST", into(i)); + int i = 0; + statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST where id < 1000", into(i)); st.execute(); st.fetch(); + assert (i == 7); + st.fetch(); + assert (i == 9); + st.fetch(); + assert (i == 11); + st.fetch(); + assert (i == 13); } + { - statement st = (sql.prepare << "select id from db2inst1.TEST", into(i, ind)); + int i = 0; + indicator i_ind = i_null; + std::string d; + indicator d_ind = i_ok; + statement st = (sql.prepare << "select id, data from db2inst1.SOCI_TEST where id = 13", into(i, i_ind), into(d, d_ind)); + st.execute(); + st.fetch(); + assert (i == 13); + assert (i_ind == i_ok); + assert (d_ind == i_null); } + { - statement st = (sql.prepare << "select id from db2inst1.TEST", into(numbers)); + std::vector numbers(100); + for (int i = 0 ; i < 100 ; i++) + { + numbers[i] = 0; + } + statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST where id >= 1000 order by id", into(numbers)); + st.execute(); + st.fetch(); + for (int i = 0 ; i < 100 ; i++) + { + assert(numbers[i] == i + 1000); + } + st.fetch(); + for (int i = 0 ; i < 100 ; i++) + { + assert(numbers[i] == i + 2000); + } } + { - statement st = (sql.prepare << "select id from db2inst1.TEST", into(numbers, inds)); + std::vector numbers(100); + std::vector inds(100); + for (int i = 0 ; i < 100 ; i++) + { + numbers[i] = 0; + inds[i] = i_null; + } + statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST where id >= 1000 order by id", into(numbers, inds)); + st.execute(); + st.fetch(); + for (int i = 0 ; i < 100 ; i++) + { + assert(numbers[i] == i + 1000); + assert(inds[i] == i_ok); + } + st.fetch(); + for (int i = 0 ; i < 100 ; i++) + { + assert(numbers[i] == i + 2000); + assert(inds[i] == i_ok); + } } + { - statement st = (sql.prepare << "select id from db2inst1.TEST", use(i)); + // XXX: what is the purpose of this test?? what is the expected value? + int i = 0; + statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST", use(i)); } + { - statement st = (sql.prepare << "select id from db2inst1.TEST", use(i, ind)); + // XXX: what is the purpose of this test?? what is the expected value? + int i = 0; + indicator ind = i_ok; + statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST", use(i, ind)); } + { - statement st = (sql.prepare << "select id from db2inst1.TEST", use(numbers)); + // XXX: what is the purpose of this test?? what is the expected value? + std::vector numbers(100); + statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST", use(numbers)); } + { - statement st = (sql.prepare << "select id from db2inst1.TEST", use(numbers, inds)); + // XXX: what is the purpose of this test?? what is the expected value? + std::vector numbers(100); + std::vector inds(100); + statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST", use(numbers, inds)); } - sql<<"DROP TABLE DB2INST1.TEST"; + sql<<"DROP TABLE DB2INST1.SOCI_TEST"; sql.commit(); } @@ -111,25 +294,42 @@ void test2() { { session sql(backEnd, connectString); - std::string query = "CREATE TABLE DB2INST1.TEST (ID BIGINT,DATA VARCHAR(8),DT TIMESTAMP)"; + std::string query = "CREATE TABLE DB2INST1.SOCI_TEST (ID BIGINT,DATA VARCHAR(8),DT TIMESTAMP)"; sql << query; - int i = 7; - std::string n("test"); - sql << "insert into db2inst1.TEST (id,data) values (:id,:name)", use(i,"id"),use(n,"name"); - sql << "select id,data from db2inst1.TEST where id=7", into(i),into(n); - - i = 8; - indicator ind = i_ok; - sql << "insert into db2inst1.TEST (id) values (:id)", use(i,"id"); - sql << "select id,data from db2inst1.TEST where id=8", into(i),into(n,ind); - assert(ind==i_null); - - std::tm dt; - sql << "select current timestamp from sysibm.sysdummy1",into(dt); - sql << "insert into db2inst1.TEST (dt) values (:dt)",use(dt,"dt"); + { + int i = 7; + std::string n("test"); + sql << "insert into db2inst1.SOCI_TEST (id,data) values (:id,:name)", use(i,"id"),use(n,"name"); + int j; + std::string m; + sql << "select id,data from db2inst1.SOCI_TEST where id=7", into(j),into(m); + assert (j == i); + assert (m == n); + } + + { + int i = 8; + sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(i,"id"); + int j; + std::string m; + indicator ind = i_ok; + sql << "select id,data from db2inst1.SOCI_TEST where id=8", into(j),into(m,ind); + assert(j == i); + assert(ind==i_null); + } - sql<<"DROP TABLE DB2INST1.TEST"; + { + std::tm dt; + sql << "select current timestamp from sysibm.sysdummy1",into(dt); + sql << "insert into db2inst1.SOCI_TEST (dt) values (:dt)",use(dt,"dt"); + std::tm dt2; + sql << "select dt from db2inst1.SOCI_TEST where dt is not null", into(dt2); + assert(dt2.tm_year == dt.tm_year && dt2.tm_mon == dt.tm_mon && dt2.tm_mday == dt.tm_mday && + dt2.tm_hour == dt.tm_hour && dt2.tm_min == dt.tm_min && dt2.tm_sec == dt.tm_sec); + } + + sql<<"DROP TABLE DB2INST1.SOCI_TEST"; sql.commit(); } @@ -139,24 +339,58 @@ void test2() { void test3() { { session sql(backEnd, connectString); + int i; - std::string query = "CREATE TABLE DB2INST1.TEST (ID BIGINT,DATA VARCHAR(8),DT TIMESTAMP)"; + std::string query = "CREATE TABLE DB2INST1.SOCI_TEST (ID BIGINT,DATA VARCHAR(8),DT TIMESTAMP)"; sql << query; - std::vector strings(100); - for(std::vector::iterator it=strings.begin();it!=strings.end();it++) { - *it="test"; + std::vector ids(100); + std::vector data(100); + std::vector dts(100); + for (int i = 0; i < 100; i++) + { + ids[i] = 1000000000LL + i; + data[i] = "test"; + dts[i].tm_year = 112; + dts[i].tm_mon = 7; + dts[i].tm_mday = 17; + dts[i].tm_hour = 0; + dts[i].tm_min = 0; + dts[i].tm_sec = i % 60; } - sql << "insert into db2inst1.TEST (data) values (:data)", use(strings,"data"); - rowset rs = (sql.prepare<<"SELECT data from db2inst1.TEST"); + + sql << "insert into db2inst1.SOCI_TEST (id, data, dt) values (:id, :data, :dt)", + use(ids, "id"), use(data,"data"), use(dts, "dt"); - sql<<"DROP TABLE DB2INST1.TEST"; + i = 0; + rowset rs = (sql.prepare<<"SELECT ID, DATA, DT FROM DB2INST1.SOCI_TEST"); + for (rowset::const_iterator it = rs.begin(); it != rs.end(); it++) + { + const row & r = *it; + const long long id = r.get(0); + const std::string data = r.get(1); + const std::tm dt = r.get(2); + + assert(id == 1000000000LL + i); + assert(data == "test"); + assert(dt.tm_year == 112); + assert(dt.tm_mon == 7); + assert(dt.tm_mday == 17); + assert(dt.tm_hour == 0); + assert(dt.tm_min == 0); + assert(dt.tm_sec == i % 60); + + i += 1; + } + + sql<<"DROP TABLE DB2INST1.SOCI_TEST"; sql.commit(); } std::cout << "test 3 passed" << std::endl; } + int main(int argc, char** argv) { @@ -182,8 +416,26 @@ int main(int argc, char** argv) std::exit(1); } + test_context tc(backEnd, connectString); + common_tests tests(tc); + tests.run(); + try { + std::cout<<"\nSOCI DB2 Tests:\n\n"; + + session sql(backEnd, connectString); + + try + { + // attempt to delete the test table from previous runs + sql << "DROP TABLE DB2INST1.SOCI_TEST"; + } + catch (soci_error const & e) + { + // if the table didn't exist, then proceed + } + test1(); test2(); test3(); diff --git a/src/backends/db2/vector-into-type.cpp b/src/backends/db2/vector-into-type.cpp index c97d4eabc..e41f34e41 100644 --- a/src/backends/db2/vector-into-type.cpp +++ b/src/backends/db2/vector-into-type.cpp @@ -52,9 +52,9 @@ void db2_vector_into_type_backend::define_by_pos( case x_integer: { cType = SQL_C_SLONG; - size = sizeof(long); - std::vector *vp = static_cast *>(data); - std::vector &v(*vp); + size = sizeof(SQLINTEGER); + std::vector *vp = static_cast *>(data); + std::vector &v(*vp); prepare_indicators(v.size()); data = &v[0]; } @@ -287,7 +287,7 @@ void db2_vector_into_type_backend::resize(std::size_t sz) break; case x_integer: { - std::vector *v = static_cast *>(data); + std::vector *v = static_cast *>(data); v->resize(sz); } break; @@ -353,7 +353,7 @@ std::size_t db2_vector_into_type_backend::size() break; case x_integer: { - std::vector *v = static_cast *>(data); + std::vector *v = static_cast *>(data); sz = v->size(); } break; diff --git a/src/backends/db2/vector-use-type.cpp b/src/backends/db2/vector-use-type.cpp index dbdbc39f3..05a810464 100644 --- a/src/backends/db2/vector-use-type.cpp +++ b/src/backends/db2/vector-use-type.cpp @@ -210,8 +210,13 @@ void db2_vector_use_type_backend::bind_helper(int &position, void *data, details void db2_vector_use_type_backend::bind_by_pos(int &position, void *data, exchange_type type) { - bind_helper(position, data, type); + if (statement_.use_binding_method_ == details::db2::BOUND_BY_NAME) + { + throw soci_error("Binding for use elements must be either by position or by name."); + } + statement_.use_binding_method_ = details::db2::BOUND_BY_POSITION; + bind_helper(position, data, type); } void db2_vector_use_type_backend::bind_by_name( @@ -220,6 +225,12 @@ void db2_vector_use_type_backend::bind_by_name( int position = -1; int count = 1; + if (statement_.use_binding_method_ == details::db2::BOUND_BY_POSITION) + { + throw soci_error("Binding for use elements must be either by position or by name."); + } + statement_.use_binding_method_ = details::db2::BOUND_BY_NAME; + for (std::vector::iterator it = statement_.names.begin(); it != statement_.names.end(); ++it) { @@ -241,7 +252,6 @@ void db2_vector_use_type_backend::bind_by_name( ss << "Unable to find name '" << name << "' to bind to"; throw soci_error(ss.str().c_str()); } - } void db2_vector_use_type_backend::pre_use(indicator const *ind) diff --git a/src/cmake/modules/FindDB2.cmake b/src/cmake/modules/FindDB2.cmake index c3cd6d150..5ff87cdee 100644 --- a/src/cmake/modules/FindDB2.cmake +++ b/src/cmake/modules/FindDB2.cmake @@ -15,22 +15,34 @@ ############################################################################### if(UNIX) + set(DB2_INSTALL_PATHS + /opt/ibm/db2/V10.1 + /opt/ibm/db2/V9.7 + /opt/ibm/db2/V9.5 + /opt/ibm/db2/V9.1) + if(CMAKE_SIZEOF_VOID_P EQUAL 4) - set(DB2_LIBDIR "lib") + set(DB2_LIBDIRS "lib32" "lib") else() - set(DB2_LIBDIR "lib64") + set(DB2_LIBDIRS "lib64") endif() - set(DB2_FIND_INCLUDE_PATHS - /opt/ibm/db2/V10.1/include - /opt/ibm/db2/V9.7/include - /opt/ibm/db2/V9.5/include - /opt/ibm/db2/V9.1/include) - set(DB2_FIND_LIB_PATHS - /opt/ibm/db2/V10.1/${DB2_LIBDIR} - /opt/ibm/db2/V9.7/${DB2_LIBDIR} - /opt/ibm/db2/V9.5/${DB2_LIBDIR} - /opt/ibm/db2/V9.1/${DB2_LIBDIR}) + set(DB2_FIND_INCLUDE_PATHS) + set(DB2_FIND_LIB_PATHS) + foreach(db2_install_path ${DB2_INSTALL_PATHS}) + if (IS_DIRECTORY ${db2_install_path}/include) + set(DB2_FIND_INCLUDE_PATHS + ${DB2_FIND_INCLUDE_PATHS} + ${db2_install_path}/include) + endif() + foreach(db2_libdir ${DB2_LIBDIRS}) + if (IS_DIRECTORY ${db2_install_path}/${db2_libdir}) + set(DB2_FIND_LIB_PATHS + ${DB2_FIND_LIB_PATHS} + ${db2_install_path}/${db2_libdir}) + endif() + endforeach(db2_libdir) + endforeach(db2_install_path) elseif(WIN32) if (CMAKE_CL_64) # 64-bit build, DB2 64-bit installed set(DB2_FIND_INCLUDE_PATHS $ENV{ProgramW6432}/IBM/SQLLIB/include) diff --git a/src/core/test/common-tests.h b/src/core/test/common-tests.h index b50ec4b84..3f81ae572 100644 --- a/src/core/test/common-tests.h +++ b/src/core/test/common-tests.h @@ -699,7 +699,7 @@ void test3() short sh; for (sh = 0; sh != rowsToTest; ++sh) { - sql << "insert into soci_test(sh) values(\'" << sh << "\')"; + sql << "insert into soci_test(sh) values(" << sh << ")"; } int count; @@ -750,7 +750,7 @@ void test3() int i; for (i = 0; i != rowsToTest; ++i) { - sql << "insert into soci_test(id) values(\'" << i << "\')"; + sql << "insert into soci_test(id) values(" << i << ")"; } int count; @@ -820,7 +820,7 @@ void test3() unsigned long ul; for (ul = 0; ul != rowsToTest; ++ul) { - sql << "insert into soci_test(ul) values(\'" << ul << "\')"; + sql << "insert into soci_test(ul) values(" << ul << ")"; } int count; @@ -871,7 +871,7 @@ void test3() double d = 0.0; for (int i = 0; i != rowsToTest; ++i) { - sql << "insert into soci_test(d) values(\'" << d << "\')"; + sql << "insert into soci_test(d) values(" << d << ")"; d += 0.6; }