Skip to content

Commit

Permalink
Passing input parameters as strings works for different column types.
Browse files Browse the repository at this point in the history
  • Loading branch information
vnaydionov committed Feb 13, 2013
1 parent 8e96b10 commit a061013
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 42 deletions.
42 changes: 12 additions & 30 deletions src/backends/firebird/common.cpp
Expand Up @@ -27,10 +27,16 @@ namespace firebird
char * allocBuffer(XSQLVAR* var)
{
std::size_t size;
if ((var->sqltype & ~1) == SQL_VARYING)
int type = var->sqltype & ~1;
if (type == SQL_VARYING)
{
size = var->sqllen + sizeof(short);
}
else if (type == SQL_TIMESTAMP or type == SQL_TYPE_TIME
or type == SQL_TYPE_DATE)
{
size = sizeof(std::tm);
}
else
{
size = var->sqllen;
Expand Down Expand Up @@ -84,7 +90,7 @@ void tmDecode(short type, void * src, std::tm * dst)
void setTextParam(char const * s, std::size_t size, char * buf_,
XSQLVAR * var)
{
std::cerr << "setTextParam: var->sqltype=" << var->sqltype << std::endl;
//std::cerr << "setTextParam: var->sqltype=" << var->sqltype << std::endl;
short sz = 0;
if (size < static_cast<std::size_t>(var->sqllen))
{
Expand All @@ -110,39 +116,15 @@ void setTextParam(char const * s, std::size_t size, char * buf_,
}
else if ((var->sqltype & ~1) == SQL_SHORT)
{
unsigned short t1;
short t2;
if (!*str2int(s, t1))
std::memcpy(buf_, &t1, sizeof(t1));
else if (!*str2int(s, t2))
std::memcpy(buf_, &t2, sizeof(t2));
else
throw soci_error("Could not parse int16 value.");
to_isc<short>(buf_, var);
parse_decimal<short, unsigned short>(buf_, var, s);
}
else if ((var->sqltype & ~1) == SQL_LONG)
{
unsigned t1;
int t2;
if (!*str2int(s, t1))
std::memcpy(buf_, &t1, sizeof(t1));
else if (!*str2int(s, t2))
std::memcpy(buf_, &t2, sizeof(t2));
else
throw soci_error("Could not parse int32 value.");
to_isc<int>(buf_, var);
parse_decimal<int, unsigned int>(buf_, var, s);
}
else if ((var->sqltype & ~1) == SQL_INT64)
{
unsigned long long t1;
long long t2;
if (!*str2int(s, t1))
std::memcpy(buf_, &t1, sizeof(t1));
else if (!*str2int(s, t2))
std::memcpy(buf_, &t2, sizeof(t2));
else
throw soci_error("Could not parse int64 value.");
to_isc<long long>(buf_, var);
parse_decimal<long long, unsigned long long>(buf_, var, s);
}
else if ((var->sqltype & ~1) == SQL_TIMESTAMP
or (var->sqltype & ~1) == SQL_TYPE_DATE)
Expand Down Expand Up @@ -195,7 +177,7 @@ void setTextParam(char const * s, std::size_t size, char * buf_,

std::string getTextParam(XSQLVAR const *var)
{
std::cerr << "getTextParam: var->sqltype=" << var->sqltype << std::endl;
//std::cerr << "getTextParam: var->sqltype=" << var->sqltype << std::endl;
short size;
std::size_t offset = 0;

Expand Down
48 changes: 37 additions & 11 deletions src/backends/firebird/common.h
Expand Up @@ -38,7 +38,7 @@ void setTextParam(char const * s, std::size_t size, char * buf_,
std::string getTextParam(XSQLVAR const *var);

template <typename IntType>
const char *str2int(const char * s, IntType &out)
const char *str2dec(const char * s, IntType &out, int &scale)
{
int sign = 1;
if ('+' == *s)
Expand All @@ -48,9 +48,18 @@ const char *str2int(const char * s, IntType &out)
sign = -1;
++s;
}
scale = 0;
bool period = false;
IntType res = 0;
for (out = 0; *s; ++s, out = res)
{
if (*s == '.')
{
if (period)
return s;
period = true;
continue;
}
int d = *s - '0';
if (d < 0 || d > 9)
return s;
Expand All @@ -65,17 +74,19 @@ const char *str2int(const char * s, IntType &out)
if (res > out)
return s;
}
if (period)
++scale;
}
return s;
}

template<typename T1>
void to_isc(void * val, XSQLVAR * var)
void to_isc(void * val, XSQLVAR * var, int x_scale = 0)
{
T1 value = *reinterpret_cast<T1*>(val);
short scale = var->sqlscale;
short scale = var->sqlscale + x_scale;
short type = var->sqltype & ~1;
ISC_INT64 tens = 1;
long long divisor = 1, multiplier = 1;

if ((std::numeric_limits<T1>::is_integer == false) && scale >= 0 &&
(type == SQL_SHORT || type == SQL_LONG || type == SQL_INT64))
Expand All @@ -84,27 +95,27 @@ void to_isc(void * val, XSQLVAR * var)
}

for (int i = 0; i > scale; --i)
{
tens *= 10;
}
multiplier *= 10;
for (int i = 0; i < scale; ++i)
divisor *= 10;

switch (var->sqltype & ~1)
switch (type)
{
case SQL_SHORT:
{
short tmp = static_cast<short>(value*tens);
short tmp = static_cast<short>(value*multiplier/divisor);
std::memcpy(var->sqldata, &tmp, sizeof(short));
}
break;
case SQL_LONG:
{
int tmp = static_cast<int>(value*tens);
int tmp = static_cast<int>(value*multiplier/divisor);
std::memcpy(var->sqldata, &tmp, sizeof(int));
}
break;
case SQL_INT64:
{
long long tmp = static_cast<long long>(value*tens);
long long tmp = static_cast<long long>(value*multiplier/divisor);
std::memcpy(var->sqldata, &tmp, sizeof(long long));
}
break;
Expand All @@ -125,6 +136,21 @@ void to_isc(void * val, XSQLVAR * var)
}
}

template<typename IntType, typename UIntType>
void parse_decimal(void * val, XSQLVAR * var, const char * s)
{
int scale;
UIntType t1;
IntType t2;
if (!*str2dec(s, t1, scale))
std::memcpy(val, &t1, sizeof(t1));
else if (!*str2dec(s, t2, scale))
std::memcpy(val, &t2, sizeof(t2));
else
throw soci_error("Could not parse decimal value.");
to_isc<IntType>(val, var, scale);
}

template<typename T1>
T1 from_isc(XSQLVAR * var)
{
Expand Down
2 changes: 1 addition & 1 deletion src/backends/firebird/statement.cpp
Expand Up @@ -287,7 +287,7 @@ void firebird_statement_backend::rewriteQuery(
void firebird_statement_backend::prepare(std::string const & query,
statement_type /* eType */)
{
std::cerr << "prepare: query=" << query << std::endl;
//std::cerr << "prepare: query=" << query << std::endl;
// clear named parametes
names_.clear();

Expand Down
46 changes: 46 additions & 0 deletions src/backends/firebird/test/test-firebird.cpp
Expand Up @@ -15,6 +15,7 @@
#include <cassert>
#include <ctime>
#include <cstring>
#include <cmath>

using namespace soci;

Expand Down Expand Up @@ -1052,6 +1053,50 @@ void test11()
std::cout << "test 11 passed" << std::endl;
}

void test12()
{
session sql(backEnd, connectString);

try
{
sql << "drop table test12";
}
catch (std::runtime_error &)
{} // ignore if error

sql << "create table test12(a decimal(10,3), b timestamp, c date, d time)";
sql.commit();
sql.begin();

// Check if passing input parameters as strings works
// for different column types.
{
std::string a = "-3.14150", b = "2013-02-28 23:36:01",
c = "2013-02-28", d = "23:36:01";
statement st = (sql.prepare <<
"insert into test12(a, b, c, d) values (?, ?, ?, ?)",
use(a), use(b), use(c), use(d));
st.execute(1);
assert(getRowCount(st, eRowsInserted) == 1);
}

{
double a;
std::tm b, c, d;
sql << "select a, b, c, d from test12",
into(a), into(b), into(c), into(d);
assert(std::fabs(a - (-3.141)) < 0.000001);
assert(b.tm_year + 1900 == 2013 && b.tm_mon + 1 == 2 && b.tm_mday == 28);
assert(b.tm_hour == 23 && b.tm_min == 36 && b.tm_sec == 1);
assert(c.tm_year + 1900 == 2013 && c.tm_mon + 1 == 2 && c.tm_mday == 28);
assert(c.tm_hour == 0 && c.tm_min == 0 && c.tm_sec == 0);
assert(d.tm_hour == 23 && d.tm_min == 36 && d.tm_sec == 1);
}

sql << "drop table test12";
std::cout << "test 12 passed" << std::endl;
}

//
// Support for soci Common Tests
//
Expand Down Expand Up @@ -1171,6 +1216,7 @@ int main(int argc, char** argv)
test9();
test10();
test11();
test12();

std::cout << "\nOK, all tests passed.\n\n";

Expand Down

0 comments on commit a061013

Please sign in to comment.