diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2014-08-14 09:37:06 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2014-11-25 06:39:57 +0200 |
commit | 8d4d2568f356cb9beb1553bf58ad69c1c800b996 (patch) | |
tree | d6466f2dec931669405b38a6cf47e8bb0998a4fd | |
parent | 46f28dcdf5a72ed430486b562213ad147b65526a (diff) |
Implement bulk database operation support for Oracle and SQL Server
-rw-r--r-- | odb/database.hxx | 56 | ||||
-rw-r--r-- | odb/database.ixx | 118 | ||||
-rw-r--r-- | odb/database.txx | 320 | ||||
-rw-r--r-- | odb/details/posix/exceptions.cxx | 6 | ||||
-rw-r--r-- | odb/details/posix/exceptions.hxx | 3 | ||||
-rw-r--r-- | odb/details/shared-ptr.hxx | 1 | ||||
-rw-r--r-- | odb/details/shared-ptr/base.cxx | 21 | ||||
-rw-r--r-- | odb/details/shared-ptr/base.hxx | 10 | ||||
-rw-r--r-- | odb/details/shared-ptr/base.ixx | 6 | ||||
-rw-r--r-- | odb/details/shared-ptr/base.txx | 26 | ||||
-rw-r--r-- | odb/details/shared-ptr/exception.hxx | 30 | ||||
-rw-r--r-- | odb/details/win32/exceptions.cxx | 6 | ||||
-rw-r--r-- | odb/details/win32/exceptions.hxx | 3 | ||||
-rw-r--r-- | odb/exception.hxx | 7 | ||||
-rw-r--r-- | odb/exceptions.cxx | 236 | ||||
-rw-r--r-- | odb/exceptions.hxx | 287 | ||||
-rw-r--r-- | odb/forward.hxx | 4 |
17 files changed, 1103 insertions, 37 deletions
diff --git a/odb/database.hxx b/odb/database.hxx index 1f56b7f..0de867f 100644 --- a/odb/database.hxx +++ b/odb/database.hxx @@ -33,6 +33,7 @@ #include <odb/details/mutex.hxx> #include <odb/details/c-string.hxx> #include <odb/details/function-wrapper.hxx> +#include <odb/details/meta/answer.hxx> namespace odb { @@ -55,6 +56,10 @@ namespace odb template <typename T> typename object_traits<T>::id_type + persist (const T& object); + + template <typename T> + typename object_traits<T>::id_type persist (T* obj_ptr); template <typename T, template <typename> class P> @@ -77,6 +82,13 @@ namespace odb typename object_traits<T>::id_type persist (const typename object_traits<T>::pointer_type& obj_ptr); + // Bulk persist. Can be a range of references or pointers (including + // smart pointers) to objects. + // + template <typename I> + void + persist (I begin, I end, bool continue_failed = true); + // Load an object. Throw object_not_persistent if not found. // template <typename T> @@ -163,6 +175,13 @@ namespace odb void update (const typename object_traits<T>::pointer_type& obj_ptr); + // Bulk update. Can be a range of references or pointers (including + // smart pointers) to objects. + // + template <typename I> + void + update (I begin, I end, bool continue_failed = true); + // Update a section of an object. Throws section_not_loaded exception // if section is not loaded. Note also that this function does not // clear the changed flag if it is set. @@ -206,6 +225,19 @@ namespace odb void erase (const typename object_traits<T>::pointer_type& obj_ptr); + // Bulk erase. + // + template <typename T, typename I> + void + erase (I id_begin, I id_end, bool continue_failed = true); + + // Can be a range of references or pointers (including smart pointers) + // to objects. + // + template <typename I> + void + erase (I obj_begin, I obj_end, bool continue_failed = true); + // Erase multiple objects matching a query predicate. // template <typename T> @@ -489,6 +521,18 @@ namespace odb typename object_traits<T>::id_type persist_ (const typename object_traits<T>::pointer_type&); + template <typename I, database_id DB> + void + persist_ (I, I, bool); + + template <typename I, typename T, database_id DB> + void + persist_ (I, I, bool, details::meta::no ptr); + + template <typename I, typename T, database_id DB> + void + persist_ (I, I, bool, details::meta::yes ptr); + template <typename T, database_id DB> typename object_traits<T>::pointer_type load_ (const typename object_traits<T>::id_type&); @@ -521,6 +565,10 @@ namespace odb void update_ (const typename object_traits<T>::pointer_type&); + template <typename I, database_id DB> + void + update_ (I, I, bool); + template <typename T, database_id DB> void update_ (const T&, const section&); @@ -537,6 +585,14 @@ namespace odb void erase_ (const typename object_traits<T>::pointer_type&); + template <typename I, typename T, database_id DB> + void + erase_id_ (I, I, bool); + + template <typename I, database_id DB> + void + erase_object_ (I, I, bool); + template <typename T, database_id DB, typename Q> typename object_traits<T>::pointer_type query_one_ (const Q&); diff --git a/odb/database.ixx b/odb/database.ixx index 739db03..43dae6a 100644 --- a/odb/database.ixx +++ b/odb/database.ixx @@ -3,11 +3,74 @@ // license : GNU GPL v2; see accompanying LICENSE file #include <cstring> // std::strlen() +#include <utility> // std::move +#include <iterator> #include <odb/transaction.hxx> +#include <odb/pointer-traits.hxx> namespace odb { + template <typename T> + struct object_pointer_traits + { + typedef details::meta::no result_type; + typedef T object_type; + static const T& get_ref (const T& x) {return x;} + }; + + template <typename T> + struct object_pointer_traits<T*> + { + typedef details::meta::yes result_type; + typedef T object_type; + static const T& get_ref (const T* p) {return *p;} + }; + + template <typename T> + struct object_pointer_traits<T* const> + { + typedef details::meta::yes result_type; + typedef T object_type; + static const T& get_ref (const T* p) {return *p;} + }; + + template <typename T, template <typename> class P> + struct object_pointer_traits<P<T> > + { + typedef details::meta::yes result_type; + typedef T object_type; + static const T& get_ref (const P<T>& p) { + return pointer_traits<P<T> >::get_ref (p);} + }; + + template <typename T, typename A1, template <typename, typename> class P> + struct object_pointer_traits<P<T, A1> > + { + typedef details::meta::yes result_type; + typedef T object_type; + static const T& get_ref (const P<T, A1>& p) { + return pointer_traits<P<T, A1> >::get_ref (p);} + }; + + template <typename T, template <typename> class P> + struct object_pointer_traits<const P<T> > + { + typedef details::meta::yes result_type; + typedef T object_type; + static const T& get_ref (const P<T>& p) { + return pointer_traits<P<T> >::get_ref (p);} + }; + + template <typename T, typename A1, template <typename, typename> class P> + struct object_pointer_traits<const P<T, A1> > + { + typedef details::meta::yes result_type; + typedef T object_type; + static const T& get_ref (const P<T, A1>& p) { + return pointer_traits<P<T, A1> >::get_ref (p);} + }; + inline database:: database (database_id id) : id_ (id), tracer_ (0), schema_version_seq_ (1) @@ -99,6 +162,13 @@ namespace odb template <typename T> inline typename object_traits<T>::id_type database:: + persist (const T& obj) + { + return persist_<const T, id_common> (obj); + } + + template <typename T> + inline typename object_traits<T>::id_type database:: persist (T* p) { typedef typename object_traits<T>::pointer_type object_pointer; @@ -165,6 +235,13 @@ namespace odb return persist_<T, id_common> (pobj); } + template <typename I> + inline void database:: + persist (I b, I e, bool cont) + { + persist_<I, id_common> (b, e, cont); + } + template <typename T> inline typename object_traits<T>::pointer_type database:: load (const typename object_traits<T>::id_type& id) @@ -326,6 +403,13 @@ namespace odb update_<T, id_common> (pobj); } + template <typename I> + inline void database:: + update (I b, I e, bool cont) + { + update_<I, id_common> (b, e, cont); + } + template <typename T> inline void database:: update (const T& obj, const section& s) @@ -415,6 +499,20 @@ namespace odb erase_<T, id_common> (pobj); } + template <typename T, typename I> + inline void database:: + erase (I idb, I ide, bool cont) + { + erase_id_<I, T, id_common> (idb, ide, cont); + } + + template <typename I> + inline void database:: + erase (I ob, I oe, bool cont) + { + erase_object_<I, id_common> (ob, oe, cont); + } + template <typename T> inline unsigned long long database:: erase_query () @@ -622,6 +720,26 @@ namespace odb // Implementations (i.e., the *_() functions). // + template <typename I, database_id DB> + inline void database:: + persist_ (I b, I e, bool cont) + { + // Sun CC with non-standard STL does not have iterator_traits. + // +#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC + typedef typename std::iterator_traits<I>::value_type value_type; +#else + // Assume iterator is just a pointer. + // + typedef typename object_pointer_traits<I>::object_type value_type; +#endif + + typedef object_pointer_traits<value_type> opt; + + persist_<I, typename opt::object_type, id_common> ( + b, e, cont, typename opt::result_type ()); + } + template <typename T, database_id DB> inline typename object_traits<T>::pointer_type database:: find_ (const typename object_traits<T>::id_type& id) diff --git a/odb/database.txx b/odb/database.txx index 5d4829a..a46b8b0 100644 --- a/odb/database.txx +++ b/odb/database.txx @@ -71,6 +71,163 @@ namespace odb return object_traits::id (obj); } + template <typename T, bool = object_traits<T>::auto_id> struct persist_type; + template <typename T> struct persist_type<T, true> {typedef T type;}; + template <typename T> struct persist_type<T, false> {typedef const T type;}; + + template <typename I, typename T, database_id DB> + void database:: + persist_ (I b, I e, bool cont, details::meta::no /*ptr*/) + { + // T can be const T while object_type will always be T. + // + typedef typename object_traits<T>::object_type object_type; + typedef object_traits_impl<object_type, DB> object_traits; + + multiple_exceptions mex (typeid (object_already_persistent)); + try + { + while (b != e && (cont || mex.empty ())) + { + std::size_t n (0); + T* a[object_traits::batch]; // T instead of persist_type for cache. + + for (; b != e && n < object_traits::batch; ++n) + { + // Compiler error pointing here? Perhaps the passed range is + // of const objects? + // + typename persist_type<object_type>::type* p (&(*b++)); + + a[n] = const_cast<T*> (p); + } + + // Compiler error pointing here? Perhaps the object or the + // database does not support bulk operations? + // + object_traits::persist ( + *this, + const_cast<typename persist_type<object_type>::type**> (a), + n, + mex); + + if (mex.fatal ()) + break; + + for (std::size_t i (0); i < n; ++i) + { + if (mex[i] != 0) // Don't cache objects that have failed. + continue; + + mex.current (i); // Set position in case the below code throws. + + typename object_traits::reference_cache_traits::position_type p ( + object_traits::reference_cache_traits::insert ( + *this, reference_cache_type<T>::convert (*a[i]))); + + object_traits::reference_cache_traits::persist (p); + } + + mex.delta (n); + } + } + catch (const odb::exception& ex) + { + mex.insert (ex, true); + } + + if (!mex.empty ()) + { + mex.prepare (); + throw mex; + } + } + + template <typename P> + struct pointer_copy + { + const P* ref; + P copy; + + void assign (const P& p) {ref = &p;} + template <typename P1> void assign (const P1& p1) + { + // The passed pointer should be the same or implicit-convertible + // to the object pointer. This way we make sure the object pointer + // does not assume ownership of the passed object. + // + const P& p (p1); + + copy = p; + ref = © + } + }; + + template <typename I, typename T, database_id DB> + void database:: + persist_ (I b, I e, bool cont, details::meta::yes /*ptr*/) + { + // T can be const T while object_type will always be T. + // + typedef typename object_traits<T>::object_type object_type; + typedef typename object_traits<T>::pointer_type pointer_type; + + typedef object_traits_impl<object_type, DB> object_traits; + + multiple_exceptions mex (typeid (object_already_persistent)); + try + { + while (b != e && (cont || mex.empty ())) + { + std::size_t n (0); + typename persist_type<object_type>::type* a[object_traits::batch]; + pointer_copy<pointer_type> p[object_traits::batch]; + + for (; b != e && n < object_traits::batch; ++n) + { + p[n].assign (*b++); + a[n] = &pointer_traits<pointer_type>::get_ref (*p[n].ref); + } + + // Compiler error pointing here? Perhaps the object or the + // database does not support bulk operations? + // + object_traits::persist (*this, a, n, mex); + + if (mex.fatal ()) + break; + + for (std::size_t i (0); i < n; ++i) + { + if (mex[i] != 0) // Don't cache objects that have failed. + continue; + + mex.current (i); // Set position in case the below code throws. + + // Get the canonical object pointer and insert it into object cache. + // + typename object_traits::pointer_cache_traits::position_type pos ( + object_traits::pointer_cache_traits::insert ( + *this, pointer_cache_type<pointer_type>::convert (*p[i].ref))); + + object_traits::pointer_cache_traits::persist (pos); + } + + mex.delta (n); + } + } + catch (const odb::exception& ex) + { + mex.insert (ex, true); + } + + if (!mex.empty ()) + { + mex.prepare (); + throw mex; + } + } + template <typename T, database_id DB> typename object_traits<T>::pointer_type database:: load_ (const typename object_traits<T>::id_type& id) @@ -120,6 +277,64 @@ namespace odb throw object_not_persistent (); } + template <typename I, database_id DB> + void database:: + update_ (I b, I e, bool cont) + { + // Sun CC with non-standard STL does not have iterator_traits. + // +#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC + typedef typename std::iterator_traits<I>::value_type value_type; +#else + // Assume iterator is just a pointer. + // + typedef typename object_pointer_traits<I>::object_type value_type; +#endif + + // object_pointer_traits<T>::object_type can be const. + // + typedef object_pointer_traits<value_type> opt; + + typedef + typename object_traits<typename opt::object_type>::object_type + object_type; + + typedef object_traits_impl<object_type, DB> object_traits; + + multiple_exceptions mex (typeid (object_not_persistent)); + try + { + while (b != e && (cont || mex.empty ())) + { + std::size_t n (0); + const object_type* a[object_traits::batch]; + + for (; b != e && n < object_traits::batch; ++n) + a[n] = &opt::get_ref (*b++); + + // Compiler error pointing here? Perhaps the object or the + // database does not support bulk operations? + // + object_traits::update (*this, a, n, mex); + + if (mex.fatal ()) + break; + + mex.delta (n); + } + } + catch (const odb::exception& ex) + { + mex.insert (ex, true); + } + + if (!mex.empty ()) + { + mex.prepare (); + throw mex; + } + } + template <typename T, database_id DB> void database:: update_ (const T& obj, const section& s) @@ -140,6 +355,111 @@ namespace odb throw section_not_in_object (); } + template <typename I, typename T, database_id DB> + void database:: + erase_id_ (I b, I e, bool cont) + { + // T is explicitly specified by the caller, so assume it is object type. + // + typedef T object_type; + typedef object_traits_impl<object_type, DB> object_traits; + typedef typename object_traits::id_type id_type; + + multiple_exceptions mex (typeid (object_not_persistent)); + try + { + while (b != e && (cont || mex.empty ())) + { + std::size_t n (0); + const id_type* a[object_traits::batch]; + + for (; b != e && n < object_traits::batch; ++n) + // Compiler error pointing here? Perhaps the object id type + // and the range element type don't match? + // + a[n] = &(*b++); + + // Compiler error pointing here? Perhaps the object or the + // database does not support bulk operations? + // + object_traits::erase (*this, a, n, mex); + + if (mex.fatal ()) + break; + + mex.delta (n); + } + } + catch (const odb::exception& ex) + { + mex.insert (ex, true); + } + + if (!mex.empty ()) + { + mex.prepare (); + throw mex; + } + } + + template <typename I, database_id DB> + void database:: + erase_object_ (I b, I e, bool cont) + { + // Sun CC with non-standard STL does not have iterator_traits. + // +#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC + typedef typename std::iterator_traits<I>::value_type value_type; +#else + // Assume iterator is just a pointer. + // + typedef typename object_pointer_traits<I>::object_type value_type; +#endif + + // object_pointer_traits<T>::object_type can be const. + // + typedef object_pointer_traits<value_type> opt; + + typedef + typename object_traits<typename opt::object_type>::object_type + object_type; + + typedef object_traits_impl<object_type, DB> object_traits; + + multiple_exceptions mex (typeid (object_not_persistent)); + try + { + while (b != e && (cont || mex.empty ())) + { + std::size_t n (0); + const object_type* a[object_traits::batch]; + + for (; b != e && n < object_traits::batch; ++n) + a[n] = &opt::get_ref (*b++); + + // Compiler error pointing here? Perhaps the object or the + // database does not support bulk operations? + // + object_traits::erase (*this, a, n, mex); + + if (mex.fatal ()) + break; + + mex.delta (n); + } + } + catch (const odb::exception& ex) + { + mex.insert (ex, true); + } + + if (!mex.empty ()) + { + mex.prepare (); + throw mex; + } + } + template <typename T, database_id DB> struct database::query_<T, DB, class_object> { diff --git a/odb/details/posix/exceptions.cxx b/odb/details/posix/exceptions.cxx index 7a50a13..b8bb702 100644 --- a/odb/details/posix/exceptions.cxx +++ b/odb/details/posix/exceptions.cxx @@ -13,5 +13,11 @@ namespace odb { return "POSIX API error"; } + + posix_exception* posix_exception:: + clone () const + { + return new posix_exception (*this); + } } } diff --git a/odb/details/posix/exceptions.hxx b/odb/details/posix/exceptions.hxx index ebea763..5ff023d 100644 --- a/odb/details/posix/exceptions.hxx +++ b/odb/details/posix/exceptions.hxx @@ -24,6 +24,9 @@ namespace odb virtual const char* what () const throw (); + virtual posix_exception* + clone () const; + private: int code_; }; diff --git a/odb/details/shared-ptr.hxx b/odb/details/shared-ptr.hxx index aecde12..9d2b62f 100644 --- a/odb/details/shared-ptr.hxx +++ b/odb/details/shared-ptr.hxx @@ -9,6 +9,7 @@ #include <odb/details/shared-ptr-fwd.hxx> #include <odb/details/shared-ptr/base.hxx> +#include <odb/details/shared-ptr/exception.hxx> namespace odb { diff --git a/odb/details/shared-ptr/base.cxx b/odb/details/shared-ptr/base.cxx index 590d844..0214c54 100644 --- a/odb/details/shared-ptr/base.cxx +++ b/odb/details/shared-ptr/base.cxx @@ -3,6 +3,7 @@ // license : GNU GPL v2; see accompanying LICENSE file #include <odb/details/shared-ptr/base.hxx> +#include <odb/details/shared-ptr/exception.hxx> using std::size_t; @@ -19,6 +20,12 @@ namespace odb return "object is not shared"; } + not_shared* not_shared:: + clone () const + { + return new not_shared (*this); + } + bool shared_base:: _dec_ref_callback () { @@ -29,6 +36,20 @@ namespace odb return r; } + + namespace bits + { + size_t* locator_common:: + counter (void* x) + { + size_t* p (static_cast<size_t*> (x)); + + if (*(--p) != 0xDEADBEEF) + throw not_shared (); + + return --p; + } + } } } diff --git a/odb/details/shared-ptr/base.hxx b/odb/details/shared-ptr/base.hxx index 6cfe1c9..b16bc8b 100644 --- a/odb/details/shared-ptr/base.hxx +++ b/odb/details/shared-ptr/base.hxx @@ -10,7 +10,6 @@ #include <new> #include <cstddef> // std::size_t -#include <odb/exception.hxx> #include <odb/details/export.hxx> #include <odb/details/shared-ptr/counter-type.hxx> @@ -45,12 +44,6 @@ namespace odb { namespace details { - struct LIBODB_EXPORT not_shared: exception - { - virtual const char* - what () const throw (); - }; - class LIBODB_EXPORT shared_base { public: @@ -69,6 +62,9 @@ namespace odb _ref_count () const; void* + operator new (std::size_t) throw (std::bad_alloc); + + void* operator new (std::size_t, share) throw (std::bad_alloc); void diff --git a/odb/details/shared-ptr/base.ixx b/odb/details/shared-ptr/base.ixx index 53105ce..c5beec6 100644 --- a/odb/details/shared-ptr/base.ixx +++ b/odb/details/shared-ptr/base.ixx @@ -64,6 +64,12 @@ namespace odb } inline void* shared_base:: + operator new (std::size_t n) throw (std::bad_alloc) + { + return ::operator new (n); + } + + inline void* shared_base:: operator new (std::size_t n, share) throw (std::bad_alloc) { return ::operator new (n); diff --git a/odb/details/shared-ptr/base.txx b/odb/details/shared-ptr/base.txx index e00bd5c..7047b7a 100644 --- a/odb/details/shared-ptr/base.txx +++ b/odb/details/shared-ptr/base.txx @@ -13,38 +13,32 @@ namespace odb { // Support for locating the counter in the memory block. // + struct LIBODB_EXPORT locator_common + { + static std::size_t* + counter (void*); + }; + template <typename X, bool poly = meta::polymorphic_p<X>::result> struct locator; template <typename X> - struct locator<X, false> + struct locator<X, false>: locator_common { static std::size_t* counter (X* x) { - std::size_t* p (reinterpret_cast<std::size_t*> (x)); - - if (*(--p) != 0xDEADBEEF) - throw not_shared (); - - return --p; + return locator_common::counter (x); } }; template <typename X> - struct locator<X, true> + struct locator<X, true>: locator_common { static std::size_t* counter (X* x) { - std::size_t* p ( - static_cast<std::size_t*> ( - dynamic_cast<void*> (x))); - - if (*(--p) != 0xDEADBEEF) - throw not_shared (); - - return --p; + return locator_common::counter (dynamic_cast<void*> (x)); } }; diff --git a/odb/details/shared-ptr/exception.hxx b/odb/details/shared-ptr/exception.hxx new file mode 100644 index 0000000..bd7ea38 --- /dev/null +++ b/odb/details/shared-ptr/exception.hxx @@ -0,0 +1,30 @@ +// file : odb/details/shared-ptr/exception.hxx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_DETAILS_SHARED_PTR_EXCEPTION_HXX +#define ODB_DETAILS_SHARED_PTR_EXCEPTION_HXX + +#include <odb/pre.hxx> + +#include <odb/exception.hxx> +#include <odb/details/export.hxx> + +namespace odb +{ + namespace details + { + struct LIBODB_EXPORT not_shared: exception + { + virtual const char* + what () const throw (); + + virtual not_shared* + clone () const; + }; + } +} + +#include <odb/post.hxx> + +#endif // ODB_DETAILS_SHARED_PTR_EXCEPTION_HXX diff --git a/odb/details/win32/exceptions.cxx b/odb/details/win32/exceptions.cxx index 75043b4..10605c2 100644 --- a/odb/details/win32/exceptions.cxx +++ b/odb/details/win32/exceptions.cxx @@ -13,5 +13,11 @@ namespace odb { return "Win32 API error"; } + + win32_exception* win32_exception:: + clone () const + { + return new win32_exception (*this); + } } } diff --git a/odb/details/win32/exceptions.hxx b/odb/details/win32/exceptions.hxx index ecc36b6..c987430 100644 --- a/odb/details/win32/exceptions.hxx +++ b/odb/details/win32/exceptions.hxx @@ -26,6 +26,9 @@ namespace odb virtual const char* what () const throw (); + virtual win32_exception* + clone () const; + private: DWORD code_; }; diff --git a/odb/exception.hxx b/odb/exception.hxx index 89b5dae..03a123d 100644 --- a/odb/exception.hxx +++ b/odb/exception.hxx @@ -10,14 +10,19 @@ #include <exception> #include <odb/forward.hxx> // odb::core + #include <odb/details/export.hxx> +#include <odb/details/shared-ptr/base.hxx> namespace odb { - struct LIBODB_EXPORT exception: std::exception + struct LIBODB_EXPORT exception: std::exception, details::shared_base { virtual const char* what () const throw () = 0; + + virtual exception* + clone () const = 0; }; namespace common diff --git a/odb/exceptions.cxx b/odb/exceptions.cxx index a5292bc..ca9e84c 100644 --- a/odb/exceptions.cxx +++ b/odb/exceptions.cxx @@ -2,7 +2,10 @@ // copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file +#include <cstring> // std::strlen #include <sstream> +#include <cassert> + #include <odb/exceptions.hxx> using namespace std; @@ -15,96 +18,192 @@ namespace odb return "NULL pointer"; } + null_pointer* null_pointer:: + clone () const + { + return new null_pointer (*this); + } + const char* already_in_transaction:: what () const throw () { return "transaction already in progress in this thread"; } + already_in_transaction* already_in_transaction:: + clone () const + { + return new already_in_transaction (*this); + } + const char* not_in_transaction:: what () const throw () { return "operation can only be performed in transaction"; } + not_in_transaction* not_in_transaction:: + clone () const + { + return new not_in_transaction (*this); + } + const char* transaction_already_finalized:: what () const throw () { return "transaction already committed or rolled back"; } + transaction_already_finalized* transaction_already_finalized:: + clone () const + { + return new transaction_already_finalized (*this); + } + const char* already_in_session:: what () const throw () { return "session already in effect in this thread"; } + already_in_session* already_in_session:: + clone () const + { + return new already_in_session (*this); + } + const char* not_in_session:: what () const throw () { return "session not in effect in this thread"; } + not_in_session* not_in_session:: + clone () const + { + return new not_in_session (*this); + } + const char* session_required:: what () const throw () { return "session required to load this object relationship"; } + session_required* session_required:: + clone () const + { + return new session_required (*this); + } + const char* deadlock:: what () const throw () { return "transaction aborted due to deadlock"; } + deadlock* deadlock:: + clone () const + { + return new deadlock (*this); + } + const char* connection_lost:: what () const throw () { return "connection to database lost"; } + connection_lost* connection_lost:: + clone () const + { + return new connection_lost (*this); + } + const char* timeout:: what () const throw () { return "database operation timeout"; } + timeout* timeout:: + clone () const + { + return new timeout (*this); + } + const char* object_not_persistent:: what () const throw () { return "object not persistent"; } + object_not_persistent* object_not_persistent:: + clone () const + { + return new object_not_persistent (*this); + } + const char* object_already_persistent:: what () const throw () { return "object already persistent"; } + object_already_persistent* object_already_persistent:: + clone () const + { + return new object_already_persistent (*this); + } + const char* object_changed:: what () const throw () { return "object changed concurrently"; } + object_changed* object_changed:: + clone () const + { + return new object_changed (*this); + } + const char* result_not_cached:: what () const throw () { return "query result is not cached"; } + result_not_cached* result_not_cached:: + clone () const + { + return new result_not_cached (*this); + } + const char* abstract_class:: what () const throw () { return "database operation on instance of abstract class"; } + abstract_class* abstract_class:: + clone () const + { + return new abstract_class (*this); + } + const char* no_type_info:: what () const throw () { return "no type information"; } + no_type_info* no_type_info:: + clone () const + { + return new no_type_info (*this); + } + prepared_already_cached:: prepared_already_cached (const char* name) : name_ (name) @@ -125,6 +224,12 @@ namespace odb return what_.c_str (); } + prepared_already_cached* prepared_already_cached:: + clone () const + { + return new prepared_already_cached (*this); + } + prepared_type_mismatch:: prepared_type_mismatch (const char* name) : name_ (name) @@ -145,8 +250,14 @@ namespace odb return what_.c_str (); } + prepared_type_mismatch* prepared_type_mismatch:: + clone () const + { + return new prepared_type_mismatch (*this); + } + unknown_schema:: - unknown_schema (const std::string& name) + unknown_schema (const string& name) : name_ (name) { what_ = "unknown database schema '"; @@ -165,6 +276,12 @@ namespace odb return what_.c_str (); } + unknown_schema* unknown_schema:: + clone () const + { + return new unknown_schema (*this); + } + unknown_schema_version:: unknown_schema_version (schema_version v) : version_ (v) @@ -186,15 +303,132 @@ namespace odb return what_.c_str (); } + unknown_schema_version* unknown_schema_version:: + clone () const + { + return new unknown_schema_version (*this); + } + const char* section_not_loaded:: what () const throw () { return "section is not loaded"; } + section_not_loaded* section_not_loaded:: + clone () const + { + return new section_not_loaded (*this); + } + const char* section_not_in_object:: what () const throw () { return "section instance is not part of an object (section was copied?)"; } + + section_not_in_object* section_not_in_object:: + clone () const + { + return new section_not_in_object (*this); + } + + // multiple_exceptions + // + multiple_exceptions:: + ~multiple_exceptions () throw () {} + + void multiple_exceptions:: + insert (size_t p, bool maybe, const odb::exception& e, bool fatal) + { + details::shared_ptr<odb::exception> pe; + + if (common_exception_ti_ != typeid (e)) + pe.reset (e.clone ()); + else + { + if (common_exception_ == 0) + common_exception_.reset (e.clone ()); + + pe = common_exception_; + } + + set_.insert (value_type (delta_ + p, maybe, pe)); + fatal_ = fatal_ || fatal; + } + + const multiple_exceptions::value_type* multiple_exceptions:: + lookup (size_t p) const + { + p += delta_; // Called while populating multiple_exceptions. + + iterator i (set_.find (value_type (p))); + return i == set_.end () ? 0 : &*i; + } + + void multiple_exceptions:: + prepare () + { + current_ = 0; + delta_ = 0; + common_exception_.reset (); + + ostringstream os; + os << "multiple exceptions, " + << attempted_ << " element" << (attempted_ != 1 ? "s" : "") << + " attempted, " + << failed () << " failed" + << (fatal_ ? ", fatal" : "") << ":"; + + bool nl (true); + for (iterator i (begin ()); i != end ();) + { + size_t p (i->position ()); + const odb::exception& e (i->exception ()); + + os << (nl ? "\n" : ""); + + if (!i->maybe ()) + { + os << '[' << p << ']'; + ++i; + } + else + { + // In this case we will normally have a large number of maybe + // failures in a row (usually the whole batch). So let's try + // to represent them all as a single range. + // + size_t n (0); + for (++i; i != end () && i->maybe (); ++i) + { + assert (&e == &i->exception ()); // The same common exception. + n++; + } + + if (n == 0) + os << '[' << p << ']'; + else + os << '[' << p << '-' << (p + n) << "] (some)"; + } + + const char* w (e.what ()); + os << ' ' << w; + nl = (w[strlen (w) - 1] != '\n'); + } + + what_ = os.str (); + } + + const char* multiple_exceptions:: + what () const throw () + { + return what_.c_str (); + } + + multiple_exceptions* multiple_exceptions:: + clone () const + { + return new multiple_exceptions (*this); + } } diff --git a/odb/exceptions.hxx b/odb/exceptions.hxx index 14f4f86..fbf960d 100644 --- a/odb/exceptions.hxx +++ b/odb/exceptions.hxx @@ -7,12 +7,16 @@ #include <odb/pre.hxx> +#include <set> #include <string> +#include <cstddef> // std::size_t +#include <typeinfo> #include <odb/forward.hxx> // schema_version, odb::core #include <odb/exception.hxx> #include <odb/details/export.hxx> +#include <odb/details/shared-ptr.hxx> namespace odb { @@ -20,6 +24,9 @@ namespace odb { virtual const char* what () const throw (); + + virtual null_pointer* + clone () const; }; // Transaction exceptions. @@ -28,18 +35,27 @@ namespace odb { virtual const char* what () const throw (); + + virtual already_in_transaction* + clone () const; }; struct LIBODB_EXPORT not_in_transaction: odb::exception { virtual const char* what () const throw (); + + virtual not_in_transaction* + clone () const; }; struct LIBODB_EXPORT transaction_already_finalized: odb::exception { virtual const char* what () const throw (); + + virtual transaction_already_finalized* + clone () const; }; // Session exceptions. @@ -48,70 +64,102 @@ namespace odb { virtual const char* what () const throw (); + + virtual already_in_session* + clone () const; }; struct LIBODB_EXPORT not_in_session: odb::exception { virtual const char* what () const throw (); + + virtual not_in_session* + clone () const; }; struct LIBODB_EXPORT session_required: odb::exception { virtual const char* what () const throw (); + + virtual session_required* + clone () const; }; // Database operations exceptions. // struct LIBODB_EXPORT recoverable: odb::exception { + // Abstract. }; struct LIBODB_EXPORT connection_lost: recoverable { virtual const char* what () const throw (); + + virtual connection_lost* + clone () const; }; struct LIBODB_EXPORT timeout: recoverable { virtual const char* what () const throw (); + + virtual timeout* + clone () const; }; struct LIBODB_EXPORT deadlock: recoverable { virtual const char* what () const throw (); + + virtual deadlock* + clone () const; }; struct LIBODB_EXPORT object_not_persistent: odb::exception { virtual const char* what () const throw (); + + virtual object_not_persistent* + clone () const; }; struct LIBODB_EXPORT object_already_persistent: odb::exception { virtual const char* what () const throw (); + + virtual object_already_persistent* + clone () const; }; struct LIBODB_EXPORT object_changed: odb::exception { virtual const char* what () const throw (); + + virtual object_changed* + clone () const; }; struct LIBODB_EXPORT result_not_cached: odb::exception { virtual const char* what () const throw (); + + virtual result_not_cached* + clone () const; }; struct LIBODB_EXPORT database_exception: odb::exception { + // Abstract. }; // Polymorphism support exceptions. @@ -120,12 +168,18 @@ namespace odb { virtual const char* what () const throw (); + + virtual abstract_class* + clone () const; }; struct LIBODB_EXPORT no_type_info: odb::exception { virtual const char* what () const throw (); + + virtual no_type_info* + clone () const; }; // Prepared query support exceptions. @@ -144,6 +198,9 @@ namespace odb virtual const char* what () const throw (); + virtual prepared_already_cached* + clone () const; + private: const char* name_; std::string what_; @@ -155,14 +212,14 @@ namespace odb ~prepared_type_mismatch () throw (); const char* - name () const - { - return name_; - } + name () const {return name_;} virtual const char* what () const throw (); + virtual prepared_type_mismatch* + clone () const; + private: const char* name_; std::string what_; @@ -176,14 +233,14 @@ namespace odb ~unknown_schema () throw (); const std::string& - name () const - { - return name_; - } + name () const {return name_;} virtual const char* what () const throw (); + virtual unknown_schema* + clone () const; + private: std::string name_; std::string what_; @@ -195,14 +252,14 @@ namespace odb ~unknown_schema_version () throw (); schema_version - version () const - { - return version_; - } + version () const {return version_;} virtual const char* what () const throw (); + virtual unknown_schema_version* + clone () const; + private: schema_version version_; std::string what_; @@ -214,12 +271,216 @@ namespace odb { virtual const char* what () const throw (); + + virtual section_not_loaded* + clone () const; }; struct LIBODB_EXPORT section_not_in_object: odb::exception { virtual const char* what () const throw (); + + virtual section_not_in_object* + clone () const; + }; + + // Bulk operation exceptions. + // + struct LIBODB_EXPORT multiple_exceptions: odb::exception + { + struct LIBODB_EXPORT value_type + { + std::size_t + position () const {return p_;} + + // If true, then this means that some positions in the batch have + // triggered the exception but it is not possible, due to the + // limitations of the underlying database API, to discern exactly + // which ones. As a result, all the positions in the batch are + // marked as "maybe failed". + // + bool + maybe () const {return m_;} + + const odb::exception& + exception () const {return *e_;} + + // Implementation details. + // + public: + value_type (std::size_t p, + bool maybe, + details::shared_ptr<odb::exception> e) + : m_ (maybe), p_ (p), e_ (e) {} + + value_type (std::size_t p): p_ (p) {} // "Key" for set lookup. + + private: + bool m_; + std::size_t p_; + details::shared_ptr<odb::exception> e_; + }; + + struct LIBODB_EXPORT comparator_type + { + bool + operator() (const value_type& x, const value_type& y) const + { + return x.position () < y.position (); + } + }; + + typedef std::set<value_type, comparator_type> set_type; + + // Iteration. + // + public: + typedef set_type::const_iterator iterator; + typedef set_type::const_iterator const_iterator; // For pedantic types. + + iterator + begin () const {return set_.begin ();} + + iterator + end () const {return set_.end ();} + + // Lookup. + // + public: + // Return NULL if the element at this position has no exception. Note + // that the returned value is value_type* and not odb::exception* in + // order to provide access to maybe(); see value_type::maybe() for + // details. + // + const value_type* + operator[] (std::size_t p) const + { + return set_.empty () ? 0 : lookup (p); + } + + // Severity, failed and attempt counts. + // + public: + // Return the number of elements for which the operation has been + // attempted. + // + std::size_t + attempted () const {return attempted_;} + + // Return the number of positions for which the operation has failed. + // Note that this count includes the maybe failed positions. + // + std::size_t + failed () const {return set_.size ();} + + // If fatal() returns true, then (some of) the exceptions were fatal. + // In this case, even for elements that were processed but did not + // cause the exception, no attempts were made to complete the bulk + // operation and the transaction must be aborted. + // + // If fatal() returns false, then the operation on the elements that + // don't have an exception has succeeded. The application can try to + // correct the errors and re-attempt the operation on the elements + // that did cause an exception. In either case, the transactions can + // be committed. + // + bool + fatal () const {return fatal_;} + + // Normally you shouldn't need to do this explicitly but you can + // "upgrade" an exception to fatal, for example, for specific + // database error codes. + // + void + fatal (bool f) {fatal_ = fatal_ || f;} + + // odb::exception interface. + // + public: + virtual const char* + what () const throw (); + + virtual multiple_exceptions* + clone () const; + + // Direct set access. + // + public: + const set_type& + set () const {return set_;} + + // Implementation details. + // + public: + ~multiple_exceptions () throw (); + + // All instances of the common exception must be equal since we are + // going to create and share just one. + // + multiple_exceptions (const std::type_info& common_exception_ti) + : common_exception_ti_ (common_exception_ti), + fatal_ (false), + delta_ (0), + current_ (0) {} + + // Set the attempted count as (delta + n). + // + void + attempted (std::size_t n) {attempted_ = delta_ + n;} + + // Increment the position of the current batch. Also resets the + // current position in the batch. + // + void + delta (std::size_t d) {delta_ += d; current_ = 0;} + + // Current position in the batch. + // + std::size_t + current () const {return current_;} + + void + current (std::size_t c) {current_ = c;} + + void + insert (std::size_t p, + bool maybe, + const odb::exception& e, + bool fatal = false); + + void + insert (std::size_t p, const odb::exception& e, bool fatal = false) + { + insert (p, false, e, fatal); + } + + void + insert (const odb::exception& e, bool fatal = false) + { + insert (current_, e, fatal); + } + + bool + empty () const {return set_.empty ();} + + void + prepare (); + + private: + const value_type* + lookup (std::size_t p) const; + + private: + const std::type_info& common_exception_ti_; + details::shared_ptr<odb::exception> common_exception_; + + set_type set_; + bool fatal_; + std::size_t attempted_; + std::size_t delta_; // Position of the batch. + std::size_t current_; // Position in the batch. + std::string what_; }; namespace common @@ -252,6 +513,8 @@ namespace odb using odb::section_not_loaded; using odb::section_not_in_object; + + using odb::multiple_exceptions; } } diff --git a/odb/forward.hxx b/odb/forward.hxx index 2e54471..29c95f3 100644 --- a/odb/forward.hxx +++ b/odb/forward.hxx @@ -153,6 +153,10 @@ namespace odb class result_impl; class prepared_query_impl; + // + // + struct multiple_exceptions; + // Polymorphism support. // template <typename R> |