aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2014-11-14 16:24:50 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2014-11-25 06:49:01 +0200
commitd7752cdb86957f7dc0caffe6033e872443ad8153 (patch)
tree8909976800ce89089ce6b12ea02c7f18ca61453d /common
parent0a4df2bc0c7962acbddc55b4b5668e09fb54ef7f (diff)
Implement bulk database operation support for Oracle and SQL Server
Diffstat (limited to 'common')
-rw-r--r--common/auto/test.hxx4
-rw-r--r--common/bulk/driver.cxx1025
-rw-r--r--common/bulk/makefile118
-rw-r--r--common/bulk/test.hxx192
-rw-r--r--common/bulk/test.std218
-rw-r--r--common/makefile3
-rw-r--r--common/section/basics/driver.cxx14
7 files changed, 1571 insertions, 3 deletions
diff --git a/common/auto/test.hxx b/common/auto/test.hxx
index 61fbcc6..cee27c5 100644
--- a/common/auto/test.hxx
+++ b/common/auto/test.hxx
@@ -34,8 +34,8 @@ private:
#pragma db object
struct auto_only
{
- #pragma db auto id
- unsigned long id_;
+ #pragma db auto id pgsql:type("BIGINT")
+ unsigned short id_;
};
#endif // TEST_HXX
diff --git a/common/bulk/driver.cxx b/common/bulk/driver.cxx
new file mode 100644
index 0000000..2903ae4
--- /dev/null
+++ b/common/bulk/driver.cxx
@@ -0,0 +1,1025 @@
+// file : common/bulk/driver.cxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test bulk database operations.
+//
+
+#include <memory> // std::auto_ptr
+#include <vector>
+#include <cassert>
+#include <iostream>
+#include <iterator>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <odb/details/meta/remove-pointer.hxx>
+
+#include <common/config.hxx> // HAVE_CXX11, DATABASE_*
+#include <common/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+using namespace std;
+using namespace odb::core;
+
+// Sun CC with non-standard STL does not have iterator_traits in which
+// case we assume iterator is just a pointer.
+//
+template <typename I,
+#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC
+ typename T = typename iterator_traits<I>::value_type
+#else
+ typename T = typename details::meta::remove_pointer<I>::result
+#endif
+ >
+struct element_traits;
+
+template <typename I, typename T>
+struct element_traits
+{
+ typedef T type;
+ typedef std::auto_ptr<T> auto_ptr;
+
+ static T& ref (T& x) {return x;}
+ static T* ptr (T* p) {return p;}
+};
+
+template <typename I, typename T>
+struct element_traits<I, T*>
+{
+ typedef T type;
+ typedef std::auto_ptr<T> auto_ptr;
+
+ static T& ref (T* p) {return *p;}
+ static T* ptr (T* p) {return p;}
+};
+
+template <typename I, typename T>
+struct element_traits<I, std::auto_ptr<T> >
+{
+ typedef T type;
+ typedef std::auto_ptr<T> auto_ptr;
+
+ static T& ref (const auto_ptr& p) {return *p;}
+ static T* ptr (const auto_ptr& p) {return p.get ();}
+};
+
+#ifdef HAVE_CXX11
+template <typename I, typename T>
+struct element_traits<I, std::unique_ptr<T>>
+{
+ typedef T type;
+ typedef std::unique_ptr<T> auto_ptr;
+
+ static T& ref (const unique_ptr<T>& p) {return *p;}
+ static T* ptr (const unique_ptr<T>& p) {return p.get ();}
+};
+#endif
+
+template <typename I>
+void
+persist (const auto_ptr<database>& db, I b, I e, bool cont = true)
+{
+ typedef element_traits<I> traits;
+ typedef typename traits::type type;
+ typedef typename traits::auto_ptr auto_ptr;
+
+ {
+ transaction t (db->begin ());
+ db->persist (b, e, cont);
+ t.commit ();
+ }
+
+ // Verify we can load the objects via their ids.
+ //
+ {
+ transaction t (db->begin ());
+
+ for (I i (b); i != e; ++i)
+ {
+ type& x (traits::ref (*i));
+ auto_ptr p (db->load<type> (x.id));
+ assert (p->n == x.n && p->s == x.s);
+ }
+
+ t.commit ();
+ }
+}
+
+template <typename I>
+void
+try_persist (const auto_ptr<database>& db, I b, I e, bool cont = true)
+{
+ try
+ {
+ persist (db, b, e, cont);
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ cout << e.what () << endl << endl;
+ }
+}
+
+template <typename I>
+void
+update (const auto_ptr<database>& db, I b, I e,
+ bool modify = true, bool cont = true)
+{
+ typedef element_traits<I> traits;
+ typedef typename traits::type type;
+ typedef typename traits::auto_ptr auto_ptr;
+
+ if (modify)
+ {
+ for (I i (b); i != e; ++i)
+ {
+ type& x (traits::ref (*i));
+ x.n++;
+ x.s[0]++;
+ }
+ }
+
+ {
+ transaction t (db->begin ());
+ db->update (b, e, cont);
+ t.commit ();
+ }
+
+ // Verify changes.
+ //
+ {
+ transaction t (db->begin ());
+
+ for (I i (b); i != e; ++i)
+ {
+ type& x (traits::ref (*i));
+ auto_ptr p (db->load<type> (x.id));
+ assert (p->n == x.n && p->s == x.s);
+ }
+
+ t.commit ();
+ }
+}
+
+template <typename I>
+void
+try_update (const auto_ptr<database>& db, I b, I e, bool cont = true)
+{
+ try
+ {
+ update (db, b, e, false, cont);
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ cout << e.what () << endl << endl;
+ }
+}
+
+template <typename I>
+void
+erase (const auto_ptr<database>& db, I b, I e)
+{
+ typedef element_traits<I> traits;
+ typedef typename traits::type type;
+
+ {
+ transaction t (db->begin ());
+ db->erase (b, e);
+ t.commit ();
+ }
+
+ // Verify the objects are gone.
+ //
+ {
+ transaction t (db->begin ());
+
+ for (I i (b); i != e; ++i)
+ {
+ type& x (traits::ref (*i));
+ assert (traits::ptr (db->find<type> (x.id)) == 0);
+ }
+
+ t.commit ();
+ }
+}
+
+template <typename T, typename I>
+void
+erase_id (const auto_ptr<database>& db, I b, I e, bool cont = true)
+{
+ typedef element_traits<T*> traits;
+ typedef T type;
+
+ {
+ transaction t (db->begin ());
+ db->erase<T> (b, e, cont);
+ t.commit ();
+ }
+
+ // Verify the objects are gone.
+ //
+ {
+ transaction t (db->begin ());
+
+ for (I i (b); i != e; ++i)
+ assert (traits::ptr (db->find<type> (*i)) == 0);
+
+ t.commit ();
+ }
+}
+
+template <typename T, typename A>
+void
+try_erase (const auto_ptr<database>& db, const A& a, bool cont = true)
+{
+ try
+ {
+ erase_id<T> (db, a, a + sizeof (a) / sizeof (a[0]), cont);
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ cout << e.what () << endl << endl;
+ }
+}
+
+
+template <typename I>
+void
+test (const auto_ptr<database>& db, I b, I e)
+{
+ persist (db, b, e);
+ update (db, b, e);
+ erase (db, b, e);
+}
+
+template <typename T>
+vector<T>
+fill (std::size_t count)
+{
+ vector<T> r;
+
+ unsigned int n (1);
+ std::string s ("a");
+
+ for (size_t i (0); i != count; ++i)
+ {
+ r.push_back (T (n, s));
+ n++;
+ s[0] = (s[0] == 'z' ? 'a' : s[0] + 1);
+ }
+
+ return r;
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv));
+
+#if defined(DATABASE_ORACLE) || defined(DATABASE_MSSQL)
+
+ // Test database class API with various forms of containers
+ // and elements (test #6 is a copy).
+ //
+ {
+ using namespace test1;
+
+ {
+ object a[2];
+ a[0] = object (1, "a");
+ a[1] = object (2, "b");
+ test (db, a, a + sizeof (a) / sizeof (a[0]));
+ }
+
+ {
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ object o1 (1, "a");
+ object o2 (2, "b");
+ object* a[2] = {&o1, &o2};
+ test (db, a, a + sizeof (a) / sizeof (a[0]));
+ }
+
+ {
+ object o1 (1, "a");
+ object o2 (2, "b");
+ vector<object*> v;
+ v.push_back (&o1);
+ v.push_back (&o2);
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ auto_ptr<auto_object> a[2];
+ a[0].reset (new auto_object (1, "a"));
+ a[1].reset (new auto_object (2, "b"));
+ test (db, a, a + sizeof (a) / sizeof (a[0]));
+ }
+
+#ifdef HAVE_CXX11
+ {
+ vector<unique_ptr<unique_object>> v;
+ v.push_back (unique_ptr<unique_object> (new unique_object (1, "a")));
+ v.push_back (unique_ptr<unique_object> (new unique_object (2, "b")));
+ test (db, v.begin (), v.end ());
+ }
+#endif
+
+ {
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+ persist (db, v.begin (), v.end ());
+
+ unsigned long id[2] = {v[0].id, v[1].id};
+ erase_id<object> (db, id, id + sizeof (id) / sizeof (id[0]));
+ }
+
+ {
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+ persist (db, v.begin (), v.end ());
+
+ vector<unsigned long> id;
+ id.push_back (v[0].id);
+ id.push_back (v[1].id);
+ erase_id<object> (db, id.begin (), id.end ());
+ }
+ }
+
+ // Test various batch sizes.
+ //
+ {
+ using namespace test1;
+
+ {
+ vector<object> v; // 0
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (1)); // 1
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (2)); // batch - 1
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (3)); // batch
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (4)); // batch + 1
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (5)); // 2 * batch - 1
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (6)); // 2 * batch
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v (fill<object> (100)); // 100
+ test (db, v.begin (), v.end ());
+ }
+ }
+
+ // Test object with manually assigned id.
+ //
+ {
+ using namespace test2;
+
+ {
+ vector<object> v;
+ v.push_back (object ("1", 1, "a"));
+ v.push_back (object ("2", 2, "b"));
+ test (db, v.begin (), v.end ());
+ }
+
+#ifdef HAVE_CXX11
+ {
+ typedef unique_ptr<unique_object> unique_ptr;
+
+ vector<unique_ptr> v;
+ v.push_back (unique_ptr (new unique_object ("1", 1, "a")));
+ v.push_back (unique_ptr (new unique_object ("2", 2, "b")));
+ test (db, v.begin (), v.end ());
+ }
+#endif
+
+ // Test const objects.
+ //
+
+ {
+ const object a[1];
+ const object* e (a + sizeof (a) / sizeof (a[0]));
+
+ transaction t (db->begin ());
+ db->persist (a, e);
+ db->erase (a, e);
+ t.commit ();
+ }
+
+ {
+ object o1 ("1", 1, "a");
+ object o2 ("2", 2, "b");
+
+ vector<const object*> v;
+ v.push_back (&o1);
+ v.push_back (&o2);
+
+ transaction t (db->begin ());
+ db->persist (v.begin (), v.end ());
+ db->erase (v.begin (), v.end ());
+ t.commit ();
+ }
+ }
+
+ // Test failure.
+ //
+ {
+ using namespace test3;
+
+ vector<object> v;
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ v.push_back (object (9, 9));
+ v.push_back (object (10, 10));
+ v.push_back (object (11, 11));
+
+ persist (db, v.begin (), v.end ());
+
+ // persist
+ //
+ {
+ {
+ vector<object> v; // 1
+ v.push_back (object (6, 6));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch + 1
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ v.push_back (object (9, 9));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2 x batch - 1
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ v.push_back (object (9, 9));
+ v.push_back (object (10, 10));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2 x batch
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ v.push_back (object (9, 9));
+ v.push_back (object (10, 10));
+ v.push_back (object (11, 11));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ // Mixture of success and failure.
+ //
+
+ {
+ vector<object> v; // 1
+ v.push_back (object (0, 0));
+ v.push_back (object (6, 6));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 1
+ v.push_back (object (6, 6));
+ v.push_back (object (0, 0));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (0, 0));
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (6, 6));
+ v.push_back (object (0, 0));
+ v.push_back (object (7, 7));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (0, 0));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (0, 0));
+ v.push_back (object (8, 8));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ v.push_back (object (0, 0));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // mixture
+ v.push_back (object (0, 0));
+ v.push_back (object (6, 6));
+ v.push_back (object (1, 1));
+ v.push_back (object (7, 7));
+ v.push_back (object (2, 2));
+ v.push_back (object (8, 8));
+ v.push_back (object (3, 3));
+ try_persist (db, v.begin (), v.end ());
+ }
+
+ // Test stopping after failure.
+ //
+ {
+ vector<object> v; // batch
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (6, 6));
+ v.push_back (object (2, 2));
+ v.push_back (object (3, 3));
+ try_persist (db, v.begin (), v.end (), false);
+ }
+ }
+
+ // update
+ //
+ {
+ {
+ vector<object> v; // 1
+ v.push_back (object (0, 0));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (2, 2));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch + 1
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (2, 2));
+ v.push_back (object (3, 3));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2 x batch - 1
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (2, 2));
+ v.push_back (object (3, 3));
+ v.push_back (object (4, 4));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2 x batch
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (2, 2));
+ v.push_back (object (3, 3));
+ v.push_back (object (4, 4));
+ v.push_back (object (5, 5));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ // Mixture of success and failure.
+ //
+
+ {
+ vector<object> v; // 1
+ v.push_back (object (6, 6));
+ v.push_back (object (0, 0));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 1
+ v.push_back (object (0, 0));
+ v.push_back (object (6, 6));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (6, 6));
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (0, 0));
+ v.push_back (object (6, 6));
+ v.push_back (object (1, 1));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // 2
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (6, 6));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (6, 6));
+ v.push_back (object (2, 2));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // batch
+ v.push_back (object (0, 0));
+ v.push_back (object (1, 1));
+ v.push_back (object (2, 2));
+ v.push_back (object (6, 6));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // mixture
+ v.push_back (object (0, 0));
+ v.push_back (object (6, 6));
+ v.push_back (object (2, 2));
+ v.push_back (object (7, 7));
+ v.push_back (object (3, 3));
+ v.push_back (object (8, 8));
+ v.push_back (object (4, 4));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ {
+ vector<object> v; // mixture
+ v.push_back (object (0, 0));
+ v.push_back (object (2, 2));
+ v.push_back (object (3, 3));
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (8, 8));
+ v.push_back (object (4, 4));
+ try_update (db, v.begin (), v.end ());
+ }
+
+ // Test stopping after failure.
+ //
+ {
+ vector<object> v; // batch
+ v.push_back (object (6, 6));
+ v.push_back (object (7, 7));
+ v.push_back (object (0, 0));
+ v.push_back (object (8, 8));
+ v.push_back (object (9, 9));
+ try_update (db, v.begin (), v.end (), false);
+ }
+
+ // Test a database exception (unique constraint violation)
+ //
+ try
+ {
+ v[0].n++;
+ v[2].n++;
+
+ update (db, v.begin (), v.begin () + 3, false);
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ assert (e.attempted () == 3 && e.failed () == 2);
+ assert (e[0] != 0 && e[1] == 0 && e[2] != 0);
+ }
+ }
+
+ // erase
+ //
+ {
+ {
+ unsigned long a[] = {0}; // 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1}; // 2
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 2}; // batch
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 2, 3}; // batch + 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 2, 3, 4}; // 2 x batch - 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 2, 3, 4, 5}; // 2 x batch
+ try_erase<object> (db, a);
+ }
+
+ // Mixture of success and failure.
+ //
+
+ {
+ unsigned long a[] = {6, 0}; // 2
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 6}; // 2
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {6, 0, 1}; // batch
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 6, 1}; // batch
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 6}; // batch
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {6, 0, 1, 2}; // batch + 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 6, 1, 2}; // batch + 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 6, 2}; // batch + 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 2, 6}; // batch + 1
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {6, 0, 7, 1, 8, 2, 9, 3}; // mixture
+ try_erase<object> (db, a);
+ }
+
+ {
+ unsigned long a[] = {0, 1, 2, 6, 7, 8, 3, 4, 5, 9}; // mixture
+ try_erase<object> (db, a);
+ }
+
+ // Test stopping after failure.
+ //
+ {
+ unsigned long a[] = {6, 7, 0, 8, 9};
+ try_erase<object> (db, a, false);
+ }
+ }
+
+ erase (db, v.begin (), v.end ());
+ }
+
+ // Test a large batch.
+ //
+ {
+ using namespace test4;
+
+ vector<object> v (fill<object> (5000));
+ test (db, v.begin (), v.end ());
+ }
+
+ // Test object without id.
+ //
+ {
+ using namespace test5;
+
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+
+ {
+ transaction t (db->begin ());
+ db->persist (v.begin (), v.end ());
+ t.commit ();
+ }
+
+ {
+ typedef odb::query<object> query;
+ typedef odb::result<object> result;
+
+ transaction t (db->begin ());
+
+ result r (db->query<object> ("ORDER BY" + query::n));
+ result::iterator i (r.begin ());
+
+ assert (i != r.end () && i->n == 1 && i->s == "a");
+ assert (++i != r.end () && i->n == 2 && i->s == "b");
+ assert (++i == r.end ());
+
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase_query<object> ();
+ t.commit ();
+ }
+ }
+
+ // Test API with persistent class template instantiations (copy of
+ // test #1).
+ {
+ using namespace test6;
+
+ // Make sure we can still call the non-bulk API.
+ //
+ {
+ object o (0, "z");
+ transaction t (db->begin ());
+ db->persist (o);
+ db->update<object> (o);
+ db->reload<object> (o);
+ db->erase<object> (o);
+ t.commit ();
+ }
+
+
+ // The rest is a copy of test #1.
+ //
+ {
+ object a[2];
+ a[0] = object (1, "a");
+ a[1] = object (2, "b");
+ test (db, a, a + sizeof (a) / sizeof (a[0]));
+ }
+
+ {
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ object o1 (1, "a");
+ object o2 (2, "b");
+ object* a[2] = {&o1, &o2};
+ test (db, a, a + sizeof (a) / sizeof (a[0]));
+ }
+
+ {
+ object o1 (1, "a");
+ object o2 (2, "b");
+ vector<object*> v;
+ v.push_back (&o1);
+ v.push_back (&o2);
+ test (db, v.begin (), v.end ());
+ }
+
+ {
+ auto_ptr<auto_object> a[2];
+ a[0].reset (new auto_object (1, "a"));
+ a[1].reset (new auto_object (2, "b"));
+ test (db, a, a + sizeof (a) / sizeof (a[0]));
+ }
+
+#ifdef HAVE_CXX11
+ {
+ vector<unique_ptr<unique_object>> v;
+ v.push_back (unique_ptr<unique_object> (new unique_object (1, "a")));
+ v.push_back (unique_ptr<unique_object> (new unique_object (2, "b")));
+ test (db, v.begin (), v.end ());
+ }
+#endif
+
+ {
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+ persist (db, v.begin (), v.end ());
+
+ unsigned long id[2] = {v[0].id, v[1].id};
+ erase_id<object> (db, id, id + sizeof (id) / sizeof (id[0]));
+ }
+
+ {
+ vector<object> v;
+ v.push_back (object (1, "a"));
+ v.push_back (object (2, "b"));
+ persist (db, v.begin (), v.end ());
+
+ vector<unsigned long> id;
+ id.push_back (v[0].id);
+ id.push_back (v[1].id);
+ erase_id<object> (db, id.begin (), id.end ());
+ }
+ }
+
+#endif
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/common/bulk/makefile b/common/bulk/makefile
new file mode 100644
index 0000000..2107cd2
--- /dev/null
+++ b/common/bulk/makefile
@@ -0,0 +1,118 @@
+# file : common/bulk/makefile
+# copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+# license : GNU GPL v2; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make
+
+cxx_tun := driver.cxx
+odb_hdr := test.hxx
+genf := $(call odb-gen,$(odb_hdr))
+gen := $(addprefix $(out_base)/,$(genf))
+cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o)) $(filter %.o,$(gen:.cxx=.o))
+cxx_od := $(cxx_obj:.o=.o.d)
+
+common.l := $(out_root)/libcommon/common/common.l
+common.l.cpp-options := $(out_root)/libcommon/common/common.l.cpp-options
+
+# Import.
+#
+$(call import,\
+ $(scf_root)/import/odb/stub.make,\
+ odb: odb,odb-rules: odb_rules)
+
+# Build.
+#
+$(driver): $(cxx_obj) $(common.l)
+$(cxx_obj) $(cxx_od): cpp_options := -I$(out_base) -I$(src_base)
+$(cxx_obj) $(cxx_od): $(common.l.cpp-options)
+
+$(gen): $(odb)
+$(gen): odb := $(odb)
+$(gen) $(dist): export odb_options += --generate-schema --generate-query \
+--table-prefix bulk_
+$(gen): cpp_options := -I$(src_base)
+$(gen): $(common.l.cpp-options)
+
+ifneq ($(db_id),common)
+$(gen): odb_options += --database $(db_id)
+else
+$(gen): odb_options += --multi-database dynamic
+endif
+
+$(call include-dep,$(cxx_od),$(cxx_obj),$(gen))
+
+# Alias for default target.
+#
+$(out_base)/: $(driver)
+
+# Dist
+#
+name := $(subst /,-,$(subst $(src_root)/common/,,$(src_base)))
+
+$(dist): sources := $(cxx_tun)
+$(dist): headers := $(odb_hdr)
+$(dist): data_dist := test.std
+$(dist): export name := $(name)
+$(dist): export extra_dist := $(data_dist) $(call vc8projs,$(name)) \
+$(call vc9projs,$(name)) $(call vc10projs,$(name)) $(call vc11projs,$(name)) \
+$(call vc12projs,$(name))
+$(dist):
+ $(call dist-data,$(sources) $(headers) $(data_dist))
+ $(call meta-automake,../template/Makefile.am)
+ $(call meta-vc8projs,../template/template,$(name))
+ $(call meta-vc9projs,../template/template,$(name))
+ $(call meta-vc10projs,../template/template,$(name))
+ $(call meta-vc11projs,../template/template,$(name))
+ $(call meta-vc12projs,../template/template,$(name))
+
+# Test.
+#
+ifneq ($(db_id),common)
+$(eval $(call test-rule))
+else
+$(foreach d,$(databases),$(eval $(call test-rule,$d)))
+endif
+
+# Clean.
+#
+$(clean): \
+ $(driver).o.clean \
+ $(addsuffix .cxx.clean,$(cxx_obj)) \
+ $(addsuffix .cxx.clean,$(cxx_od)) \
+ $(addsuffix .hxx.clean,$(filter %.cxx,$(gen)))
+ $(call message,,rm -f $(out_base)/test.out)
+
+# Generated .gitignore.
+#
+ifeq ($(out_base),$(src_base))
+$(driver): | $(out_base)/.gitignore
+
+$(out_base)/.gitignore: files := driver $(genf)
+$(clean): $(out_base)/.gitignore.clean
+
+$(call include,$(bld_root)/git/gitignore.make)
+endif
+
+# How to.
+#
+$(call include,$(bld_root)/dist.make)
+$(call include,$(bld_root)/meta/vc8proj.make)
+$(call include,$(bld_root)/meta/vc9proj.make)
+$(call include,$(bld_root)/meta/vc10proj.make)
+$(call include,$(bld_root)/meta/vc11proj.make)
+$(call include,$(bld_root)/meta/vc12proj.make)
+$(call include,$(bld_root)/meta/automake.make)
+
+$(call include,$(bld_root)/cxx/standard.make) # cxx_standard
+ifdef cxx_standard
+$(gen): odb_options += --std $(cxx_standard)
+$(call include,$(odb_rules))
+endif
+
+$(call include,$(bld_root)/cxx/cxx-d.make)
+$(call include,$(bld_root)/cxx/cxx-o.make)
+$(call include,$(bld_root)/cxx/o-e.make)
+
+# Dependencies.
+#
+$(call import,$(src_root)/libcommon/makefile)
diff --git a/common/bulk/test.hxx b/common/bulk/test.hxx
new file mode 100644
index 0000000..526762f
--- /dev/null
+++ b/common/bulk/test.hxx
@@ -0,0 +1,192 @@
+// file : common/driver/test.hxx
+// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <common/config.hxx> // HAVE_CXX11
+
+#include <string>
+#include <memory> // std::auto_ptr, std::unique_ptr
+
+#include <odb/core.hxx>
+
+// Test basic functionality.
+//
+#pragma db namespace table("t1_")
+namespace test1
+{
+ #pragma db object bulk(3) session
+ struct object
+ {
+ object (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), n (n_), s (s_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ unsigned int n;
+
+ #pragma db oracle:type("CLOB") mssql:type("VARCHAR(max)") // Long data.
+ std::string s;
+ };
+
+ #pragma db object bulk(3) pointer(std::auto_ptr)
+ struct auto_object
+ {
+ auto_object (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), n (n_), s (s_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ unsigned int n;
+ std::string s;
+ };
+
+#ifdef HAVE_CXX11
+ #pragma db object bulk(3) pointer(std::unique_ptr)
+ struct unique_object
+ {
+ unique_object (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), n (n_), s (s_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ unsigned int n;
+ std::string s;
+ };
+#endif
+}
+
+// Test object with manually assigned id.
+//
+#pragma db namespace table("t2_")
+namespace test2
+{
+ #pragma db object bulk(3) session
+ struct object
+ {
+ // Can't use empty id because of Oracle.
+ //
+ object (std::string id_ = "!", unsigned int n_ = 0, std::string s_ = "")
+ : id (id_), n (n_), s (s_) {}
+
+ #pragma db id
+ std::string id;
+
+ unsigned int n;
+ std::string s;
+ };
+
+#ifdef HAVE_CXX11
+#pragma db object bulk(3) pointer(std::unique_ptr)
+ struct unique_object
+ {
+ unique_object (std::string id_ = "",
+ unsigned int n_ = 0,
+ std::string s_ = "")
+ : id (id_), n (n_), s (s_) {}
+
+ #pragma db id
+ std::string id;
+
+ unsigned int n;
+ std::string s;
+ };
+#endif
+}
+
+// Test failure.
+//
+#pragma db namespace table("t3_")
+namespace test3
+{
+ #pragma db object bulk(3)
+ struct object
+ {
+ object (unsigned long id_ = 0, unsigned int n_ = 0)
+ : id (id_), n (n_), s ("abc") {}
+
+ #pragma db id
+ unsigned long id;
+
+ #pragma db unique
+ unsigned int n;
+ std::string s;
+ };
+}
+
+// Test a large batch.
+//
+#pragma db namespace table("t4_")
+namespace test4
+{
+ #pragma db object bulk(3000)
+ struct object
+ {
+ object (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), n (n_), s (s_) {}
+
+ #pragma db id auto
+ unsigned long id;
+
+ unsigned int n;
+
+ #pragma db oracle:type("CLOB") mssql:type("VARCHAR(max)") // Long data.
+ std::string s;
+ };
+}
+
+// Test object without id.
+//
+#pragma db namespace table("t5_")
+namespace test5
+{
+ #pragma db object no_id bulk(3)
+ struct object
+ {
+ object (unsigned int n_ = 0, std::string s_ = ""): n (n_), s (s_) {}
+
+ unsigned int n;
+ std::string s;
+ };
+}
+
+// Test API with persistent class template instantiations.
+//
+#pragma db namespace table("t6_")
+namespace test6
+{
+ template <int>
+ struct object_template
+ {
+ object_template (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), n (n_), s (s_) {}
+
+ unsigned long id;
+ unsigned int n;
+ std::string s;
+ };
+
+ typedef object_template<1> object;
+
+ #pragma db object(object) bulk(3)
+ #pragma db member(object::id) id auto
+
+ typedef object_template<2> auto_object;
+
+ #pragma db object(auto_object) bulk(3) pointer(std::auto_ptr)
+ #pragma db member(auto_object::id) id auto
+
+#ifdef HAVE_CXX11
+ typedef object_template<3> unique_object;
+
+ #pragma db object(unique_object) bulk(3) pointer(std::unique_ptr)
+ #pragma db member(unique_object::id) id auto
+#endif
+}
+
+#endif // TEST_HXX
diff --git a/common/bulk/test.std b/common/bulk/test.std
new file mode 100644
index 0000000..6c4bdf9
--- /dev/null
+++ b/common/bulk/test.std
@@ -0,0 +1,218 @@
+multiple exceptions, 1 element attempted, 1 failed:
+[0] object already persistent
+
+multiple exceptions, 2 elements attempted, 2 failed:
+[0] object already persistent
+[1] object already persistent
+
+multiple exceptions, 3 elements attempted, 3 failed:
+[0] object already persistent
+[1] object already persistent
+[2] object already persistent
+
+multiple exceptions, 4 elements attempted, 4 failed:
+[0] object already persistent
+[1] object already persistent
+[2] object already persistent
+[3] object already persistent
+
+multiple exceptions, 5 elements attempted, 5 failed:
+[0] object already persistent
+[1] object already persistent
+[2] object already persistent
+[3] object already persistent
+[4] object already persistent
+
+multiple exceptions, 6 elements attempted, 6 failed:
+[0] object already persistent
+[1] object already persistent
+[2] object already persistent
+[3] object already persistent
+[4] object already persistent
+[5] object already persistent
+
+multiple exceptions, 2 elements attempted, 1 failed:
+[1] object already persistent
+
+multiple exceptions, 2 elements attempted, 1 failed:
+[0] object already persistent
+
+multiple exceptions, 3 elements attempted, 2 failed:
+[1] object already persistent
+[2] object already persistent
+
+multiple exceptions, 3 elements attempted, 2 failed:
+[0] object already persistent
+[2] object already persistent
+
+multiple exceptions, 3 elements attempted, 2 failed:
+[0] object already persistent
+[1] object already persistent
+
+multiple exceptions, 4 elements attempted, 3 failed:
+[0] object already persistent
+[1] object already persistent
+[3] object already persistent
+
+multiple exceptions, 4 elements attempted, 3 failed:
+[0] object already persistent
+[1] object already persistent
+[2] object already persistent
+
+multiple exceptions, 7 elements attempted, 3 failed:
+[1] object already persistent
+[3] object already persistent
+[5] object already persistent
+
+multiple exceptions, 3 elements attempted, 1 failed:
+[2] object already persistent
+
+multiple exceptions, 1 element attempted, 1 failed:
+[0] object not persistent
+
+multiple exceptions, 2 elements attempted, 2 failed:
+[0] object not persistent
+[1] object not persistent
+
+multiple exceptions, 3 elements attempted, 3 failed:
+[0] object not persistent
+[1] object not persistent
+[2] object not persistent
+
+multiple exceptions, 4 elements attempted, 4 failed:
+[0] object not persistent
+[1] object not persistent
+[2] object not persistent
+[3] object not persistent
+
+multiple exceptions, 5 elements attempted, 5 failed:
+[0] object not persistent
+[1] object not persistent
+[2] object not persistent
+[3] object not persistent
+[4] object not persistent
+
+multiple exceptions, 6 elements attempted, 6 failed:
+[0] object not persistent
+[1] object not persistent
+[2] object not persistent
+[3] object not persistent
+[4] object not persistent
+[5] object not persistent
+
+multiple exceptions, 2 elements attempted, 2 failed:
+[0-1] (some) object not persistent
+
+multiple exceptions, 2 elements attempted, 2 failed:
+[0-1] (some) object not persistent
+
+multiple exceptions, 3 elements attempted, 3 failed:
+[0-2] (some) object not persistent
+
+multiple exceptions, 3 elements attempted, 3 failed:
+[0-2] (some) object not persistent
+
+multiple exceptions, 3 elements attempted, 3 failed:
+[0-2] (some) object not persistent
+
+multiple exceptions, 4 elements attempted, 4 failed:
+[0-2] (some) object not persistent
+[3] object not persistent
+
+multiple exceptions, 4 elements attempted, 3 failed:
+[0] object not persistent
+[1] object not persistent
+[2] object not persistent
+
+multiple exceptions, 7 elements attempted, 7 failed:
+[0-5] (some) object not persistent
+[6] object not persistent
+
+multiple exceptions, 7 elements attempted, 4 failed:
+[0] object not persistent
+[1] object not persistent
+[2] object not persistent
+[6] object not persistent
+
+multiple exceptions, 3 elements attempted, 3 failed:
+[0-2] (some) object not persistent
+
+multiple exceptions, 1 element attempted, 1 failed:
+[0] object not persistent
+
+multiple exceptions, 2 elements attempted, 2 failed:
+[0] object not persistent
+[1] object not persistent
+
+multiple exceptions, 3 elements attempted, 3 failed:
+[0] object not persistent
+[1] object not persistent
+[2] object not persistent
+
+multiple exceptions, 4 elements attempted, 4 failed:
+[0] object not persistent
+[1] object not persistent
+[2] object not persistent
+[3] object not persistent
+
+multiple exceptions, 5 elements attempted, 5 failed:
+[0] object not persistent
+[1] object not persistent
+[2] object not persistent
+[3] object not persistent
+[4] object not persistent
+
+multiple exceptions, 6 elements attempted, 6 failed:
+[0] object not persistent
+[1] object not persistent
+[2] object not persistent
+[3] object not persistent
+[4] object not persistent
+[5] object not persistent
+
+multiple exceptions, 2 elements attempted, 2 failed:
+[0-1] (some) object not persistent
+
+multiple exceptions, 2 elements attempted, 2 failed:
+[0-1] (some) object not persistent
+
+multiple exceptions, 3 elements attempted, 3 failed:
+[0-2] (some) object not persistent
+
+multiple exceptions, 3 elements attempted, 3 failed:
+[0-2] (some) object not persistent
+
+multiple exceptions, 3 elements attempted, 3 failed:
+[0-2] (some) object not persistent
+
+multiple exceptions, 4 elements attempted, 4 failed:
+[0-2] (some) object not persistent
+[3] object not persistent
+
+multiple exceptions, 4 elements attempted, 4 failed:
+[0-2] (some) object not persistent
+[3] object not persistent
+
+multiple exceptions, 4 elements attempted, 4 failed:
+[0-2] (some) object not persistent
+[3] object not persistent
+
+multiple exceptions, 4 elements attempted, 3 failed:
+[0] object not persistent
+[1] object not persistent
+[2] object not persistent
+
+multiple exceptions, 8 elements attempted, 8 failed:
+[0-7] (some) object not persistent
+
+multiple exceptions, 10 elements attempted, 6 failed:
+[0] object not persistent
+[1] object not persistent
+[2] object not persistent
+[6] object not persistent
+[7] object not persistent
+[8] object not persistent
+
+multiple exceptions, 3 elements attempted, 3 failed:
+[0-2] (some) object not persistent
+
diff --git a/common/makefile b/common/makefile
index aa195ba..04e3efa 100644
--- a/common/makefile
+++ b/common/makefile
@@ -8,6 +8,7 @@ tests := \
access \
auto \
blob \
+bulk \
callback \
changelog \
circular/single \
@@ -63,7 +64,7 @@ cxx11_tests := session/custom
no_dist_tests := changelog include
-no_multi_tests := changelog include
+no_multi_tests := bulk changelog include
$(default):
$(call include,$(bld_root)/cxx/standard.make) # cxx_standard
diff --git a/common/section/basics/driver.cxx b/common/section/basics/driver.cxx
index f34ed90..a2b7006 100644
--- a/common/section/basics/driver.cxx
+++ b/common/section/basics/driver.cxx
@@ -1273,6 +1273,13 @@ main (int argc, char* argv[])
db->update (o1, o1.s);
assert (o.v != o1.v);
+ // Double-check object version was updated.
+ //
+ {
+ auto_ptr<object> p1 (db->load<object> (o.id));
+ assert (o1.v == p1->v);
+ }
+
try
{
db->load (*p, p->s);
@@ -1393,6 +1400,13 @@ main (int argc, char* argv[])
db->update (o1, o1.s);
assert (o.v != o1.v);
+ // Double-check object version was updated.
+ //
+ {
+ auto_ptr<object> p1 (db->load<object> (o.id));
+ assert (o1.v == p1->v);
+ }
+
try
{
db->load (*p, p->s);