From dcb6d84266a0b291781855cf623aa4d4c395f14d Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 12 Jul 2013 12:00:18 +0200 Subject: Add support for object sections Sections are an optimization mechanism that allows the partitioning of data members of a persistent class into groups that can be separately loaded and/or updated. --- common/section/basics/driver.cxx | 1689 ++++++++++++++++++++++++++++++++++++++ common/section/basics/makefile | 115 +++ common/section/basics/test.hxx | 600 ++++++++++++++ common/section/basics/test.std | 0 4 files changed, 2404 insertions(+) create mode 100644 common/section/basics/driver.cxx create mode 100644 common/section/basics/makefile create mode 100644 common/section/basics/test.hxx create mode 100644 common/section/basics/test.std (limited to 'common/section/basics') diff --git a/common/section/basics/driver.cxx b/common/section/basics/driver.cxx new file mode 100644 index 0000000..67e09ef --- /dev/null +++ b/common/section/basics/driver.cxx @@ -0,0 +1,1689 @@ +// file : common/section/basics/driver.cxx +// copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +// Test object section basics. +// + +#include // std::auto_ptr +#include +#include + +#include +#include +#include + +#include + +#include "test.hxx" +#include "test-odb.hxx" + +using namespace std; +using namespace odb::core; + +struct failed {}; + +int +main (int argc, char* argv[]) +{ + try + { + auto_ptr db (create_database (argc, argv)); + + // Test lazy-loaded, always updating section. + // + { + using namespace test1; + + object o (123, "abc"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s.loaded ()); + assert (p->n == o.n && + p->sn != o.sn && p->ss != o.ss && p->sv != o.sv); + + db->load (*p, p->s); + + assert (p->s.loaded ()); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + + t.commit (); + } + + // Update. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + t.commit (); + } + + // We can also update just the section. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + + { + transaction t (db->begin ()); + db->update (o, o.s); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n != o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + t.commit (); + } + + // Test updating unloaded section. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + o.s.unload (); + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n == o.n && + p->sn != o.sn && p->ss != o.ss && p->sv != o.sv); + t.commit (); + } + + // Test reloading of loaded/unloaded sections. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + o.s.unload (); + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + db->reload (o); + assert (p->n == o.n && + p->sn != o.sn && p->ss != o.ss && p->sv != o.sv); + db->load (o, o.s); + t.commit (); + } + + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + db->reload (o); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + t.commit (); + } + + typedef odb::query query; + typedef odb::result result; + + // Make sure we can access section members in queries. + // + { + transaction t (db->begin ()); + + result r (db->query (query::ss == o.ss)); + result::iterator i (r.begin ()); + + assert (i != r.end () && !i->s.loaded ()); + + db->load (*i, i->s); + assert (i->n == o.n && + i->sn == o.sn && i->ss == o.ss && i->sv == o.sv); + + assert (++i == r.end ()); + + t.commit (); + } + + // Make sure we can load/update sections without messing up the + // loaded object's image. + // + { + transaction t (db->begin ()); + + result r (db->query (query::ss == o.ss)); + result::iterator i (r.begin ()); + + assert (i != r.end ()); + + object o1; + i.load (o1); + db->load (o1, o1.s); + assert (o1.n == o.n && + o1.sn == o.sn && o1.ss == o.ss && o1.sv == o.sv); + + o.sn++; + o.ss += 'd'; + o.sv[0]++; + db->update (o, o.s); + + object o2; + i.load (o2); + db->load (o2, o2.s); + assert (o2.n == o1.n && + o2.sn == o.sn && o2.ss == o.ss && o2.sv == o.sv); + + assert (++i == r.end ()); + + t.commit (); + } + } + + // Test lazy-loaded, change-updated section. + // + { + using namespace test2; + + object o (123, "abc"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s.loaded ()); + assert (p->n == o.n && + p->sn != o.sn && p->ss != o.ss && p->sv != o.sv); + + db->load (*p, p->s); + + assert (p->s.loaded ()); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + + t.commit (); + } + + // Update but don't mark as changed. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n == o.n && + p->sn != o.sn && p->ss != o.ss && p->sv != o.sv); + t.commit (); + } + + // Mark as changed. + // + o.s.change (); + + { + transaction t (db->begin ()); + db->update (o); + assert (!o.s.changed ()); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + t.commit (); + } + + // We can also update just the section manually. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + o.s.change (); + + { + transaction t (db->begin ()); + db->update (o, o.s); + assert (!o.s.changed ()); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n != o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + t.commit (); + } + } + + // Test lazy-loaded, manually-updated section. + // + { + using namespace test3; + + object o (123, "abc"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s.loaded ()); + assert (p->n == o.n && + p->sn != o.sn && p->ss != o.ss && p->sv != o.sv); + + db->load (*p, p->s); + + assert (p->s.loaded ()); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + + t.commit (); + } + + // Update the object only. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n == o.n && + p->sn != o.sn && p->ss != o.ss && p->sv != o.sv); + t.commit (); + } + + // Update both the object and section. + // + o.n++; + + { + transaction t (db->begin ()); + db->update (o); + db->update (o, o.s); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + t.commit (); + } + + // We can also update just the section. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + + { + transaction t (db->begin ()); + db->update (o, o.s); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n != o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + t.commit (); + } + + // Test detection of unloaded section update. + // + try + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->update (*p, p->s); + assert (false); + } + catch (const section_not_loaded&) + { + } + } + + // Test eager-loaded, change-updated section. + // + { + using namespace test4; + + object o (123, "abc"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (p->s.loaded ()); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + + t.commit (); + } + + // Update but don't mark as changed. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + assert (p->n == o.n && + p->sn != o.sn && p->ss != o.ss && p->sv != o.sv); + t.commit (); + } + + // Mark as changed. + // + o.s.change (); + + { + transaction t (db->begin ()); + db->update (o); + assert (!o.s.changed ()); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + t.commit (); + } + } + + // Test eager-loaded, manually-updated section. + // + { + using namespace test5; + + object o (123, "abc"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (p->s.loaded ()); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + + t.commit (); + } + + // Update the object only. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + assert (p->n == o.n && + p->sn != o.sn && p->ss != o.ss && p->sv != o.sv); + t.commit (); + } + + // Update both the object and section. + // + o.n++; + + { + transaction t (db->begin ()); + db->update (o); + db->update (o, o.s); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + t.commit (); + } + + // We can also update just the section. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + + { + transaction t (db->begin ()); + db->update (o, o.s); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + assert (p->n != o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + t.commit (); + } + } + + // Test value-only and container-only section. Also multiple sections + // in an object. + // + { + using namespace test6; + + object o (123, "abc"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s1.loaded ()); + assert (o.s2.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s1.loaded ()); + assert (!p->s2.loaded ()); + assert (p->n == o.n && + p->sn != o.sn && p->ss != o.ss && p->sv != o.sv); + + db->load (*p, p->s1); + assert (p->s1.loaded ()); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv != o.sv); + + db->load (*p, p->s2); + assert (p->s2.loaded ()); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + + t.commit (); + } + + // Update. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s1); + db->load (*p, p->s2); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + t.commit (); + } + + // We can also update just the section. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.sv[0]++; + + { + transaction t (db->begin ()); + db->update (o, o.s1); + db->update (o, o.s2); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s1); + db->load (*p, p->s2); + assert (p->n != o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + t.commit (); + } + } + + // Test value-only and container-only section. Also multiple sections + // in an object. + // + { + using namespace test7; + + object o (123, "abc", true); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s1.loaded ()); + assert (o.s2.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s1.loaded ()); + assert (!p->s2.loaded ()); + assert (p->sn1 != o.sn1 && p->ss1 != o.ss1 && + p->sn2 != o.sn2 && p->ss2 != o.ss2 && p->sb2 != o.sb2); + + db->load (*p, p->s1); + db->load (*p, p->s2); + assert (p->s1.loaded ()); + assert (p->s2.loaded ()); + assert (p->sn1 == o.sn1 && p->ss1 == o.ss1 && + p->sn2 == o.sn2 && p->ss2 == o.ss2 && p->sb2 == o.sb2); + + + t.commit (); + } + + // Update. + // + o.sn1++; + o.sn2++; + o.ss1 += 'd'; + o.ss2 += 'd'; + o.sb2 = !o.sb2; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s1); + db->load (*p, p->s2); + assert (p->sn1 == o.sn1 && p->ss1 == o.ss1 && + p->sn2 == o.sn2 && p->ss2 == o.ss2 && p->sb2 == o.sb2); + t.commit (); + } + + // Manual update of just the section. + // + o.sn1++; + o.sn2++; + o.ss1 += 'd'; + o.ss2 += 'd'; + o.sb2 = !o.sb2; + + { + transaction t (db->begin ()); + db->update (o, o.s2); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s1); + db->load (*p, p->s2); + assert (p->sn1 != o.sn1 && p->ss1 != o.ss1 && + p->sn2 == o.sn2 && p->ss2 == o.ss2 && p->sb2 == o.sb2); + t.commit (); + } + } + + // Test readonly and inverse section members. + // + { + using namespace test8; + + object1 o1 (new object (123, "abc")); + object& o (*o1.p); + o.sp = &o1; + + { + transaction t (db->begin ()); + db->persist (o); + db->persist (o1); + t.commit (); + + assert (o.s.loaded ()); + } + + { + session s; + + transaction t (db->begin ()); + auto_ptr p1 (db->load (o1.id)); + object* p (p1->p); + + assert (!p->s.loaded ()); + assert (p->n == o.n && + p->sn != o.sn && p->ss != o.ss && p->sp == 0); + + db->load (*p, p->s); + + assert (p->s.loaded ()); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sp->id == o.sp->id); + + t.commit (); + } + + // Update. + // + o.n++; + o.sn++; + o.ss += 'd'; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + session s; + + transaction t (db->begin ()); + auto_ptr p1 (db->load (o1.id)); + object* p (p1->p); + + db->load (*p, p->s); + assert (p->n == o.n && + p->sn != o.sn && p->ss == o.ss && p->sp->id == o.sp->id); + + t.commit (); + } + } + + // Test object without any columns to load or update. + // + { + using namespace test9; + + object o (123, "abc"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s.loaded ()); + assert (o.id == o.id && + p->sn != o.sn && p->ss != o.ss); + + db->load (*p, p->s); + + assert (p->s.loaded ()); + assert (o.id == o.id && + p->sn == o.sn && p->ss == o.ss); + + t.commit (); + } + + // Update object. + // + o.sn++; + o.ss += 'd'; + + { + transaction t (db->begin ()); + db->update (o); // No-op. + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (o.id == o.id && + p->sn != o.sn && p->ss != o.ss); + t.commit (); + } + + // Update section. + // + { + transaction t (db->begin ()); + db->update (o, o.s); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (o.id == o.id && + p->sn == o.sn && p->ss == o.ss); + t.commit (); + } + } + + // Test section without any columns or containers to update. + // + { + using namespace test10; + + object o (123); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s.loaded ()); + assert (p->n == o.n && p->sn != o.sn); + + db->load (*p, p->s); + + assert (p->s.loaded ()); + assert (p->n == o.n && p->sn == o.sn); + + t.commit (); + } + + // Update. + // + o.n++; + o.sn++; + + { + transaction t (db->begin ()); + db->update (o); + //db->update (o, o.s); // Error. + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n == o.n && p->sn != o.sn); + t.commit (); + } + } + + // Test section with composite member. + // + { + using namespace test11; + + object o (123, "abc"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s.loaded ()); + assert (p->n == o.n && + p->sn != o.sn && p->sc.s != o.sc.s && p->sc.v != o.sc.v); + + db->load (*p, p->s); + + assert (p->s.loaded ()); + assert (p->n == o.n && + p->sn == o.sn && p->sc.s == o.sc.s && p->sc.v == o.sc.v); + + t.commit (); + } + + // Update. + // + o.n++; + o.sn++; + o.sc.s += 'd'; + o.sc.v[0]++; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n == o.n && + p->sn == o.sn && p->sc.s == o.sc.s && p->sc.v == o.sc.v); + t.commit (); + } + } + + // Test change state restoration on transaction rollback. + // + { + using namespace test12; + + object o (123, "abc"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + // Update. + // + o.n++; + o.sn++; + o.ss += 'd'; + o.s.change (); + + try + { + transaction t (db->begin ()); + db->update (o); + assert (!o.s.changed ()); + throw failed (); + } + catch (const failed&) + { + assert (o.s.changed ()); + } + + // Retry. Also test the object destruction before transaction + // termination case. + // + { + transaction t (db->begin ()); + { + object c (o); + db->update (c); + assert (!c.s.changed ()); + } + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n == o.n && p->sn == o.sn && p->ss == o.ss); + t.commit (); + } + } + + // Test section accessor/modifier. + // + { + using namespace test13; + + object o (123, "abc"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s ().loaded ()); + } + + // Load. + // + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s ().loaded ()); + assert (p->n == o.n && p->sn != o.sn && p->ss != o.ss); + + db->load (*p, p->rw_s ()); + + assert (p->s ().loaded ()); + assert (p->n == o.n && p->sn == o.sn && p->ss == o.ss); + + t.commit (); + } + + // Update. + // + o.n++; + o.sn++; + o.ss += 'd'; + + { + transaction t (db->begin ()); + db->update (o); + db->update (o, o.s ()); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->rw_s ()); + assert (p->n == o.n && p->sn == o.sn && p->ss == o.ss); + t.commit (); + } + + // Test detection of section copy. + // + try + { + transaction t (db->begin ()); + section c (o.s ()); + db->update (o, c); + assert (false); + } + catch (const section_not_in_object&) + { + } + } + + // Test LOB in section streaming, column re-ordering. + // + { + using namespace test14; + + object o (123, "\x01\x02\x03\x04\x05"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s.loaded ()); + assert (p->n == o.n && p->sn != o.sn && p->sb != o.sb); + + db->load (*p, p->s); + + assert (p->s.loaded ()); + assert (p->n == o.n && p->sn == o.sn && p->sb == o.sb); + + t.commit (); + } + + // Update. + // + o.n++; + o.sn++; + o.sb.push_back ('\x06'); + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n == o.n && p->sn == o.sn && p->sb == o.sb); + t.commit (); + } + + // We can also update just the section. + // + o.n++; + o.sn++; + o.sb.push_back ('\x07'); + + { + transaction t (db->begin ()); + db->update (o, o.s); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->n != o.n && p->sn == o.sn && p->sb == o.sb); + t.commit (); + } + } + + // Test sections and optimistic concurrency. + // + { + using namespace test15; + + object o (123, "abc"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s.loaded ()); + assert (p->n == o.n && + p->sn != o.sn && p->ss != o.ss && p->sv != o.sv); + + db->load (*p, p->s); + + assert (p->s.loaded ()); + assert (p->n == o.n && + p->sn == o.sn && p->ss == o.ss && p->sv == o.sv); + + t.commit (); + } + + // Update object. + // + object o1 (o); + o1.n++; + o1.sn++; + o1.ss += 'd'; + o1.sv[0]++; + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + db->update (o1); + assert (o.v != o1.v); + + try + { + db->load (*p, p->s); + assert (false); + } + catch (const object_changed&) + { + db->reload (*p); + assert (!p->s.loaded ()); + db->load (*p, p->s); + + assert (p->n == o1.n && + p->sn == o1.sn && p->ss == o1.ss && p->sv == o1.sv); + } + + db->reload (o); + assert (o.v == o1.v); + assert (o.n == o1.n && + o.sn == o1.sn && o.ss == o1.ss && o.sv == o1.sv); + t.commit (); + } + + // Update section. + // + o1.sn++; + o1.ss += 'd'; + o1.sv[0]++; + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + db->update (o1, o1.s); + assert (o.v != o1.v); + + try + { + db->load (*p, p->s); + assert (false); + } + catch (const object_changed&) + { + db->reload (*p); + assert (!p->s.loaded ()); + db->load (*p, p->s); + + assert (p->n == o1.n && + p->sn == o1.sn && p->ss == o1.ss && p->sv == o1.sv); + } + + db->reload (o); + assert (o.v == o1.v); + assert (o.n == o1.n && + o.sn == o1.sn && o.ss == o1.ss && o.sv == o1.sv); + t.commit (); + } + + // Update changed section. + // + o1.sn++; + o1.ss += 'd'; + o1.sv[0]++; + + { + transaction t (db->begin ()); + db->update (o1, o1.s); + + try + { + db->update (o, o.s); + assert (false); + } + catch (const object_changed&) + { + db->reload (o); + db->update (o, o.s); + } + + t.commit (); + } + } + + // Test container-only sections and optimistic concurrency. + // + { + using namespace test16; + + object o (123); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s.loaded ()); + assert (p->sv != o.sv); + + db->load (*p, p->s); + + assert (p->s.loaded ()); + assert (p->sv == o.sv); + + t.commit (); + } + + // Update object. + // + object o1 (o); + o1.sv[0]++; + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + db->update (o1); + assert (o.v != o1.v); + + try + { + db->load (*p, p->s); + assert (false); + } + catch (const object_changed&) + { + db->reload (*p); + assert (!p->s.loaded ()); + db->load (*p, p->s); + + assert (p->sv == o1.sv); + } + + db->reload (o); + assert (o.v == o1.v); + assert (o.sv == o1.sv); + t.commit (); + } + + // Update section. + // + o1.sv[0]++; + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + db->update (o1, o1.s); + assert (o.v != o1.v); + + try + { + db->load (*p, p->s); + assert (false); + } + catch (const object_changed&) + { + db->reload (*p); + assert (!p->s.loaded ()); + db->load (*p, p->s); + + assert (p->sv == o1.sv); + } + + db->reload (o); + assert (o.v == o1.v); + assert (o.sv == o1.sv); + t.commit (); + } + + // Update changed section. + // + o1.sv[0]++; + + { + transaction t (db->begin ()); + db->update (o1, o1.s); + + try + { + db->update (o, o.s); + assert (false); + } + catch (const object_changed&) + { + db->reload (o); + db->update (o, o.s); + } + + t.commit (); + } + } + + // Test reuse-inheritance, sections, and optimistic concurrency. + // + { + using namespace test17; + + object o (123); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s1.loaded ()); + assert (o.s2.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s1.loaded ()); + assert (!p->s2.loaded ()); + assert (p->s1n != o.s1n && p->s2v != o.s2v); + + db->load (*p, p->s1); + db->load (*p, p->s2); + + assert (p->s1.loaded ()); + assert (p->s2.loaded ()); + assert (p->s1n == o.s1n && p->s2v == o.s2v); + + t.commit (); + } + + object o1 (o); + + // Update object. + // + for (unsigned short s (1); s < 3; ++s) + { + o1.s1n++; + o1.s2v[0]++; + + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + db->update (o1); + assert (o.v != o1.v); + + try + { + switch (s) + { + case 1: db->load (*p, p->s1); break; + case 2: db->load (*p, p->s2); break; + default: break; + } + assert (false); + } + catch (const object_changed&) + { + db->reload (*p); + + assert (!p->s1.loaded ()); + assert (!p->s2.loaded ()); + + db->load (*p, p->s1); + db->load (*p, p->s2); + + assert (p->s1n == o1.s1n && p->s2v == o1.s2v); + } + + db->reload (o); + assert (o.v == o1.v); + assert (o.s1n == o1.s1n && o.s2v == o1.s2v); + t.commit (); + } + + // Update section. + // + for (unsigned short s (1); s < 3; ++s) + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + switch (s) + { + case 1: + o1.s1n++; + db->update (o1, o1.s1); + assert (o.v != o1.v); + break; + case 2: + o1.s2v[0]++; + db->update (o1, o1.s2); + assert (o.v != o1.v); + break; + default: break; + } + + try + { + switch (s) + { + case 1: db->load (*p, p->s1); break; + case 2: db->load (*p, p->s2); break; + default: break; + } + assert (false); + } + catch (const object_changed&) + { + db->reload (*p); + + assert (!p->s1.loaded ()); + assert (!p->s2.loaded ()); + + db->load (*p, p->s1); + db->load (*p, p->s2); + + assert (p->s1n == o1.s1n && p->s2v == o1.s2v); + } + + db->reload (o); + assert (o.v == o1.v); + assert (o.s1n == o1.s1n && o.s2v == o1.s2v); + t.commit (); + } + + // Update changed section. + // + for (unsigned short s (1); s < 3; ++s) + { + transaction t (db->begin ()); + + switch (s) + { + case 1: + o1.s1n++; + db->update (o1, o1.s1); + break; + case 2: + o1.s2v[0]++; + db->update (o1, o1.s2); + break; + default: break; + } + + try + { + switch (s) + { + case 1: db->update (o, o.s1); break; + case 2: db->update (o, o.s2); break; + default: break; + } + assert (false); + } + catch (const object_changed&) + { + db->reload (o); + + switch (s) + { + case 1: db->update (o, o.s1); break; + case 2: db->update (o, o.s2); break; + default: break; + } + } + + db->reload (o1); + + t.commit (); + } + } + + // Test change-updated section and change-tracking container. + // + { + using namespace test18; + + object o (123); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + assert (o.s.loaded ()); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + + assert (!p->s.loaded ()); + assert (p->sn != o.sn && p->sv != o.sv); + + db->load (*p, p->s); + + assert (p->s.loaded ()); + assert (p->sn == o.sn && p->sv == o.sv); + + t.commit (); + } + + // Update but don't mark as changed. + // + o.sn++; + o.sv.modify (0)++; + + { + transaction t (db->begin ()); + db->update (o); // Automatically marked as changed. + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->sn == o.sn && p->sv == o.sv); + t.commit (); + } + + // Test updating just the section manually. + // + o.sn++; + o.sv.modify (0)++; + + { + transaction t (db->begin ()); + db->update (o, o.s); + t.commit (); + } + + { + transaction t (db->begin ()); + auto_ptr p (db->load (o.id)); + db->load (*p, p->s); + assert (p->sn == o.sn && p->sv == o.sv); + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/common/section/basics/makefile b/common/section/basics/makefile new file mode 100644 index 0000000..f0484e8 --- /dev/null +++ b/common/section/basics/makefile @@ -0,0 +1,115 @@ +# file : common/section/basics/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 t_section_b_ +$(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)) +$(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)) + +# 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/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/section/basics/test.hxx b/common/section/basics/test.hxx new file mode 100644 index 0000000..c129f3e --- /dev/null +++ b/common/section/basics/test.hxx @@ -0,0 +1,600 @@ +// file : common/section/basics/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 +#include + +#include +#include +#include + +#ifdef ODB_COMPILER +# if defined(ODB_DATABASE_PGSQL) +# define BLOB_TYPE "BYTEA" +# elif defined(ODB_DATABASE_MSSQL) +# define BLOB_TYPE "VARBINARY(max)" +# else +# define BLOB_TYPE "BLOB" +# endif +#endif + +// Test lazy-loaded, always-updated section. +// +#pragma db namespace table("t1_") +namespace test1 +{ + #pragma db object + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx") + : sn (n_), n (n_), ss (s_) {sv.push_back (n_);} + + #pragma db id auto + unsigned long id; + + #pragma db load(lazy) + odb::section s; + + #pragma db section(s) + int sn; + + int n; + + #pragma db section(s) + std::string ss; + + #pragma db section(s) + std::vector sv; + }; +} + +// Test lazy-loaded, change-updated section. +// +#pragma db namespace table("t2_") +namespace test2 +{ + #pragma db object + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx") + : sn (n_), n (n_), ss (s_) {sv.push_back (n_);} + + #pragma db id auto + unsigned long id; + + #pragma db load(lazy) update(change) + odb::section s; + + #pragma db section(s) + int sn; + + int n; + + #pragma db section(s) + std::string ss; + + #pragma db section(s) + std::vector sv; + }; +} + +// Test lazy-loaded, manually-updated section. +// +#pragma db namespace table("t3_") +namespace test3 +{ + #pragma db object + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx") + : sn (n_), n (n_), ss (s_) {sv.push_back (n_);} + + #pragma db id auto + unsigned long id; + + #pragma db load(lazy) update(manual) + odb::section s; + + #pragma db section(s) + int sn; + + int n; + + #pragma db section(s) + std::string ss; + + #pragma db section(s) + std::vector sv; + }; +} + +// Test eager-loaded, change-updated section. +// +#pragma db namespace table("t4_") +namespace test4 +{ + #pragma db object + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx") + : sn (n_), n (n_), ss (s_) {sv.push_back (n_);} + + #pragma db id auto + unsigned long id; + + #pragma db section(s) + int sn; + + #pragma db update(change) + odb::section s; + + int n; + + #pragma db section(s) + std::string ss; + + #pragma db section(s) + std::vector sv; + }; +} + +// Test eager-loaded, manually-updated section. +// +#pragma db namespace table("t5_") +namespace test5 +{ + #pragma db object + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx") + : sn (n_), n (n_), ss (s_) {sv.push_back (n_);} + + #pragma db id auto + unsigned long id; + + #pragma db section(s) + int sn; + + #pragma db update(manual) + odb::section s; + + int n; + + #pragma db section(s) + std::string ss; + + #pragma db section(s) + std::vector sv; + }; +} + +// Test value-only and container-only section. Also multiple sections +// in an object. +// +#pragma db namespace table("t6_") +namespace test6 +{ + #pragma db object + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx") + : n (n_), sn (n_), ss (s_) {sv.push_back (n_);} + + #pragma db id auto + unsigned long id; + + int n; + + #pragma db load(lazy) + odb::section s1; + + #pragma db load(lazy) + odb::section s2; + + #pragma db section(s1) + int sn; + + #pragma db section(s1) + std::string ss; + + #pragma db section(s2) + std::vector sv; + }; +} + +// Test sections and reuse inheritance. +// +#pragma db namespace table("t7_") +namespace test7 +{ + #pragma db object abstract + struct base + { + #pragma db id auto + unsigned long id; + + #pragma db load(lazy) + odb::section s1; // Empty section. + + #pragma db load(lazy) + odb::section s2; + + #pragma db section(s2) + int sn2; + }; + + #pragma db object abstract + struct interm: base + { + // Section s1 is still empty. + + #pragma db section(s2) + std::string ss2; + }; + + #pragma db object + struct derived: interm + { + #pragma db section(s1) + int sn1; + }; + + #pragma db object + struct object: derived + { + object (int n = 999, const std::string& s = "xxx", bool b = false) + { + sn1 = sn2 = n; + ss1 = ss2 = s; + sb2 = b; + } + + #pragma db section(s1) + std::string ss1; + + #pragma db section(s2) + bool sb2; + }; +} + +// Test readonly and inverse section members. +// +#pragma db namespace table("t8_") +namespace test8 +{ + struct object1; + + #pragma db object session + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx", object1* p_ = 0) + : n (n_), sn (n_), ss (s_), sp (p_) {} + + #pragma db id auto + unsigned long id; + + int n; + + #pragma db load(lazy) + odb::section s; + + #pragma db section(s) readonly + int sn; + + #pragma db section(s) + std::string ss; + + #pragma db inverse(p) section(s) + object1* sp; + }; + + #pragma db object session + struct object1 + { + object1 (object* p_ = 0): p (p_) {} + ~object1 () {delete p;} + + #pragma db id auto + unsigned long id; + + object* p; + }; +} + +// Test object without any columns to load or update. +// +#pragma db namespace table("t9_") +namespace test9 +{ + #pragma db object + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx"): sn (n_), ss (s_) {} + + #pragma db id auto + unsigned long id; + + #pragma db load(lazy) update(manual) + odb::section s; + + #pragma db section(s) + int sn; + + #pragma db section(s) + std::string ss; + }; +} + +// Test section without any columns or containers to update. +// +#pragma db namespace table("t10_") +namespace test10 +{ + #pragma db object + struct object + { + object (int n_ = 999): n (n_), sn (n_) {} + + #pragma db id auto + unsigned long id; + + int n; + + #pragma db load(lazy) + odb::section s; + + #pragma db section(s) readonly + int sn; + }; +} + +// Test section with composite member. +// +#pragma db namespace table("t11_") +namespace test11 +{ + #pragma db value + struct comp + { + comp (int n_, const std::string& s_): s (s_) {v.push_back (n_);} + + std::string s; + std::vector v; + }; + + #pragma db object + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx") + : n (n_), sn (n_), sc (n_, s_) {} + + #pragma db id auto + unsigned long id; + + int n; + + #pragma db load(lazy) + odb::section s; + + #pragma db section(s) + int sn; + + #pragma db section(s) + comp sc; + }; +} + +// Test change state restoration on transaction rollback. +// +#pragma db namespace table("t12_") +namespace test12 +{ + #pragma db object + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx") + : sn (n_), n (n_), ss (s_) {} + + #pragma db id auto + unsigned long id; + + #pragma db load(lazy) update(change) + odb::section s; + + #pragma db section(s) + int sn; + + int n; + + #pragma db section(s) + std::string ss; + }; +} + +// Test section accessor/modifier. +// +#pragma db namespace table("t13_") +namespace test13 +{ + #pragma db object + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx") + : n (n_), sn (n_), ss (s_) {} + + #pragma db id auto + unsigned long id; + + int n; + + #pragma db section(s_) + int sn; + + #pragma db section(s_) + std::string ss; + + public: + const odb::section& + s () const {return s_;} + + odb::section& + rw_s () {return s_;} + + private: + #pragma db load(lazy) update(manual) + odb::section s_; + }; +} + +// Test LOB in section streaming, column re-ordering. +// +#pragma db namespace table("t14_") +namespace test14 +{ + #pragma db object + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx") + : sb (s_.begin (), s_.end ()), sn (n_), n (n_) {} + + #pragma db id auto + unsigned long id; + + #pragma db load(lazy) + odb::section s; + + #pragma db section(s) type(BLOB_TYPE) + std::vector sb; // Comes before sn. + + #pragma db section(s) + int sn; + + int n; + }; +} + +// Test sections and optimistic concurrency. +// +#pragma db namespace table("t15_") +namespace test15 +{ + #pragma db object optimistic + struct object + { + object (int n_ = 999, const std::string& s_ = "xxx") + : sn (n_), n (n_), ss (s_) {sv.push_back (n_);} + + #pragma db id auto + unsigned long id; + + #pragma db version mssql:type("ROWVERSION") + unsigned long long v; + + #pragma db load(lazy) + odb::section s; + + #pragma db section(s) + int sn; + + int n; + + #pragma db section(s) + std::string ss; + + #pragma db section(s) + std::vector sv; + }; +} + +// Test container-only sections and optimistic concurrency. +// +#pragma db namespace table("t16_") +namespace test16 +{ + #pragma db object optimistic + struct object + { + object (int n = 999) {sv.push_back (n);} + + #pragma db id auto + unsigned long id; + + #pragma db version // mssql:type("ROWVERSION") + unsigned long long v; + + #pragma db load(lazy) + odb::section s; + + #pragma db section(s) + std::vector sv; + }; +} + +// Test reuse-inheritance, sections, and optimistic concurrency. +// +#pragma db namespace table("t17_") +namespace test17 +{ + #pragma db object optimistic sectionable abstract + struct root + { + #pragma db id auto + unsigned long id; + + #pragma db version + unsigned long long v; + }; + + #pragma db object + struct base: root + { + }; + + #pragma db object + struct object: base + { + object (int n = 999): s1n (n) {s2v.push_back (n);} + + #pragma db load(lazy) + odb::section s1; + + #pragma db section(s1) + int s1n; + + #pragma db load(lazy) + odb::section s2; + + #pragma db section(s2) + std::vector s2v; + }; +} + +// Test change-updated section and change-tracking container. +// +#pragma db namespace table("t18_") +namespace test18 +{ + #pragma db object + struct object + { + object (int n = 999): sn (n) {sv.push_back (n);} + + #pragma db id auto + unsigned long id; + + #pragma db load(lazy) update(change) + odb::section s; + + #pragma db section(s) + int sn; + + #pragma db section(s) + odb::vector sv; + }; +} + +#endif // TEST_HXX diff --git a/common/section/basics/test.std b/common/section/basics/test.std new file mode 100644 index 0000000..e69de29 -- cgit v1.1