Commit 48650b34 authored by Denis Chapligin's avatar Denis Chapligin

Added DB2 backend

parent c5135ee6
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")
......
//
// 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 */)
{
// ...
}
//
// 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 <backend-loader.h>
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"
//
// 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()
{
// ...
}
......@@ -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<<message;
}
return ss.str();
}
void db2_session_backend::parseKeyVal(std::string const & keyVal) {
size_t delimiter=keyVal.find_first_of("=");
std::string key=keyVal.substr(0,delimiter);
std::string value=keyVal.substr(delimiter+1,keyVal.length());
if (!key.compare("Dsn")) {
this->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);
}
}
......
......@@ -28,16 +28,25 @@
#include <cstddef>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <cstring>
#include <sqlcli1.h>
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<SQLLEN> 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<SQLLEN> 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<std::string> 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;
......
//
// 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 <ctime>
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<SQLUSMALLINT>(this->position),
static_cast<SQLUSMALLINT>(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<char*>(data);
*c = buf[0];
}
if (type == x_stdstring)
{
std::string *s = static_cast<std::string *>(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<std::tm *>(data);
TIMESTAMP_STRUCT * ts = reinterpret_cast<TIMESTAMP_STRUCT*>(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;
}
}
// 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 <cctype>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <sstream>
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<std::string*>(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<SQLUSMALLINT>(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<std::string>::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<char*>(data);
buf[0] = *c;
buf[1] = '\0';
}
else if (type == x_stdstring)
{
std::string *s = static_cast<std::string *>(data);