// file : common/section/basics/driver.cxx // copyright : Copyright (c) 2009-2019 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file // Test object section basics. // #include <memory> // std::auto_ptr #include <cassert> #include <iostream> #include <odb/session.hxx> #include <odb/database.hxx> #include <odb/transaction.hxx> #include <common/common.hxx> #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<database> 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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> query; typedef odb::result<object> result; // Make sure we can access section members in queries. // { transaction t (db->begin ()); result r (db->query<object> (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<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object1> p1 (db->load<object1> (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<object1> p1 (db->load<object1> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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 (1, 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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (o.id)); 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); 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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (o.id)); 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); 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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (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<object> p (db->load<object> (o.id)); db->load (*p, p->s); assert (p->sn == o.sn && p->sv == o.sv); t.commit (); } } // Regression: BLOB in a value type used as a map value that is in a // section. // { using namespace test19; object o; o.m[1].b.assign (560, 'x'); // Size greater than the default image. { transaction t (db->begin ()); db->persist (o); t.commit (); } { // Hold "old" connection to force a new set of statements/image // buffers. // connection_ptr c (db->connection ()); transaction t (db->begin ()); auto_ptr<object> p (db->load<object> (o.id)); db->load (*p, p->s); assert (p->m[1].b == o.m[1].b); t.commit (); } } } catch (const odb::exception& e) { cerr << e.what () << endl; return 1; } }