Commit 20f1b5f5 authored by msobczak's avatar msobczak

Completed support for tuple.

parent 50d0f7b7
......@@ -21,8 +21,8 @@ struct type_conversion<boost::optional<T> >
{
typedef typename type_conversion<T>::base_type base_type;
static void from_base(base_type const &in, eIndicator ind,
boost::optional<T> &out)
static void from_base(base_type const & in, eIndicator ind,
boost::optional<T> & out)
{
if (ind == eNull)
{
......@@ -36,8 +36,8 @@ struct type_conversion<boost::optional<T> >
}
}
static void to_base(boost::optional<T> &in,
base_type &out, eIndicator &ind)
static void to_base(boost::optional<T> const & in,
base_type & out, eIndicator & ind)
{
if (in.is_initialized())
{
......
......@@ -31,7 +31,8 @@ struct type_conversion<boost::tuple<T0> >
static void to_base(boost::tuple<T0> &in,
base_type &out, eIndicator &ind)
{
throw soci_error("Conversions from boost::tuple are not supported.");
out
<< boost::tuples::get<0>(in);
}
};
......@@ -51,7 +52,9 @@ struct type_conversion<boost::tuple<T0, T1> >
static void to_base(boost::tuple<T0, T1> &in,
base_type &out, eIndicator &ind)
{
throw soci_error("Conversions from boost::tuple are not supported.");
out
<< boost::tuples::get<0>(in)
<< boost::tuples::get<1>(in);
}
};
......@@ -72,7 +75,10 @@ struct type_conversion<boost::tuple<T0, T1, T2> >
static void to_base(boost::tuple<T0, T1, T2> &in,
base_type &out, eIndicator &ind)
{
throw soci_error("Conversions from boost::tuple are not supported.");
out
<< boost::tuples::get<0>(in)
<< boost::tuples::get<1>(in)
<< boost::tuples::get<2>(in);
}
};
......@@ -94,7 +100,11 @@ struct type_conversion<boost::tuple<T0, T1, T2, T3> >
static void to_base(boost::tuple<T0, T1, T2, T3> &in,
base_type &out, eIndicator &ind)
{
throw soci_error("Conversions from boost::tuple are not supported.");
out
<< boost::tuples::get<0>(in)
<< boost::tuples::get<1>(in)
<< boost::tuples::get<2>(in)
<< boost::tuples::get<3>(in);
}
};
......@@ -117,7 +127,12 @@ struct type_conversion<boost::tuple<T0, T1, T2, T3, T4> >
static void to_base(boost::tuple<T0, T1, T2, T3, T4> &in,
base_type &out, eIndicator &ind)
{
throw soci_error("Conversions from boost::tuple are not supported.");
out
<< boost::tuples::get<0>(in)
<< boost::tuples::get<1>(in)
<< boost::tuples::get<2>(in)
<< boost::tuples::get<3>(in)
<< boost::tuples::get<4>(in);
}
};
......@@ -142,7 +157,13 @@ struct type_conversion<boost::tuple<T0, T1, T2, T3, T4, T5> >
static void to_base(boost::tuple<T0, T1, T2, T3, T4, T5> &in,
base_type &out, eIndicator &ind)
{
throw soci_error("Conversions from boost::tuple are not supported.");
out
<< boost::tuples::get<0>(in)
<< boost::tuples::get<1>(in)
<< boost::tuples::get<2>(in)
<< boost::tuples::get<3>(in)
<< boost::tuples::get<4>(in)
<< boost::tuples::get<5>(in);
}
};
......@@ -168,7 +189,14 @@ struct type_conversion<boost::tuple<T0, T1, T2, T3, T4, T5, T6> >
static void to_base(boost::tuple<T0, T1, T2, T3, T4, T5, T6> &in,
base_type &out, eIndicator &ind)
{
throw soci_error("Conversions from boost::tuple are not supported.");
out
<< boost::tuples::get<0>(in)
<< boost::tuples::get<1>(in)
<< boost::tuples::get<2>(in)
<< boost::tuples::get<3>(in)
<< boost::tuples::get<4>(in)
<< boost::tuples::get<5>(in)
<< boost::tuples::get<6>(in);
}
};
......@@ -195,7 +223,15 @@ struct type_conversion<boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7> >
static void to_base(boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7> &in,
base_type &out, eIndicator &ind)
{
throw soci_error("Conversions from boost::tuple are not supported.");
out
<< boost::tuples::get<0>(in)
<< boost::tuples::get<1>(in)
<< boost::tuples::get<2>(in)
<< boost::tuples::get<3>(in)
<< boost::tuples::get<4>(in)
<< boost::tuples::get<5>(in)
<< boost::tuples::get<6>(in)
<< boost::tuples::get<7>(in);
}
};
......@@ -223,7 +259,16 @@ struct type_conversion<boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8> >
static void to_base(boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8> &in,
base_type &out, eIndicator &ind)
{
throw soci_error("Conversions from boost::tuple are not supported.");
out
<< boost::tuples::get<0>(in)
<< boost::tuples::get<1>(in)
<< boost::tuples::get<2>(in)
<< boost::tuples::get<3>(in)
<< boost::tuples::get<4>(in)
<< boost::tuples::get<5>(in)
<< boost::tuples::get<6>(in)
<< boost::tuples::get<7>(in)
<< boost::tuples::get<8>(in);
}
};
......@@ -252,7 +297,17 @@ struct type_conversion<boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> >
static void to_base(boost::tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> &in,
base_type &out, eIndicator &ind)
{
throw soci_error("Conversions from boost::tuple are not supported.");
out
<< boost::tuples::get<0>(in)
<< boost::tuples::get<1>(in)
<< boost::tuples::get<2>(in)
<< boost::tuples::get<3>(in)
<< boost::tuples::get<4>(in)
<< boost::tuples::get<5>(in)
<< boost::tuples::get<6>(in)
<< boost::tuples::get<7>(in)
<< boost::tuples::get<8>(in)
<< boost::tuples::get<9>(in);
}
};
......
......@@ -78,31 +78,47 @@ void statement_impl::bind(values& values)
for (std::vector<details::standard_use_type*>::iterator it =
values.uses_.begin(); it != values.uses_.end(); ++it)
{
// only bind those variables which are actually
// referenced in the statement
const std::string name = ":" + (*it)->get_name();
// only bind those variables which are:
// - either named and actually referenced in the statement,
// - or positional
size_t pos = query_.find(name);
if (pos != std::string::npos)
std::string const & useName = (*it)->get_name();
if (useName.empty())
{
const char nextChar = query_[pos + name.size()];
if (nextChar == ' ' || nextChar == ',' ||
nextChar == '\0' || nextChar == ')')
// positional use element
int position = static_cast<int>(uses_.size());
(*it)->bind(*this, position);
uses_.push_back(*it);
indicators_.push_back(values.indicators_[cnt]);
}
else
{
// named use element - check if it is used
const std::string placeholder = ":" + useName;
size_t pos = query_.find(placeholder);
if (pos != std::string::npos)
{
int position = static_cast<int>(uses_.size());
(*it)->bind(*this, position);
uses_.push_back(*it);
indicators_.push_back(values.indicators_[cnt]);
const char nextChar = query_[pos + placeholder.size()];
if (nextChar == ' ' || nextChar == ',' ||
nextChar == '\0' || nextChar == ')')
{
int position = static_cast<int>(uses_.size());
(*it)->bind(*this, position);
uses_.push_back(*it);
indicators_.push_back(values.indicators_[cnt]);
}
else
{
values.add_unused(*it, values.indicators_[cnt]);
}
}
else
{
values.add_unused(*it, values.indicators_[cnt]);
}
}
else
{
values.add_unused(*it, values.indicators_[cnt]);
}
cnt++;
}
......@@ -111,7 +127,7 @@ void statement_impl::bind(values& values)
{
for (size_t i = ++cnt; i != values.uses_.size(); ++i)
{
values.add_unused(uses_[i], values.indicators_[i]);
values.add_unused(values.uses_[i], values.indicators_[i]);
}
throw;
}
......
......@@ -84,7 +84,6 @@ template<> struct type_conversion<PhonebookEntry>
static void from_base(values const &v, eIndicator /* ind */, PhonebookEntry &pe)
{
// here we ignore the possibility the the whole object might be NULL
pe.name = v.get<std::string>("NAME");
pe.phone = v.get<std::string>("PHONE", "<NULL>");
}
......@@ -3056,60 +3055,103 @@ void test28()
auto_table_creator tableCreator(tc_.table_creator_2(sql));
{
sql << "insert into soci_test(num_float, num_int, name) values(3.5, 7, 'Joe Hacker')";
boost::tuple<double, int, std::string> t1(3.5, 7, "Joe Hacker");
assert(t1.get<0>() == 3.5);
assert(t1.get<1>() == 7);
assert(t1.get<2>() == "Joe Hacker");
sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1);
// basic query
boost::tuple<double, int, std::string> t;
sql << "select num_float, num_int, name from soci_test", into(t);
boost::tuple<double, int, std::string> t2;
sql << "select num_float, num_int, name from soci_test", into(t2);
assert(t2.get<0>() == 3.5);
assert(t2.get<1>() == 7);
assert(t2.get<2>() == "Joe Hacker");
assert(t.get<0>() == 3.5);
assert(t.get<1>() == 7);
assert(t.get<2>() == "Joe Hacker");
sql << "delete from soci_test";
}
{
// composability with boost::optional
boost::tuple<double, boost::optional<int>, std::string> t;
sql << "select num_float, num_int, name from soci_test", into(t);
// use:
boost::tuple<double, boost::optional<int>, std::string> t1(
3.5, boost::optional<int>(7), "Joe Hacker");
assert(t1.get<0>() == 3.5);
assert(t1.get<1>().is_initialized());
assert(t1.get<1>().get() == 7);
assert(t1.get<2>() == "Joe Hacker");
sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1);
// into:
boost::tuple<double, boost::optional<int>, std::string> t2;
sql << "select num_float, num_int, name from soci_test", into(t2);
assert(t2.get<0>() == 3.5);
assert(t2.get<1>().is_initialized());
assert(t2.get<1>().get() == 7);
assert(t2.get<2>() == "Joe Hacker");
assert(t.get<0>() == 3.5);
assert(t.get<1>().is_initialized());
assert(t.get<1>().get() == 7);
assert(t.get<2>() == "Joe Hacker");
sql << "delete from soci_test";
}
{
// composability with user-provided conversions
boost::tuple<double, MyInt, std::string> t;
sql << "select num_float, num_int, name from soci_test", into(t);
// use:
boost::tuple<double, MyInt, std::string> t1(3.5, 7, "Joe Hacker");
assert(t1.get<0>() == 3.5);
assert(t1.get<1>().get() == 7);
assert(t1.get<2>() == "Joe Hacker");
assert(t.get<0>() == 3.5);
assert(t.get<1>().get() == 7);
assert(t.get<2>() == "Joe Hacker");
sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1);
// into:
boost::tuple<double, MyInt, std::string> t2;
sql << "select num_float, num_int, name from soci_test", into(t2);
assert(t2.get<0>() == 3.5);
assert(t2.get<1>().get() == 7);
assert(t2.get<2>() == "Joe Hacker");
sql << "delete from soci_test";
}
{
// let's have fun
// let's have fun - composition of tuple, optional and user-defined type
// use:
boost::tuple<double, boost::optional<MyInt>, std::string> t1(
3.5, boost::optional<MyInt>(7), "Joe Hacker");
assert(t1.get<0>() == 3.5);
assert(t1.get<1>().is_initialized());
assert(t1.get<1>().get().get() == 7);
assert(t1.get<2>() == "Joe Hacker");
sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1);
boost::tuple<double, boost::optional<MyInt>, std::string> t;
// into:
boost::tuple<double, boost::optional<MyInt>, std::string> t2;
sql << "select num_float, num_int, name from soci_test", into(t);
sql << "select num_float, num_int, name from soci_test", into(t2);
assert(t.get<0>() == 3.5);
assert(t.get<1>().is_initialized());
assert(t.get<1>().get().get() == 7);
assert(t.get<2>() == "Joe Hacker");
assert(t2.get<0>() == 3.5);
assert(t2.get<1>().is_initialized());
assert(t2.get<1>().get().get() == 7);
assert(t2.get<2>() == "Joe Hacker");
sql << "update soci_test set num_int = NULL";
sql << "select num_float, num_int, name from soci_test", into(t);
sql << "select num_float, num_int, name from soci_test", into(t2);
assert(t.get<0>() == 3.5);
assert(t.get<1>().is_initialized() == false);
assert(t.get<2>() == "Joe Hacker");
assert(t2.get<0>() == 3.5);
assert(t2.get<1>().is_initialized() == false);
assert(t2.get<2>() == "Joe Hacker");
}
{
......
......@@ -20,7 +20,7 @@ struct type_conversion
{
typedef T base_type;
static void from_base(base_type const &in, eIndicator ind, T &out)
static void from_base(base_type const & in, eIndicator ind, T & out)
{
if (ind == eNull)
{
......@@ -29,10 +29,13 @@ struct type_conversion
out = in;
}
static void to_base(T const &in, base_type &out, eIndicator &ind)
static void to_base(T const & in, base_type & out, eIndicator & ind)
{
out = in;
ind = eOK;
// Note: if the T type does not provide any specific NULL semantics,
// then the indicator is left untouched
// - this is the case for fundamental types
}
};
......
......@@ -49,6 +49,7 @@ public:
virtual void post_use(bool /*gotData*/)
{
v_.reset_get_counter();
convert_from_base();
}
......
......@@ -44,7 +44,7 @@ class SOCI_DECL values
public:
values() : row_(NULL), uppercaseColumnNames_(false) {}
values() : row_(NULL), currentPos_(0), uppercaseColumnNames_(false) {}
eIndicator indicator(std::size_t pos) const;
eIndicator indicator(std::string const &name) const;
......@@ -52,7 +52,7 @@ public:
template <typename T>
T get(std::size_t pos) const
{
if (row_)
if (row_ != NULL)
{
return row_->get<T>(pos);
}
......@@ -73,7 +73,7 @@ public:
template <typename T>
T get(std::size_t pos, T const &nullValue) const
{
if (row_)
if (row_ != NULL)
{
return row_->get<T>(pos, nullValue);
}
......@@ -90,23 +90,72 @@ public:
template <typename T>
T get(std::string const &name) const
{
return row_ ? row_->get<T>(name) : get_from_uses<T>(name);
return row_ != NULL ? row_->get<T>(name) : get_from_uses<T>(name);
}
template <typename T>
T get(std::string const &name, T const &nullValue) const
{
return row_ ? row_->get<T>(name, nullValue)
return row_ != NULL ? row_->get<T>(name, nullValue)
: get_from_uses<T>(name, nullValue);
}
template <typename T>
values const & operator>>(T &value) const
values const & operator>>(T & value) const
{
*row_ >> value;
if (row_ != NULL)
{
// row maintains its own position counter
// which is automatically reset when needed
*row_ >> value;
}
else if (*indicators_[currentPos_] != eNull)
{
// if there is no row object, then the data can be
// extracted from the locally stored use elements,
// but for this the position counter has to be maintained
// as well
value = get_from_uses<T>(currentPos_);
++currentPos_;
}
else
{
std::ostringstream msg;
msg << "Column at position "
<< static_cast<unsigned long>(currentPos_)
<< " contains NULL value and no default was provided";
throw soci_error(msg.str());
}
return *this;
}
void skip(size_t num = 1) const
{
if (row_ != NULL)
{
row_->skip(num);
}
else
{
currentPos_ += num;
}
}
void reset_get_counter() const
{
if (row_ != NULL)
{
row_->reset_get_counter();
}
else
{
currentPos_ = 0;
}
}
template <typename T>
void set(std::string const &name, const T &value, eIndicator indic = eOK)
{
......@@ -115,10 +164,41 @@ public:
eIndicator* pind = new eIndicator(indic);
indicators_.push_back(pind);
details::copy_holder<T> * pcopy = new details::copy_holder<T>(value);
typedef typename type_conversion<T>::base_type base_type;
base_type baseValue;
type_conversion<T>::to_base(value, baseValue, *pind);
details::copy_holder<base_type> * pcopy =
new details::copy_holder<base_type>(baseValue);
deepCopies_.push_back(pcopy);
uses_.push_back(new details::use_type<base_type>(
pcopy->value_, *pind, name));
}
template <typename T>
void set(const T & value, eIndicator indic = eOK)
{
eIndicator* pind = new eIndicator(indic);
indicators_.push_back(pind);
typedef typename type_conversion<T>::base_type base_type;
base_type baseValue;
type_conversion<T>::to_base(value, baseValue, *pind);
details::copy_holder<base_type> * pcopy =
new details::copy_holder<base_type>(baseValue);
deepCopies_.push_back(pcopy);
uses_.push_back(new details::use_type<T>(pcopy->value_, *pind, name));
uses_.push_back(new details::use_type<base_type>(
pcopy->value_, *pind));
}
template <typename T>
values & operator<<(const T & value)
{
set(value);
return *this;
}
void uppercase_column_names(bool forceToUpper)
......@@ -135,6 +215,8 @@ private:
std::map<std::string, size_t> index_;
std::vector<details::copy_base *> deepCopies_;
mutable size_t currentPos_;
bool uppercaseColumnNames_;
// When TypeConversion::to() is called, a values object is created
......@@ -170,12 +252,6 @@ private:
std::map<std::string, size_t>::const_iterator pos = index_.find(name);
if (pos != index_.end())
{
if (*indicators_[pos->second] == eNull)
{
throw soci_error("Column " + name + " contains NULL value and"
" no default was provided");
}
try
{
return get_from_uses<T>(pos->second);
......@@ -193,9 +269,17 @@ private:
T get_from_uses(size_t pos) const
{
details::standard_use_type* u = uses_[pos];
if (dynamic_cast<details::use_type<T>* >(u))
typedef typename type_conversion<T>::base_type base_type;
if (dynamic_cast<details::use_type<base_type>* >(u))
{
return *static_cast<T*>(u->get_data());
base_type const & baseValue = *static_cast<base_type*>(u->get_data());
T val;
eIndicator ind = *indicators_[pos];
type_conversion<T>::from_base(baseValue, ind, val);
return val;
}
else
{
......
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