aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2014-08-14 09:37:06 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2014-11-25 06:39:57 +0200
commit8d4d2568f356cb9beb1553bf58ad69c1c800b996 (patch)
treed6466f2dec931669405b38a6cf47e8bb0998a4fd
parent46f28dcdf5a72ed430486b562213ad147b65526a (diff)
Implement bulk database operation support for Oracle and SQL Server
-rw-r--r--odb/database.hxx56
-rw-r--r--odb/database.ixx118
-rw-r--r--odb/database.txx320
-rw-r--r--odb/details/posix/exceptions.cxx6
-rw-r--r--odb/details/posix/exceptions.hxx3
-rw-r--r--odb/details/shared-ptr.hxx1
-rw-r--r--odb/details/shared-ptr/base.cxx21
-rw-r--r--odb/details/shared-ptr/base.hxx10
-rw-r--r--odb/details/shared-ptr/base.ixx6
-rw-r--r--odb/details/shared-ptr/base.txx26
-rw-r--r--odb/details/shared-ptr/exception.hxx30
-rw-r--r--odb/details/win32/exceptions.cxx6
-rw-r--r--odb/details/win32/exceptions.hxx3
-rw-r--r--odb/exception.hxx7
-rw-r--r--odb/exceptions.cxx236
-rw-r--r--odb/exceptions.hxx287
-rw-r--r--odb/forward.hxx4
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 = &copy;
+ }
+ };
+
+ 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>