From 48650b34c7f1addb7ed7454f221602cd22756d25 Mon Sep 17 00:00:00 2001 From: Denis Chapligin Date: Wed, 9 Feb 2011 16:06:36 +0200 Subject: [PATCH] Added DB2 backend --- src/backends/db2/CMakeLists.txt | 2 +- src/backends/db2/blob.cpp | 62 ++++ src/backends/db2/factory.cpp | 40 +++ src/backends/db2/row-id.cpp | 28 ++ src/backends/db2/session.cpp | 71 +++- src/backends/db2/soci-db2.h | 67 +++- src/backends/db2/standard-into-type.cpp | 171 ++++++++++ src/backends/db2/standard-use-type.cpp | 204 +++++++++++ src/backends/db2/statement.cpp | 301 ++++++++++++++++ src/backends/db2/test/CMakeLists.txt | 2 +- src/backends/db2/test/test-db2.cpp | 109 +++--- src/backends/db2/vector-into-type.cpp | 436 ++++++++++++++++++++++++ src/backends/db2/vector-use-type.cpp | 404 ++++++++++++++++++++++ 13 files changed, 1836 insertions(+), 61 deletions(-) create mode 100644 src/backends/db2/blob.cpp create mode 100644 src/backends/db2/factory.cpp create mode 100644 src/backends/db2/row-id.cpp create mode 100644 src/backends/db2/standard-into-type.cpp create mode 100644 src/backends/db2/standard-use-type.cpp create mode 100644 src/backends/db2/statement.cpp create mode 100644 src/backends/db2/vector-into-type.cpp create mode 100644 src/backends/db2/vector-use-type.cpp diff --git a/src/backends/db2/CMakeLists.txt b/src/backends/db2/CMakeLists.txt index d1eab576b..35f7c5f10 100644 --- a/src/backends/db2/CMakeLists.txt +++ b/src/backends/db2/CMakeLists.txt @@ -1,7 +1,7 @@ soci_backend(DB2 DEPENDS DB2 HEADERS soci-db2.h - DESCRIPTION "SOCI backend for DB2 Ubiversal Database" + DESCRIPTION "SOCI backend for DB2 Universal Database" AUTHORS "Denis Chapligin" MAINTAINERS "Denis Chapligin") diff --git a/src/backends/db2/blob.cpp b/src/backends/db2/blob.cpp new file mode 100644 index 000000000..db24de4a3 --- /dev/null +++ b/src/backends/db2/blob.cpp @@ -0,0 +1,62 @@ +// +// 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_blob_backend::db2_blob_backend(db2_session_backend &session) + : session_(session) +{ + // ... +} + +db2_blob_backend::~db2_blob_backend() +{ + // ... +} + +std::size_t db2_blob_backend::get_len() +{ + // ... + return 0; +} + +std::size_t db2_blob_backend::read( + std::size_t /* offset */, char * /* buf */, std::size_t /* toRead */) +{ + // ... + return 0; +} + +std::size_t db2_blob_backend::write( + std::size_t /* offset */, char const * /* buf */, + std::size_t /* toWrite */) +{ + // ... + return 0; +} + +std::size_t db2_blob_backend::append( + char const * /* buf */, std::size_t /* toWrite */) +{ + // ... + return 0; +} + +void db2_blob_backend::trim(std::size_t /* newLen */) +{ + // ... +} diff --git a/src/backends/db2/factory.cpp b/src/backends/db2/factory.cpp new file mode 100644 index 000000000..99f3fdb36 --- /dev/null +++ b/src/backends/db2/factory.cpp @@ -0,0 +1,40 @@ +// +// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney +// 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" +#include + +using namespace soci; +using namespace soci::details; + + +// concrete factory for ODBC concrete strategies +db2_session_backend * db2_backend_factory::make_session( + std::string const &connectString) const +{ + return new db2_session_backend(connectString); +} + +db2_backend_factory const soci::db2; + +extern "C" +{ + +// for dynamic backend loading +SOCI_DB2_DECL backend_factory const * factory_db2() +{ + return &soci::db2; +} + +SOCI_DB2_DECL void register_factory_db2() +{ + soci::dynamic_backends::register_backend("db2", soci::db2); +} + +} // extern "C" diff --git a/src/backends/db2/row-id.cpp b/src/backends/db2/row-id.cpp new file mode 100644 index 000000000..a03e4c936 --- /dev/null +++ b/src/backends/db2/row-id.cpp @@ -0,0 +1,28 @@ +// +// 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_rowid_backend::db2_rowid_backend(db2_session_backend & /* session */) +{ + // ... +} + +db2_rowid_backend::~db2_rowid_backend() +{ + // ... +} diff --git a/src/backends/db2/session.cpp b/src/backends/db2/session.cpp index 5ee1b9df0..57be1a30f 100644 --- a/src/backends/db2/session.cpp +++ b/src/backends/db2/session.cpp @@ -16,10 +16,70 @@ 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); + + + SQLCHAR message[SQL_MAX_MESSAGE_LENGTH + 1]; + SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1]; + SQLINTEGER sqlcode; + SQLSMALLINT length; + + if ( SQLGetDiagRec(htype, + hndl, + 1, + sqlstate, + &sqlcode, + message, + SQL_MAX_MESSAGE_LENGTH + 1, + &length) == SQL_SUCCESS ) { + ss<<" SQLMESSAGE: "; + ss<dsn=value; + } + if (!key.compare("Uid")) { + this->username=value; + } + if (!key.compare("Pwd")) { + this->password=value; + } + if (!key.compare("autocommit")) { + if (!value.compare("off")) { + this->autocommit=false; + } else { + this->autocommit=true; + } + } +} + +void db2_session_backend::parseConnectString(std::string const & connectString) { + std::string processingString(connectString); + size_t delimiter=processingString.find_first_of(";"); + while(delimiter!=std::string::npos) { + std::string keyVal=processingString.substr(0,delimiter); + parseKeyVal(keyVal); + processingString=processingString.erase(0,delimiter+1); + delimiter=processingString.find_first_of(";"); + } + if (!processingString.empty()) { + parseKeyVal(processingString); + } +} db2_session_backend::db2_session_backend( std::string const & connectString /* DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;AutoCommit=off */) { + parseConnectString(connectString); SQLRETURN cliRC = SQL_SUCCESS; /* Prepare handles */ @@ -30,24 +90,27 @@ db2_session_backend::db2_session_backend( cliRC = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc); if (cliRC != SQL_SUCCESS) { + std::string msg=db2_soci_error::sqlState("Error while allocating the connection handle",SQL_HANDLE_ENV,hEnv); SQLFreeHandle(SQL_HANDLE_ENV,hEnv); - throw db2_soci_error("Error while allocating the connection handle",cliRC); + throw db2_soci_error(msg,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) { + std::string msg=db2_soci_error::sqlState("Error while setting autocommit attribute",SQL_HANDLE_DBC,hDbc); SQLFreeHandle(SQL_HANDLE_DBC,hDbc); SQLFreeHandle(SQL_HANDLE_ENV,hEnv); - throw db2_soci_error("Error while setting autocommit attribute",cliRC); + throw db2_soci_error(msg,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); + cliRC = SQLConnect(hDbc,(SQLCHAR*)dsn.c_str(),SQL_NTS,(SQLCHAR*)username.c_str(),SQL_NTS,(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); SQLFreeHandle(SQL_HANDLE_ENV,hEnv); - throw db2_soci_error("Error connecting to database",cliRC); + throw db2_soci_error(msg,cliRC); } } diff --git a/src/backends/db2/soci-db2.h b/src/backends/db2/soci-db2.h index 10e542202..9bbed7988 100644 --- a/src/backends/db2/soci-db2.h +++ b/src/backends/db2/soci-db2.h @@ -28,16 +28,25 @@ #include #include +#include +#include +#include +#include #include namespace soci { + 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: db2_soci_error(std::string const & msg, SQLRETURN rc) : soci_error(msg),errorCode(rc) {}; - + ~db2_soci_error() throw() { }; + + //We have to extract error information before exception throwing, cause CLI handles could be broken at the construction time + static const std::string sqlState(std::string const & msg,const SQLSMALLINT htype,const SQLHANDLE hndl); + SQLRETURN errorCode; }; @@ -46,7 +55,7 @@ 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) + : statement_(st),buf(NULL) {} void define_by_pos(int& position, void* data, details::exchange_type type); @@ -57,12 +66,19 @@ struct SOCI_DB2_DECL db2_standard_into_type_backend : details::standard_into_typ void clean_up(); db2_statement_backend& statement_; + + char* buf; + void *data; + details::exchange_type type; + int position; + SQLSMALLINT cType; + SQLLEN valueLen; }; 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) + : statement_(st),buf(NULL) {} void define_by_pos(int& position, void* data, details::exchange_type type); @@ -76,12 +92,22 @@ struct SOCI_DB2_DECL db2_vector_into_type_backend : details::vector_into_type_ba void clean_up(); db2_statement_backend& statement_; + + void prepare_indicators(std::size_t size); + + SQLLEN *indptr; + std::vector indVec; + void *data; + char *buf; + details::exchange_type type; + SQLSMALLINT cType; + std::size_t colSize; }; 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) + : statement_(st),buf(NULL) {} void bind_by_pos(int& position, void* data, details::exchange_type type, bool readOnly); @@ -93,12 +119,22 @@ struct SOCI_DB2_DECL db2_standard_use_type_backend : details::standard_use_type_ void clean_up(); db2_statement_backend& statement_; + + void prepare_for_bind(void *&data, SQLLEN &size, SQLSMALLINT &sqlType, SQLSMALLINT &cType); + void bind_helper(int &position, void *data, details::exchange_type type); + + void *data; + details::exchange_type type; + int position; + std::string name; + char* buf; + SQLLEN indptr; }; 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) {} + : statement_(st),buf(NULL) {} 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); @@ -110,6 +146,17 @@ struct SOCI_DB2_DECL db2_vector_use_type_backend : details::vector_use_type_back void clean_up(); db2_statement_backend& statement_; + + void prepare_indicators(std::size_t size); + void prepare_for_bind(void *&data, SQLUINTEGER &size,SQLSMALLINT &sqlType, SQLSMALLINT &cType); + void bind_helper(int &position, void *data, details::exchange_type type); + + SQLLEN *indptr; + std::vector indVec; + void *data; + char *buf; + details::exchange_type type; + std::size_t colSize; }; struct db2_session_backend; @@ -131,6 +178,7 @@ struct SOCI_DB2_DECL db2_statement_backend : details::statement_backend int prepare_for_describe(); void describe_column(int colNum, data_type& dtype, std::string& columnName); + std::size_t column_size(int col); db2_standard_into_type_backend* make_into_type_backend(); db2_standard_use_type_backend* make_use_type_backend(); @@ -138,6 +186,12 @@ struct SOCI_DB2_DECL db2_statement_backend : details::statement_backend db2_vector_use_type_backend* make_vector_use_type_backend(); db2_session_backend& session_; + + SQLHANDLE hStmt; + std::string query_; + std::vector names; + bool hasVectorUseElements; + SQLUINTEGER numRowsFetched; }; struct db2_rowid_backend : details::rowid_backend @@ -180,6 +234,9 @@ struct db2_session_backend : details::session_backend db2_rowid_backend* make_rowid_backend(); db2_blob_backend* make_blob_backend(); + void parseConnectString(std::string const &); + void parseKeyVal(std::string const &); + std::string dsn; std::string username; std::string password; diff --git a/src/backends/db2/standard-into-type.cpp b/src/backends/db2/standard-into-type.cpp new file mode 100644 index 000000000..89cb6ae00 --- /dev/null +++ b/src/backends/db2/standard-into-type.cpp @@ -0,0 +1,171 @@ +// +// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney +// 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" +#include + +using namespace soci; +using namespace soci::details; + + +void db2_standard_into_type_backend::define_by_pos( + int & position, void * data, exchange_type type) +{ + this->data = data; + this->type = type; + this->position = position; + position++; + + SQLUINTEGER size = 0; + + switch (type) + { + case x_char: + cType = SQL_C_CHAR; + size = sizeof(char) + 1; + buf = new char[size]; + data = buf; + break; + case x_stdstring: + cType = SQL_C_CHAR; + // Patch: set to min between column size and 100MB (used ot be 32769) + // Column size for text data type can be too large for buffer allocation + size = statement_.column_size(this->position); + size = size > maxBuffer ? maxBuffer : size; + size++; + buf = new char[size]; + data = buf; + break; + case x_short: + cType = SQL_C_SSHORT; + size = sizeof(short); + break; + case x_integer: + cType = SQL_C_SLONG; + size = sizeof(long); + break; + case x_unsigned_long: + cType = SQL_C_ULONG; + size = sizeof(unsigned long); + break; + case x_long_long: + cType = SQL_C_SBIGINT; + size = sizeof(long long); + break; + case x_unsigned_long_long: + cType = SQL_C_UBIGINT; + size = sizeof(unsigned long long); + break; + case x_double: + cType = SQL_C_DOUBLE; + size = sizeof(double); + break; + case x_stdtm: + cType = SQL_C_TYPE_TIMESTAMP; + size = sizeof(TIMESTAMP_STRUCT); + buf = new char[size]; + data = buf; + break; + case x_rowid: + cType = SQL_C_UBIGINT; + size = sizeof(unsigned long long); + break; + default: + throw soci_error("Into element used with non-supported type."); + } + + valueLen = 0; + + SQLRETURN cliRC = SQLBindCol(statement_.hStmt, static_cast(this->position), + static_cast(cType), data, size, &valueLen); + if (cliRC != SQL_SUCCESS) + { + throw db2_soci_error("Error while pre-fething into type",cliRC); + } +} + +void db2_standard_into_type_backend::pre_fetch() +{ + //... +} + +void db2_standard_into_type_backend::post_fetch( + bool gotData, bool calledFromFetch, indicator * ind) +{ + if (calledFromFetch == true && gotData == false) + { + // this is a normal end-of-rowset condition, + // no need to do anything (fetch() will return false) + return; + } + + if (gotData) + { + // first, deal with indicators + if (SQL_NULL_DATA == valueLen) + { + if (ind == NULL) + { + throw soci_error( + "Null value fetched and no indicator defined."); + } + + *ind = i_null; + return; + } + else + { + if (ind != NULL) + { + *ind = i_ok; + } + } + + // only std::string and std::tm need special handling + if (type == x_char) + { + char *c = static_cast(data); + *c = buf[0]; + } + if (type == x_stdstring) + { + std::string *s = static_cast(data); + *s = buf; + if (s->size() >= (maxBuffer - 1)) + { + throw soci_error("Buffer size overflow; maybe got too large string"); + } + } + else if (type == x_stdtm) + { + std::tm *t = static_cast(data); + + TIMESTAMP_STRUCT * ts = reinterpret_cast(buf); + t->tm_isdst = -1; + t->tm_year = ts->year - 1900; + t->tm_mon = ts->month - 1; + t->tm_mday = ts->day; + t->tm_hour = ts->hour; + t->tm_min = ts->minute; + t->tm_sec = ts->second; + + // normalize and compute the remaining fields + std::mktime(t); + } + } +} + +void db2_standard_into_type_backend::clean_up() +{ + if (buf) + { + delete [] buf; + buf = 0; + } +} diff --git a/src/backends/db2/standard-use-type.cpp b/src/backends/db2/standard-use-type.cpp new file mode 100644 index 000000000..76fe26393 --- /dev/null +++ b/src/backends/db2/standard-use-type.cpp @@ -0,0 +1,204 @@ +// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney +// 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" +#include +#include +#include +#include +#include + +using namespace soci; +using namespace soci::details; + +void db2_standard_use_type_backend::prepare_for_bind( + void *&data, SQLLEN &size, SQLSMALLINT &sqlType, SQLSMALLINT &cType) +{ + switch (type) + { + // simple cases + case x_short: + sqlType = SQL_SMALLINT; + cType = SQL_C_SSHORT; + size = sizeof(short); + break; + case x_integer: + sqlType = SQL_INTEGER; + cType = SQL_C_SLONG; + size = sizeof(int); + break; + case x_unsigned_long: + sqlType = SQL_INTEGER; + cType = SQL_C_ULONG; + size = sizeof(unsigned long); + break; + case x_long_long: + sqlType = SQL_BIGINT; + cType = SQL_C_SBIGINT; + size = sizeof(long long); + break; + case x_unsigned_long_long: + sqlType = SQL_BIGINT; + cType = SQL_C_UBIGINT; + size = sizeof(unsigned long long); + break; + case x_double: + sqlType = SQL_DOUBLE; + cType = SQL_C_DOUBLE; + size = sizeof(double); + break; + + // cases that require adjustments and buffer management + case x_char: + sqlType = SQL_CHAR; + cType = SQL_C_CHAR; + size = sizeof(char) + 1; + buf = new char[size]; + data = buf; + indptr = SQL_NTS; + break; + case x_stdstring: + { + // TODO: No textual value is assigned here! + + std::string* s = static_cast(data); + sqlType = SQL_LONGVARCHAR; + cType = SQL_C_CHAR; + size = s->size() + 1; + buf = new char[size]; + data = buf; + indptr = SQL_NTS; + } + break; + case x_stdtm: + sqlType = SQL_TIMESTAMP; + cType = SQL_C_TIMESTAMP; + buf = new char[sizeof(TIMESTAMP_STRUCT)]; + data = buf; + size = 19; // This number is not the size in bytes, but the number + // of characters in the date if it was written out + // yyyy-mm-dd hh:mm:ss + break; + + case x_blob: + break; + case x_statement: + case x_rowid: + break; + } +} + +void db2_standard_use_type_backend::bind_helper(int &position, void *data, details::exchange_type type) +{ + this->data = data; // for future reference + this->type = type; // for future reference + + SQLSMALLINT sqlType; + SQLSMALLINT cType; + SQLLEN size; + + prepare_for_bind(data, size, sqlType, cType); + + SQLRETURN cliRC = SQLBindParameter(statement_.hStmt, + static_cast(position++), + SQL_PARAM_INPUT, + cType, sqlType, size, 0, data, 0, &indptr); + + if (cliRC != SQL_SUCCESS) + { + throw db2_soci_error("Error while binding value",cliRC); + } +} + +void db2_standard_use_type_backend::bind_by_pos( + int &position, void *data, exchange_type type, bool /* readOnly */) +{ + 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 */) +{ + int position = -1; + int count = 1; + + for (std::vector::iterator it = statement_.names.begin(); + it != statement_.names.end(); ++it) + { + if (*it == name) + { + position = count; + break; + } + count++; + } + + if (position != -1) + { + bind_helper(position, data, type); + } + else + { + std::ostringstream ss; + ss << "Unable to find name '" << name << "' to bind to"; + throw soci_error(ss.str().c_str()); + } +} + +void db2_standard_use_type_backend::pre_use(indicator const *ind) +{ + // first deal with data + if (type == x_char) + { + char *c = static_cast(data); + buf[0] = *c; + buf[1] = '\0'; + } + else if (type == x_stdstring) + { + std::string *s = static_cast(data); + std::size_t const bufSize = s->size() + 1; + + std::size_t const sSize = s->size(); + std::size_t const toCopy = sSize < bufSize -1 ? sSize + 1 : bufSize - 1; + strncpy(buf, s->c_str(), toCopy); + buf[toCopy] = '\0'; + } + else if (type == x_stdtm) + { + std::tm *t = static_cast(data); + TIMESTAMP_STRUCT * ts = reinterpret_cast(buf); + + ts->year = static_cast(t->tm_year + 1900); + ts->month = static_cast(t->tm_mon + 1); + ts->day = static_cast(t->tm_mday); + ts->hour = static_cast(t->tm_hour); + ts->minute = static_cast(t->tm_min); + ts->second = static_cast(t->tm_sec); + ts->fraction = 0; + } + + // then handle indicators + if (ind != NULL && *ind == i_null) + { + indptr = SQL_NULL_DATA; // null + } +} + +void db2_standard_use_type_backend::post_use(bool gotData, indicator *ind) +{ +} + +void db2_standard_use_type_backend::clean_up() +{ + if (buf != NULL) + { + delete [] buf; + buf = NULL; + } +} diff --git a/src/backends/db2/statement.cpp b/src/backends/db2/statement.cpp new file mode 100644 index 000000000..9dffb3d32 --- /dev/null +++ b/src/backends/db2/statement.cpp @@ -0,0 +1,301 @@ +// +// 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_statement_backend::db2_statement_backend(db2_session_backend &session) + : session_(session),hasVectorUseElements(false) +{ +} + +void db2_statement_backend::alloc() +{ + SQLRETURN cliRC = SQL_SUCCESS; + + cliRC = SQLAllocHandle(SQL_HANDLE_STMT,session_.hDbc,&hStmt); + if (cliRC != SQL_SUCCESS) { + throw db2_soci_error("Error while allocation statement handle",cliRC); + } +} + +void db2_statement_backend::clean_up() +{ + SQLFreeHandle(SQL_HANDLE_STMT,hStmt); +} + +void db2_statement_backend::prepare(std::string const & query , + statement_type /* eType */) +{ + // rewrite the query by transforming all named parameters into + // the markers (:abc -> ?, etc.) + + enum { normal, in_quotes, in_name } state = normal; + + std::string name; + + for (std::string::const_iterator it = query.begin(), end = query.end(); + it != end; ++it) + { + switch (state) + { + case normal: + if (*it == '\'') + { + query_ += *it; + state = in_quotes; + } + else if (*it == ':') + { + // Check whether this is a cast operator (e.g. 23::float) + // and treat it as a special case, not as a named binding + const std::string::const_iterator next_it = it + 1; + if ((next_it != end) && (*next_it == ':')) + { + query_ += "::"; + ++it; + } + else + { + state = in_name; + } + } + else // regular character, stay in the same state + { + query_ += *it; + } + break; + case in_quotes: + if (*it == '\'') + { + query_ += *it; + state = normal; + } + else // regular quoted character + { + query_ += *it; + } + break; + case in_name: + if (std::isalnum(*it) || *it == '_') + { + name += *it; + } + else // end of name + { + names.push_back(name); + name.clear(); + std::ostringstream ss; + ss << '?'; + query_ += ss.str(); + query_ += *it; + state = normal; + + } + break; + } + } + + if (state == in_name) + { + names.push_back(name); + std::ostringstream ss; + ss << '?'; + query_ += ss.str(); + } + + SQLRETURN cliRC = SQLPrepare(hStmt,(SQLCHAR*)query_.c_str(),SQL_NTS); + if (cliRC!=SQL_SUCCESS) { + throw db2_soci_error("Error while preparing query",cliRC); + } +} + +statement_backend::exec_fetch_result +db2_statement_backend::execute(int number ) +{ + SQLSMALLINT rows_processed = 0; + + if (hasVectorUseElements) + { + SQLSetStmtAttr(hStmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &rows_processed, 0); + } + + // 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); + if (cliRC != SQL_SUCCESS) + { + throw db2_soci_error(db2_soci_error::sqlState("Statement execution error",SQL_HANDLE_STMT,hStmt),cliRC); + } + + SQLSMALLINT colCount; + SQLNumResultCols(hStmt, &colCount); + + if (number > 0 && colCount > 0) + { + return fetch(number); + } + + SQLFreeStmt(hStmt,SQL_RESET_PARAMS); //Prepare for next call + + return ef_success; +} + +statement_backend::exec_fetch_result +db2_statement_backend::fetch(int number ) +{ + numRowsFetched = 0; + + SQLSetStmtAttr(hStmt, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0); + SQLSetStmtAttr(hStmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)number, 0); + SQLSetStmtAttr(hStmt, SQL_ATTR_ROWS_FETCHED_PTR, &numRowsFetched, 0); + + SQLRETURN cliRC = SQLFetch(hStmt); + + if (SQL_NO_DATA == cliRC) + { + return ef_no_data; + } + + if (cliRC != SQL_SUCCESS) + { + throw db2_soci_error("Error while fetching data",cliRC); + } + + return ef_success; +} + +long long db2_statement_backend::get_affected_rows() +{ + // ... + return -1; +} + +int db2_statement_backend::get_number_of_rows() +{ + return numRowsFetched; +} + +std::string db2_statement_backend::rewrite_for_procedure_call( + std::string const &query) +{ + return query; +} + +int db2_statement_backend::prepare_for_describe() +{ + SQLSMALLINT numCols; + SQLNumResultCols(hStmt, &numCols); + return numCols; +} + +void db2_statement_backend::describe_column(int colNum, + data_type & type, std::string & columnName ) +{ +SQLCHAR colNameBuffer[2048]; + SQLSMALLINT colNameBufferOverflow; + SQLSMALLINT dataType; + SQLULEN colSize; + SQLSMALLINT decDigits; + SQLSMALLINT isNullable; + + SQLRETURN cliRC = SQLDescribeCol(hStmt, static_cast(colNum), + colNameBuffer, 2048, + &colNameBufferOverflow, &dataType, + &colSize, &decDigits, &isNullable); + + if (cliRC != SQL_SUCCESS) + { + throw db2_soci_error("Error while describing column",cliRC); + } + + char const *name = reinterpret_cast(colNameBuffer); + columnName.assign(name, std::strlen(name)); + + switch (dataType) + { + case SQL_TYPE_DATE: + case SQL_TYPE_TIME: + case SQL_TYPE_TIMESTAMP: + type = dt_date; + break; + case SQL_DOUBLE: + case SQL_DECIMAL: + case SQL_REAL: + case SQL_FLOAT: + case SQL_NUMERIC: + type = dt_double; + break; + case SQL_TINYINT: + case SQL_SMALLINT: + case SQL_INTEGER: + type = dt_integer; + break; + case SQL_BIGINT: + type = dt_long_long; + break; + case SQL_CHAR: + case SQL_VARCHAR: + case SQL_LONGVARCHAR: + default: + type = dt_string; + break; + } +} + +std::size_t db2_statement_backend::column_size(int col) { + SQLCHAR colNameBuffer[2048]; + SQLSMALLINT colNameBufferOverflow; + SQLSMALLINT dataType; + SQLULEN colSize; + SQLSMALLINT decDigits; + SQLSMALLINT isNullable; + + SQLRETURN cliRC = SQLDescribeCol(hStmt, static_cast(col), + colNameBuffer, 2048, + &colNameBufferOverflow, &dataType, + &colSize, &decDigits, &isNullable); + + if (cliRC != SQL_SUCCESS) + { + throw db2_soci_error(db2_soci_error::sqlState("Error while detecting column size",SQL_HANDLE_STMT,hStmt),cliRC); + } + + return colSize; +} + +db2_standard_into_type_backend * db2_statement_backend::make_into_type_backend() +{ + return new db2_standard_into_type_backend(*this); +} + +db2_standard_use_type_backend * db2_statement_backend::make_use_type_backend() +{ + return new db2_standard_use_type_backend(*this); +} + +db2_vector_into_type_backend * +db2_statement_backend::make_vector_into_type_backend() +{ + return new db2_vector_into_type_backend(*this); +} + +db2_vector_use_type_backend * db2_statement_backend::make_vector_use_type_backend() +{ + hasVectorUseElements = true; + return new db2_vector_use_type_backend(*this); +} diff --git a/src/backends/db2/test/CMakeLists.txt b/src/backends/db2/test/CMakeLists.txt index 62d7aad4a..d9565ad22 100644 --- a/src/backends/db2/test/CMakeLists.txt +++ b/src/backends/db2/test/CMakeLists.txt @@ -1,4 +1,4 @@ soci_backend_test( BACKEND DB2 SOURCE test-db2.cpp - CONNSTR "dummy") \ No newline at end of file + CONNSTR "Dsn=SAMPLE;Uid=db2inst1;Pwd=db2inst1;autocommit=off") \ No newline at end of file diff --git a/src/backends/db2/test/test-db2.cpp b/src/backends/db2/test/test-db2.cpp index 304153657..3bc8bc865 100644 --- a/src/backends/db2/test/test-db2.cpp +++ b/src/backends/db2/test/test-db2.cpp @@ -6,7 +6,7 @@ // #include "soci.h" -#include "soci-empty.h" +#include "soci-db2.h" #include #include #include @@ -16,7 +16,7 @@ using namespace soci; std::string connectString; -backend_factory const &backEnd = *soci::factory_empty(); +backend_factory const &backEnd = *soci::factory_db2(); // NOTE: @@ -33,98 +33,107 @@ backend_factory const &backEnd = *soci::factory_empty(); // 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."; + sql << "SELECT CURRENT TIMESTAMP FROM SYSIBM.SYSDUMMY1"; + sql << "SELECT " << 123 << " FROM SYSIBM.SYSDUMMY1"; - std::string query = "some query"; + std::string query = "CREATE TABLE DB2INST1.TEST (ID BIGINT,DATA VARCHAR(8))"; sql << query; int i = 7; - sql << "insert", use(i); - sql << "select", into(i); + sql << "insert into db2inst1.TEST (id) values (:id)", use(i,"id"); + sql << "select id from db2inst1.TEST where id=7", into(i); + #if defined (__LP64__) || ( __WORDSIZE == 64 ) long int li = 9; - sql << "insert", use(li); - sql << "select", into(li); + sql << "insert into db2inst1.TEST (id) values (:id)", use(li,"id"); + sql << "select id from db2inst1.TEST where id=9", into(li); #endif long long ll = 11; - sql << "insert", use(ll); - sql << "select", into(ll); + sql << "insert into db2inst1.TEST (id) values (:id)", use(ll,"id"); + sql << "select id from db2inst1.TEST where id=11", into(ll); indicator ind = i_ok; - sql << "insert", use(i, ind); - sql << "select", into(i, ind); + sql << "insert into db2inst1.TEST (id) values (:id)", use(i,ind,"id"); + sql << "select id from db2inst1.TEST where id=7", into(i,ind); std::vector numbers(100); - sql << "insert", use(numbers); - sql << "select", into(numbers); + sql << "insert into db2inst1.TEST (id) values (:id)", use(numbers,"id"); + sql << "select id from db2inst1.TEST", into(numbers); std::vector inds(100); - sql << "insert", use(numbers, inds); - sql << "select", into(numbers, inds); + sql << "insert into db2inst1.TEST (id) values (:id)", use(numbers,inds,"id"); + sql << "select id from db2inst1.TEST", into(numbers,inds); { - statement st = (sql.prepare << "select", into(i)); + statement st = (sql.prepare << "select id from db2inst1.TEST", 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 id from db2inst1.TEST", into(i, ind)); } { - statement st = (sql.prepare << "select", into(numbers, inds)); + statement st = (sql.prepare << "select id from db2inst1.TEST", into(numbers)); } { - statement st = (sql.prepare << "insert", use(i)); + statement st = (sql.prepare << "select id from db2inst1.TEST", into(numbers, inds)); } { - statement st = (sql.prepare << "insert", use(i, ind)); + statement st = (sql.prepare << "select id from db2inst1.TEST", use(i)); } { - statement st = (sql.prepare << "insert", use(numbers)); + statement st = (sql.prepare << "select id from db2inst1.TEST", use(i, ind)); } { - statement st = (sql.prepare << "insert", use(numbers, inds)); + statement st = (sql.prepare << "select id from db2inst1.TEST", use(numbers)); } { - Person p; - sql << "select person", into(p); + statement st = (sql.prepare << "select id from db2inst1.TEST", use(numbers, inds)); } + sql<<"DROP TABLE DB2INST1.TEST"; + + sql.commit(); } std::cout << "test 1 passed" << std::endl; } +void test2() { + { + session sql(backEnd, connectString); + + std::string query = "CREATE TABLE DB2INST1.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"); + + sql<<"DROP TABLE DB2INST1.TEST"; + sql.commit(); + } + + std::cout << "test 2 passed" << std::endl; +} int main(int argc, char** argv) { @@ -147,14 +156,14 @@ int main(int argc, char** argv) std::cout << "usage: " << argv[0] << " connectstring\n" << "example: " << argv[0] - << " \'connect_string_for_empty_backend\'\n"; + << " \'Dsn=SAMPLE;Uid=db2inst1;Pwd=db2inst1;autocommit=off\'\n"; std::exit(1); } try { test1(); - // test2(); + test2(); // ... std::cout << "\nOK, all tests passed.\n\n"; diff --git a/src/backends/db2/vector-into-type.cpp b/src/backends/db2/vector-into-type.cpp new file mode 100644 index 000000000..07c91e59c --- /dev/null +++ b/src/backends/db2/vector-into-type.cpp @@ -0,0 +1,436 @@ +// +// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney +// 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" +#include +#include +#include +#include +#include + +using namespace soci; +using namespace soci::details; + +void db2_vector_into_type_backend::prepare_indicators(std::size_t size) +{ + if (size == 0) + { + throw soci_error("Vectors of size 0 are not allowed."); + } + + indVec.resize(size); + indptr = &indVec[0]; +} + +void db2_vector_into_type_backend::define_by_pos( + int &position, void *data, exchange_type type) +{ + this->data = data; // for future reference + this->type = type; // for future reference + + SQLINTEGER size = 0; // also dummy + + switch (type) + { + // simple cases + case x_short: + { + cType = SQL_C_SSHORT; + size = sizeof(short); + std::vector *vp = static_cast *>(data); + std::vector &v(*vp); + prepare_indicators(v.size()); + data = &v[0]; + } + break; + case x_integer: + { + cType = SQL_C_SLONG; + size = sizeof(long); + std::vector *vp = static_cast *>(data); + std::vector &v(*vp); + prepare_indicators(v.size()); + data = &v[0]; + } + break; + case x_unsigned_long: + { + cType = SQL_C_ULONG; + size = sizeof(unsigned long); + std::vector *vp + = static_cast *>(data); + std::vector &v(*vp); + prepare_indicators(v.size()); + data = &v[0]; + } + break; + case x_long_long: + { + cType = SQL_C_SBIGINT; + size = sizeof(long long); + std::vector *vp + = static_cast *>(data); + std::vector &v(*vp); + prepare_indicators(v.size()); + data = &v[0]; + } + break; + case x_unsigned_long_long: + { + cType = SQL_C_UBIGINT; + size = sizeof(unsigned long long); + std::vector *vp + = static_cast *>(data); + std::vector &v(*vp); + prepare_indicators(v.size()); + data = &v[0]; + } + break; + case x_double: + { + cType = SQL_C_DOUBLE; + size = sizeof(double); + std::vector *vp = static_cast *>(data); + std::vector &v(*vp); + prepare_indicators(v.size()); + data = &v[0]; + } + break; + + // cases that require adjustments and buffer management + + case x_char: + { + cType = SQL_C_CHAR; + + std::vector *v + = static_cast *>(data); + + prepare_indicators(v->size()); + + size = sizeof(char) * 2; + std::size_t bufSize = size * v->size(); + + colSize = size; + + buf = new char[bufSize]; + data = buf; + } + break; + case x_stdstring: + { + cType = SQL_C_CHAR; + std::vector *v + = static_cast *>(data); + colSize = statement_.column_size(position) + 1; + std::size_t bufSize = colSize * v->size(); + buf = new char[bufSize]; + + prepare_indicators(v->size()); + + size = static_cast(colSize); + data = buf; + } + break; + case x_stdtm: + { + cType = SQL_C_TYPE_TIMESTAMP; + std::vector *v + = static_cast *>(data); + + prepare_indicators(v->size()); + + size = sizeof(TIMESTAMP_STRUCT); + colSize = size; + + std::size_t bufSize = size * v->size(); + + buf = new char[bufSize]; + data = buf; + } + break; + + case x_statement: break; // not supported + case x_rowid: break; // not supported + case x_blob: break; // not supported + } + + SQLRETURN cliRC = SQLBindCol(statement_.hStmt, static_cast(position++), + cType, data, size, indptr); + if (cliRC != SQL_SUCCESS) + { + throw db2_soci_error("Error while pre-fetching into vector",cliRC); + } +} + +void db2_vector_into_type_backend::pre_fetch() +{ + // nothing to do for the supported types +} + +void db2_vector_into_type_backend::post_fetch(bool gotData, indicator *ind) +{ + if (gotData) + { + // first, deal with data + + // only std::string, std::tm and Statement need special handling + if (type == x_char) + { + std::vector *vp + = static_cast *>(data); + + std::vector &v(*vp); + char *pos = buf; + std::size_t const vsize = v.size(); + for (std::size_t i = 0; i != vsize; ++i) + { + v[i] = *pos; + pos += colSize; + } + } + if (type == x_stdstring) + { + std::vector *vp + = static_cast *>(data); + + std::vector &v(*vp); + + char *pos = buf; + std::size_t const vsize = v.size(); + for (std::size_t i = 0; i != vsize; ++i) + { + v[i].assign(pos, strlen(pos)); + pos += colSize; + } + } + else if (type == x_stdtm) + { + std::vector *vp + = static_cast *>(data); + + std::vector &v(*vp); + char *pos = buf; + std::size_t const vsize = v.size(); + for (std::size_t i = 0; i != vsize; ++i) + { + std::tm t; + + TIMESTAMP_STRUCT * ts = reinterpret_cast(pos); + t.tm_isdst = -1; + t.tm_year = ts->year - 1900; + t.tm_mon = ts->month - 1; + t.tm_mday = ts->day; + t.tm_hour = ts->hour; + t.tm_min = ts->minute; + t.tm_sec = ts->second; + + // normalize and compute the remaining fields + std::mktime(&t); + v[i] = t; + pos += colSize; + } + } + + // then - deal with indicators + if (ind != NULL) + { + std::size_t const indSize = statement_.get_number_of_rows(); + for (std::size_t i = 0; i != indSize; ++i) + { + if (indVec[i] > 0) + { + ind[i] = i_ok; + } + else if (indVec[i] == SQL_NULL_DATA) + { + ind[i] = i_null; + } + else + { + ind[i] = i_truncated; + } + } + } + else + { + std::size_t const indSize = statement_.get_number_of_rows(); + for (std::size_t i = 0; i != indSize; ++i) + { + if (indVec[i] == SQL_NULL_DATA) + { + // fetched null and no indicator - programming error! + throw soci_error( + "Null value fetched and no indicator defined."); + } + } + } + } + else // gotData == false + { + // nothing to do here, vectors are truncated anyway + } +} + +void db2_vector_into_type_backend::resize(std::size_t sz) +{ + indVec.resize(sz); + switch (type) + { + // simple cases + case x_char: + { + std::vector *v = static_cast *>(data); + v->resize(sz); + } + break; + case x_short: + { + std::vector *v = static_cast *>(data); + v->resize(sz); + } + break; + case x_integer: + { + std::vector *v = static_cast *>(data); + v->resize(sz); + } + break; + case x_unsigned_long: + { + std::vector *v + = static_cast *>(data); + v->resize(sz); + } + break; + case x_long_long: + { + std::vector *v + = static_cast *>(data); + v->resize(sz); + } + break; + case x_unsigned_long_long: + { + std::vector *v + = static_cast *>(data); + v->resize(sz); + } + break; + case x_double: + { + std::vector *v + = static_cast *>(data); + v->resize(sz); + } + break; + case x_stdstring: + { + std::vector *v + = static_cast *>(data); + v->resize(sz); + } + break; + case x_stdtm: + { + std::vector *v + = static_cast *>(data); + v->resize(sz); + } + break; + + case x_statement: break; // not supported + case x_rowid: break; // not supported + case x_blob: break; // not supported + } +} + +std::size_t db2_vector_into_type_backend::size() +{ + std::size_t sz = 0; // dummy initialization to please the compiler + switch (type) + { + // simple cases + case x_char: + { + std::vector *v = static_cast *>(data); + sz = v->size(); + } + break; + case x_short: + { + std::vector *v = static_cast *>(data); + sz = v->size(); + } + break; + case x_integer: + { + std::vector *v = static_cast *>(data); + sz = v->size(); + } + break; + case x_unsigned_long: + { + std::vector *v + = static_cast *>(data); + sz = v->size(); + } + break; + case x_long_long: + { + std::vector *v + = static_cast *>(data); + sz = v->size(); + } + break; + case x_unsigned_long_long: + { + std::vector *v + = static_cast *>(data); + sz = v->size(); + } + break; + case x_double: + { + std::vector *v + = static_cast *>(data); + sz = v->size(); + } + break; + case x_stdstring: + { + std::vector *v + = static_cast *>(data); + sz = v->size(); + } + break; + case x_stdtm: + { + std::vector *v + = static_cast *>(data); + sz = v->size(); + } + break; + + case x_statement: break; // not supported + case x_rowid: break; // not supported + case x_blob: break; // not supported + } + + return sz; +} + +void db2_vector_into_type_backend::clean_up() +{ + if (buf != NULL) + { + delete [] buf; + buf = NULL; + } +} diff --git a/src/backends/db2/vector-use-type.cpp b/src/backends/db2/vector-use-type.cpp new file mode 100644 index 000000000..30710963f --- /dev/null +++ b/src/backends/db2/vector-use-type.cpp @@ -0,0 +1,404 @@ +// +// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney +// 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" +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +// disables the warning about converting int to void*. This is a 64 bit compatibility +// warning, but odbc requires the value to be converted on this line +// SQLSetStmtAttr(statement_.hstmt_, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)arraySize, 0); +#pragma warning(disable:4312) +#endif + + +using namespace soci; +using namespace soci::details; + +void db2_vector_use_type_backend::prepare_indicators(std::size_t size) +{ + if (size == 0) + { + throw soci_error("Vectors of size 0 are not allowed."); + } + + indVec.resize(size); + indptr = &indVec[0]; +} + +void db2_vector_use_type_backend::prepare_for_bind(void *&data, SQLUINTEGER &size, + SQLSMALLINT &sqlType, SQLSMALLINT &cType) +{ + switch (type) + { // simple cases + case x_short: + { + sqlType = SQL_SMALLINT; + cType = SQL_C_SSHORT; + size = sizeof(short); + std::vector *vp = static_cast *>(data); + std::vector &v(*vp); + prepare_indicators(v.size()); + data = &v[0]; + } + break; + case x_integer: + { + sqlType = SQL_INTEGER; + cType = SQL_C_SLONG; + size = sizeof(int); + std::vector *vp = static_cast *>(data); + std::vector &v(*vp); + prepare_indicators(v.size()); + data = &v[0]; + } + break; + case x_unsigned_long: + { + sqlType = SQL_INTEGER; + cType = SQL_C_ULONG; + size = sizeof(unsigned long); + std::vector *vp + = static_cast *>(data); + std::vector &v(*vp); + prepare_indicators(v.size()); + data = &v[0]; + } + break; + case x_long_long: + { + sqlType = SQL_BIGINT; + cType = SQL_C_SBIGINT; + size = sizeof(long long); + std::vector *vp + = static_cast *>(data); + std::vector &v(*vp); + prepare_indicators(v.size()); + data = &v[0]; + } + break; + case x_unsigned_long_long: + { + sqlType = SQL_BIGINT; + cType = SQL_C_UBIGINT; + size = sizeof(unsigned long long); + std::vector *vp + = static_cast *>(data); + std::vector &v(*vp); + prepare_indicators(v.size()); + data = &v[0]; + } + break; + case x_double: + { + sqlType = SQL_DOUBLE; + cType = SQL_C_DOUBLE; + size = sizeof(double); + std::vector *vp = static_cast *>(data); + std::vector &v(*vp); + prepare_indicators(v.size()); + data = &v[0]; + } + break; + + // cases that require adjustments and buffer management + case x_char: + { + std::vector *vp + = static_cast *>(data); + std::size_t const vsize = vp->size(); + + prepare_indicators(vsize); + + size = sizeof(char) * 2; + buf = new char[size * vsize]; + + char *pos = buf; + + for (std::size_t i = 0; i != vsize; ++i) + { + *pos++ = (*vp)[i]; + *pos++ = 0; + } + + sqlType = SQL_CHAR; + cType = SQL_C_CHAR; + data = buf; + } + break; + case x_stdstring: + { + sqlType = SQL_CHAR; + cType = SQL_C_CHAR; + + std::vector *vp + = static_cast *>(data); + std::vector &v(*vp); + + std::size_t maxSize = 0; + std::size_t const vecSize = v.size(); + prepare_indicators(vecSize); + for (std::size_t i = 0; i != vecSize; ++i) + { + std::size_t sz = v[i].length() + 1; // add one for null + indVec[i] = static_cast(sz); + maxSize = sz > maxSize ? sz : maxSize; + } + + buf = new char[maxSize * vecSize]; + memset(buf, 0, maxSize * vecSize); + + char *pos = buf; + for (std::size_t i = 0; i != vecSize; ++i) + { + strncpy(pos, v[i].c_str(), v[i].length()); + pos += maxSize; + } + + data = buf; + size = static_cast(maxSize); + } + break; + case x_stdtm: + { + std::vector *vp + = static_cast *>(data); + + prepare_indicators(vp->size()); + + buf = new char[sizeof(TIMESTAMP_STRUCT) * vp->size()]; + + sqlType = SQL_TYPE_TIMESTAMP; + cType = SQL_C_TYPE_TIMESTAMP; + data = buf; + size = 19; // This number is not the size in bytes, but the number + // of characters in the date if it was written out + // yyyy-mm-dd hh:mm:ss + } + break; + + case x_statement: break; // not supported + case x_rowid: break; // not supported + case x_blob: break; // not supported + } + + colSize = size; +} + +void db2_vector_use_type_backend::bind_helper(int &position, void *data, details::exchange_type type) +{ + this->data = data; // for future reference + this->type = type; // for future reference + + SQLSMALLINT sqlType; + SQLSMALLINT cType; + SQLUINTEGER size; + + prepare_for_bind(data, size, sqlType, cType); + + SQLINTEGER arraySize = (SQLINTEGER)indVec.size(); + SQLSetStmtAttr(statement_.hStmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)arraySize, 0); + + SQLRETURN cliRC = SQLBindParameter(statement_.hStmt, static_cast(position++), + SQL_PARAM_INPUT, cType, sqlType, size, 0, + static_cast(data), size, indptr); + + if ( cliRC != SQL_SUCCESS ) + { + throw db2_soci_error("Error while binding value to column", cliRC); + } +} + +void db2_vector_use_type_backend::bind_by_pos(int &position, + void *data, exchange_type type) +{ + bind_helper(position, data, type); + +} + +void db2_vector_use_type_backend::bind_by_name( + std::string const &name, void *data, exchange_type type) +{ + int position = -1; + int count = 1; + + for (std::vector::iterator it = statement_.names.begin(); + it != statement_.names.end(); ++it) + { + if (*it == name) + { + position = count; + break; + } + count++; + } + + if (position != -1) + { + bind_helper(position, data, type); + } + else + { + std::ostringstream ss; + 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) +{ + // first deal with data + if (type == x_stdtm) + { + std::vector *vp + = static_cast *>(data); + + std::vector &v(*vp); + + char *pos = buf; + std::size_t const vsize = v.size(); + for (std::size_t i = 0; i != vsize; ++i) + { + std::tm t = v[i]; + TIMESTAMP_STRUCT * ts = reinterpret_cast(pos); + + ts->year = static_cast(t.tm_year + 1900); + ts->month = static_cast(t.tm_mon + 1); + ts->day = static_cast(t.tm_mday); + ts->hour = static_cast(t.tm_hour); + ts->minute = static_cast(t.tm_min); + ts->second = static_cast(t.tm_sec); + ts->fraction = 0; + pos += sizeof(TIMESTAMP_STRUCT); + } + } + + // then handle indicators + if (ind != NULL) + { + std::size_t const vsize = size(); + for (std::size_t i = 0; i != vsize; ++i, ++ind) + { + if (*ind == i_null) + { + indVec[i] = SQL_NULL_DATA; // null + } + else + { + // for strings we have already set the values + if (type != x_stdstring) + { + indVec[i] = SQL_NTS; // value is OK + } + } + } + } + else + { + // no indicators - treat all fields as OK + std::size_t const vsize = size(); + for (std::size_t i = 0; i != vsize; ++i, ++ind) + { + // for strings we have already set the values + if (type != x_stdstring) + { + indVec[i] = SQL_NTS; // value is OK + } + } + } +} + +std::size_t db2_vector_use_type_backend::size() +{ + std::size_t sz = 0; // dummy initialization to please the compiler + switch (type) + { + // simple cases + case x_char: + { + std::vector *vp = static_cast *>(data); + sz = vp->size(); + } + break; + case x_short: + { + std::vector *vp = static_cast *>(data); + sz = vp->size(); + } + break; + case x_integer: + { + std::vector *vp = static_cast *>(data); + sz = vp->size(); + } + break; + case x_unsigned_long: + { + std::vector *vp + = static_cast *>(data); + sz = vp->size(); + } + break; + case x_long_long: + { + std::vector *vp + = static_cast *>(data); + sz = vp->size(); + } + break; + case x_unsigned_long_long: + { + std::vector *vp + = static_cast *>(data); + sz = vp->size(); + } + break; + case x_double: + { + std::vector *vp + = static_cast *>(data); + sz = vp->size(); + } + break; + case x_stdstring: + { + std::vector *vp + = static_cast *>(data); + sz = vp->size(); + } + break; + case x_stdtm: + { + std::vector *vp + = static_cast *>(data); + sz = vp->size(); + } + break; + + case x_statement: break; // not supported + case x_rowid: break; // not supported + case x_blob: break; // not supported + } + + return sz; +} + +void db2_vector_use_type_backend::clean_up() +{ + if (buf != NULL) + { + delete [] buf; + buf = NULL; + } +}