summaryrefslogtreecommitdiff
path: root/odb-tests/common/bulk
diff options
context:
space:
mode:
Diffstat (limited to 'odb-tests/common/bulk')
-rw-r--r--odb-tests/common/bulk/buildfile51
-rw-r--r--odb-tests/common/bulk/driver.cxx1203
-rw-r--r--odb-tests/common/bulk/test.hxx213
-rw-r--r--odb-tests/common/bulk/testscript533
4 files changed, 2000 insertions, 0 deletions
diff --git a/odb-tests/common/bulk/buildfile b/odb-tests/common/bulk/buildfile
new file mode 100644
index 0000000..61d4fac
--- /dev/null
+++ b/odb-tests/common/bulk/buildfile
@@ -0,0 +1,51 @@
+# file : common/bulk/buildfile
+# license : GNU GPL v2; see accompanying LICENSE file
+
+if ($build.meta_operation != 'dist')
+{
+ assert (!$pgsql || $pgsql_bulk || $size($databases) != 1) \
+ "bulk operations are disabled for pgsql which is specified as single database"
+}
+
+import libodb = libodb%lib{odb}
+import libcommon = lib{common}
+
+libs =
+
+for db: $databases
+{
+ if ($db != 'pgsql' || $pgsql_bulk)
+ import libs += libodb-$db%lib{odb-$db}
+}
+
+exe{driver}: {hxx cxx}{* -*-odb -*-odb-*} {hxx ixx cxx}{test-odb} testscript
+
+# Introduce the metadata library target to make sure the libodb library is
+# resolved for the odb_compile ad hoc rule (see build/root.build for details).
+#
+libue{test-meta}: $libodb $libcommon
+
+<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta}
+
+for db: $databases
+{
+ exe{driver}: {hxx ixx cxx}{test-odb-$db}: \
+ include = ($multi && ($db != 'pgsql' || $pgsql_bulk))
+
+ <{hxx ixx cxx}{test-odb-$db}>: hxx{test} libue{test-meta}
+}
+
+exe{driver}: libue{test-meta} $libs
+
+# Specify the ODB custom options to be used by the odb_compile ad hoc rule
+# (see build/root.build for details).
+#
+odb_options = --table-prefix bulk_ \
+ --generate-schema \
+ --generate-query
+
+cxx.poptions =+ "-I$out_base" "-I$src_base"
+
+# Testscript's run-time prerequisites.
+#
+exe{driver}: ../../alias{database-client}: include = adhoc
diff --git a/odb-tests/common/bulk/driver.cxx b/odb-tests/common/bulk/driver.cxx
new file mode 100644
index 0000000..f598dcc
--- /dev/null
+++ b/odb-tests/common/bulk/driver.cxx
@@ -0,0 +1,1203 @@
+// file : common/bulk/driver.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test bulk database operations.
+//
+
+#include <memory> // std::unique_ptr
+#include <vector>
+#include <iostream>
+#include <iterator>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <odb/details/meta/remove-pointer.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_*
+#include <libcommon/common.hxx>
+
+#include "test.hxx"
+#include "test-odb.hxx"
+
+#undef NDEBUG
+#include <cassert>
+
+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 odb::details::meta::remove_pointer<I>::result
+#endif
+ >
+struct element_traits;
+
+template <typename I, typename T>
+struct element_traits
+{
+ typedef T type;
+ typedef T* pointer;
+ typedef std::unique_ptr<T> unique_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 T* pointer;
+ typedef std::unique_ptr<T> unique_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::unique_ptr<T> >
+{
+ typedef T type;
+ typedef std::unique_ptr<T> pointer;
+ typedef std::unique_ptr<T> unique_ptr;
+
+ static T& ref (const unique_ptr& p) {return *p;}
+ static T* ptr (const unique_ptr& p) {return p.get ();}
+};
+
+template <typename I>
+void
+persist (const unique_ptr<database>& db, I b, I e, bool cont = true)
+{
+ typedef element_traits<I> traits;
+ typedef typename traits::type type;
+ typedef typename traits::unique_ptr unique_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));
+ unique_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 unique_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 unique_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::unique_ptr unique_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));
+ unique_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 unique_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 unique_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));
+ typename traits::pointer p (db->find<type> (x.id));
+ assert (traits::ptr (p) == 0);
+ }
+
+ t.commit ();
+ }
+}
+
+template <typename T, typename I>
+void
+erase_id (const unique_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 unique_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 unique_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
+ {
+ unique_ptr<database> db (create_database (argc, argv));
+
+#if !defined(MULTI_DATABASE) && \
+ (defined(DATABASE_ORACLE) || \
+ defined(DATABASE_MSSQL) || \
+ defined(DATABASE_PGSQL))
+
+ // 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 ());
+ }
+
+ {
+ 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 ());
+ }
+
+ {
+ 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 ());
+ }
+
+ {
+ 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 ());
+ }
+
+ // 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)
+ {
+#ifndef DATABASE_PGSQL
+ assert (e.attempted () == 3 && e.failed () == 2);
+ assert (e[0] != 0 && e[1] == 0 && e[2] != 0);
+#else
+ // In PosgreSQL no further statements are attempted after the first
+ // failure.
+ //
+ assert (e.attempted () == 1 && e.failed () == 1);
+ assert (e[0] != 0);
+#endif
+ }
+ }
+
+ // 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 ());
+ }
+
+ {
+ 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 ());
+ }
+
+ {
+ 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 optimistic concurrency.
+ //
+ {
+ using namespace test7;
+
+ std::vector<object> v (fill<object> (4));
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (v.begin (), v.end ());
+ t.commit ();
+
+ assert (v[0].v != 0 &&
+ v[1].v != 0 &&
+ v[2].v != 0 &&
+ v[3].v != 0);
+ }
+
+ // update
+ //
+ {
+ std::vector<object> c (v);
+
+ transaction t (db->begin ());
+ db->update (v.begin (), v.end ());
+ t.commit ();
+
+ assert (v[0].v > c[0].v &&
+ v[1].v > c[1].v &&
+ v[2].v > c[2].v &&
+ v[3].v > c[3].v);
+ }
+
+ {
+ object o2 (v[1]);
+ object o4 (v[3]);
+
+ o2.n++;
+ o4.n++;
+
+ transaction t (db->begin ());
+ db->update (o2);
+ db->update (o4);
+ t.commit ();
+ }
+
+ try
+ {
+ // Some updates may succeed spoiling the version for erase tests.
+ //
+ std::vector<object> c (v);
+
+ transaction t (db->begin ());
+ db->update (c.begin (), c.end ());
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ cout << e.what () << endl << endl;
+ }
+
+ // erase
+ //
+ try
+ {
+ transaction t (db->begin ());
+ db->erase (v.begin (), v.end ());
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ cout << e.what () << endl << endl;
+ }
+
+ {
+ transaction t (db->begin ());
+ db->reload (v[1]);
+ db->reload (v[3]);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (v.begin (), v.end ());
+ t.commit ();
+ }
+ }
+
+ // Test SQL Server optimistic concurrency with ROWVERSION.
+ //
+#if !defined(MULTI_DATABASE) && defined(DATABASE_MSSQL)
+ {
+ using namespace test8;
+
+ std::vector<object> v (fill<object> (4));
+
+ v[0].id = 1;
+ v[1].id = 2;
+ v[2].id = 3;
+ v[3].id = 4;
+
+
+ // persist
+ //
+ {
+ transaction t (db->begin ());
+ db->persist (v.begin (), v.end ());
+ t.commit ();
+
+ assert (v[0].v != 0 &&
+ v[1].v != 0 &&
+ v[2].v != 0 &&
+ v[3].v != 0);
+
+ //cerr << v[0].v << endl
+ // << v[1].v << endl
+ // << v[2].v << endl
+ // << v[3].v << endl;
+ }
+
+ // update
+ //
+
+ /*
+ {
+ std::vector<object> c (v);
+
+ transaction t (db->begin ());
+ db->update (v.begin (), v.end ());
+ t.commit ();
+
+ assert (v[0].v > c[0].v &&
+ v[1].v > c[1].v &&
+ v[2].v > c[2].v &&
+ v[3].v > c[3].v);
+ }
+ */
+
+ {
+ object o2 (v[1]);
+ object o4 (v[3]);
+
+ o2.n++;
+ o4.n++;
+
+ transaction t (db->begin ());
+ db->update (o2);
+ db->update (o4);
+ t.commit ();
+ }
+
+ /*
+ try
+ {
+ transaction t (db->begin ());
+ db->update (v.begin (), v.end ());
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ cout << e.what () << endl << endl;
+ }
+ */
+
+ // erase
+ //
+ try
+ {
+ transaction t (db->begin ());
+ db->erase (v.begin (), v.end ());
+ assert (false);
+ }
+ catch (const multiple_exceptions& e)
+ {
+ assert (e.attempted () == 4 && e.failed () == 4);
+ }
+
+ {
+ transaction t (db->begin ());
+ db->reload (v[1]);
+ db->reload (v[3]);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ db->erase (v.begin (), v.end ());
+ t.commit ();
+ }
+ }
+#endif
+
+#endif
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/odb-tests/common/bulk/test.hxx b/odb-tests/common/bulk/test.hxx
new file mode 100644
index 0000000..60663f7
--- /dev/null
+++ b/odb-tests/common/bulk/test.hxx
@@ -0,0 +1,213 @@
+// file : common/bulk/test.hxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#ifndef TEST_HXX
+#define TEST_HXX
+
+#include <string>
+#include <memory> // std::unique_ptr
+
+#include <odb/core.hxx>
+
+#include <libcommon/config.hxx> // DATABASE_XXX
+
+// 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::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;
+ };
+}
+
+// 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;
+ };
+
+#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;
+ };
+}
+
+// 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<3> unique_object;
+
+ #pragma db object(unique_object) bulk(3) pointer(std::unique_ptr)
+ #pragma db member(unique_object::id) id auto
+}
+
+// Test optimistic concurrency.
+//
+#pragma db namespace table("t7_")
+namespace test7
+{
+ #pragma db object optimistic bulk(3)
+ struct object
+ {
+ object (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), v (0), n (n_), s (s_) {}
+
+ #pragma db id auto
+ unsigned long long id;
+
+ #pragma db version
+ unsigned long long v;
+
+ unsigned int n;
+ std::string s;
+ };
+}
+
+// Test SQL Server optimistic concurrency with ROWVERSION.
+//
+#if !defined(MULTI_DATABASE) && defined(DATABASE_MSSQL)
+#pragma db namespace table("t8_")
+namespace test8
+{
+ #pragma db object optimistic bulk(3)
+ struct object
+ {
+ object (unsigned int n_ = 0, std::string s_ = "")
+ : id (0), v (0), n (n_), s (s_) {}
+
+ #pragma db id
+ unsigned long long id;
+
+ #pragma db version type("ROWVERSION")
+ unsigned long long v;
+
+ unsigned int n;
+ std::string s;
+ };
+}
+#endif
+
+#endif // TEST_HXX
diff --git a/odb-tests/common/bulk/testscript b/odb-tests/common/bulk/testscript
new file mode 100644
index 0000000..8dcdc85
--- /dev/null
+++ b/odb-tests/common/bulk/testscript
@@ -0,0 +1,533 @@
+# file : common/bulk/testscript
+# license : GNU GPL v2; see accompanying LICENSE file
+
+.include ../../database-options.testscript
+
++cat <<EOI >=output
+ 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
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0-2] (some) object changed concurrently
+ [3] object changed concurrently
+
+ multiple exceptions, 4 elements attempted, 4 failed:
+ [0-2] (some) object changed concurrently
+ [3] object changed concurrently
+
+ EOI
+
++cat <<EOI >=pgsql-output
+ multiple exceptions, 1 element attempted, 1 failed:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed:
+ [1] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed, fatal:
+ [1] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 1 element attempted, 1 failed, fatal:
+ [0] object already persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed, fatal:
+ [1] 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, 1 failed:
+ [1] object not persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed:
+ [0] object not persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [1] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [0] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [0] object not persistent
+ [1] object not persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [0] object not persistent
+ [1] 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, 4 failed:
+ [0] object not persistent
+ [2] object not persistent
+ [4] 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, 1 failed:
+ [2] 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, 1 failed:
+ [1] object not persistent
+
+ multiple exceptions, 2 elements attempted, 1 failed:
+ [0] object not persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [1] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [0] object not persistent
+ [2] object not persistent
+
+ multiple exceptions, 3 elements attempted, 2 failed:
+ [0] object not persistent
+ [1] object not persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [1] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [0] object not persistent
+ [2] object not persistent
+ [3] object not persistent
+
+ multiple exceptions, 4 elements attempted, 3 failed:
+ [0] object not persistent
+ [1] 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, 4 failed:
+ [1] object not persistent
+ [3] object not persistent
+ [5] object not persistent
+ [7] 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, 1 failed:
+ [2] object not persistent
+
+ multiple exceptions, 4 elements attempted, 2 failed:
+ [1] object changed concurrently
+ [3] object changed concurrently
+
+ multiple exceptions, 4 elements attempted, 2 failed:
+ [1] object changed concurrently
+ [3] object changed concurrently
+
+ EOI
+
+: mysql
+:
+if $mysql
+{
+ .include ../../mysql.testscript
+
+ $create_schema;
+ $*
+}
+
+: sqlite
+:
+if $sqlite
+{
+ .include ../../sqlite.testscript
+
+ $*
+}
+
+: pgsql
+:
+if ($pgsql && $pgsql_bulk)
+{
+ .include ../../pgsql.testscript
+
+ $create_schema;
+
+ # Query the PostgreSQL server version and only run the test if it is 7.4 or
+ # above.
+ #
+ $pgsql_client_cmd --tuples-only -c 'SELECT VERSION()' | \
+ sed -n -e 's/.*PostgreSQL (\d+\.\d+).*/\1/p' | \
+ set version [string];
+
+ if ("$version" == "")
+ exit "unable to obtain PostgreSQL server version"
+ end;
+
+ sed -n -e 's/(.+)\..+/\1/p' <"$version" | set major_version [uint64];
+ sed -n -e 's/.+\.(.+)/\1/p' <"$version" | set minor_version [uint64];
+
+ if (($major_version == 7 && minor_version >= 4) || $major_version > 7)
+ if $multi
+ $* # Noop.
+ else
+ $* >>>../pgsql-output
+ end
+ end
+}
+
+: oracle
+:
+if $oracle
+{
+ .include ../../oracle.testscript
+
+ $create_schema;
+
+ if $multi
+ $* # Noop.
+ else
+ $* >>>../output
+ end
+}
+
+: mssql
+:
+if $mssql
+{
+ .include ../../mssql.testscript
+
+ $create_schema;
+
+ if $multi
+ $* # Noop.
+ else
+ $* >>>../output
+ end
+}