Skip to content

Commit

Permalink
Merge pull request #97 from toonen/db2-patches
Browse files Browse the repository at this point in the history
DB2 backend patches
  • Loading branch information
mloskot committed Mar 11, 2013
2 parents 2efba38 + d0c5481 commit 2952cff
Show file tree
Hide file tree
Showing 10 changed files with 476 additions and 126 deletions.
61 changes: 53 additions & 8 deletions src/backends/db2/session.cpp
Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<SQLCHAR *>((const SQLCHAR *) dsn.c_str()), SQL_NTS,
const_cast<SQLCHAR *>((const SQLCHAR *) username.c_str()), SQL_NTS,
const_cast<SQLCHAR *>((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);
Expand All @@ -124,31 +127,73 @@ 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);
}
}
}

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);
}
}
}

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);
Expand Down
17 changes: 15 additions & 2 deletions src/backends/db2/soci-db2.h
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -192,6 +203,7 @@ struct SOCI_DB2_DECL db2_statement_backend : details::statement_backend
std::vector<std::string> names;
bool hasVectorUseElements;
SQLUINTEGER numRowsFetched;
details::db2::binding_method use_binding_method_;
};

struct db2_rowid_backend : details::rowid_backend
Expand Down Expand Up @@ -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 */
Expand Down
2 changes: 1 addition & 1 deletion src/backends/db2/standard-into-type.cpp
Expand Up @@ -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;
Expand Down
25 changes: 18 additions & 7 deletions src/backends/db2/standard-use-type.cpp
Expand Up @@ -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:
{
Expand All @@ -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:
Expand Down Expand Up @@ -102,7 +102,7 @@ void db2_standard_use_type_backend::bind_helper(int &position, void *data, detai
SQLRETURN cliRC = SQLBindParameter(statement_.hStmt,
static_cast<SQLUSMALLINT>(position++),
SQL_PARAM_INPUT,
cType, sqlType, size, 0, data, size, &indptr);
cType, sqlType, size, 0, data, size, &ind);

if (cliRC != SQL_SUCCESS)
{
Expand All @@ -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;

Expand All @@ -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)
Expand Down Expand Up @@ -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
}
}

Expand Down
45 changes: 26 additions & 19 deletions src/backends/db2/statement.cpp
Expand Up @@ -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)
{
}

Expand All @@ -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);
Expand Down Expand Up @@ -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<SQLCHAR *>((const SQLCHAR *) query_.c_str()), SQL_NTS);
if (cliRC!=SQL_SUCCESS) {
throw db2_soci_error("Error while preparing query",cliRC);
}
Expand All @@ -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)
{
Expand All @@ -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);

Expand All @@ -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;
}

Expand All @@ -185,18 +181,29 @@ 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;
}

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()
Expand Down

0 comments on commit 2952cff

Please sign in to comment.