From 329515e7fee4325f34ceaa5da83fc0fddb54a346 Mon Sep 17 00:00:00 2001 From: Aleksey Vyskubov Date: Sat, 3 Jun 2023 01:52:30 +0300 Subject: [PATCH] add support of bulk operations for ORM in ORACLE and SQLite backends --- include/soci/row.h | 41 +++++-- include/soci/soci-platform.h | 2 + include/soci/sqlite3/soci-sqlite3.h | 9 ++ include/soci/statement.h | 5 +- include/soci/type-conversion.h | 44 ++++--- include/soci/type-holder.h | 136 +++++++++++++++++++--- include/soci/values-exchange.h | 73 +++++++++++- include/soci/values.h | 5 +- src/backends/oracle/statement.cpp | 5 + src/backends/sqlite3/statement.cpp | 20 +--- src/backends/sqlite3/vector-into-type.cpp | 53 ++++++++- src/core/row.cpp | 6 +- src/core/statement.cpp | 7 ++ tests/oracle/test-oracle.cpp | 81 +++++++++++++ tests/sqlite3/test-sqlite3.cpp | 56 +++++++++ 15 files changed, 471 insertions(+), 72 deletions(-) diff --git a/include/soci/row.h b/include/soci/row.h index 8cd816dc4..45ead3d6e 100644 --- a/include/soci/row.h +++ b/include/soci/row.h @@ -19,6 +19,12 @@ namespace soci { +struct CaseInsensitiveComparator { + bool operator()(const std::string& a, const std::string& b) const noexcept + { + return ::strcasecmp(a.c_str(), b.c_str()) < 0; + } +}; class SOCI_DECL column_properties { @@ -40,7 +46,7 @@ class SOCI_DECL column_properties class SOCI_DECL row { public: - row(); + row(std::size_t bulk_size = 1); ~row(); row(row &&other) = default; @@ -51,13 +57,17 @@ class SOCI_DECL row std::size_t size() const; void clean_up(); + //bulk buffer size + std::size_t bulk_size() const { return bulk_size_; } + void bulk_size(const std::size_t sz) { bulk_size_ = sz; } + indicator get_indicator(std::size_t pos) const; indicator get_indicator(std::string const& name) const; template - inline void add_holder(T* t, indicator* ind) + inline void add_holder(std::vector* t, std::vector* ind) { - holders_.push_back(new details::type_holder(t)); + holders_.push_back(new details::vector_type_holder(t)); indicators_.push_back(ind); } @@ -68,17 +78,17 @@ class SOCI_DECL row T get(std::size_t pos) const { typedef typename type_conversion::base_type base_type; - base_type const& baseVal = holders_.at(pos)->get(); + base_type const& baseVal = holders_.at(pos)->get(bulk_pos_); T ret; - type_conversion::from_base(baseVal, *indicators_.at(pos), ret); + type_conversion::from_base(baseVal, (*indicators_.at(pos))[bulk_pos_], ret); return ret; } template T get(std::size_t pos, T const &nullValue) const { - if (i_null == *indicators_.at(pos)) + if (i_null == (*indicators_.at(pos))[bulk_pos_]) { return nullValue; } @@ -98,7 +108,7 @@ class SOCI_DECL row { std::size_t const pos = find_column(name); - if (i_null == *indicators_[pos]) + if (i_null == (*indicators_[pos])[bulk_pos_]) { return nullValue; } @@ -122,6 +132,13 @@ class SOCI_DECL row void reset_get_counter() const { currentPos_ = 0; + bulk_pos_ = 0; + } + + void next(std::size_t num = 1) const + { + currentPos_ = 0; + bulk_pos_ += num; } private: @@ -130,12 +147,14 @@ class SOCI_DECL row std::size_t find_column(std::string const& name) const; std::vector columns_; - std::vector holders_; - std::vector indicators_; - std::map index_; + std::vector holders_; + std::vector*> indicators_; + std::map index_; bool uppercaseColumnNames_; - mutable std::size_t currentPos_; + mutable std::size_t currentPos_; //column + mutable std::size_t bulk_pos_; //row + mutable std::size_t bulk_size_; }; } // namespace soci diff --git a/include/soci/soci-platform.h b/include/soci/soci-platform.h index 3f94cb57b..d3096d7e7 100644 --- a/include/soci/soci-platform.h +++ b/include/soci/soci-platform.h @@ -64,6 +64,8 @@ namespace std { } } #endif // _MSC_VER < 1800 +#define strncasecmp _strnicmp +#define strcasecmp _stricmp #endif // _MSC_VER #if defined(__CYGWIN__) || defined(__MINGW32__) diff --git a/include/soci/sqlite3/soci-sqlite3.h b/include/soci/sqlite3/soci-sqlite3.h index 2520dfd4b..1332c1e3f 100644 --- a/include/soci/sqlite3/soci-sqlite3.h +++ b/include/soci/sqlite3/soci-sqlite3.h @@ -60,6 +60,10 @@ class SOCI_SQLITE3_DECL sqlite3_soci_error : public soci_error int result_; }; + +typedef std::map sqlite3_data_type_map; +sqlite3_data_type_map get_data_type_map(); + struct sqlite3_statement_backend; struct sqlite3_standard_into_type_backend : details::standard_into_type_backend { @@ -84,6 +88,7 @@ struct sqlite3_standard_into_type_backend : details::standard_into_type_backend int position_; }; +struct sqlite3_column; struct sqlite3_vector_into_type_backend : details::vector_into_type_backend { sqlite3_vector_into_type_backend(sqlite3_statement_backend &st) @@ -106,6 +111,10 @@ struct sqlite3_vector_into_type_backend : details::vector_into_type_backend void *data_; details::exchange_type type_; int position_; + +private: + data_type get_column_type(int colNum); + sqlite3_column get_column(int colNum); }; struct sqlite3_standard_use_type_backend : details::standard_use_type_backend diff --git a/include/soci/statement.h b/include/soci/statement.h index b356762e0..00cebea2e 100644 --- a/include/soci/statement.h +++ b/include/soci/statement.h @@ -138,8 +138,9 @@ class SOCI_DECL statement_impl template void into_row() { - T * t = new T(); - indicator * ind = new indicator(i_ok); + std::size_t bulk_size = row_->bulk_size(); + std::vector* t = new std::vector(bulk_size); + std::vector* ind = new std::vector(bulk_size); row_->add_holder(t, ind); exchange_for_row(into(*t, *ind)); } diff --git a/include/soci/type-conversion.h b/include/soci/type-conversion.h index 3b96ab770..4a1debaf0 100644 --- a/include/soci/type-conversion.h +++ b/include/soci/type-conversion.h @@ -38,7 +38,7 @@ struct base_value_holder // Automatically create into_type from a type_conversion -template +template class conversion_into_type : private base_value_holder, public into_type::base_type> @@ -80,7 +80,7 @@ class conversion_into_type // Automatically create use_type from a type_conversion -template +template class conversion_use_type : private base_value_holder, public use_type::base_type> @@ -195,8 +195,8 @@ struct base_vector_holder // Automatically create a std::vector based into_type from a type_conversion -template -class conversion_into_type > +template +class conversion_into_type , X> : private base_vector_holder, public into_type::base_type> > { @@ -295,8 +295,8 @@ class conversion_into_type > // Automatically create a std::vector based use_type from a type_conversion -template -class conversion_use_type > +template +class conversion_use_type, X > : private base_vector_holder, public use_type::base_type> > { @@ -408,13 +408,19 @@ class conversion_use_type > template into_type_ptr do_into(T & t, user_type_tag) { - return into_type_ptr(new conversion_into_type(t)); + return into_type_ptr(new conversion_into_type::base_type>(t)); +} + +template +into_type_ptr do_into(std::vector& t, user_type_tag) +{ + return into_type_ptr(new conversion_into_type, typename type_conversion::base_type>(t)); } template into_type_ptr do_into(T & t, indicator & ind, user_type_tag) { - return into_type_ptr(new conversion_into_type(t, ind)); + return into_type_ptr(new conversion_into_type::base_type>(t, ind)); } template @@ -422,14 +428,14 @@ into_type_ptr do_into(std::vector & t, std::size_t begin, size_t * end, user_type_tag) { return into_type_ptr( - new conversion_into_type >(t, begin, end)); + new conversion_into_type , typename type_conversion::base_type>(t, begin, end)); } template into_type_ptr do_into(std::vector & t, std::vector & ind, user_type_tag) { - return into_type_ptr(new conversion_into_type >(t, ind)); + return into_type_ptr(new conversion_into_type , typename type_conversion::base_type>(t, ind)); } template @@ -437,33 +443,33 @@ into_type_ptr do_into(std::vector & t, std::vector & ind, std::size_t begin, size_t * end, user_type_tag) { return into_type_ptr( - new conversion_into_type >(t, ind, begin, end)); + new conversion_into_type , typename type_conversion::base_type>(t, ind, begin, end)); } template use_type_ptr do_use(T & t, std::string const & name, user_type_tag) { - return use_type_ptr(new conversion_use_type(t, name)); + return use_type_ptr(new conversion_use_type::base_type>(t, name)); } template use_type_ptr do_use(T const & t, std::string const & name, user_type_tag) { - return use_type_ptr(new conversion_use_type(t, name)); + return use_type_ptr(new conversion_use_type::base_type>(t, name)); } template use_type_ptr do_use(T & t, indicator & ind, std::string const & name, user_type_tag) { - return use_type_ptr(new conversion_use_type(t, ind, name)); + return use_type_ptr(new conversion_use_type::base_type>(t, ind, name)); } template use_type_ptr do_use(T const & t, indicator & ind, std::string const & name, user_type_tag) { - return use_type_ptr(new conversion_use_type(t, ind, name)); + return use_type_ptr(new conversion_use_type::base_type>(t, ind, name)); } template @@ -472,7 +478,7 @@ use_type_ptr do_use(std::vector & t, std::string const & name, user_type_tag) { return use_type_ptr( - new conversion_use_type >(t, begin, end, name)); + new conversion_use_type , typename type_conversion::base_type>(t, begin, end, name)); } template @@ -481,7 +487,7 @@ use_type_ptr do_use(const std::vector & t, std::string const & name, user_type_tag) { return use_type_ptr( - new conversion_use_type >(t, begin, end, name)); + new conversion_use_type , typename type_conversion::base_type>(t, begin, end, name)); } template @@ -490,7 +496,7 @@ use_type_ptr do_use(std::vector & t, std::vector & ind, std::string const & name, user_type_tag) { return use_type_ptr( - new conversion_use_type >(t, ind, begin, end, name)); + new conversion_use_type , typename type_conversion::base_type>(t, ind, begin, end, name)); } template @@ -499,7 +505,7 @@ use_type_ptr do_use(const std::vector & t, std::vector & ind, std::string const & name, user_type_tag) { return use_type_ptr( - new conversion_use_type >(t, ind, begin, end, name)); + new conversion_use_type , typename type_conversion::base_type>(t, ind, begin, end, name)); } } // namespace details diff --git a/include/soci/type-holder.h b/include/soci/type-holder.h index f103edb58..07afea8e4 100644 --- a/include/soci/type-holder.h +++ b/include/soci/type-holder.h @@ -12,6 +12,7 @@ // std #include #include +#include namespace soci { @@ -19,6 +20,21 @@ namespace soci namespace details { +// this class is for pass test [commontests.h, test12()], +// otherwise the test will failed with undefined runtime error. +// default:: any type convert is not allowed +template +class convertible { +public: + enum { ALLOW = 0 }; +}; + +template +class convertible { +public: + enum { ALLOW = 1 }; +}; + // Returns U* as T*, if the dynamic type of the pointer is really T. // // This should be used instead of dynamic_cast<> because using it doesn't work @@ -47,21 +63,47 @@ T* checked_ptr_cast(U* ptr) // Base class holder + derived class type_holder for storing type data // instances in a container of holder objects template -class type_holder; +class vector_type_holder; -class holder +class vector_holder { public: - holder() {} - virtual ~holder() {} + vector_holder() {} + virtual ~vector_holder() { } template - T get() + T get(std::size_t pos) { - type_holder* p = checked_ptr_cast >(this); - if (p) + // statement_impl::bind_into have those types:: + // std::string, double, int, long long, unsigned long long, std::tm + + // typeid() + static_cast is faster than dynamic_cast + + const std::type_info& ti = this->type(); + + if (ti == typeid(int)) + { + return cast(pos); + } + else if (ti == typeid(double)) + { + return cast(pos); + } + else if (ti == typeid(std::string)) + { + return cast(pos); + } + else if (ti == typeid(long long)) + { + return cast(pos); + } + else if (ti == typeid(unsigned long long)) { - return p->template value(); + return cast(pos); + } + else if (ti == typeid(std::tm)) + { + return cast(pos); } else { @@ -69,26 +111,90 @@ class holder } } + virtual const std::type_info& type() const = 0; + private: + template + class ret + { + public: + static T return_value(const Holder_T& h) + { + return (T)h; + } + }; - template - T value(); + template + class ret { + public: + static T return_value(const Holder_T&) + { + throw std::bad_cast(); + } + }; + + template + T cast(std::size_t pos) + { + vector_type_holder* p = static_cast*>(this); + Holder_T& h = p->template value(pos); + return ret::ALLOW>::return_value(h); + } + + template + T& value(std::size_t pos) const; }; template -class type_holder : public holder +class vector_type_holder : public vector_holder { public: - type_holder(T * t) : t_(t) {} - ~type_holder() override { delete t_; } + vector_type_holder(std::vector* vec) : vec_(vec) {} + ~vector_type_holder() override { delete vec_; } template - TypeValue value() const { return *t_; } + TypeValue& value(std::size_t pos) const { return (*vec_)[pos]; } + + const std::type_info& type() const override { return typeid(T); } private: - T * t_; + std::vector* vec_; }; +// partial convertible class +#define CONVERTIBLE(T, R, yes_no) \ + template <> \ + class convertible \ + { \ + public: \ + enum \ + { \ + ALLOW = yes_no \ + }; \ + } + +#define INTEGRAL_CONVERTIBLE(T) \ + CONVERTIBLE(T, bool, 1); \ + CONVERTIBLE(T, char, 1); \ + CONVERTIBLE(T, unsigned char, 1); \ + CONVERTIBLE(T, short, 1); \ + CONVERTIBLE(T, unsigned short, 1); \ + CONVERTIBLE(T, int, 1); \ + CONVERTIBLE(T, unsigned int, 1); \ + CONVERTIBLE(T, long, 1); \ + CONVERTIBLE(T, unsigned long, 1); \ + CONVERTIBLE(T, long long, 1); \ + CONVERTIBLE(T, unsigned long long, 1); \ + CONVERTIBLE(T, float, 1); \ + CONVERTIBLE(T, double, 1); \ + CONVERTIBLE(T, long double, 1) + +// int, double, long long, unsigned long long +INTEGRAL_CONVERTIBLE(int); +INTEGRAL_CONVERTIBLE(double); +INTEGRAL_CONVERTIBLE(long long); +INTEGRAL_CONVERTIBLE(unsigned long long); + } // namespace details } // namespace soci diff --git a/include/soci/values-exchange.h b/include/soci/values-exchange.h index eae91e604..e9de55287 100644 --- a/include/soci/values-exchange.h +++ b/include/soci/values-exchange.h @@ -116,13 +116,17 @@ template <> class into_type : public into_type { public: - into_type(values & v) + into_type(values & v, std::size_t bulk_size = 1) : into_type(v.get_row()), v_(v) - {} + { + v.row_->bulk_size(bulk_size); + } - into_type(values & v, indicator & ind) + into_type(values & v, indicator & ind, std::size_t bulk_size = 1) : into_type(v.get_row(), ind), v_(v) - {} + { + v.row_->bulk_size(bulk_size); + } void clean_up() override { @@ -143,6 +147,67 @@ class into_type > into_type(); }; +//this is for support bulk ORM +template +class conversion_into_type, values > + :private base_value_holder, //val_ is values + public into_type< values > +{ +public: + typedef values base_type; + + conversion_into_type(std::vector & value) + : into_type(base_value_holder::val_, value.size()) + , value_(value) + // , ind_(ownInd_) + {} + + //ind is not used + conversion_into_type(std::vector & value, std::vector &) + : into_type(base_value_holder::val_, value.size()) + , value_(value) + {} + + virtual std::size_t size() const + { + // the user might have resized his vector in the meantime + // -> synchronize the base-value mirror to have the same size + + return value_.size(); + } + + virtual void resize(std::size_t sz) + { + value_.resize(sz); + //ind_.resize(sz); + } + +private: + void convert_from_base() + { + values& v = base_value_holder::val_; + std::size_t data_size = value_.size(); + + for (std::size_t i=0; i::from_base(v, i_ok, value_[i]); + + v.row_->next(); + } + } + + std::vector & value_; + + //do we need indicators for ORM? ... currently I don't know how to do this, so I just comment it ... + + //std::vector ownInd_; + + // ind_ refers to either ownInd_, or the one provided by the user + // in any case, ind_ refers to some valid vector of indicators + // and can be used by conversion routines + //std::vector & ind_; +}; + } // namespace details } // namespace soci diff --git a/include/soci/values.h b/include/soci/values.h index 8d782ad21..cc3c27081 100644 --- a/include/soci/values.h +++ b/include/soci/values.h @@ -47,6 +47,9 @@ class SOCI_DECL values friend class details::into_type; friend class details::use_type; + template + friend class details::conversion_into_type; + public: values() : row_(NULL), currentPos_(0), uppercaseColumnNames_(false) {} @@ -243,7 +246,7 @@ class SOCI_DECL values std::vector uses_; std::map unused_; std::vector indicators_; - std::map index_; + std::map index_; std::vector deepCopies_; mutable std::size_t currentPos_; diff --git a/src/backends/oracle/statement.cpp b/src/backends/oracle/statement.cpp index b0a4815cf..27eb1e3d5 100644 --- a/src/backends/oracle/statement.cpp +++ b/src/backends/oracle/statement.cpp @@ -298,6 +298,11 @@ void oracle_statement_backend::describe_column(int colNum, data_type &type, { type = dt_double; } + } + //create table T(t number)--> select t+0 from T; we get: scale=precision=0, size=22 + else if (dbscale == 0 && dbprec == 0 && dbsize == 22) + { + type = dt_double; } else if (dbprec <= std::numeric_limits::digits10) { diff --git a/src/backends/sqlite3/statement.cpp b/src/backends/sqlite3/statement.cpp index b97c7e694..e96989974 100644 --- a/src/backends/sqlite3/statement.cpp +++ b/src/backends/sqlite3/statement.cpp @@ -19,12 +19,6 @@ #pragma warning(disable:4355) #endif -// This is used instead of tolower() just to avoid warnings about int to char -// casts inside MSVS std::transform() implementation. -char toLowerCh(char c) { - return static_cast( std::tolower(c) ); -} - using namespace soci; using namespace soci::details; using namespace sqlite_api; @@ -230,6 +224,9 @@ sqlite3_statement_backend::load_one() } else if (SQLITE_ROW == res) { + // get_number_of_rows() need return 1. since we do not fetch data, + // sqlite3_vector_into_type_backend::post_fetch() need process this case + dataCache_.resize(1); } else { @@ -353,11 +350,7 @@ sqlite3_statement_backend::execute(int number) statement_backend::exec_fetch_result sqlite3_statement_backend::fetch(int number) { - if (hasVectorIntoElements_ || number == 0) - return load_rowset(number); - else - return load_one(); - + return (1 == number) ? load_one() : load_rowset(number); } long long sqlite3_statement_backend::get_affected_rows() @@ -408,8 +401,7 @@ int sqlite3_statement_backend::prepare_for_describe() return sqlite3_column_count(stmt_); } -typedef std::map sqlite3_data_type_map; -static sqlite3_data_type_map get_data_type_map() +sqlite3_data_type_map soci::get_data_type_map() { sqlite3_data_type_map m; @@ -505,7 +497,7 @@ void sqlite3_statement_backend::describe_column(int colNum, data_type & type, dt.resize(siter - dt.begin()); // do all comparisons in lower case - std::transform(dt.begin(), dt.end(), dt.begin(), toLowerCh); + std::transform(dt.begin(), dt.end(), dt.begin(), [](const char c){ return static_cast(std::tolower(c)); }); sqlite3_data_type_map::const_iterator iter = dataTypeMap.find(dt); if (iter != dataTypeMap.end()) diff --git a/src/backends/sqlite3/vector-into-type.cpp b/src/backends/sqlite3/vector-into-type.cpp index 8feb99d00..58652aecc 100644 --- a/src/backends/sqlite3/vector-into-type.cpp +++ b/src/backends/sqlite3/vector-into-type.cpp @@ -13,14 +13,13 @@ #include "soci-cstrtoi.h" #include "soci-dtocstr.h" #include "soci-exchange-cast.h" -#include "soci/blob.h" -#include "soci/rowid.h" #include "soci/soci-platform.h" #include "soci/sqlite3/soci-sqlite3.h" #include "soci-cstrtod.h" #include "soci-mktime.h" #include "common.h" // std +#include #include #include #include @@ -118,6 +117,44 @@ void set_number_in_vector(void *p, int idx, const sqlite3_column &col) } // namespace anonymous +sqlite3_column sqlite3_vector_into_type_backend::get_column(int colNum) +{ + sqlite3_column col; + { + data_type type; + std::string columnName; + statement_.describe_column(position_, type, columnName); + col.isNull_ = (sqlite3_column_type(statement_.stmt_, colNum) == SQLITE_NULL); + col.type_ = type; + } + if (col.isNull_) + return col; + switch (col.type_) + { + case dt_date: + case dt_string: + case dt_blob: + case dt_xml: + col.buffer_.size_ = sqlite3_column_bytes(statement_.stmt_, colNum); + col.buffer_.data_ = new char[col.buffer_.size_ + 1]; + memcpy(col.buffer_.data_, sqlite3_column_text(statement_.stmt_, colNum), col.buffer_.size_ + 1); + break; + + case dt_double: + col.double_ = details::cstring_to_double(reinterpret_cast(sqlite3_column_text(statement_.stmt_, colNum))); + break; + + case dt_integer: + case dt_long_long: + case dt_unsigned_long_long: + { + details::cstring_to_integer(col.int64_, reinterpret_cast(sqlite3_column_text(statement_.stmt_, colNum))); + break; + } + } + return col; +} + void sqlite3_vector_into_type_backend::post_fetch(bool gotData, indicator * ind) { using namespace details; @@ -132,8 +169,16 @@ void sqlite3_vector_into_type_backend::post_fetch(bool gotData, indicator * ind) int const endRow = static_cast(statement_.dataCache_.size()); for (int i = 0; i < endRow; ++i) { - sqlite3_column &col = statement_.dataCache_[i][position_-1]; - + std::shared_ptr col1; + if (1 == endRow) + { + col1 = std::make_unique(get_column(position_ - 1)); + } + else + { + col1 = std::make_unique(statement_.dataCache_[i][position_ - 1]); + } + sqlite3_column& col = *col1; if (col.isNull_) { if (ind == NULL) diff --git a/src/core/row.cpp b/src/core/row.cpp index 2612e6009..e3f1a2b03 100644 --- a/src/core/row.cpp +++ b/src/core/row.cpp @@ -16,9 +16,11 @@ using namespace soci; using namespace details; -row::row() +row::row(std::size_t bulk_size) : uppercaseColumnNames_(false) , currentPos_(0) + , bulk_pos_(0) + , bulk_size_(bulk_size) {} row::~row() @@ -79,7 +81,7 @@ void row::clean_up() indicator row::get_indicator(std::size_t pos) const { - return *indicators_.at(pos); + return (*indicators_.at(pos))[bulk_pos_]; } indicator row::get_indicator(std::string const &name) const diff --git a/src/core/statement.cpp b/src/core/statement.cpp index 49346af0c..3d83c8c34 100644 --- a/src/core/statement.cpp +++ b/src/core/statement.cpp @@ -520,6 +520,13 @@ bool statement_impl::resize_intos(std::size_t upperBound) intos_[i]->resize((std::size_t)rows); } + //for bulk ORM operations + std::size_t const ifrsize = intosForRow_.size(); + for (std::size_t i = 0; i != ifrsize; ++i) + { + intosForRow_[i]->resize((std::size_t)rows); + } + return rows > 0 ? true : false; } diff --git a/tests/oracle/test-oracle.cpp b/tests/oracle/test-oracle.cpp index fcb952e53..9a8095d78 100644 --- a/tests/oracle/test-oracle.cpp +++ b/tests/oracle/test-oracle.cpp @@ -1524,6 +1524,87 @@ class test_context :public test_context_base } }; +void test_orm_bulk() +{ + //session sql(backEndFactory_, connectString_); + //auto_table_creator tableCreator(tc_.table_creator_3(sql)); + session sql(backEnd, connectString); + table_creator_three one(sql); + + sql << "insert into soci_test values('john', '(404)123-4567')"; + sql << "insert into soci_test values('doe', '(404)123-4567')"; + + std::vector vec_orm(10); + sql << "select * from soci_test", into(vec_orm); + + assert(vec_orm.size() == 2); + + for (std::vector::iterator it=vec_orm.begin(); + it != vec_orm.end(); + ++it + ) + { + if (it->name == "john") + { + assert(it->phone == "(404)123-4567"); + } + else if (it->name == "doe") + { + assert(it->phone == "(404)123-4567"); + } + } + + std::cout << "test ORM bulk select passed" << std::endl; +} + +void test_convert() +{ + //session sql(backEndFactory_, connectString_); + //auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + session sql(backEnd, connectString); + table_creator_one one(sql); + + sql << "insert into soci_test(id, sh) values(1, 10.5)"; + + double d1, d2; + sql << "select sh+0, sh from soci_test", into(d1), into(d2); + assert(are_doubles_approx_equal(d1, d2)); + assert(are_doubles_approx_equal(d1, 10.5)); + + soci::rowset rs1 = ( + sql.prepare << "select sh+0.6 sh_0, sh from soci_test"); + + soci::rowset::const_iterator it1 = rs1.begin(); + soci::row const & row1 = *it1; + + { + int i; + i = row1.get(0); + assert(i == 11); + i = row1.get(1); + assert(i == 10); + } + + { + double i; + i = row1.get(0); + assert(are_doubles_approx_equal(i, 11.1)); + i = row1.get(1); + assert(are_doubles_approx_equal(i, 10.5)); + } + + { + float i; + i = row1.get(0); + assert(are_doubles_approx_equal(i, 11.1f)); + i = row1.get(1); + assert(are_doubles_approx_equal(i, 10.5f)); + } + + std::cout << "test convert passed" << std::endl; +} + int main(int argc, char** argv) { #ifdef _MSC_VER diff --git a/tests/sqlite3/test-sqlite3.cpp b/tests/sqlite3/test-sqlite3.cpp index dcd34dc10..f7baf6442 100644 --- a/tests/sqlite3/test-sqlite3.cpp +++ b/tests/sqlite3/test-sqlite3.cpp @@ -397,6 +397,32 @@ struct longlong_table_creator : table_creator_base } }; +TEST_CASE("SQLite bulk test", "[sqlite][bulk]") +{ + // we need to have an table that uses autoincrement to test this. + session sql(backEnd, connectString); + + test4_table_creator tableCreator(sql); + + { + std::vector vec_name { "John", "James" }; + sql << "insert into soci_test(name) values(:name)", use(vec_name); + assert(vec_name.size() == 2); + } + + { + std::vector vec_name(2); + sql << "select name from soci_test", into(vec_name); + assert(vec_name.size() == 2); + } + + { + std::vector vec_name(1); + sql << "select name from soci_test", into(vec_name); + assert(vec_name.size() == 1); + } +} + // long long test TEST_CASE("SQLite long long", "[sqlite][longlong]") { @@ -676,6 +702,36 @@ class test_context : public test_context_base } }; +TEST_CASE("SQLite bulk ORM test", "[sqlite][orm][bulk]") +{ + // session sql(backEndFactory_, connectString_); + // auto_table_creator tableCreator(tc_.table_creator_3(sql)); + session sql(backEnd, connectString); + table_creator_three one(sql); + + sql << "insert into soci_test values('john', '(404)123-4567')"; + sql << "insert into soci_test values('doe', '(404)123-4567')"; + + std::vector vec_orm(10); + sql << "select * from soci_test", into(vec_orm); + + assert(vec_orm.size() == 2); + + for (std::vector::iterator it = vec_orm.begin(); + it != vec_orm.end(); + ++it) + { + if (it->name == "john") + { + assert(it->phone == "(404)123-4567"); + } + else if (it->name == "doe") + { + assert(it->phone == "(404)123-4567"); + } + } +} + int main(int argc, char** argv) {