Commit 2ceafcce authored by Ricardo Andrade's avatar Ricardo Andrade Committed by Vadim Zeitlin

Fixes test_prepared_insert_with_orm_type with DB2.

DB2 backend seems to follow the implementation ODBC one, so this fix is almost a copy of odbc_standard_use_type_backend.
db2_standard_use_type_backend calculates the size of variables during bind() while ODBC does it during pre_use().
If the value (strings specifically) has its size changed between bind (statement::prepare) and pre_use (statement::execute) the backend will have the previous size.
This difference results in wrongly sized buffers what may lead to memory corruption.
parent 594f8be6
......@@ -110,6 +110,7 @@ struct SOCI_DB2_DECL db2_vector_into_type_backend : details::vector_into_type_ba
std::vector<SQLLEN> indVec;
void *data;
char *buf;
int position_;
details::exchange_type type;
SQLSMALLINT cType;
std::size_t colSize;
......@@ -131,8 +132,7 @@ struct SOCI_DB2_DECL db2_standard_use_type_backend : details::standard_use_type_
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 *prepare_for_bind(void *data, SQLLEN &size, SQLSMALLINT &sqlType, SQLSMALLINT &cType);
void *data;
details::exchange_type type;
......@@ -261,7 +261,7 @@ struct db2_session_backend : details::session_backend
struct SOCI_DB2_DECL db2_backend_factory : backend_factory
{
db2_backend_factory() {}
db2_backend_factory() {}
db2_session_backend* make_session(
connection_parameters const & parameters) const;
};
......
......@@ -16,8 +16,8 @@
using namespace soci;
using namespace soci::details;
void db2_standard_use_type_backend::prepare_for_bind(
void *&data, SQLLEN &size, SQLSMALLINT &sqlType, SQLSMALLINT &cType)
void *db2_standard_use_type_backend::prepare_for_bind(
void *data, SQLLEN &size, SQLSMALLINT &sqlType, SQLSMALLINT &cType)
{
switch (type)
{
......@@ -50,12 +50,16 @@ void db2_standard_use_type_backend::prepare_for_bind(
// 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;
ind = SQL_NTS;
{
sqlType = SQL_CHAR;
cType = SQL_C_CHAR;
size = sizeof(char) + 1;
buf = new char[size];
char *c = static_cast<char*>(data);
buf[0] = *c;
buf[1] = '\0';
ind = SQL_NTS;
}
break;
case x_stdstring:
{
......@@ -66,18 +70,31 @@ void db2_standard_use_type_backend::prepare_for_bind(
cType = SQL_C_CHAR;
size = s->size() + 1;
buf = new char[size];
data = buf;
strncpy(buf, s->c_str(), size);
ind = 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
{
sqlType = SQL_TIMESTAMP;
cType = SQL_C_TIMESTAMP;
buf = new char[sizeof(TIMESTAMP_STRUCT)];
std::tm *t = static_cast<std::tm *>(data);
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
TIMESTAMP_STRUCT * ts = reinterpret_cast<TIMESTAMP_STRUCT*>(buf);
ts->year = static_cast<SQLSMALLINT>(t->tm_year + 1900);
ts->month = static_cast<SQLUSMALLINT>(t->tm_mon + 1);
ts->day = static_cast<SQLUSMALLINT>(t->tm_mday);
ts->hour = static_cast<SQLUSMALLINT>(t->tm_hour);
ts->minute = static_cast<SQLUSMALLINT>(t->tm_min);
ts->second = static_cast<SQLUSMALLINT>(t->tm_sec);
ts->fraction = 0;
}
break;
case x_blob:
......@@ -86,28 +103,10 @@ void db2_standard_use_type_backend::prepare_for_bind(
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, size, &ind);
if (cliRC != SQL_SUCCESS)
{
throw db2_soci_error("Error while binding value",cliRC);
}
// Return either the pointer to C++ data itself or the buffer that we
// allocated, if any.
return buf ? buf : data;
}
void db2_standard_use_type_backend::bind_by_pos(
......@@ -119,7 +118,9 @@ void db2_standard_use_type_backend::bind_by_pos(
}
statement_.use_binding_method_ = details::db2::BOUND_BY_POSITION;
bind_helper(position, data, type);
this->data = data; // for future reference
this->type = type; // for future reference
this->position = position++;
}
void db2_standard_use_type_backend::bind_by_name(
......@@ -147,7 +148,9 @@ void db2_standard_use_type_backend::bind_by_name(
if (position != -1)
{
bind_helper(position, data, type);
this->data = data; // for future reference
this->type = type; // for future reference
this->position = position;
}
else
{
......@@ -160,34 +163,20 @@ void db2_standard_use_type_backend::bind_by_name(
void db2_standard_use_type_backend::pre_use(indicator const *ind_ptr)
{
// 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);
std::size_t const bufSize = s->size() + 1;
SQLSMALLINT sqlType;
SQLSMALLINT cType;
SQLLEN size;
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)
void *sqlData = prepare_for_bind(data, size, sqlType, cType);
SQLRETURN cliRC = SQLBindParameter(statement_.hStmt,
static_cast<SQLUSMALLINT>(position),
SQL_PARAM_INPUT,
cType, sqlType, size, 0, sqlData, size, &ind);
if (cliRC != SQL_SUCCESS)
{
std::tm *t = static_cast<std::tm *>(data);
TIMESTAMP_STRUCT * ts = reinterpret_cast<TIMESTAMP_STRUCT*>(buf);
ts->year = static_cast<SQLSMALLINT>(t->tm_year + 1900);
ts->month = static_cast<SQLUSMALLINT>(t->tm_mon + 1);
ts->day = static_cast<SQLUSMALLINT>(t->tm_mday);
ts->hour = static_cast<SQLUSMALLINT>(t->tm_hour);
ts->minute = static_cast<SQLUSMALLINT>(t->tm_min);
ts->second = static_cast<SQLUSMALLINT>(t->tm_sec);
ts->fraction = 0;
throw db2_soci_error("Error while binding value",cliRC);
}
// then handle indicators
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment